Shrinks the system display density on a Galaxy Watch so sideloaded phone
apps fit the round screen, then restores it automatically when you leave the
app. Built for the Galaxy Watch Ultra / Ultra 2 (One UI Watch / Wear OS), but
works on any Wear OS watch where adb shell wm density works.
- No root. A one-time
adbgrant, then the watch is standalone forever. - No account, no network, no telemetry. One permission, and the full source is right here so you can verify that.
- No Gradle. Plain
javac → d8 → aapt → apksigner.
Why this exists: phone apps like YouTube, Uber, Proton, or Bitwarden are laid out for a large rectangular screen. At the watch's stock density their UI overflows the round display and controls fall outside the visible circle. Lowering display density makes those layouts fit.
You need a computer once to grant one permission. After that the watch never needs it again — the grant survives reboots and app updates.
-
Download
DensityToggle.apkfrom the latest release. -
Enable wireless debugging on the watch (Settings → Developer options).
-
Run the installer (it installs, grants the permission, and verifies it):
# macOS / Linux bash install.sh <watch-ip>:<port>
:: Windows install.bat <watch-ip>:<port>
…or the three manual
adbcommands if you prefer — see INSTALL.md for the full walkthrough (pairing, finding the IP:port, troubleshooting).
Then on the watch: open Density Toggle, tap Auto, tap Choose apps, and pick the apps you want to auto-shrink.
No magic — four commands you could run yourself, all listed in INSTALL.md:
| Step | Command | Why |
|---|---|---|
| Install | adb install -r DensityToggle.apk |
the app itself |
| Grant | adb shell pm grant com.dt.toggle android.permission.WRITE_SECURE_SETTINGS |
the one permission density changing needs — Android has no dialog for it, adb is the only way |
| Verify | adb shell dumpsys package com.dt.toggle | grep WRITE_SECURE_SETTINGS |
confirms granted=true |
| Usage access | adb shell appops set com.dt.toggle android:get_usage_stats allow |
lets the service read which app is in front from the system's usage ledger — accessibility events alone miss warm re-opens of running apps (YouTube especially). It reads app-switch timestamps, not content |
Rotation lock (default; skip with --no-lock-rotation) |
adb shell cmd window fixed-to-user-rotation enabled |
pins the screen upright so shrunk apps stop rotating with your wrist; persists across reboots; undo with …disabled |
Nothing else is changed, nothing runs on your computer afterwards, and the grants survive reboots — the watch never needs the computer again.
An accessibility service watches which app is in front. When a chosen app appears it shrinks the display to that app's size; when you leave, it restores stock density. You enable the service from inside the app — no trip to Settings.
Every chosen app has its own density (a [−] value [+] stepper in Choose
apps). A main-screen Auto size · default applies to apps without their
own value. Hold a value to make it follow the default again; hold −/+ for
bigger steps.
Password managers (Bitwarden, etc.) set FLAG_SECURE, which hides their screen
from other apps. Density Toggle identifies the foreground app from window
events, not screen content, so those apps shrink and restore correctly like
any other.
When you tap a watched app, the shrink is applied before the app finishes launching, so it opens straight into the right size instead of visibly rescaling mid-launch.
A side effect of shrinking: phone apps switch to their tablet layouts, and
tablet layouts rotate with the accelerometer — so apps flip sideways when you
tilt your wrist. Android's auto-rotate setting can't stop this (apps that
request sensor orientation bypass it), and the mode that can — the window
manager's fixed-to-user-rotation — is guarded by a signature permission no
sideloaded app can hold. So this is not an in-app toggle; it's one
persistent adb command that the installer runs by default (skip with
--no-lock-rotation; details in INSTALL.md):
adb shell cmd window fixed-to-user-rotation enabled # undo: ...disabledThe system remembers it across reboots.
Round screens physically clip the corners of rectangular phone layouts, and no density setting can move a corner button into the visible circle. Reach is the workaround: a small translucent ⌕ button opens a list of the app's clickable controls that sit outside the circle — named by the app, or by position ("bottom-right control") when the app doesn't name them — and taps them for you (accessibility proxy-click; a control doesn't need to be visible to be clicked).
- Modes (main screen): off · shrunk apps (default) · all apps — "all apps" serves unshrunk third-party apps too, since corners are clipped at stock density as well. System apps never get the button.
- Per-app override: each app in Choose apps has a "⌕ Reach button" chip to hide the button just there.
- Transparency: adjustable 10–90% on the main screen (greyed out while Reach is off).
- Works for any app that exposes its UI tree; apps that hide it show "no hidden controls found".
A Restore to stock button appears on the main screen whenever the display is actually shrunk — one tap to get back to normal if anything ever sticks.
A small grey line at the bottom reports which method changed the density, or the exact error if it failed. For deeper debugging the service logs every decision:
adb logcat -s DensityToggleChanging display density is gated by WRITE_SECURE_SETTINGS — the same gate
adb shell wm density passes through. It is not a permission an app can
grant itself or that Android shows a dialog for; it's handed over once via
adb shell pm grant, and that grant persists across reboots. That single
one-time command is the only reason a computer is needed at all.
WindowManagerService.setForcedDisplayDensityForUser() is hidden API, so the
app reaches it by reflection. targetSdkVersion is pinned to 27 on purpose:
hidden members marked max-target-o stay reachable for apps targeting API 27
or lower. See CLAUDE.md for the full mechanism, the fallback chain,
and the invariants that are easy to break.
The release APK is ready to install; build it yourself only if you want to.
bash build-macos.sh # or build-termux.sh on Android/TermuxNeeds a JDK and Android SDK build-tools — not 34.0.0, whose dexer crashes
on this project (the script skips it automatically). First build generates a
local signing key; the resulting DensityToggle.apk is signed and installable.
| Tested on | Galaxy Watch Ultra (SM-L705U) & Ultra 2 (SM-L715U), Wear OS / Android 16–17 |
| Min Wear OS | API 24 |
| Requires | WRITE_SECURE_SETTINGS via one-time adb grant |
Note on the Ultra / Ultra 2: both are 32-bit (
armeabi-v7a). This app is pure Java so that doesn't affect it, but it does affect which other apps you can sideload — an arm64-only APK won't install. Prefer universal APKs.
UI too small to tap. Tap Restore to stock in the app. If it's already
too small for that, plug in and run adb shell wm density reset.
Installer says "no device / offline". The watch's wireless-debugging port
changes every time it sleeps — re-read it on the watch and reconnect; clear
stale entries with adb disconnect. Full details in INSTALL.md.
An app resizes oddly. Run adb logcat -s DensityToggle and open an issue
with the output — it shows exactly what the service decided and why.
MIT — see LICENSE. Provided as-is, no warranty. It changes a system
display setting through a documented permission; the in-app Restore control and
adb shell wm density reset undo anything it does.