Skip to content

Android app

The Android app lives in android/. It is a native app written in Kotlin with Jetpack Compose and Material 3, and it talks to the same HTTP API as the web pages. Nothing on the server is specific to it.

How it is built

Part What it uses
Screens Jetpack Compose, Material 3, Navigation Compose; Plus Jakarta Sans from Google Fonts (downloaded by the phone)
Network OkHttp with a cookie jar that keeps vistilla_session across launches; kotlinx.serialization for JSON
Player Media3 ExoPlayer, with Compose controls of its own (ui/watch/VistillaPlayer.kt) copying the web player's look, through OkHttp, so the session cookie reaches /media/videos/{id} and the redirect to the bucket is followed
Images Coil, on the same OkHttp client
Camera CameraX VideoCapture (Full HD, MP4)
Screen MediaProjection + MediaRecorder (H.264 + AAC) in a foreground service of type mediaProjection
Uploads Uploads (reserve with live: true, 16 MiB parts, poster from MediaMetadataRetriever, complete), kept alive by a dataSync foreground service
File Role
data/Api.kt Every server call, the cookie jar, errors as ApiException(status, message)
data/Models.kt The JSON shapes, as @Serializable classes
data/Uploads.kt The upload queue and its progress (StateFlow<List<Upload>>)
service/UploadService.kt The foreground service and its progress notification
service/ScreenRecordService.kt Screen capture (MediaProjection + MediaRecorder); with the camera, a CameraX bubble in an overlay window (SYSTEM_ALERT_WINDOW, foreground type camera) that the capture records with the screen
ui/MainActivity.kt Navigation, the signed-in state (AppModel), links and shared videos
ui/library, ui/watch, ui/record, ui/settings, ui/notifications, ui/auth The screens
ui/theme, ui/components Colours, type, the glowing background, cards, the progress ring, avatars
res/drawable/logo.xml, ic_launcher_*.xml, ic_splash.xml The logo as vectors, redrawn from web/assets/logo.svg: whole on the sign-in screen and the splash screen, and split into the light tile (background) and the screen with its bar (foreground, inside the 66 dp safe circle) for the launcher icon, with a one-colour version for themed icons

Building

cd android
./gradlew :app:assembleRelease
  • It needs JDK 17 or newer and the Android SDK (platform 36), with sdk.dir in android/local.properties.
  • The release is signed with android/keystore/vistilla-release.jks, described by android/keystore/keystore.properties. Both are kept out of git; a new version must be signed with the same key to install over the old one. Without them the build falls back to the debug key.
  • The APK the site offers is web/assets/app/vistilla.apk: copy app/build/outputs/apk/release/app-release.apk there and raise versionCode in app/build.gradle.kts for each new one.
  • web/assets/app/version.json (versionCode, versionName, notes) says which version that is. The app (data/Updates.kt) reads it when the library opens; a higher versionCode than its own shows the Update card, which downloads the APK into the cache and opens the installer through a FileProvider (REQUEST_INSTALL_PACKAGES).
  • The website's Get the Android app button (web/assets/apk.js, under the sign-in card and at the foot of the library's rail) reads the same file: it shows versionName and the APK's size (bytes, which scripts/worker-site.sh adds from the file itself) and links vistilla.apk?v=<versionCode>, so it always gives the newest APK with no change of its own when a version ships.
  • Shrinking (R8) and release lint are off: they need more memory than the build machine has.

Languages

The app is written in English and shown in English or Arabic. Every screen imports ui/i18n/Text.kt's Text instead of Material's, which passes its words through I18n.tr: the same list as the website (web/assets/i18n/ar.json, packed as an asset by sourceSets.main.assets), whole strings by exact match and text built from parts by its patterns. In Arabic, VistillaTheme turns the layout right to left (LocalLayoutDirection) and sets the type in Cairo (a Google Font fetched by the phone). The choice is kept on the phone (LanguageSwitch, in Settings and on the sign-in screen); until made, the phone's language decides. Styled text (AnnotatedString: comments, transcripts) is people's own words and is never translated.