Skip to main content

Mobile

Mobile support (Android + iOS) is isolated in source/mobile/ and source/android/, so desktop code paths stay untouched.

First run — mobile.CopyState​

Lime ships assets inside the APK, but mods, saves and editor output need a writable location. On launch Main checks mobile.CopyState.checkExistingFiles(); if assets have not been copied yet, CopyState becomes the initial state and copies them into app storage with a progress screen. Files listed in CopyState-Ignore.txt are skipped.

After that, Sys.setCwd(mobile.StorageUtil.getStorageDirectory()) makes every relative path — including mods/ — resolve inside storage.

Storage & permissions​

mobile.StorageUtil:

  • requestPermissions() — called from Main before anything else on Android,
  • getStorageDirectory() — the writable root,
  • helpers for external/internal paths and for creating directories.

android.platform.AndroidStorage wraps the Java side (scoped storage, MANAGE_ALL_FILES).

Known issue

Android may still ask for image/sound access even with "manage all files" granted (low severity), and a song with valid files can show the "no JSON/voices/instrumental" dialog — accepting it works fine.

Touch controls​

ModuleRole
mobile.MobileControlschooses and owns the active control scheme
mobile.flixel.FlxVirtualPaddirectional + action pad for menus
mobile.flixel.FlxHitboxthe four-lane gameplay hitbox
mobile.flixel.FlxButtontouch-friendly button base
mobile.MobileControlsSelectSubStatelayout picker
mobile.options.MobileOptionsSubStatemobile-only settings (listed in the options menu on android/ios only)

Opacity and extra toggles come from ClientPrefs.mobileCAlpha and mobileCEx. Menus add a virtual pad; PlayState adds the hitbox. Both route into the same backend.Controls actions as keyboard input, so gameplay code never checks for touch.

File picking and app files​

mobile.files.MobileFilePicker bridges to the platform picker, used by the editors for loading and saving charts and characters on device.

On Android, PhoenixDocumentsProvider adds two roots to the system Files picker when Lime's existing SHARE_FILES feature is enabled. Lime derives this from the SHARE_MOBILE_FILES Haxe define, which project.hxp sets for mobile builds, so normal Android builds retain the previous system-visible sharing behavior.

  • Crash Logs — read-only access to crash reports written in crash/.
  • Mods — browse and manage the runtime mods/ directory; the provider supports creating folders/files, reading and writing files, renaming files, and deleting files or subfolders.

Both roots use the same app-specific external-files directory as the game's runtime working directory. Only these two folders are exposed. Android startup creates mods/, so the Mods root is a real, writable folder from the first launch; crash/ appears once CrashHandler writes a report. Browsing a root never creates its directory. If getExternalFilesDir(null) returns no path, or Android reports its volume as unavailable, no roots are returned. Mounted read-only storage remains browsable but cannot be written; if storage later becomes unavailable, document operations fail. Static traversal and symlink escapes are rejected, but Java path-based checks cannot make the check/use pair atomic against an independent concurrent filesystem writer.

The Android platform layer​

Full detail lives in docs/ANDROID_PLATFORM.md and Platform layer. In short:

gameplay → android.platform.* → AndroidBridge (JNI) → quack.fnf.phoenix.android.*
ServiceWhat it provides
AndroidPlatformboots everything from Main
AndroidLifecyclepause/resume/focus events
AndroidMedia + PhoenixMedia/PhoenixMediaServiceMedia3 MediaSession metadata, lock-screen and Bluetooth controls, MediaSessionService-backed background visibility
AndroidDisplaycutouts, insets, refresh rate, immersive mode
AndroidStoragepermissions and scoped storage
AndroidHardwareInput, AndroidGamepad, AndroidHapticskeys, controllers, vibration
AndroidIntents, AndroidNotificationintents (including phoenix:// deep links) and notifications
AndroidDiscord, AndroidSystemRPC and device info

Off Android, every one of these modules compiles to an empty body, so calling them from shared code is safe.

Building for mobile​

lime build android # runs setup/android-copy-stl.sh via project.hxp
lime build ios -nosign # as CI does

The Android build must include libc++_shared.so; see Troubleshooting. CI (.github/workflows/mobile.yml) builds both platforms and verifies the APK's native libraries; mobile-release.yml publishes them.

Window and orientation​

project.hxp sets mobile windows to 0 × 0 (native resolution), LANDSCAPE, not fullscreen, resizable on Android and fixed on iOS.