This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Java/Swing desktop app for managing many Android devices over adb. Packaged for Mac/Windows/Linux via jdeploy. Tested with up to 45 devices over USB hubs.
Java 17+, Maven. Entry point: com.jpage4500.devicemanager.MainApplication.
mvn compile exec:java— run from source (orscripts/run.sh)mvn -B package— build fat jar totarget/AndroidDeviceManager.jar(orscripts/build.sh)scripts/clear-preferences.sh— wipes saved JavaPreferencesscripts/gen_release_notes.sh <type> <format> [tag]— release notes from git log (falls back to PR titles for merge commits); CI callstag brief <prev-tag>- No test suite is set up (
package.json'stestscript is a stub).
CI: .github/workflows/jdeploy.yml. Push to develop → job 1 tags 1.0.<git commit count> → the tag push triggers job 2, which builds and publishes installers to GitHub Releases via the shannah/jdeploy action (deploy_target: github, no longer npm). The tag is the version: job 2 passes it as -Drevision=, which sets ${project.version} and lands in app.properties. Local builds need the same flag (scripts/build.sh handles it); a bare mvn package falls back to 1.0.0. Job 2 then attaches the fat jar and rewrites the release body as generated notes + the installer download links jDeploy wrote (don't swap the gh release upload step back to softprops/action-gh-release — it blanks the body). Requires the ADM_JDEPLOY_GITHUB_TOKEN secret (a PAT with contents: write — the default GITHUB_TOKEN can't trigger the tag-push job); setup steps are in README.md under "Releasing".
adb(Android platform-tools)scrcpy(for device mirroring)
The app shells out to platform-specific scripts in src/main/resources/scripts/ (mirror.sh/.bat, terminal.sh/.bat, start-server.sh/.bat, record-screen.sh/.bat, run-custom.sh, shell.sh). They are extracted to a temp dir at runtime; see DeviceManager constants SCRIPT_*.
MainApplication— boot, FlatLaf setup, taskbar icon, file-open handler (apk/xapk drag-onto-app), createsDeviceScreen.ui/— Swing screens. Each top-level screen extendsBaseScreen:DeviceScreen(main list),ExploreScreen(file browser),ViewLogsScreen,SaveLogsScreen,InputScreen,MessageViewScreen.ui/dialog/andui/views/hold dialogs and reusable Swing widgets.manager/— non-UI business logic.DeviceManageris a singleton coordinatingadbvia the bundled jadb library, holding the device list, scheduled refresh, and per-device logging state.RemoteConnectionManager+RemoteConnectionconnect to other ADM instances;RemoteServerManager+RemoteHttpServer(NanoHTTPD) expose this instance's devices over HTTP so another ADM instance can use them.NetworkDiscoveryManageruses jmDNS/SSDP to find peers.data/— POJOs (Device,DeviceFile,LogEntry,RemoteServerConfig, etc.). Annotate fields with@ExcludeFromSerializationto keep Gson from persisting them (seeAnnotationExclusionStrategy).table/—TableModels;table/utils/holds matchingCellRenderers,RowSorters,RowFilters.logging/— custom SLF4J binding (AppLoggerFactoryregistered viaMETA-INF/services/org.slf4j.spi.SLF4JServiceProvider). Levels are the Android-style ints inLog(VERBOSE=2 … ASSERT=7). File logs go to~/.device_manager/device_manager_log.txt.utils/— Swing helpers, file I/O, Gson wrapper, network/UPnP helpers,PreferenceUtils.
A vendored copy of jadb — a pure-Java ADB client. The app talks to the local adb server via JadbConnection/JadbDevice rather than spawning adb for every command. Treat this package as a third-party dep; prefer not to modify it.
ADM can act as both client and server. RemoteServerManager runs RemoteHttpServer on port 8765 with bearer-token auth and exposes /api/devices, /api/execute, /api/files/list|download|upload, /api/screenshot. Headers x-client-ip / x-client-name / authorization identify the caller. RemoteConnectionManager is the matching client. UpnpUtils does optional port forwarding; jmDNS handles LAN discovery.
- Java
PreferencesAPI viaPreferenceUtils— keys are enums (Pref,PrefBoolean,PrefInt).scripts/clear-preferences.shwipes them. ~/.device_manager/(fromUtils.getDeviceManagerFolder()) — log file, downloaded files, etc.
- All Swing work goes through
SwingUtilities.invokeLater. DeviceManagerowns two pools:commandExecutorService(blocking shell-outs like mirror/terminal) andscheduledExecutorService(periodic device refresh onDEVICE_REFRESH_MINS).
- Keep comments very concise — one line. Say what it is, not the reasoning behind it. Drop the follow-up
NOTE:/rationale lines; if a comment needs a paragraph, the code needs the work instead. Example — keep only the first line of:// -- keep a few days of battery/disk values so they can be graphed over time -- // NOTE: this rides along on the refresh that just ran rather than asking the device for // anything; the not-booted path above returns before either fetch, so it can't record blanks
- Use SLF4J:
private static final Logger log = LoggerFactory.getLogger(Foo.class);— calllog.debug/info/warn/errorwith{}placeholders. - Layouts use MigLayout (
net.miginfocom.swing.MigLayout). TextUtilsmirrors Android's helper (useisEmpty,equalsIgnoreCase, etc. instead of rolling your own).- Gson via
GsonHelper; respect@ExcludeFromSerialization. - Log filter syntax (used in
ViewLogsScreen) is documented inLOGS.md.