Aircraft is a Kotlin Android vertical-scrolling shooter built on a custom SurfaceView + Canvas game loop. The current app combines a first-launch privacy gate, a two-screen onboarding flow, 10 time-based combat stages, 9 interleaved puzzle gates (one after each non-final combat stage), boss fights, collectible power-ups, QR code and flashlight utilities, local save/resume support, localized About screens, and debug-only developer tools. The canonical repository is https://github.com/tobecrazy/Aircraft.
The demo above walks through the end-to-end player experience on a real device:
- First-launch flow — cinematic privacy gate over the animated
StarFieldView, followed by the two-page Compose onboarding carousel that introduces controls and power-ups. - Launch hub — jet selection, continue/new-game dialog when a saved run exists, and entry points to History, Settings, and the QR/Flashlight utilities.
- Combat gameplay — 30 FPS
SurfaceViewrendering with drag-to-move controls, auto-firing bullets, scrolling backgrounds, the two-row tactical HUD (mission/hull cards + countdown timer), and screen-shake/damage-flash feedback. - Power-ups in action — red envelopes detonating into AoE rockets, medical kits restoring HP, shields granting blink-indicated invincibility, and time freezes locking enemies in place.
- Boss fight — end-of-level boss with bomb attacks, scaling HP, and the multi-phase particle explosion on defeat.
- Puzzle gate — Compose-based sliding puzzle that gates progression between combat levels (3×3 / 4×4 / 5×5 by difficulty).
- Utility screens — QR code scan/generate, flashlight with SOS and brightness control, device info telemetry, and the localized About / History screens.
For the full UML class diagram, see class_diagram.svg. For detailed developer documentation, see DOCUMENT.md. For release history, see ChangeLogs.md.
| Package | Color | Key Classes | Responsibility |
|---|---|---|---|
common/ |
Green | AircraftApplication, GameStateManager |
App lifecycle, game-state broadcasting via SharedFlow |
data/ |
Orange | PlayerAircraft, EnemyState, BossState, RedEnvelopeState, RocketState, MedicalKitState, ShieldState, TimeFreezeState, PlayerGameData, PlayerGameDataDao, AppDatabase, SettingsRepository, GameState, GameMode, GameDifficulty, AircraftConstants, ImageDetails, ImageDetailsSource, BannerDetails, BannerDetailsSource |
Data models, Room persistence, SharedPreferences repository, game state enums, HUD constants, image details contracts |
ui/ (Game Engine) |
Blue | DrawBaseObject, Aircraft, DrawBackground, DrawHeader, Enemies, BossEnemy, RedEnvelopes, MedicalKits, Shields, TimeFreezes, ExplosionEffect, GameCoreView, GameHudFormatter |
30 FPS rendering, collision detection, level progression, HUD formatting |
viewmodel/ |
Teal | GameViewModel, SettingsViewModel, LaunchViewModel, HistoryViewModel, OnboardingViewModel, PrivacyPolicyViewModel, DevelopSettingsViewModel, AboutAircraftViewModel, AboutMeViewModel, DeviceInfoViewModel, QRCodeToolViewModel, FlashlightViewModel, RichTextEditorViewModel, ShowImageDetailsViewModel, BannerDetailsViewModel |
MVVM mediation between Views and Repositories/DAOs |
gui/ (Presentation) |
Purple | PrivacyPolicyAcceptActivity, OnboardingActivity, LaunchActivity, MainActivity, PuzzleActivity, HistoryActivity, HistoryFragment, HistoryAdapter, SettingsActivity, QRCodeToolActivity, FlashlightActivity, ShowImageDetailsActivity, BannerDetailsActivity, StarFieldView |
Activity screens, navigation, ViewBinding + Compose UI |
service/ |
Pink | MusicService, MusicBinder, FlashlightService |
BGM/SFX bound service + camera-torch foreground service with wakelock-backed SOS |
providers/ |
Gray | DatabaseProvider |
Singleton DB provider |
utils/ |
Light green | ScreenUtils, BitmapUtils, FilePickerHelper, HallOfHeroesNameUtils |
Screen metrics, bitmap utilities, file URI/cache helpers, name formatting |
GameCoreViewcomposes all game-engine objects (Aircraft,Enemies,DrawBackground,DrawHeader,BossEnemy, etc.) and orchestrates the 30 FPS loop- All drawable game objects extend the abstract
DrawBaseObject(providesonDraw,updateGame,getEnemyBounds) - UI layer classes hold references to their corresponding data state classes (e.g.,
Enemies→EnemyState,BossEnemy→BossState) - Activities delegate to ViewModels for data access:
MainActivity→GameViewModel,SettingsActivity→SettingsViewModel,LaunchActivity→LaunchViewModel, etc. - ViewModels mediate access to
SettingsRepositoryandPlayerGameDataDao— Activities never access repositories or DAOs directly MainActivitybindsMusicServiceand collectsGameStateManager.gameStateflowDatabaseProvidersingleton createsAppDatabase, which exposesPlayerGameDataDaooperating onPlayerGameDataentitiesSettingsRepositorymaps difficulty strings toGameDifficultyenum values withfireRateMultiplier
- Custom 30 FPS
SurfaceViewengine with no third-party game framework - Green tactical in-game shell for
MainActivitywith a mission-briefing card, pause overlay, and themed end-of-run dialogs - First-launch privacy acceptance flow with cinematic
StarFieldView - Compose-powered two-page onboarding carousel with animated entrance effects
- 10 combat levels with boss fights, scaling kill targets, and randomized scrolling backgrounds
- Puzzle-gate flow: after clearing combat levels 1-9, players must clear the same-numbered puzzle level before entering the next combat level (Easy = 3×3, Normal = 4×4, Hard = 5×5)
- Compose-based
PuzzleActivityshell with the standard tactical 52dp header (back button saves progress) and status-bar inset handling - Four power-up systems: red envelopes/rockets, medical kits, shields, and time freezes
- Difficulty presets that adjust fire rate: Easy (
1.2x), Normal (1.0x), Hard (0.8x) - Room persistence for leaderboard data and saved progress, including jet selection and difficulty
- Leaderboard top record highlighting with a medal/star badge and gold first-place styling
- History screen with Chinese ink-painting background (
launch_background.jpeg) - Compose-powered About Me and Onboarding screens with localized copy and smooth transition animations
- Coil-based network image loading with crossfade animations (
AsyncImagefor Compose,ImageView.load()for Views) - Image details viewer (
ShowImageDetailsActivity) supporting both local drawables and network URLs with download capability FileProviderpaths includePictures/,Download/, and app cache, enabling shared file URIs for exported/generated assets- Rich-text preview image tap support: clicking an image in
RichTextEditorActivityopensShowImageDetailsActivity - Utility screens for history, QR code scanning/generation/save-to-device, flashlight/SOS/brightness control, image details, device info, about-aircraft, about-me, privacy policy, and debug-only developer settings
- Firebase Analytics and Crashlytics integration
- English and Chinese localization
- Progression: 10 combat levels with timers decreasing from 300s to 120s, plus 9 puzzle levels gated between combat levels
- Puzzle difficulty: piece count is fixed per difficulty preset — Easy
3×3(9 pieces), Normal4×4(16 pieces), Hard5×5(25 pieces) - Boss fights: every level ends with a boss that scales from 1,000 HP to 1,900 HP
- Controls: drag the plane to move; bullets auto-fire during play
- Power-ups:
- Red envelopes take 3 hits, then launch rockets with AoE damage
- Medical kits restore the player to full HP
- Shields grant temporary invincibility with a blink indicator
- Time freezes can freeze enemies or the player for 5 seconds depending on who collects them
- Progress persistence: saves now persist mode-aware progress (
AIR_BATTLEorPUZZLE) and resume into the matching mode with puzzle score included in total score - Debug flow: debug builds expose Developer Settings, test-crash tooling, hidden invincible-mode toggle, an Android Dev Assistant tools hub (
AndroidDevAssistantToolsActivity), and a QR Tool notification navigation test
- 12-way per-frame collision system covering enemies, bullets, bosses, rockets, and pickups
- In-game briefing panel shows live launch context including sector, difficulty profile, and selected airframe
- Particle-based explosion effects with flash, fireball, debris, and smoke phases
- Screen shake, red damage flash, and low-health vignette effects
- Background music via
MediaPlayerand combat SFX viaSoundPool - Jet selection with 4 playable plane sprites and saved
jet_plane_index - QR code utility with live camera scan, gallery image import, rich-text encoding input, framed preview output, and long-press save to device
- Flashlight utility backed by a camera-type foreground service (
FlashlightService): Camera2 torch on/off, SOS blink mode withPARTIAL_WAKE_LOCKfor accurate pacing when the screen is off, Android 13+ brightness levels, persistent notification with a "Turn off" action, and a one-shot battery-optimization whitelist prompt that fires after the first successful torch-on - Device information screen with CPU, memory, disk, battery, and network telemetry
- Robolectric coverage for onboarding, privacy gate, QR tool flows, About Me Compose UI wiring, leaderboard styling, string parity, and gameplay formulas
app/src/main/java/com/young/aircraft/
├── common/
│ ├── AircraftApplication.kt # Application entry point; emits LOW_MEMORY events
│ └── GameStateManager.kt # SharedFlow game-state broadcaster + debug invincible flag
├── data/
│ ├── AppDatabase.kt # Room database (v2031) + migrations
│ ├── PlayerGameData.kt # Saved run entity
│ ├── PlayerGameDataDao.kt # Leaderboard/save DAO
│ ├── PlayerAircraft.kt # Player HP and damage model
│ ├── EnemyState.kt # Enemy position and bullet state
│ ├── BossState.kt # Boss HP, bombs, and sprite state
│ ├── RedEnvelopeState.kt # Red envelope pickup state
│ ├── RocketState.kt # Rocket projectile state
│ ├── MedicalKitState.kt # Medical kit pickup state
│ ├── ShieldState.kt # Shield pickup state
│ ├── TimeFreezeState.kt # Time-freeze pickup state
│ ├── GameMode.kt # AIR_BATTLE / PUZZLE mode enum for save/resume routing
│ ├── GameDifficulty.kt # EASY/NORMAL/HARD enum with fireRateMultiplier
│ ├── AircraftConstants.kt # HUD labels/colors, intent extras, URLs, privacy asset paths
│ ├── SettingsRepository.kt # SharedPreferences-backed privacy/difficulty/puzzle-guide/install-id store
│ ├── GameState.kt # PLAYING / PAUSED / GAME_OVER / LEVEL_COMPLETE / GAME_WON / LOW_MEMORY
│ ├── ImageDetails.kt # Image details contract (local resource or network URL)
│ └── BannerDetails.kt # Legacy banner details contract kept in source
├── gui/
│ ├── PrivacyPolicyAcceptActivity.kt # Launcher privacy gate
│ ├── OnboardingActivity.kt # Compose-based onboarding carousel with HorizontalPager
│ ├── LaunchActivity.kt # Main menu, jet selection, continue-game dialog
│ ├── MainActivity.kt # Game host, tactical overlay shell, pause flow, dialogs, and DB save flow
│ ├── PuzzleActivity.kt # Compose puzzle gate levels and progress saves
│ ├── HistoryActivity.kt # History screen container
│ ├── HistoryFragment.kt # Leaderboard fragment
│ ├── HistoryAdapter.kt # RecyclerView adapter for saved runs
│ ├── SettingsActivity.kt # Difficulty, sound, and navigation hub
│ ├── QRCodeToolActivity.kt # QR scan/generate utility with camera preview, gallery import, save-to-device, and rich-text encoding
│ ├── FlashlightActivity.kt # Compose flashlight utility with torch, SOS, and brightness controls
│ ├── RichTextEditorActivity.kt # DEBUG rich-text editor with WebView preview; preview image taps open ShowImageDetailsActivity
│ ├── ShowImageDetailsActivity.kt # Image details viewer (local drawable or network URL) with download capability
│ ├── BannerDetailsActivity.kt # Legacy banner details viewer retained in source
│ ├── DevelopSettingsActivity.kt # Debug-only crash/invincibility tools, Android Dev Assistant entry, and QR Tool notification test
│ ├── AndroidDevAssistantToolsActivity.kt # Debug-only Android Developer Assistant tool hub (module toggles + actions)
│ ├── SupperBannerConfig.kt # Banner carousel timing bounds
│ ├── SupperBannerItem.kt # Banner carousel data model
│ ├── SupperBannerView.kt # Banner carousel custom view
│ ├── DeviceInfoActivity.kt # Live system monitor
│ ├── AboutAircraftActivity.kt # Project overview, GitHub link, and clickable project image viewer
│ ├── AboutMeActivity.kt # Compose-based developer profile and project details screen
│ ├── PrivacyPolicyActivity.kt # Standalone privacy policy viewer
│ └── StarFieldView.kt # Animated cinematic background
├── providers/
│ └── DatabaseProvider.kt # Singleton Room provider
├── service/
│ ├── MusicService.kt # Bound BGM + SFX playback service
│ └── FlashlightService.kt # Foreground service (foregroundServiceType=camera) owning the torch + SOS coroutine + PARTIAL_WAKE_LOCK
├── ui/
│ ├── GameCoreView.kt # Main game loop and collision orchestration
│ ├── DrawBaseObject.kt # Base drawable/update contract
│ ├── DrawBackground.kt # Mirrored seamless background renderer
│ ├── DrawHeader.kt # Two-row HUD: mission/hull cards top, timer below
│ ├── Aircraft.kt # Player sprite and bullet system
│ ├── Enemies.kt # Enemy spawning, movement, and bullets
│ ├── BossEnemy.kt # Boss AI, bombs, and scaling HP
│ ├── RedEnvelopes.kt # Rocket power-up and explosion handling
│ ├── MedicalKits.kt # HP pickup spawning and lifetime rules
│ ├── Shields.kt # Shield pickup spawning and lifetime rules
│ ├── TimeFreezes.kt # Freeze pickup spawning and 5s freeze logic
│ ├── ExplosionEffect.kt # Particle explosion effect
│ └── GameHudFormatter.kt # HUD data formatting (time, health %, score)
├── utils/
│ ├── BitmapUtils.kt # Bitmap loading, scaling, mirroring, rotation
│ ├── FilePickerHelper.kt # FileProvider URI and cache helpers for QR image export/import
│ ├── HallOfHeroesNameUtils.kt # Hero-name formatting and anonymous fallback logic
│ └── ScreenUtils.kt # Screen metrics and dp/sp conversions
└── viewmodel/
├── GameViewModel.kt # Save/load game, sound prefs, player ID (MainActivity)
├── SettingsViewModel.kt # Difficulty + sound toggles StateFlow (SettingsActivity)
├── SettingsUiState.kt # UI state data class for settings screen
├── LaunchViewModel.kt # Saved-game check and delete (LaunchActivity)
├── HistoryViewModel.kt # Leaderboard data loading and deletion (HistoryFragment)
├── HistoryUiState.kt # UI state data class for history screen
├── OnboardingViewModel.kt # Onboarding completion gate (OnboardingActivity)
├── PrivacyPolicyViewModel.kt # Privacy acceptance gate (PrivacyPolicyAcceptActivity)
├── DevelopSettingsViewModel.kt # Invincible mode toggle (DevelopSettingsActivity)
├── AboutAircraftViewModel.kt # Project info StateFlow (AboutAircraftActivity)
├── AboutAircraftUiState.kt # UI state for about-aircraft screen
├── AboutMeViewModel.kt # Developer profile data (AboutMeActivity)
├── DeviceInfoViewModel.kt # CPU/memory/disk/network telemetry (DeviceInfoActivity)
├── DeviceInfoUiState.kt # UI state for device info screen
├── QRCodeToolViewModel.kt # QR encode/decode logic (QRCodeToolActivity)
├── QRCodeToolUiState.kt # UI state for QR tool screen
├── FlashlightViewModel.kt # Drives FlashlightService via intents; observes torch state via TorchCallback and SOS state via FlashlightService.isSosRunning
├── RichTextEditorViewModel.kt # Edit/preview mode state (RichTextEditorActivity)
├── ShowImageDetailsViewModel.kt # Image details display logic (ShowImageDetailsActivity)
└── BannerDetailsViewModel.kt # Legacy banner details display logic
app/src/test includes:
- data-model tests for gameplay and persistence state classes
GameCoreViewFormulaTestfor level duration and kill-target mathHistoryAdapterTestfor first-place badge visibility and gold score stylingQRCodeToolActivityTestfor scan/generate screen state, bottom-sheet result dialog, save-to-device flow, gallery pick button, and Settings navigationFlashlightViewModelTestfor SOS timing pattern and brightness-strength mappingAboutMeActivityTestfor localized About Me copy, repo URL rendering, and back navigationMainActivityTestfor tactical overlay behavior, mission-briefing chips, and low-memory pause handlingDrawBackgroundTestfor seamless mirrored tile coverageOnboardingActivityTestandPrivacyPolicyAcceptActivityTestfor first-run flow behaviorLaunchActivityTestfor saved-game detection, continue/new-game dialog, and jet selectionDevelopSettingsViewModelTest,PrivacyPolicyViewModelTest,OnboardingViewModelTest,LaunchViewModelTestfor ViewModel unit coveragePlayerGameDataTestfor timestamp-aware data-class behaviorStarFieldViewTestfor the animated onboarding/privacy backgroundStringResourceTestfor locale parity and resource usage coverage
Instrumented tests belong in app/src/androidTest.
| Category | Count | Details |
|---|---|---|
| Enemy sprites | 15 | enemy_1.png to enemy_15.png |
| Boss sprites | 7 | boss_1.png to boss_7.png |
| Missile sprites | 3 | missile_1.png to missile_3.png |
| Jet planes | 4 | jet_plane_1.png to jet_plane_4.png |
| Red envelopes | 2 | red_box_1.png, red_box_2.png |
| Medical kits | 2 | red_heart_1.png, red_heart_2.png |
| Shields | 3 | shield_1.png, shield_2.png, shield_3.png |
| Time freezes | 3 | timer_1.png, timer_2.png, timer_3.png |
| Rocket | 1 | rocket.png |
| Backgrounds | 5 | background.jpg, background_1.jpg to background_4.jpg |
| Audio | 6 | 2 BGM tracks + fire/hit/enemy-hit/game-over SFX |
| Localization | 2 | English (values/) + Chinese (values-zh/) |
| Level | Time Limit | Required Kills | Enemies/Row | Boss HP |
|---|---|---|---|---|
| 1 | 300s | 100 | 6 | 1,000 |
| 2 | 280s | 110 | 7 | 1,100 |
| 3 | 260s | 120 | 8 | 1,200 |
| 4 | 240s | 130 | 9 | 1,300 |
| 5 | 220s | 140 | 10 | 1,400 |
| 6 | 200s | 150 | 11 | 1,500 |
| 7 | 180s | 160 | 12 | 1,600 |
| 8 | 160s | 170 | 13 | 1,700 |
| 9 | 140s | 180 | 14 | 1,800 |
| 10 | 120s | 190 | 15 | 1,900 |
- Version:
1.2.8 - Android Studio: Meerkat (
2024.3.1) or later - Compile SDK:
37 - Min SDK:
30 - Target SDK:
36 - Java:
17 - Gradle Wrapper:
9.4.1 - Android Gradle Plugin:
9.1.1
./gradlew assembleDebug # Build debug APK
./gradlew assembleRelease # Build release APK
./gradlew test # Run unit and Robolectric tests
./gradlew connectedAndroidTest # Run instrumented tests (device/emulator required)
./gradlew lint # Run Android lint
./gradlew clean # Clean build outputs- Clone the repository:
git clone https://github.com/tobecrazy/Aircraft.git cd Aircraft - Open the project in Android Studio.
- Sync Gradle and run on a device or emulator with Android 11+.
This project is open source and available for educational purposes.
