This document explains how Android support works in Lame, why the app prefers MTP, and how the new ADB fallback behaves when MTP cannot enumerate the device.
Lame now has two Android backends:
/sdcard when MTP fails.The UI still exposes a single Android device row. AndroidDeviceManager chooses the backend at runtime and updates the row label to show whether the current session is using MTP or ADB.
On macOS, Android support is fundamentally different from iPhone support:
MTP works well when the phone is unlocked and set to File Transfer, but in practice it can fail for reasons outside the app’s control:
ADB is not a replacement for the normal Android path. It is a recovery path when the user already has USB Debugging enabled and the phone has authorized the Mac.
Lame first tries the existing libmtp flow:
ioregmtp-detectmtp-filesmtp-thumb, mtp-getfile, and mtp-delfile for operationsThis is still the best default because it matches the standard Android user flow and does not require Developer Options.
If MTP enumeration fails and adb is installed, Lame tries the ADB backend:
adb devices -l/sdcard with adb shell findstat when availableadb pull for copy and thumbnail fallbackadb shell rm -rf -- <path> for deleteThis fallback is intentionally scoped to /sdcard, not the full device filesystem. That keeps the implementation focused on the user-visible shared storage area the app is meant to browse.
flowchart TD
A[Poll via ioreg] --> B{Android device visible?}
B -- No --> A
B -- Yes --> C[Try mtp-detect]
C --> D[User selects Android row]
D --> E[Try mtp-files]
E --> F{Enumeration succeeded?}
F -- Yes --> G[Use MTP backend]
F -- No --> H{ADB installed and authorized?}
H -- No --> I[Show retryable error]
H -- Yes --> J[Walk /sdcard over ADB]
J --> K[Use ADB backend]
Both Android backends support the same user-visible surfaces:
The implementation differs by backend.
mtp-filesmtp-thumbmtp-getfilemtp-delfileadb shell find /sdcard ...adb shell stat -c '%s\t%Y\t%n'adb pulladb shell rm -rf -- <path>To support both backends without duplicating UI code, AndroidMediaFile now wraps a backend-neutral location:
.mtp(id: UInt32).adb(path: String)The UI selects rows using a stable string selectionID, so MTP object IDs and ADB paths can both participate in selection, navigation, copy, and delete flows through the same views.
ADB fallback is useful, but it has tradeoffs:
/sdcardLame tries to recover file size and modified date with stat, but shell capabilities vary across Android builds. When metadata is unavailable, the app still browses the file tree and shows unknown size/date where needed.
Android support now works with either of these setups:
brew install libmtpbrew install android-platform-toolsIf neither toolchain is installed, the sidebar shows a single install hint covering both.
The fallback touches these core files:
Lame/AndroidDeviceManager.swift: backend selection and shared Android stateLame/MTPBridge.swift: libmtp subprocess bridgeLame/ADBBridge.swift: ADB subprocess bridgeLame/AndroidMediaFile.swift: backend-neutral Android file modelLame/AndroidMediaGridView.swift: Android media UILame/AndroidFileBrowserView.swift: Android file browser UIFor normal Android usage:
brew install libmtpFor fallback usage:
brew install android-platform-toolsIf both are installed, Lame will prefer MTP and only fall back to ADB when MTP enumeration fails.