Scalara changes your Android device's display resolution and density (DPI)
without root, using the WRITE_SECURE_SETTINGS permission and the same
hidden WindowManager APIs Android itself uses behind adb shell wm size /
wm density. No root, no persistent PC connection, no reboot — grant the
permission once (via ADB or Shizuku) and Scalara
does the rest, entirely on-device.
Android exposes a way to override the display's rendered size and density
(IWindowManager.setForcedDisplaySize / setForcedDisplayDensityForUser,
the exact mechanism behind adb shell wm size and wm density) without
requiring root. Scalara reaches this hidden API via reflection
(DisplayResolutionController), the same approach the shell command itself
uses internally.
The catch: calling it from a third-party app requires the
WRITE_SECURE_SETTINGS permission, which has a signature|privileged
protection level — it can never be granted through the normal runtime
permission dialog. Scalara supports two ways to grant it once, up front,
during first-run setup:
Option A — ADB. Connect your device to a computer and run:
adb shell pm grant id.ditzzy.scalara android.permission.WRITE_SECURE_SETTINGSOption B — Shizuku. If you'd rather not
type an ADB command yourself, Shizuku gives Scalara the same permission
through a privileged, on-device broker instead. Once Shizuku itself is
running (via its own one-time ADB/wireless-debugging/root activation),
Scalara binds a small privileged helper service
(MyUserService, launched through Shizuku's bindUserService API) that runs
pm grant … WRITE_SECURE_SETTINGS on your behalf, on-device — no typing
ADB commands, no computer needed for this step. The setup wizard
(SetupActivity) walks through requesting the Shizuku permission and
triggering this grant with a couple of taps.
Shizuku's role ends there. pm grant is a one-shot call — Scalara binds the
helper service, runs it, and unbinds immediately (see ShizukuManager). The
resulting WRITE_SECURE_SETTINGS grant is tracked by Android's own
PackageManager, the same place it'd live had you used Option A, so it is
not contingent on Shizuku staying installed, active, or authorized for
Scalara afterward. Disabling the Shizuku service or removing Scalara's
access from the Shizuku app does not revoke it.
Either way, once granted:
- Scalara can change resolution and density directly, instantly, without root and without a persistent ADB/computer connection.
- The grant is checked every time the app resumes (
MainActivity's permission guard), by askingPackageManagerforWRITE_SECURE_SETTINGSdirectly — regardless of which option was used to grant it. If it's ever actually revoked — by the system or bypm revoke— Scalara detects it and routes you back into setup rather than failing silently mid-action.
- Change screen resolution (width × height) and density (DPI) independently, applied instantly — no reboot.
- Reset to default with one tap, restoring the device's real physical resolution and density at any time.
- Read-only default resolution card on the home screen, always showing your device's true (unforced) resolution and DPI for reference.
- Preview any resolution/DPI combination for a short window (default 10s, configurable 3–60s in Settings) before committing to it.
- Runs as a foreground service (
PreviewRevertService) with a live countdown notification, so it reliably reverts even if you leave the app, rotate the screen, or it gets backgrounded — a plain in-app timer couldn't guarantee that. - "Revert now" action right from the notification if you want to bail out early.
- Save named width/height/DPI combinations for instant reuse later.
- Four independent actions per preset, matching real workflows: Apply (save + apply immediately), Try it out (preview only, nothing saved), Save (just save it), or apply/delete an existing one from its own options sheet.
- Before applying or previewing a value that deviates more than 50% from your device's default width, height, or DPI, Scalara shows a confirmation dialog first — the exact kind of change that can otherwise leave a device hard to see or navigate.
- Toggleable in Settings, on by default.
- Export a single preset or your entire saved list to a password-protected
.sclfile, and import it back later or on another device. - Real encryption, not obfuscation: AES-256-GCM with a PBKDF2-HMAC-SHA256 key (600,000 iterations, per OWASP's current guidance) derived from your password and a random per-file salt. See Preset export format below.
- Import supports merge (add to your existing presets) or replace (start fresh from the imported file), with a preview of what's inside before anything is committed.
- Android Studio (recent stable) or the command line with JDK 17 if you want to build scalara locally
- An Android device or emulator running API 24+
- ADB (or Shizuku) to grant
WRITE_SECURE_SETTINGS— see How it works
./gradlew assembleDebugThe debug APK is written to app/build/outputs/apk/debug/.
./gradlew testDebugUnitTest
./gradlew lintDebugContributions are welcome. Please read CONTRIBUTING.md before opening a PR, and check existing issues first.
Scalara's in-app language picker (Settings → Language) never hardcodes which
languages are available — a Gradle task
(generateLocalesList, in app/build.gradle.kts)
scans app/src/main/res/ at build time for values-<lang> directories and
generates the picker's option list from whatever it finds. Currently shipped:
🇬🇧 English (values/, the default) and 🇮🇩 Bahasa Indonesia (values-in/).
To add a new language, copy
app/src/main/res/values/strings.xml
into a new app/src/main/res/values-<tag>/strings.xml (e.g. values-es/ for
Spanish, values-pt-rBR/ for Brazilian Portuguese) and translate every
string. That's it
Licensed under the Apache License 2.0. See also NOTICE.
