# raster-dial
**Repository Path**: kdjdhe/raster-dial
## Basic Information
- **Project Name**: raster-dial
- **Description**: Raster Dial - Android 光栅动态壁纸:倾斜手机在 2-3 张图之间沿软渐变边界切换,支持多组自定义、三种过渡方式与应用内更新。
- **Primary Language**: Unknown
- **License**: MIT
- **Default Branch**: main
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 1
- **Forks**: 0
- **Created**: 2026-11-03
- **Last Updated**: 2026-11-15
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
**English** | [简体中文](README.md)


# Raster Dial
A lenticular live wallpaper for Android 9+: tilt the phone and the image transitions between 2–3 layers along a single soft gradient boundary. 5 built-in demo groups, multiple named custom groups, tablet/landscape support, and debouncing.
## Features
- **Gradient line (default)**: one ~18% screen-width soft gradient band between A and B, not a stripe or a hard cut.
- **2D direction**: left/right, up/down, and diagonal tilts all work; the boundary follows the tilt direction.
- **Cycling**: `phase` is unbounded; 2–3 images cycle as `1→2→3→1`.
- **Debouncing**: One Euro Filter + soft dead zone + direction dead zone + a 3 cycles/s phase limit; the half-range angle is multiplied by 1.2 on tablets/landscape.
- **Three transition styles**: gradient line / Venetian blinds (百叶窗: six strips with synchronized boundaries, strips rotate with the axis) / OriginOS-like (类OriginOS: one wide soft gradient sweeping across; instantaneous tilt-to-scene mapping with fixed 100ms committed switches, no intermediate states).
- **Configurable axis**: gradient line 全向 / 仅横向 / 仅竖向; blinds and OriginOS-like 横向 / 竖向.
- **Eye-care strength**: 0–30% persistent luminance attenuation (leftmost = off) that reduces switch flashing; haptic tick per step.
- **5 built-in demo groups + custom**: Yixuan, Pyrois, Miyabi, Vivian, Ye Shunguang.
- **Multiple custom groups**: create, name/rename, delete; 1–6 images per group with pick, rotate 90°, long-press drag reorder, in-editor full-screen preview, and delete.
- **Tablets/foldables**: the runtime surface aspect ratio selects portrait/landscape assets; aspect-fill cropping; display-rotation compensation.
- **Wallpaper only**: no lock-screen takeover, no flashing, no boot service.
- **In-app updates**: Chengdu primary → Gitee secondary → GitHub backup; SHA-256 verification; system installer.
- **6 themes**: Obsidian / Glacier / Moss / Violet / Crimson / Gold; one-tap switch, remembered.
- **Customizable UI**: long-press drag the four main cards to reorder; card corner-radius slider (0–36dp); swipe the segmented controls to switch style/axis; 百叶窗 / 类OriginOS axis gains 自动 (auto).
- **System font scaling**: labels stay fully visible from 0.85x to 2.0x; cards, segmented controls and dialogs grow with the text, decorative glyphs keep a fixed dp size.
- **Long-press drag reordering**: lift a card in the picker, 1:1 tracking, spring make-way, per-slot haptic.
- **Search / export & import**: search groups in the picker; bundle a custom group to share, import others'.
### Effect Details
- 2–3 images per group: Pyrois has 2; the other 4 have 3; 5 groups / 14 images in total.
- Images are ranked by colorfulness (1 = most colorful; 2/3 decrease). Scores are in `app/src/main/assets/groups/groups.json` under `scores`.
- Portrait assets are the source images rotated 90° clockwise; landscape assets use the original orientation.
- Half-range angle (tilt needed for one transition): the 0–100% sensitivity slider maps to 34° → 8.6°, default 55% ≈ 20°. A full A→B→A cycle is 2 × the half-range.
- Pose uses the axis-angle `(x, y)` of the relative quaternion, not Euler angles; face-down or near-horizontal poses do not strobe from roll/pitch flips.
- Debounce: soft dead zone 0.015 rad (~0.86°), direction dead zone 0.02 rad (~1.15°), phase limit 3 cycles/s.
- 3-image groups: 0→1 first→second, 1→2 second→third, 2→3 third→first; 2-image groups: 0→1 first→second, 1→2 second→first.
## Table of Contents
- [Features](#features)
- [Quick Start](#quick-start)
- [Project Structure](#project-structure)
- [Configuration and Usage](#configuration-and-usage)
- [Common Commands](#common-commands)
- [Testing](#testing)
- [Custom Groups](#custom-groups)
- [Assets and License](#assets-and-license)
- [Tested Lock-Screen Support](#tested-lock-screen-support)
- [Known Limitations and Roadmap](#known-limitations-and-roadmap)
- [License](#license)
## Quick Start
### Prerequisites
- JDK 17 or newer (`sourceCompatibility = VERSION_17` in `app/build.gradle.kts`; AGP 9.2.1)
- Android SDK Platform 36 (`compileSdk = 36`), with `ANDROID_HOME` pointing at the SDK root
- An Android 9+ (API 28) device with USB debugging enabled
- _(Optional)_ Python 3.10+, Pillow, and NumPy — only for regenerating the built-in assets
### Build and Install
```bash
git clone https://github.com/kdjdhe/raster-dial.git
cd raster-dial
# Command-line builds need local.properties; Android Studio creates it automatically.
echo "sdk.dir=$ANDROID_HOME" > local.properties
./gradlew assembleDebug
adb install -r app/build/outputs/apk/debug/app-debug.apk
```
Open Raster Dial → choose a group → **Set as live wallpaper** → confirm in the system live-wallpaper preview.
Or download the [latest release APK](https://github.com/kdjdhe/raster-dial/releases/latest) (debug-signed; sideload only).
## Project Structure
| Path | Purpose |
|---|---|
| `app/src/main/java/com/raster/dial/` | Java sources: wallpaper service, GL preview, sensors/debouncing, group picker, custom groups |
| `app/src/main/AndroidManifest.xml` | App, preview, wallpaper service, and debug receiver declarations |
| `app/src/main/assets/groups/` | Portrait assets 1080×2400 (5 groups; source images rotated 90°) |
| `app/src/main/assets/groups_land/` | Landscape assets 1920×1080 (source images unrotated) |
| `app/src/main/assets/groups/groups.json` | Group catalog, names, colorfulness scores |
| `app/src/main/res/` | Icons, strings, wallpaper metadata |
| `tools/build_groups.py` | Builds portrait assets and `groups.json` |
| `tools/build_landscape.py` | Builds landscape assets |
| `tools/publish_update.py` | Builds `update.json` from an APK |
| `tools/sync_chengdu.sh` | Uploads `update.json` + APK to the Chengdu server |
| `tools/sync_gitee.sh` | Pushes `main` and uploads the APK to a Gitee Release (domestic mirror) |
| `update.json` | App update manifest (versionCode, SHA-256, Chengdu/Gitee/GitHub mirrors) |
| `docs/UPDATE_SOURCE.md` | Chengdu + Gitee/GitHub update source deployment and release guide |
| `docs/SIGNING.md` | debug/release signing, keystore, and update migration |
| `keystore.properties.example` | Release signing example (the real file is gitignored) |
| `docs/COMPATIBILITY.md` | Compatibility analysis for other Android devices and tablets |
| `docs/screenshots/home.webp` `docs/screenshots/landscape.webp` | README screenshots (home / landscape) |
## Configuration and Usage
| Control | Purpose |
|---|---|
| Group | 5 built-in demo groups + multiple custom groups; picker cards; "Chinese name · English" or the custom name |
| Sensitivity | 0–100% continuous slider (live label), half-range 34° → 8.6°, default 55% ≈ 20° |
| Wallpaper debug line | Bottom-left: group, portrait/landscape, rotation, `phase`, segment, segment progress, direction, frame time |
| Invert sweep | Swap the side the gradient sweeps from |
| Select group | Opens the picker; tap a card; custom cards can be edited / named / deleted |
| Manage custom groups | Create groups; 1–6 images each; pick / rotate / drag-reorder / delete / full-screen preview |
| Set as live wallpaper | Opens the system live-wallpaper picker |
| Clear wallpaper | Restores the system default static wallpaper |
| Check for updates | Reads manifests in Chengdu → Gitee → GitHub order; downloads and verifies SHA-256 |
Debug broadcast (the `-n` component is required; implicit broadcasts are not delivered on this EMUI build):
```bash
# Switch group
adb shell am broadcast -n com.raster.dial/.WallDebugReceiver \
-a com.raster.dial.WALL_DEBUG --es group Miyabi
# Pin phase/direction (45° diagonal at 50%)
adb shell am broadcast -n com.raster.dial/.WallDebugReceiver \
-a com.raster.dial.WALL_DEBUG --ef progress 0.5 --ef dirX 0.707 --ef dirY 0.707
# Clear pinned values and return to the sensors
adb shell am broadcast -n com.raster.dial/.WallDebugReceiver \
-a com.raster.dial.WALL_DEBUG --ez clear true
```
> The debug broadcast writes `wall_progress` into SharedPreferences; the wallpaper stays at the pinned value until it is cleared. The in-app "Clear debug override" action is equivalent.
### Tablet and Landscape
The code does not hard-code resolutions. It checks the surface aspect ratio and the current display rotation at runtime:
1. `onSurfaceChanged(width, height)` sets `landscape = width > height`;
2. portrait loads `app/src/main/assets/groups/`; landscape loads `app/src/main/assets/groups_land/`;
3. drawing uses aspect-fill center cropping, never stretching;
4. sensor direction is rotated by `Display.getRotation()` into the current screen coordinates;
5. the half-range angle is multiplied by 1.2 on tablets/landscape.
The same code covers 20:9 phones, 19.5:9, 21:9, 16:10 tablets, and folded/unfolded foldables without a per-resolution asset set. See [docs/COMPATIBILITY.md](docs/COMPATIBILITY.md).

## Common Commands
```bash
./gradlew assembleDebug # build the debug APK
./gradlew test # unit-test task (no test sources currently)
./gradlew lint # Android Lint
./gradlew assembleRelease # needs keystore.properties; outputs app-release.apk
python tools/build_groups.py # rebuild portrait assets + groups.json
python tools/build_landscape.py # rebuild landscape assets
python tools/publish_update.py --chengdu-base https://updates.example.com/raster-dial
CHENGDU_HOST=user@your-server CHENGDU_KEY=~/.ssh/your-deploy-key tools/sync_chengdu.sh work/raster-dial-0.1.8.apk
```
Asset generation reads the repository's `art` directory by default; you can also point `RASTER_SRC` at your source-art directory:
```bash
RASTER_SRC=/path/to/art python tools/build_groups.py
RASTER_SRC=/path/to/art python tools/build_landscape.py
```
The `sources` field in `groups.json` records the original file for each slot (1/2/3).
## Testing
No test files are present in the repository; `./gradlew test` runs but skips. Build checks:
```bash
./gradlew assembleDebug
./gradlew test
gradle test # equivalent when Gradle 9.7.1 is installed
./gradlew lint
```
Manual acceptance on a device:
1. Install and set the wallpaper.
2. Tilt left/right, up/down, and diagonally; confirm there is only one gradient boundary.
3. Keep tilting; confirm the `1→2→3→1` cycle.
4. Hold still for 30 seconds; confirm there is no visible jitter.
5. Rotate to landscape or force a tablet size; confirm landscape assets and direction.
6. Create multiple custom groups; name/rename, pick, rotate, reorder, delete, and switch.
7. Switch groups; confirm the wallpaper updates within about 0.5 seconds.
## Custom Groups
- Multiple custom groups, each with a user name; create, rename, delete;
- Picker cards with thumbnails, a current marker, and built-in/custom badges; tap to switch; custom cards have "Edit";
- Editor: name field + save; **全屏预览(传感器)** previews the group being edited without switching the current group; 1–6 images per group (a new group may start empty; at least 1 to "Use this group");
- Each slot: pick (system file picker), rotate 90°, long-press drag reorder (槽位 N renumbers live with position), delete;
- The old single custom group migrates to a group named 自定义; a fresh install seeds one "自定义" group with 2 Pyrois images;
- Files live in the app's private `files` directory under `custom`: `groups.json` plus each image's `m_*.webp` (master) and `s_*.webp` (rotated display);
- Imports are EXIF-corrected; the master's longest edge is 3000 px and the display's longest edge is 1600 px.
## Assets and License
- **Code**: MIT; see [LICENSE](LICENSE).
- **Bundled images**: `app/src/main/assets/groups/` and `app/src/main/assets/groups_land/` come from the official miHoYo / 米游社 (Miyoushe) Zenless Zone Zero "绳网情报站" wiki (https://baike.mihoyo.com/zzz/wiki/channel/map/2/43); copyright miHoYo / HoYoverse. They are **not covered by the MIT License**; the public repository is a personal demonstration only and grants no redistribution or commercial rights. The app icon, thumbnail, and README screenshots also contain official artwork. If you redistribute or use the project commercially, remove/replace these images or obtain permission; see [NOTICE](NOTICE).
- Regenerate assets: put the originals in the `art` directory or set `RASTER_SRC`, then run `tools/build_groups.py` and `tools/build_landscape.py`.
## Tested Lock-Screen Support
The app does not implement a lock screen itself. Whether the system live-wallpaper preview offers "set as lock screen / set for both" depends on each vendor's wallpaper manager and keyguard; AOSP provides no unified guarantee. Observed on real devices (2026-11-03):
| Device | OS | Android | Settable as lock screen | Notes |
|---|---|---|---|---|
| Xiaomi 15 Pro | HyperOS 3 (OS3.0.309.0.WOBCNXM.C11) | 16 | ✅ | Preview offers wallpaper / lock screen / both |
| vivo X200s | OriginOS 6 | 16 | ✅ | Same as above |
| NZONE | EMUI 11 | 10 | ❌ | Wallpaper only; live wallpaper cannot be set on the lock screen |
| HUAWEI P30 Pro | HarmonyOS 4.0.0.112 | 10 | ❌ | Wallpaper only; live wallpaper cannot be set on the lock screen |
| Honor Pad 9 Pro | MagicOS 8 | 14 | ❌ | Same as above |
| HONOR 300 Pro | MagicOS 10.0 (10.0.0.175 C00E170R201P1) | 16 | ❌ | Wallpaper only; live wallpaper cannot be set on the lock screen |
Pattern: HyperOS / OriginOS integrate live wallpapers into the keyguard and expose the lock-screen entry; EMUI / HarmonyOS / MagicOS lock-screen wallpapers go through their own static pipelines.
> **Please help test on your device**: install the app, try setting the live wallpaper (and the
> lock screen where the system offers it), and submit device model / OS and version / Android
> version / whether the lock screen is settable / observed behavior via
> [GitHub Issues](https://github.com/kdjdhe/raster-dial/issues). Results will be added to the table.
## Known Limitations and Roadmap
- **Home-screen live wallpaper by design**; whether it can also be set as the lock-screen wallpaper depends on the vendor's keyguard implementation — see [Tested Lock-Screen Support](#tested-lock-screen-support).
- The wallpaper uses a Canvas `saveLayer` + `LinearGradient` renderer; tablets/4K panels could use EGL + GLES2 to reduce GPU work.
- While visible, it redraws every 16 ms; event-driven + dirty rendering is planned to approach zero redraws when idle.
- Memory headroom: downscale for the target screen, RGB_565, and a 1600 px display edge for custom images.
- A real tablet/foldable smoke test is on the roadmap.
- The official artwork carries the IP risk described above.
- The update APK must be signed with the same key as the installed app; current builds are debug-signed, and the Chengdu server must mirror the exact APK. Signing steps: [docs/SIGNING.md](docs/SIGNING.md).
See [CHANGELOG.md](CHANGELOG.md) for version history.
## Documentation Maintenance
Update this README when:
- a built-in group is added/removed, or `groups.json` fields change;
- the package name, `minSdk`/`targetSdk`, or version changes;
- the debug broadcast action or component changes;
- the asset scripts or the `RASTER_SRC` convention change;
- permissions, the wallpaper service, or an app entry point is added/removed.
## License
MIT License; see [LICENSE](LICENSE). The bundled official artwork is not covered by MIT; see [NOTICE](NOTICE).