An offline, open-source Android file manager with X-plore's workflow — dual-pane tree browsing, archive-as-folder, app manager, APK/AAB/XAPK install, root & Shizuku access — on the latest Android stack with a Material 3 Expressive UI.
English · 简体中文
Real capture on a OnePlus 7 Pro (Android 16), sped up. One run: file copy → App manager (an app's components & APK splits) → Root (the real filesystem, /data and all).
- X-plore broke on Waydroid over the past half-year; I needed a replacement.
- It's the LLM era — if a tool doesn't fit, build your own.
- Software with dangerous root powers should be open-source, fully offline, and
collect nothing. XFiles has no
INTERNETpermission and no analytics.
Grab an APK from Releases:
vX.Y— stable, cut wheneverversionNameis bumped.nightly— a single rolling prerelease, refreshed on every push tomain.
Requires Android 8.0 (API 26) or newer. On first launch, grant All files access (the app deep-links to the system page). Or build it yourself.
X-plore's signature: two independent panes — side-by-side on wide screens, a swipeable pager on phones. Folders expand in place as a tree with item counts and branch guide lines along the focused folder, and each pane carries its own floating breadcrumb pill. On phones, a target chip at the top keeps the hidden pane's folder visible and switches to that pane when tapped.
Archives sit in the tree like any other folder — the breadcrumb descends straight into
project.zip.
Images and video poster frames render inline. Video frames are extracted once at
thumbnail size and disk-cached, so they are instant after a restart, with a play badge
and an icon fallback while loading. .apk files and split bundles (.apks / .apkm /
.xapk) wear the launcher icon of the package inside (also disk-cached), and app
folders under Android/data show their owning app's icon.
Multi-select via right-edge checkmarks. The other pane is the destination for
copy, move, zip and extract: set its folder, return to the source pane, then run the
operation directly. Copy to… / Move to… in the long-press menu remain available when
you want a one-off explicit destination.
Deleting on internal storage, an SD card, or a USB drive moves files to the volume's Recycle Bin for easy restore; emptying the bin — or deleting under Root or a network location — removes them permanently. Also supports rename, new folder, and new text file.
A background engine drives it all with progress (the wavy Expressive indicator), cancellation, and Skip / Overwrite / Keep-both conflict resolution. Long copy, move, zip and extract operations keep running in a foreground service when the app is backgrounded, shown in an ongoing notification with a Cancel action and a wake lock; the service self-stops when idle.
Mounted USB OTG drives appear as pane roots next to internal storage and SD cards, and the list updates automatically when a drive is plugged or unplugged. On Android 8–10, writes to SD cards, USB drives and other secondary volumes work through a one-time SAF grant.
Add location (at the bottom of each pane or in Settings) grants a document tree from
another app — RSAF (rclone), CIFS Documents Provider, Nextcloud, or any
DocumentsProvider — and pins it as a pane root. Copy and move then stream through the
system; XFiles still has no INTERNET permission, so the other app is what actually
talks to the network. A tree that is already local storage is pinned as a favorite
instead of a duplicate root. Step-by-step:
Add a network location
(source).
Browse zip/jar/apk, 7z, tar(.gz/.bz2/.xz) and rar read-only; extract by copying out. Anything installable installs from right there — see below.
Creation deflates every entry across all CPU cores (commons-compress
ParallelScatterZipCreator, STORE for already-compressed media). Extraction runs one
ZipFile handle per worker off a shared queue. Zip-Slip guarded; falls back to
single-threaded streaming when temp space is tight.
Installed and system apps in two groups, with real icons, version/package badges and rich details. Install, launch, uninstall, or copy an APK out to share an app as a file.
Expand an app and you get everything that belongs to it in one place: a Components
node broken down into activities / providers / receivers / services, plus base.apk and
every split_config.* APK — each one expandable, because an APK is just a zip.
Drill into a category and each component shows its class name and its real manifest
state — exported / not exported, enabled / disabled. Launch activities, create
shortcuts, and enable/disable components where the system permits.
APKs install with a tap — and so does everything APK-shaped: split bundles
(.apks / .apkm / .xapk, with XAPK OBB expansion files placed where the game
expects them) and even raw .aab files. A vendored
bundletool converts the bundle on the phone
into split APKs matched to the device and signs them with a built-in certificate — no
PC, no Play Store. Installs run in the foreground service, so backgrounding the app
mid-install doesn't kill the session.
On by default. A Root entry (/) sits with the storage roots — turn Root access
off in Settings to hide it. A separate Read-only switch (also on by default) blocks
anything needing privilege to write, so you can go look without being able to break your
system.
Two interchangeable transports power it, and Settings lets you pick (or leave it on auto):
su— full superuser on rooted devices./dataopens up toadb,anr,app,app-private,dalvik-cache— directories a normal app can't even list — with list/read/write/mkdir/rename/delete under/data,/system, …- Shizuku — no root required: XFiles binds a Shizuku
user service running at shell (ADB) privilege that hands over real file descriptors.
Settings walks you through setup and permission. Without
su, Shizuku can still list/and browse what the adb shell can see (/system,/proc,/storage,Android/data), while/dataand/data/datastay closed until superuser is granted.
Whichever transport is live also kicks in transparently where plain file access is
denied — most notably Android/data and Android/obb, which open like any
other folder. Thumbnails, viewers and even video playback work on privileged paths.
Built-in PDF viewer, image viewer (pager + pinch zoom), hex viewer with on-demand paging, audio player, text viewer & editor (loads large files in on-demand stretches rather than all at once, with undo/redo, line wrap and GBK detection), and a video player (Media3/ExoPlayer) with frame-accurate stepping.
In the video player, tap the time readout to swap it for a frame counter — current frame, total, and the real frame rate — then step ±1 frame, swipe on the picture to scrub by time or frames with live preview, drag the compact control card out of the way, or go fullscreen immersive.
Live streaming recursive search with */? wildcards. Descends into archives and
network folders, and reveals results in the tree on tap. A long search pauses after
each ten-second round and asks whether to keep going or search to the end. Switch
from the current folder to All storage to search every volume; on Android 11+
that reads the system's file index instead of walking each folder, so results arrive
almost at once.
Off by default so XFiles never hijacks anything. Five opt-in toggles in Settings register XFiles with the system resolver for archives, images, videos, PDFs and text files — after that, "open with XFiles" from any app lands in the archive tree or the right viewer.
MaterialExpressiveTheme + expressive motion, dynamic color (Android 12+),
light/dark/system, floating bottom toolbar, LoadingIndicator /
LinearWavyProgressIndicator, and Activity-style predictive back (Navigation 3).
True edge-to-edge: no top app bar — content scrolls under the status bar behind a
gradient scrim, with a floating breadcrumb pill. Settings also covers hidden files,
folders-first, sort key and direction.
The UI follows the system language: English plus Arabic, Chinese (Simplified and Traditional), Dutch, French, German, Hindi, Indonesian, Italian, Japanese, Korean, Polish, Portuguese (Brazil), Russian, Spanish, Turkish and Vietnamese.
No network permission, no telemetry, no accounts, no ads. Every permission the app declares, and why:
| Permission | Why |
|---|---|
MANAGE_EXTERNAL_STORAGE |
Browse and modify all of shared storage — the whole point of an X-plore-style manager |
READ_EXTERNAL_STORAGE (≤ API 32) |
Legacy read path on older Android |
WRITE_EXTERNAL_STORAGE (≤ API 29) |
Legacy write path on older Android |
QUERY_ALL_PACKAGES |
The App manager lists what is installed |
REQUEST_DELETE_PACKAGES |
Uninstall from the App manager |
REQUEST_INSTALL_PACKAGES |
The package installer: APKs, split bundles (.apks/.apkm/.xapk), AABs |
POST_NOTIFICATIONS |
Show the progress notification for long operations |
FOREGROUND_SERVICE, FOREGROUND_SERVICE_DATA_SYNC |
Keep a copy/move or install running when backgrounded |
WAKE_LOCK |
Don't sleep mid-operation |
INTERNET |
Not requested. The app cannot talk to the network at all |
That last row is enforced by the OS, not by policy — verify it yourself in
AndroidManifest.xml or with
aapt dump permissions on the APK.
| Layer | Choice |
|---|---|
| Language / UI | Kotlin, Jetpack Compose (BOM 2026.09.00), material3 1.5.0-alpha28 (Expressive APIs), Navigation 3 (predictive back) |
| Build | AGP 9.4.1 (built-in Kotlin, no KGP), Gradle 9.7.1, compileSdk 37 / target 37 / min 26, Baseline Profile (:baselineprofile + ProfileInstaller) |
| Architecture | MVVM + StateFlow, manual DI composition root (di/Graph); app module + a shaded bundletool vendor module |
| Persistence | DataStore Preferences |
| Media/Images | Coil 3 (GIF, custom fetchers: app icons, APK/bundle icons, disk-cached video thumbnails), Media3 ExoPlayer |
| Archives | java.util.zip, commons-compress (+xz), junrar |
| Privileged access | Shizuku 13.1.5 (user service, real fds) · su shell |
| Package install | PackageInstaller sessions · vendored bundletool 1.18.3 · ARSCLib (in-process aapt2) · minimal self-signed signer |
Note: material3 is pinned to the 1.5 alpha line (1.5.0-alpha28) because the
Expressive APIs are internal in the 1.4.0 stable release.
app/src/main/java/app/local1st/files/
├── core/
│ ├── fs/ XEntry model, XId id scheme, XFileSystem + FsRegistry,
│ │ │ Local/Archive/Apps/Root/Saf/Trash filesystems, storage roots, legacy SAF writes
│ │ └── priv/ privileged transports — su shell & Shizuku user service (real fds)
│ ├── ops/ OperationEngine (copy/move/delete/compress + conflicts), OpsService
│ ├── search/ recursive SearchEngine
│ ├── prefs/ DataStore settings
│ ├── thumb/ Coil fetchers: app & APK/bundle icons, disk-cached video thumbnails
│ └── util/ formatters, mime/category mapping, intents; package install —
│ PackageInstaller sessions, AAB→APKs (bundletool), XAPK/OBB,
│ in-process aapt2 (ARSCLib), self-signed signing
├── di/ Graph (composition root) + GraphInit wiring
└── ui/
├── browser/ PaneController (tree state machine), PaneView, EntryRow
├── components/ shared Compose bits (tooltips, predictive back)
├── main/ MainViewModel, MainScreen (dual pane + floating toolbar), PermissionGate
├── dialogs/ rename/new-folder/new-file/delete/zip/details, ops progress + conflicts
├── viewer/ PDF / image / text / hex viewers, audio player, frame-accurate video player
├── search/ search overlay
├── settings/ settings screen
├── appinfo/ app details overlay
└── theme/ MaterialExpressiveTheme setup
baselineprofile/ Baseline Profile generator (:baselineprofile) for AOT compilation
vendor/bundletool-shaded/ Gradle module shading bundletool 1.18.3 + its pinned deps
Entry ids are URI-like strings: file:///abs/path,
zip:///abs/archive.zip!/inner/path, apps://package.name, root:///abs/path,
saf://{locationId}/…, trash://{volumeId}/….
./gradlew :app:assembleDebug
adb install -r app/build/outputs/apk/debug/app-debug.apkRequires JDK 17+ and an Android SDK with platform 37. On first launch grant "All files access" (the app deep-links to the system page).
A GitHub Actions workflow (.github/workflows/release.yml)
builds a signed APK on every push to main on GitHub-hosted Ubuntu runners:
- The build number (
versionCode) increments each run (github.run_number). versionNamelives inversion.properties. While it's unchanged, each push just refreshes a single rollingnightlyprerelease with the latest build. BumpversionNameto cut a new stablevX.Yrelease.- Signing keys/passwords come from repo secrets:
KEYSTORE_BASE64,KEYSTORE_PASSWORD,KEY_ALIAS,KEY_PASSWORD.
GPL-3.0-only. A file manager that can be handed root deserves a licence that keeps every future copy open — if you ship a modified XFiles, ship its source too.
This is a study/clone project inspired by X-plore File Manager; it shares no code or assets with the original.