[package] name = "sleek" # cargo-apk derives Android's versionCode directly from this field — # (1 << 24) | (major << 16) | (minor << 8) | patch (ndk-build's # VersionCode::to_code, apk_id hardcoded to 1) — and panics if # [package.metadata.android] tries to set version_code explicitly. 0.1.0 # encoded to versionCode 16777472, which Play Console had already seen # ("Version code 16777472 has already been used"), so every subsequent # upload with the same crate version collided. Bump this on every release # that needs a new versionCode. version = "0.1.1" edition = "2021" description = "Sleek — mobile freeq client with Vidya (egui)" license = "MIT" publish = false # No [workspace] section (used to have `members = ["."]` — a self- # referencing single-member workspace, once needed by an older cargo-apk/ # cargo_toml that errored on an empty [workspace]). The *current* # cargo-apk (0.10.0, crates.io's latest and unchanged since 2023 — its # dep cargo-subcommand 0.12.0) has the opposite problem: it re-parses # each declared member's own manifest and rejects any member that itself # contains a `[workspace]` table — which a self-referencing member=["."] # always does, since it's the same file. Confirmed hitting "Did not # expect a `[workspace]`" with the old section present. A plain manifest # with no [workspace] at all sidesteps that whole codepath (cargo- # subcommand's map_nonvirtual_package, not its buggy members() scan) and # is otherwise identical for a real cargo build (no ambient parent # workspace exists to accidentally sweep this crate into either). # Line tables on workspace crates; no debuginfo on deps (biggest link/disk win). # Full DWARF: CARGO_PROFILE_DEV_DEBUG=2 cargo build … [profile.dev] debug = "line-tables-only" [profile.dev.package."*"] debug = false [lib] name = "sleek" path = "src/lib.rs" # lib-only: cargo-apk panics if this package also has [[bin]] targets crate-type = ["cdylib", "rlib"] [features] default = [] # Muted H.264-in-MP4 chat previews need vidya's optional `video` feature. # Radicle Garden tip of vidya does not ship that feature yet (GitHub tip does). # When Garden catches up, uncomment and add to default: # video-previews = ["vidya/video"] # default = ["video-previews"] [dependencies] vidya = { path = "../patched-crates/vidya" } # Guest IRC + freeq events. Prefer aws-lc-rs to match moq-native / iroh # (aws-lc-sys is already required). Note: `ring` still builds via quinn / # rustls-webpki defaults — this only avoids enabling rustls' ring *provider* # from freeq-sdk on top of aws-lc. freeq-sdk = { path = "../patched-crates/freeq/freeq-sdk", default-features = false, features = [ "aws-lc-rs", "rustls-tls", "websocket", ] } tokio = { version = "1", features = [ "rt-multi-thread", "macros", "sync", "time", "net", "io-util", ] } chrono = { version = "0.4", default-features = false, features = ["clock", "std"] } anyhow = "1" log = "0.4" serde = { version = "1", features = ["derive"] } serde_json = "1" reqwest = { version = "0.12", default-features = false, features = [ "json", "rustls-tls", "multipart", ] } open = "5" urlencoding = "2" url = "2" base64 = "0.22" dirs = "5" # Local message index for durable unread counts (independent of the server). rusqlite = { version = "0.38", features = ["bundled"] } # Decode (file pick) + PNG encode (compose bar uploads). image = { version = "0.25", default-features = false, features = [ "png", "jpeg", "gif", "webp", "bmp", ] } # Full Unicode emoji set with names + shortcodes for the reaction picker. emojis = "0.6" # Shared glow + fonts only — do NOT enable wayland/x11 here (feature unification # would pull them into the Android build and can make the activity exit immediately). eframe = { version = "0.31", default-features = false, features = [ "default_fonts", "glow", ] } # freeq AV MoQ media plane (mic + remote A/V; desktop also local camera). # Same revs as freeq-av / freeq-av-client. Shared by desktop + Android so # mobile no longer stubs BrowserOnly — cpal aaudio handles mic/speaker. iroh-live = { git = "https://github.com/n0-computer/iroh-live", rev = "edd9bcc530a615f3ab3a6919e96790a077905376", default-features = false, features = [ "opus", "h264", ] } moq-native = { git = "https://github.com/Frando/moq", branch = "deps/iroh-main", default-features = false, features = [ "noq", "websocket", "aws-lc-rs", ] } moq-lite = { git = "https://github.com/Frando/moq", branch = "deps/iroh-main" } rustls = { version = "0.23", default-features = false, features = ["aws-lc-rs"] } # BoxFuture for AudioStreamFactory impls (freeq-av-style PCM tap in tests). n0-future = "0.3" [target.'cfg(not(target_os = "android"))'.dependencies] eframe = { version = "0.31", default-features = false, features = [ "default_fonts", "glow", "wayland", "x11", ] } env_logger = "0.11" # Image paste from OS clipboard (Ctrl/Cmd+V in the compose bar). arboard = { version = "3", default-features = false, features = [ "image-data", "wayland-data-control", ] } # Native OS file dialog for compose-bar image attach (xdg-desktop-portal / GTK / Win / macOS). # Run on a background thread only — blocking the egui loop freezes the app. rfd = { version = "0.15", default-features = false, features = [ "xdg-portal", "tokio", ] } # Local camera publish uses V4L2 / platform capture (not available under cargo-apk). iroh-live = { git = "https://github.com/n0-computer/iroh-live", rev = "edd9bcc530a615f3ab3a6919e96790a077905376", default-features = false, features = [ "opus", "h264", "capture-camera", ] } # Enable cpal's native PipeWire host (feature-unified into moq-media's cpal). # Without this we only see ALSA PCMs ("pipewire", "sysdefault") and cannot pick # the real OS sources the browser uses (e.g. "EMEET SmartCam C960 Mono"). cpal = { path = "../third-party/cpal", features = ["pipewire"] } # V4L2 diagnostics + fallback capture path (probe example, loopback workaround). v4l2r = "0.0.7" libc = "0.2" [target.'cfg(target_os = "android")'.dependencies] eframe = { version = "0.31", default-features = false, features = [ "default_fonts", "glow", "android-native-activity", ] } egui-winit = { path = "../third-party/egui-winit", default-features = false, features = [ "android-native-activity", ] } android_logger = "0.14" winit = { version = "0.30", default-features = false, features = ["android-native-activity"] } # Same major as android-activity — runtime permission prompts for image attach. jni = "0.22" # rusty-codecs format.rs references `ndk` under cfg(android) — enable the # iroh-live `android` feature so `dep:ndk` is linked (MediaCodec optional path). iroh-live = { git = "https://github.com/n0-computer/iroh-live", rev = "edd9bcc530a615f3ab3a6919e96790a077905376", default-features = false, features = [ "opus", "h264", "android", ] } [package.metadata.android] package = "uk.nandi.sleek" apk_name = "sleek" # aarch64 = physical phones; x86_64 = Waydroid / emulators build_targets = ["aarch64-linux-android", "x86_64-linux-android"] # mipmap/ic_launcher.png densities — without this, Waydroid gets a blank stub icon. resources = "res" # Without INTERNET, DNS fails as "no address associated with hostname" # and WebSocket/TLS IRC cannot connect on device. [[package.metadata.android.uses_permission]] name = "android.permission.INTERNET" [[package.metadata.android.uses_permission]] name = "android.permission.ACCESS_NETWORK_STATE" # Compose-bar image attach (in-app browser over shared storage). [[package.metadata.android.uses_permission]] name = "android.permission.READ_MEDIA_IMAGES" [[package.metadata.android.uses_permission]] name = "android.permission.READ_EXTERNAL_STORAGE" max_sdk_version = 32 # Voice calls — native MoQ over cpal aaudio (mic + speaker). [[package.metadata.android.uses_permission]] name = "android.permission.RECORD_AUDIO" [[package.metadata.android.uses_permission]] name = "android.permission.MODIFY_AUDIO_SETTINGS" # AV video publish — Camera2 ImageReader → H.264 via iroh-live. [[package.metadata.android.uses_permission]] name = "android.permission.CAMERA" [[package.metadata.android.uses_feature]] name = "android.hardware.camera" required = false [[package.metadata.android.uses_feature]] name = "android.hardware.camera.any" required = false [package.metadata.android.sdk] min_sdk_version = 28 target_sdk_version = 35 [package.metadata.android.application] label = "Sleek" # Required so the system loads APK classes.dex (SleekActivity). cargo-apk # defaults hasCode=false for pure-native apps. has_code = true orientations = ["Portrait", "Landscape"] # No action bar; SleekActivity still draws edge-to-edge under transparent # system bars (not Theme.*.Fullscreen, which would hide the status bar). theme = "@android:style/Theme.DeviceDefault.NoActionBar" icon = "@mipmap/ic_launcher" # Subclass of NativeActivity (classes.dex injected post cargo-apk) so # onNewIntent can capture freeq:// OAuth callbacks while the app is warm. # freeq-android uses the same scheme + singleTask pattern. [package.metadata.android.application.activity] name = "uk.nandi.sleek.SleekActivity" # Reuse the existing task when the browser opens freeq://auth — do not spawn # a second NativeActivity (which would re-enter android_main mid-session). launch_mode = "singleTask" exported = true # OAuth / notification deep links: freeq://auth?… and freeq://chat/… [[package.metadata.android.application.activity.intent_filter]] actions = ["android.intent.action.VIEW"] categories = [ "android.intent.category.DEFAULT", "android.intent.category.BROWSABLE", ] [[package.metadata.android.application.activity.intent_filter.data]] scheme = "freeq" # Local patch: smithay→arboard text fallback + emit Ctrl+V Key when clipboard has no text # (so compose-bar image paste can run). Upstream still swallows V on image-only paste. # iroh-live needs unreleased iroh 0.97 (same pins as freeq workspace). [patch.crates-io] egui-winit = { path = "../third-party/egui-winit" } # GLES mediump → highp for font-atlas UVs (emilk/egui#6893). Drop on egui ≥ 0.32. egui_glow = { path = "../third-party/egui_glow" } # See host/Cargo.toml's patch comment — same reason, kept in sync. khronos_api = { path = "../third-party/khronos_api" } # Pin rev (not floating main) — same as freeq Cargo.lock; main tip is 1.1.0 and breaks iroh 0.97. noq = { git = "https://github.com/n0-computer/noq", rev = "ab042ea7e3f7bae1e43eaba2ac9dba47ddb52e69" } noq-proto = { git = "https://github.com/n0-computer/noq", rev = "ab042ea7e3f7bae1e43eaba2ac9dba47ddb52e69" } noq-udp = { git = "https://github.com/n0-computer/noq", rev = "ab042ea7e3f7bae1e43eaba2ac9dba47ddb52e69" } iroh = { git = "https://github.com/n0-computer/iroh", rev = "8af8370b" } iroh-base = { git = "https://github.com/n0-computer/iroh", rev = "8af8370b" } iroh-relay = { git = "https://github.com/n0-computer/iroh", rev = "8af8370b" } iroh-gossip = { git = "https://github.com/n0-computer/iroh-gossip", rev = "1038f721" } web-transport-iroh = { git = "https://github.com/Frando/web-transport", rev = "f7a523f1" } web-transport-noq = { git = "https://github.com/Frando/web-transport", rev = "f7a523f1" } web-transport-proto = { git = "https://github.com/Frando/web-transport", rev = "f7a523f1" } web-transport-trait = { git = "https://github.com/Frando/web-transport", rev = "f7a523f1" } # moq-media depends on cpal git `branch = "master"` (floats). Pin via path so # Cargo.lock cannot pick a master tip that dropped StreamError / Device::name(). # Same rev freeq locks: 63ea9fffbd3b15af0180f0800a61a3c4fec9b88a [patch."https://github.com/RustAudio/cpal.git"] cpal = { path = "../third-party/cpal" }