A native, high-performance Android jigsaw puzzle game built with Jetpack Compose where the background is a continuously animating GIF instead of a static image.
- Language: Kotlin 1.9.22
- UI Toolkit: Jetpack Compose (using Material 3)
- Image/GIF Library: Coil 2.5.0 (for hardware-accelerated animated GIF decoding and frame sharing)
- SDK Compatibility: Target SDK 34, Min SDK 24
- Build System: Gradle 8.2 (Kotlin/Groovy DSL)
Animating a jigsaw puzzle requires splitting a moving image (GIF) into individual pieces. If each piece decodes the GIF separately, 16 separate decoders (for a 4x4 grid) would run, leading to significant CPU usage, battery drain, and Out of Memory (OOM) crashes on Android.
- Single Decoder: The parent container (
JigsawBoard) decodes the animated GIF only once using a single CoilrememberAsyncImagePainterinstance. - Sync Draw: Each individual
JigsawPieceutilizes Compose's nativeCanvasto draw from this single shared painter. - Clipping & Translation:
- In each piece canvas, the clip path matches the irregular jigsaw piece borders (generated dynamically inside
JigsawPathHelper). - The canvas is translated:
translate(-col * cellWidth, -row * cellHeight). - The entire background GIF painter is drawn:
painter.draw(totalPuzzleSize).
- In each piece canvas, the clip path matches the irregular jigsaw piece borders (generated dynamically inside
- Result: Seamless playback synchrony across all pieces, hardware-accelerated rendering, and minimal memory overhead.
c:\deps\quiz\
├── settings.gradle # Gradle multi-project layout configuration
├── build.gradle # Root-level build configuration
├── local.properties # Path to Android SDK location
├── gradle.properties # JVM args and AndroidX flags
├── gradlew / gradlew.bat # Gradle wrapper scripts
├── app/
│ ├── build.gradle # Module build configuration, dependencies
│ └── src/main/
│ ├── AndroidManifest.xml # Entry activity and permission requests
│ ├── res/
│ │ ├── raw/ # Pre-packaged animated GIFs (earth.gif, cradle.gif)
│ │ └── mipmap-*/ # App launcher icon assets
│ └── java/com/example/gifjigsaw/
│ ├── MainActivity.kt # Navigation UI, theme setup, victory overlay
│ ├── game/
│ │ └── JigsawPathHelper.kt # Jigsaw path contour shape cubic curves generator
│ └── ui/
│ ├── JigsawViewModel.kt # ViewModel managing game status, drag-drops, difficulty levels
│ └── components/
│ ├── JigsawBoard.kt # Main board canvas, drag physics, snaps
│ └── GifLibraryScreen.kt # Previews library select UI with live moving cards
To run the project on a clean computer, ensure you have JDK 17 installed and that your environment variables point to your Android SDK folders.
Open a terminal in the project root directory:
Windows PowerShell:
$env:JAVA_HOME="C:\Path\To\JDK-17"
$env:ANDROID_HOME="C:\Path\To\Android\Sdk"
.\gradlew.bat assembleDebugLinux / macOS:
export JAVA_HOME="/path/to/jdk-17"
export ANDROID_HOME="/path/to/android/sdk"
./gradlew assembleDebugThe compiled debug APK will be located at app/build/outputs/apk/debug/app-debug.apk.
To run structural layout and puzzle coordinate snapping logic local unit tests:
./gradlew testDifficulty configurations are managed in JigsawViewModel.kt's Difficulty enum:
- EASY: 3x3 grid, rotation disabled.
- MEDIUM: 4x4 grid, rotation disabled.
- HARD: 5x5 grid, rotation allowed (rotates 90 degrees clockwise upon tapping a piece).
- EXPERT: 6x6 grid, rotation allowed.
- MASTER: 8x8 grid, rotation allowed.