Vibe Coded by George Keller
A 2x2 Android home-screen widget that answers your yes/no questions. Tap the ball and it spins over, a fortune floats up through the liquid window, holds for a few seconds, then rolls back to the resting "8". Every tap gives a fresh, uniformly random answer.
- No third-party libraries. The widget uses only the Android framework and the Kotlin standard library. No AndroidX, no Material, no support libraries, so the APK stays tiny.
- No bundled art. The glossy ball, the liquid window, and the floating fortune triangle are
drawn at runtime on a
Canvaswith per-pixel shading. There are no images or GIFs in the app. - One quiet launcher icon. The app itself has no screen to browse. Its launcher icon simply offers to pin the widget to your home screen, then closes.
- Offline and private. No network, no accounts, no analytics, no ads.
A home-screen widget does not own its pixels. The launcher draws them from a RemoteViews
description, so there is no place to run a normal animation loop. The reveal is therefore a short
series of bitmaps pushed to the launcher, one pose at a time:
| Concern | Approach |
|---|---|
| The motion | An 11-pose sequence: spin to reveal the window, float the fortune up, hold, roll back |
| Rendering | Each frame is drawn on a Canvas, then pushed with setImageViewBitmap |
| IPC size | Frames render at 384 px, which stays within the RemoteViews transaction limit |
| Reliability | The sequence runs inside goAsync() so the process survives the four-second hold |
| Repeat taps | A per-widget guard ignores taps that land while a reveal is playing |
| Feedback | A short haptic tick fires the moment you tap |
Requires the Android SDK (API 35) and JDK 17.
git clone https://github.com/georgeckeller/magic-8-ball.git
cd magic-8-ball
./gradlew assembleDebug
adb install -r app/build/outputs/apk/debug/app-debug.apkThen add the widget one of two ways:
- Tap the Magic 8-Ball app icon and accept the prompt to pin it, or
- Long-press the home screen, choose Widgets, find Magic 8-Ball, and drop it on the grid.
Every push to main also builds the debug APK in GitHub Actions (see .github/workflows/build.yml),
and the APK is attached to the run as an artifact.
app/src/main/
├── java/com/vibe/magic8ball/
│ ├── Magic8BallWidgetProvider.kt Taps, the reveal sequence, and the RemoteViews updates
│ ├── Magic8BallRenderer.kt Draws the glossy ball, liquid window, and fortune on a Canvas
│ ├── PinWidgetActivity.kt Launcher shim that offers to pin the widget
│ └── EightBallMessages.kt The 20 answers and the random draw
├── res/
│ ├── layout/widget_magic8ball.xml A single ImageView
│ ├── xml/magic8ball_widget_info.xml Widget metadata (2x2)
│ ├── drawable/ic_launcher.xml Vector 8-ball icon and widget preview
│ └── values/strings.xml
└── AndroidManifest.xml One activity (the pin shim) and one widget receiver
- minSdk 26 (Android 8.0)
- targetSdk / compileSdk 35
Released under the MIT License. See LICENSE.
Vibe Coded by George Keller
