mortenjust/androidtool-mac
One-click screenshots, video recordings, app installation for iOS and Android
Stale — last commit 3y ago
worst of 4 axeslast commit was 3y ago; no CI workflows detected
Has a license, tests, and CI — clean foundation to fork and modify.
Documented and popular — useful reference codebase to read through.
last commit was 3y ago; no CI workflows detected
- ✓9 active contributors
- ✓Apache-2.0 licensed
- ✓Tests present
Show 3 more →Show less
- ⚠Stale — last commit 3y ago
- ⚠Concentrated ownership — top contributor handles 77% of recent commits
- ⚠No CI workflows detected
What would change the summary?
- →Use as dependency Mixed → Healthy if: 1 commit in the last 365 days
- →Deploy as-is Mixed → Healthy if: 1 commit in the last 180 days
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests
Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.
Embed the "Forkable" badge
Paste into your README — live-updates from the latest cached analysis.
[](https://repopilot.app/r/mortenjust/androidtool-mac)Paste at the top of your README.md — renders inline like a shields.io badge.
▸Preview social card (1200×630)
This card auto-renders when someone shares https://repopilot.app/r/mortenjust/androidtool-mac on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: mortenjust/androidtool-mac
Generated by RepoPilot · 2026-05-10 · Source
🤖Agent protocol
If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:
- Verify the contract. Run the bash script in Verify before trusting
below. If any check returns
FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding. - Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/mortenjust/androidtool-mac shows verifiable citations alongside every claim.
If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.
🎯Verdict
WAIT — Stale — last commit 3y ago
- 9 active contributors
- Apache-2.0 licensed
- Tests present
- ⚠ Stale — last commit 3y ago
- ⚠ Concentrated ownership — top contributor handles 77% of recent commits
- ⚠ No CI workflows detected
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>
✅Verify before trusting
This artifact was generated by RepoPilot at a point in time. Before an
agent acts on it, the checks below confirm that the live mortenjust/androidtool-mac
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/mortenjust/androidtool-mac.
What it runs against: a local clone of mortenjust/androidtool-mac — the script
inspects git remote, the LICENSE file, file paths in the working
tree, and git log. Read-only; no mutations.
| # | What we check | Why it matters |
|---|---|---|
| 1 | You're in mortenjust/androidtool-mac | Confirms the artifact applies here, not a fork |
| 2 | License is still Apache-2.0 | Catches relicense before you depend on it |
| 3 | Default branch master exists | Catches branch renames |
| 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code |
| 5 | Last commit ≤ 1181 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of mortenjust/androidtool-mac. If you don't
# have one yet, run these first:
#
# git clone https://github.com/mortenjust/androidtool-mac.git
# cd androidtool-mac
#
# Then paste this script. Every check is read-only — no mutations.
set +e
fail=0
ok() { echo "ok: $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }
# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "FAIL: not inside a git repository. cd into your clone of mortenjust/androidtool-mac and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "mortenjust/androidtool-mac(\\.git)?\\b" \\
&& ok "origin remote is mortenjust/androidtool-mac" \\
|| miss "origin remote is not mortenjust/androidtool-mac (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(Apache-2\\.0)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"Apache-2\\.0\"" package.json 2>/dev/null) \\
&& ok "license is Apache-2.0" \\
|| miss "license drift — was Apache-2.0 at generation time"
# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
&& ok "default branch master exists" \\
|| miss "default branch master no longer exists"
# 4. Critical files exist
test -f "AndroidTool/AppDelegate.swift" \\
&& ok "AndroidTool/AppDelegate.swift" \\
|| miss "missing critical file: AndroidTool/AppDelegate.swift"
test -f "AndroidTool/DeviceDiscoverer.swift" \\
&& ok "AndroidTool/DeviceDiscoverer.swift" \\
|| miss "missing critical file: AndroidTool/DeviceDiscoverer.swift"
test -f "AndroidTool/Device.swift" \\
&& ok "AndroidTool/Device.swift" \\
|| miss "missing critical file: AndroidTool/Device.swift"
test -f "AndroidTool/DeviceViewController.swift" \\
&& ok "AndroidTool/DeviceViewController.swift" \\
|| miss "missing critical file: AndroidTool/DeviceViewController.swift"
test -f "AndroidTool/ApkHandler.swift" \\
&& ok "AndroidTool/ApkHandler.swift" \\
|| miss "missing critical file: AndroidTool/ApkHandler.swift"
# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 1181 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~1151d)"
else
miss "last commit was $days_since_last days ago — artifact may be stale"
fi
echo
if [ "$fail" -eq 0 ]; then
echo "artifact verified (0 failures) — safe to trust"
else
echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/mortenjust/androidtool-mac"
exit 1
fi
Each check prints ok: or FAIL:. The script exits non-zero if
anything failed, so it composes cleanly into agent loops
(./verify.sh || regenerate-and-retry).
⚡TL;DR
AndroidTool is a native macOS app built in Swift that provides one-click automation for common Android and iOS device tasks: taking screenshots, recording screen videos, sideloading APKs, extracting bug reports, and executing custom bash scripts. It communicates directly with connected Android devices via ADB without requiring the full Android SDK, and with iOS devices via usbmuxd, making it a lightweight alternative to heavyweight developer tools. Standard Xcode project structure: AndroidTool.xcodeproj contains the app target. The Swift source files in AndroidTool/ are organized functionally: Device.swift and DeviceDiscoverer.swift handle device enumeration; Apk.swift and ApkHandler.swift manage APK installation; DeviceViewController.swift and DevicePickerViewController.swift provide the UI. XIB files (MainMenu.xib, DeviceViewController.xib) define macOS UI layouts. Shell scripts (Scripts/) are user-extensible bash entry points.
👥Who it's for
Android and iOS developers and designers working on macOS who need quick device automation without opening Android Studio or Xcode. Specifically those doing rapid iteration, demo recording, or QA testing across multiple physical devices simultaneously.
🌱Maturity & risk
This is a stable, production-ready macOS application with a clear single-maintainer history (mortenjust). The codebase is ~111KB of Swift with minimal external dependencies, suggesting lean, maintainable code. However, the repository shows signs of moderate age with no visible recent activity or CI/CD setup in the file list, suggesting it may be in maintenance mode rather than actively developed.
Single-maintainer risk is present (mortenjust appears to be the only contributor). The project relies on external binaries (FFmpeg, ImageMagick) bundled at runtime, which could break with macOS updates. No visible test suite in the file list (no Tests/ directory) is a red flag for refactoring confidence. Dependencies on ADB and usbmuxd versions are implicit and undocumented.
Active areas of work
No active development signals are visible in the provided file list. The repository appears to be in maintenance mode, likely receiving occasional bug fixes and compatibility updates but no major feature development. The presence of xcuserdata and .DS_Store suggests this is a developer's working directory snapshot.
🚀Get running
Clone the repository, open AndroidTool.xcodeproj in Xcode, and build via Product > Build or Cmd+B. No package manager is needed (Swift Package Manager integration not visible). Ensure Android SDK platform-tools (for ADB) and libimobiledevice (for iOS) are installed locally via brew: brew install android-platform-tools libimobiledevice.
Daily commands:
Open AndroidTool.xcodeproj in Xcode, select the AndroidTool scheme, and press Cmd+R to build and run. The app launches as a macOS menu bar app and presents the main UI window. For command-line testing of individual features, run adb devices to verify ADB connectivity before opening the app.
🗺️Map of the codebase
AndroidTool/AppDelegate.swift— Application entry point and lifecycle management; initializes device discovery and main UI windowAndroidTool/DeviceDiscoverer.swift— Core service that discovers and monitors connected Android/iOS devices via ADB and USB; essential for all device operationsAndroidTool/Device.swift— Fundamental data model representing a connected device with properties and capabilities; central to all device interactionsAndroidTool/DeviceViewController.swift— Main UI controller managing device display, screenshot/video capture, and APK installation workflowsAndroidTool/ApkHandler.swift— Handles APK file validation, drag-drop reception, and installation via ADB push/installAndroidTool/IOSDeviceHelper.swift— Bridges iOS-specific operations (screenshots, video recording) via libimobiledevice; enables cross-platform supportAndroidTool/Constants.swift— Centralized configuration for ADB commands, file paths, and system integration; critical for maintenance and debugging
🧩Components & responsibilities
- DeviceDiscoverer (Swift, ADB, libimobiledevice, Threading) — Maintains live list of connected devices by polling ADB (
adb devices) and querying libimobiledevice for iOS. Notifies listeners on device connect/disconnect.- Failure mode: If ADB daemon is not running or iOS library missing, no devices detected; user sees empty device list
- Device Model (Swift Codable) — Immutable data structure capturing device identity, type (Android/iOS), screen resolution, and available capabilities (screenshot, video, APK install).
- Failure mode: Incorrect device properties prevent correct command selection; wrong screen dimensions break screenshot display
- DeviceViewController (AppKit, Cocoa, NSViewController) — UI layer managing device display, button state, and orchestrating user actions. Spawns ADB commands and renders results (screenshots, videos).
- Failure mode: UI blocking on long ADB operations; crashed process handling may leave zombie ADB sessions
- ApkHandler (Swift, ADB, File I/O) — Validates dropped APK files (signature, manifest parsing) and executes
adb installcommand with proper flags and error handling.- Failure mode: Invalid APK structure not caught; installation fails silently or with unclear error message
- IOSDeviceHelper (Swift, C interop, libimobiledevice) — Wrapper around libimobiledevice C API exposing iOS-specific operations (screenshot, video recording, device properties).
- Failure mode: Outdated libimobiledevice version incompatible with newer iOS; missing iOS device detection
🔀Data flow
User (device connect)→DeviceDiscoverer (ADB— undefined
🛠️How to make changes
Add a new device action (screenshot, video, bug report)
- Add the action button and handler in DeviceViewController.swift where screenshot/video buttons are defined (
AndroidTool/DeviceViewController.swift) - Define the ADB command string in Constants.swift under the appropriate category (e.g., screenshotCommand, videoCommand) (
AndroidTool/Constants.swift) - Implement command execution using shell call via C.swift's system command wrapper (
AndroidTool/C.swift) - Handle output file (screenshots/videos) and update UI to display result or notify user
Add support for a new device type or platform
- Extend Device.swift with new device type enum case and capability flags (
AndroidTool/Device.swift) - Update DeviceDiscoverer.swift to detect the new device type by querying ADB or libimobiledevice (
AndroidTool/DeviceDiscoverer.swift) - Create a platform-specific helper (similar to IOSDeviceHelper.swift) if needed for device-specific operations (
AndroidTool/IOSDeviceHelper.swift) - Update DeviceViewController.swift to display appropriate controls/UI for the new device type
Add a new APK feature or installation workflow
- Extend Apk.swift data model to include new metadata or capability fields (
AndroidTool/Apk.swift) - Update ApkHandler.swift to parse new metadata or validate new constraints (
AndroidTool/ApkHandler.swift) - Add corresponding ADB install commands to Constants.swift if new installation method is needed (
AndroidTool/Constants.swift) - Update DropReceiverView.swift or DeviceViewController.swift UI to surface new APK feature to user
🔧Why these technologies
- Swift with Cocoa/AppKit — Native macOS development for responsive UI and deep OS integration; no external framework dependencies required
- ADB (Android Debug Bridge) — Industry-standard Android device communication protocol; allows screenshots, video recording, APK installation without SDK
- libimobiledevice — Open-source library for iOS device communication; enables iOS screenshots and video without Xcode or Apple tools
- Shell command execution (Process) — Lightweight approach to invoke ADB and system utilities; minimizes external dependencies
⚖️Trade-offs already made
-
No external dependency management (CocoaPods, SPM)
- Why: Reduce complexity and external version management; bundle tools directly
- Consequence: Manual library updates; potential security gaps if libimobiledevice is outdated; harder to add new dependencies
-
Polling-based device discovery instead of event-based system framework
- Why: Cross-platform compatibility (Android + iOS) with single discovery mechanism; simpler to implement
- Consequence: Latency in detecting device connect/disconnect (500–1000ms); minor CPU overhead from polling loop
-
Synchronous ADB command execution via shell Process
- Why: Straightforward implementation; easy debugging and error handling
- Consequence: UI thread blocking during long operations (video recording, large APK installs); potential jank if not backgrounded
-
Single-window macOS app with XIB layouts
- Why: Lightweight, cohesive UX; fast launch and minimal memory footprint
- Consequence: Limited multi-device workflow; all operations serialize through single view controller
🚫Non-goals (don't propose these)
- Does not provide Android/iOS SDK components; relies on user-provided ADB and libimobiledevice
- Does not perform app development, compilation, or debugging; purely a device utility
- Does not offer cloud or remote device management; local USB/wireless connections only
- Does not handle authentication or app signing; assumes already-signed APKs for installation
- Does not support Windows or Linux; macOS-only application
🪤Traps & gotchas
FFmpeg and ImageMagick are required external binaries—they must be present in the macOS PATH or bundled with the app; the build process likely embeds these but this is not visible in the file list. ADB and usbmuxd availability is assumed but not validated at startup. XIB files are XML under the hood but edited via Interface Builder; direct XML editing can break the file. The app likely requires special entitlements (com.apple.security.device.usb) for USB device access on recent macOS; check .entitlements file in project settings. No build configuration file or environment variable documentation visible—SDK paths and tool versions may be implicit.
🏗️Architecture
💡Concepts to learn
- Android Debug Bridge (ADB) — The primary communication protocol between macOS and Android devices; understanding ADB subcommands (shell, push, install) is essential to modifying Device.swift and ApkHandler.swift
- USB Multiplexing (usbmuxd) — The protocol layer enabling macOS to communicate with iOS devices over USB; IOSDeviceHelper.swift relies on usbmuxd but it's an opaque dependency that can be cryptic to debug
- APK File Format — AndroidTool parses and installs .apk files (ZIP archives with metadata); Apk.swift extracts package name and version by reading AndroidManifest.xml inside the APK
- Process Spawning via NSTask — The app invokes external binaries (adb, ffmpeg, imagemagick) as subprocesses; NSTask is the macOS equivalent of exec() and output parsing from stdout/stderr is error-prone
- macOS XIB/Storyboard UI Definition — UI layouts are defined in XML (.xib files) rather than code; Interface Builder obscures the underlying structure, making git diffs and merges difficult
- Screen Recording via ADB Screenrecord — Android 4.4+ devices have native screenrecord binary; DeviceViewController.swift calls adb shell screenrecord to capture video, then pipes the output back to macOS
- GIF Animation from Video Frames — AndroidTool outputs both MP4 and animated GIF; ImageMagick converts frame sequences to GIF, and FFmpeg is used for frame extraction; the pipeline is complex and frame rate/palette choices affect output quality
🔗Related repos
android/platform-tools-base— Official Android SDK tool suite that AndroidTool wraps; understanding ADB internals improves screenshot/install reliabilitylibimobiledevice/libimobiledevice— The underlying iOS USB/usbmuxd library that IOSDeviceHelper.swift depends on; needed for iOS feature developmentFFmpeg/FFmpeg— Video encoding engine bundled by AndroidTool; customizing video quality or format requires FFmpeg parameter tuninggoogle/androidstudio-wrapper— Alternative GUI wrapper around Android tools; competitor project showing different UI/UX for similar device automationDeviceFarmer/stf— Device farm management system offering remote device control; broader ecosystem for multi-device orchestration at scale
🪄PR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Refactor DeviceViewController.swift into separate iOS and Android handlers
DeviceViewController.swift currently handles both iOS and Android device operations (screenshots, video recording, APK installation). This violates separation of concerns and makes the codebase harder to maintain. Splitting into IOSDeviceViewController and AndroidDeviceViewController would improve readability and testability, especially given that IOSDeviceHelper.swift already exists as a separate helper.
- [ ] Create AndroidDeviceViewController.swift to handle Android-specific UI logic (screenshots, video, APK install)
- [ ] Refactor DeviceViewController.swift to serve as a base or router that delegates to platform-specific controllers
- [ ] Move Android-specific button actions and logic from DeviceViewController.xib and its controller to AndroidDeviceViewController
- [ ] Update DevicePickerViewController.swift to instantiate the correct view controller based on device platform
- [ ] Verify all IBOutlets and IBActions are correctly wired in the updated XIB files
Add unit tests for Device.swift and DeviceDiscoverer.swift
The Device.swift and DeviceDiscoverer.swift files handle critical functionality for detecting and managing connected devices (both iOS and Android), but there are no visible test files in the repository. Adding unit tests would ensure device detection logic remains reliable across macOS versions and ADB/Xcode updates.
- [ ] Create AndroidToolTests target if it doesn't exist in AndroidTool.xcodeproj
- [ ] Write unit tests for Device.swift covering device initialization, property parsing, and platform detection
- [ ] Write unit tests for DeviceDiscoverer.swift covering ADB/Xcode process spawning, device list parsing, and change notifications
- [ ] Mock external process calls (adb, xcrun) to avoid requiring actual devices or SDKs
- [ ] Add test cases for edge cases: malformed device output, disconnected devices, multiple devices
Create a CI/CD workflow to notarize and codesign the macOS app
The README mentions downloading a .app but there's no GitHub Actions workflow visible to automate notarization and codesigning. macOS 10.15+ requires notarization for unsigned apps. Adding a build workflow would ensure releases are properly signed, reduce distribution friction, and help new contributors understand the release process.
- [ ] Create .github/workflows/build-and-notarize.yml workflow file
- [ ] Configure the workflow to build AndroidTool.app from the Xcode project on push to release branches
- [ ] Add steps to codesign the app using GitHub-stored certificates (or ad-hoc signing for contributors)
- [ ] Add notarization step using Apple's notary service with app-specific passwords stored as secrets
- [ ] Configure the workflow to create a release and attach the notarized .app or .dmg artifact
🌿Good first issues
- Add a test suite: Create Tests/DeviceDiscovererTests.swift to unit-test device enumeration logic on mock ADB output. Currently zero test coverage visible.
- Document the scripts folder: Add a SCRIPTS.md guide showing the API for custom user scripts (how to access the selected device, environment variables, expected outputs). The existing example script in Scripts/ lacks inline documentation.
- Implement error handling UI: Several .swift files lack user-facing error messages (ApkHandler.swift, IOSDeviceHelper.swift). Add NSAlert popups for common failures like 'Device disconnected' or 'ADB not found'.
⭐Top contributors
Click to expand
Top contributors
- @mortenjust — 77 commits
- @muandrew — 8 commits
- @jlhonora — 4 commits
- @Dibel — 3 commits
- @pt2121 — 3 commits
📝Recent commits
Click to expand
Recent commits
9fc699b— Merge pull request #137 from muandrew/twotothree (mortenjust)b727c76— Fix Crash when there is no device brand. (muandrew)1b8c694— Fix deprecation Warning (muandrew)d7f8f93— Use Recommended Project Configurations (muandrew)5377794— Fix compilation warning/errors from Swift 2 to 3 automated migration. (muandrew)5264870— Add @escaping when needed. (muandrew)06cceb9— Automated Swift 2 to 3 Conversion by XCode 8.3.3 (muandrew)aab7c1f— Clean up warnings (muandrew)9714a18— Clean up Task class (muandrew)b3906d0— Change version code (mortenjust)
🔒Security observations
This macOS application for Android/iOS device management presents a moderate security posture. The primary concerns are potential command injection vulnerabilities when interfacing with adb and shell commands, and path traversal risks in file handling operations. The application lacks visible input validation and sanitization mechanisms. No hardcoded secrets or credentials were identified in the provided file structure. The project contains some minor issues with Xcode user data in version control. Overall, the application would benefit from implementing comprehensive input validation, using secure APIs for shell command execution, and proper file path validation.
- Medium · Potential Command Injection via ADB/Shell Commands —
AndroidTool/DeviceDiscoverer.swift, AndroidTool/Device.swift, AndroidTool/ApkHandler.swift. The application appears to interface with Android Debug Bridge (adb) and execute shell commands for screenshots, video recording, and APK installation. Without visible input sanitization in the file structure, there is a risk of command injection if user inputs or device data are not properly escaped before being passed to shell execution. Fix: Implement strict input validation and sanitization for all user inputs and device identifiers before passing them to shell commands. Use Process APIs with argument arrays instead of string concatenation to prevent shell injection attacks. - Low · Xcodeproj User Data Exposure —
.gitignore, AndroidTool.xcodeproj/xcuserdata/. The repository contains Xcode user data in xcuserdata directories, including debug breakpoints and scheme information. While not directly a security vulnerability in the app itself, this can expose development patterns and debugging information if committed to version control. Fix: Ensure xcuserdata/ directories are properly listed in .gitignore. Verify that sensitive user-specific Xcode data has not been committed to the repository. Consider using a global Git configuration to automatically exclude these directories. - Low · Potential Path Traversal in File Operations —
AndroidTool/ApkHandler.swift, AndroidTool/DropReceiverView.swift. The application handles APK file installation and may perform file operations. Without visible path validation, there could be potential for path traversal attacks if file paths are not properly validated. Fix: Implement strict path validation to ensure only intended directories are accessible. Canonicalize paths and validate them against a whitelist of allowed directories before performing file operations. - Low · Missing Security Headers and Transport Security —
AndroidTool (InfoPlist configuration). As a macOS application that may communicate with devices or external services, there is no visible evidence of App Transport Security (ATS) configuration or certificate pinning for secure communications. Fix: Ensure Info.plist includes proper App Transport Security settings. Disable ATS only for specific trusted domains if required. Implement certificate pinning for critical communications.
LLM-derived; treat as a starting point, not a security audit.
👉Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.