The root screen opened on a list of volumes, so "where are my photos?" began with knowing which folder they were in. It now opens on Libraries - Images, Videos, Audio, Documents, Archives and Recent - each row saying how many files the index knows of that kind, and each opening an ordinary listing with thumbnails, sorting, multi-select, details and the existing viewers, so a tap on a photo opens the image viewer and a tap on a PDF the PDF viewer. Downloads and New files are deliberately absent: one is a folder shortcut and the other a time window, and presenting three different things as one feature is how a listing starts lying about itself. Documents includes .txt, because a user looking for their documents means those too, and DIRECTORY, APK and OTHER belong to no library at all - a test asserts the three omissions so they stay decisions rather than oversights.
A library has to ask for "every image" without reading a name, so IndexedFile gained a derived kind beside FileEntry's and the index stores a copy of it to filter on: schema v3, with a migration that rebuilds the files table and deletes the volume stamps with it, because a stamp means "the rows behind this are this current" and a rebuilt table has no rows behind it. Rebuilding rather than backfilling is legitimate only because this index is a cache of a walk that can be repeated; SqliteTrashIndex's rule is the opposite and its own documentation says why. A library then obeys the indexed search's two rules exactly: the answer comes first with the walk behind it, and every row is confirmed against the filesystem before it is shown, with the rows whose file has gone forgotten rather than displayed. On a device that has never been indexed the first tap on Images therefore starts a walk - which the screen admits to, using the progress row the search already had, instead of showing an empty library that reads as an empty device.
"Find duplicates" in the overflow compares contents in three passes, cheapest first: size, which the index already knows, so the candidate list is one query and reads nothing at all; the first 64 KB of every candidate, a filter whose only power is to rule files out; and the whole file for whatever survives, which is the pass that decides. The separation is enforced by a test that builds two files sharing their first 64 KB and differing afterwards and asserts both were read in full - it would fail if the cheap pass were ever treated as an answer. The screen shows only what was measured: groups ordered by the space deleting the spare copies would give back, each header stating the copies and what they cost, every row carrying its folder as well as its size, and nothing preselected, because which copy to keep is the one decision the screen exists to hand over. A file whose bytes cannot be read is counted and reported rather than skipped, "nothing found" is never claimed without saying how many files were compared, and deleting closes the scan instead of patching it - its rows came from comparing bytes, and quietly dropping some of them would leave a list that still looks like a scan while its headers claim numbers nobody measured.
The thumbnails went from 28 to 48 dp in a list row and 40 to 96 dp in a grid cell, with the grid's minimum column width raised to 144 dp to suit them; ListItem measures its leading slot rather than capping it, so a row grows to fit instead of clipping. That turned out to matter: the commonest complaint in the competitive survey is thumbnails that load only once scrolling stops, which is the failure mode a larger thumbnail invites. Two smaller corrections came out of the same work - a mutation now re-reads whatever is on screen, so a delete from inside a library removes the row instead of doing nothing at all because there was no path to reload, and returning to the app re-reads a library rather than dropping the user back to the roots.
docs/COMPETITIVE_ANALYSIS.md records the market leader's features and what its users praise and complain about, with the sources that were blocked and the size of the review sample stated rather than glossed over; it is where both features came from, and the gaps that cost no permission are listed as candidates in the roadmap. 323 JVM tests (34 added across the two features), lint clean.
Tested on the real phone - the same M2101K7AG, with the debug build installed past MIUI's two gates. The libraries on a cold index (no counts rather than a row of zeroes, "Indexing 300 files..." on the first tap, then "Index updated" and real thumbnails), the counts afterwards (Images 6238, Recent uncounted), a tap opening the viewer and BACK returning with the scroll position, and a duplicate scan that found four genuine groups in the device's own storage with nothing crashing. The duplicate delete was deliberately not pressed on a device that is not ours to empty, the store screenshots predate the new home screen, and the library views turn the M10 screenshot rule into a privacy question as well as a cosmetic one - all of that is recorded in docs/EDGE_CASE_TESTING.md rather than left implicit.
# Competitive analysis: File Manager Plus (AlphaInventor)
A structured look at the category leader we are actually up against, and what its users say
about it. The point is not to copy it — most of what it does needs `INTERNET`, which this app
will not declare — but to know (a) which of its features are table stakes for a file manager,
(b) which of its users' complaints we can answer for free, and (c) which of our differences are
worth putting in the listing.
Surveyed **2026-10-17**. Sources and their limits are at the bottom; every claim here is
attributed, and the ones I could not confirm are marked as unconfirmed rather than smoothed over.
## The app at a glance
| | |
|---|---|
| Play listing name | **File Manager** (branded in-app as **File Manager Plus** / **File Manager +**) |
| Package | `com.alphainventor.filemanager` |
| Developer | 주식회사 알파인벤터 (AlphaInventor Inc.), Seoul, South Korea |
| Category / rating band | Productivity · Everyone / PEGI 3 / "Rated for 3+" |
| Play rating | **4.8★** on the default (en) listing, 4.7★ US/GB/NG, 4.6★ IN — **1.71 M ratings** (1.54 M in the breakdown) |
| Installs | **100 M+** |
| Money | **Free, ad-supported, plus in-app purchases** — the listing is labelled "Contains ads · In-app purchases" |
| Version / size / age | **3.8.3**, updated **Aug 6, 2026**; ~12.9 MB APK; Android 5.0+ |
| Form factors | Phone, tablet, **Android TV** (the ratings breakdown also shows a desktop/ChromeOS bucket) |
| Declared data safety | May **share***Device or other IDs* with third parties; may **collect***App activity*, *App info and performance*, *Device or other IDs*; encrypted in transit; **"Data can't be deleted"** |
| Support | `app.support@alphainventor.com`, +82 70-4509-2539 |
The two lines that matter most for us are the money row and the data-safety row: this is a free
app whose feature surface is paid for by ads and by sharing a device identifier with third
parties, and whose own Data Safety form says data cannot be deleted on request. That is the
sharpest contrast available against a one-time-paid app with no `INTERNET` permission.
## How this was researched — and what that does not tell you
| Source | What it gave |
|---|---|
| Play listing, fetched in `en`/US, GB, IN, CA, NG | The developer's own description, "What's new" block, price/ads labels, Data Safety form, rating, installs, and the *"most helpful"* reviews (about three per locale) |
| APKPure mirror | Version history, APK size, a security scan (0/34 vendors flagged) and a **third-party** prose description |
| Play **search** pages for the query `file manager`, fetched in 10 storefronts (IN, US, BR, ID, PH, NG, MX, VN, DE, KR) | Where the app ranks in the organic results, and its star rating *per storefront* — see "Markets" |
| Everything else | AppBrain, APKGK, apk.support all returned **HTTP 403**; SimilarWeb needs JavaScript; DuckDuckGo's HTML endpoint served a bot challenge; Reddit's JSON API returned 403; Uptodown 404 and Softonic 412 |
Four honest limitations, because they shape how much weight the review section deserves:
- **The review sample is ~10 reviews, not a survey.** Play's server-rendered page only carries a
handful of "most helpful" reviews per locale, and it repeats them across regions — the same
Brian Witte review appeared in five of the five locales fetched. Treat the themes below as
*hypotheses about the user base*, not as a measured distribution. The 100 M+ installs and
4.8★ are the measured numbers; the complaints are anecdotes that recur.
- **Per-review star ratings were not in the fetched HTML.** Only the aggregate is. So no star
value is attributed to any review quoted below — the ones shown carry a date and a
*"N people found this review helpful"* count instead, which is the honest substitute.
- **Reviews are user claims, not facts.** Several complaints are performance reports; those can
be device- or SD-card-bound, and the developer says as much in a public reply (quoted below).
Nobody here re-tested the app.
- **APKPure's description contradicts the developer's own listing.** It claims RAR creation,
duplicate detection, cache cleaning and link sharing, none of which the store listing mentions
(which offers **zip** for compression and **zip/gz/xz/tar** for decompression). That prose
reads like generated copy. It is recorded below as *unconfirmed*, not as a feature.
## What it ships
Grouped by what a user is trying to do, with the source for each claim:
| Capability | Detail | Where it comes from |
|---|---|---|
| Internal storage / SD card / USB OTG | "manage all files and folders on both your internal storage and external storage" | Store description |
| Automatic category libraries | **Downloads · New files · Images · Audio · Videos · Documents · Apps**, "automatically sorted by their types and characteristics" | Store description |
| App manager | "see and manage all applications installed on your local device" | Store description |
| Built-in viewers | "Internal image viewer / Internal music player / Internal text editor … for faster and better performance" | Store description |
| Cloud targets | **Google Drive, OneDrive, Dropbox, Box, Yandex** (+ **pCloud**, added in 3.7.3) | Store description / What's new |
| Remote / network targets | **NAS**, **FTP server**, and — from the changelog — **FTPS** (3.8.3 fixes "an FTPS connection error"); APKPure additionally says **SMB** | What's new / APKPure |
| Phone → PC access | runs an FTP server so a computer can reach the phone's storage | Store description |
| Storage analysis | "analyze local storages to clean up useless files… which files and apps take up the most space" | Store description |
| Full action set | "open, search, navigate directory, copy and paste, cut, delete, rename, compress, decompress, transfer, download, bookmark, and organize" | Store description |
| Media + APK formats | "for various file formats including media and apk" | Store description |
| Form factors | "Android TV, phone and tablet" | Store description |
| Post-paste conveniences | **"Copy again"** and **"Return to source folder"** after pasting (3.6.8) | What's new |
| Freshly-changed files stand out | "Temporarily highlight new files with a dot after file operations" (3.6.5) | What's new |
| Settings depth | switchable music-player engine, a date-format setting (3.7.3), a toggle for video-player gestures (3.6.8) | What's new |
| Localisation | multi-language (APKPure lists a long language set for its own mirror; the Play listing is served localised) | APKPure (unconfirmed in detail) |
| *Unconfirmed* | RAR **creation**, **duplicate** detection, cache cleaning, share-by-link | APKPure prose only — absent from the developer's listing |
Two observations from that table:
- **The changelog is the more truthful feature list.** The store bullets stop at "FTP server",
while the "What's new" block shows the network surface is actually broader (FTPS, and pCloud
as a seventh cloud). For a listing audit, the *changelog* is where a competitor's real surface
shows up — worth remembering when writing our own "What's new".
- **Maintenance looks genuinely active.** 3.8.3 → 3.8.2 → 3.7.2 all landed inside 2026, and the
entries are specific ("Fixed an FTPS connection error", "pCloud support added") rather than
only "bug fixes and performance improvements". A competitor that ships is harder to beat on
freshness; we win on the permission set and the business model, not on cadence.
## What users value
Six themes, each with the review it comes from. All quotes are verbatim (trimmed); "N helpful"
is the count Play showed beside the review.
1.**It is simple *and* powerful, at the same time.** This is the single most repeated remark,
and it is always the same shape: the UI is unfussy, the capability is not.
> "it's quick, it's easy, and clean to look at, and so nice to organize your stuff"
> — hennie postma, Jun 25 2025 · **998 helpful**
>
> "easy to use and easily organizes my all files according to types, sizes, and dates … I
> preferred this app than other apps like Google folder and system folder because it easy it
> has not so many options, so you can easily access this"
> — Nirmalya Chakraborty, Jun 30 2024 · **540 helpful**
>
> "Love this app for it's simplicity while still being powerful."
> — Karen Hellawell, Sep 8 2026 · 6 helpful
2.**Feature depth is valued even when unused.** A high-helpfulness review says this explicitly:
> "There's a huge amount of powerful functionality that I'll not likely use/need, but it's
> always better to have it than vice versa…!"
> — Ivan Lewis-Coker, Jul 1 2024 · **392 helpful**
3.**The storage-analysis screen is a reason to install.** This is the only feature a user names
as the reason they came:
> "I especially enjoy the analyse feature to find large files and folders … It's what brought
> me to this app in fact."
> — Sean Nym, Aug 19 2024 · **166 helpful**
4.**Search that answers quickly.**
> "The search function is quick and delivers good results."
> — Sean Nym, Aug 19 2024
5.**Bulk network transfer is the killer feature nobody markets.** The most enthusiastic review
is about one big push to a PC over the app's own FTP server:
> "I literally just transferred my ENTIRE picture/video/music collection to my computer
> (equalling out to around 150-185gigs …) in around 3-4min... And I did it all at once!"
> — Duncan Elliott, Jul 21 2026 · 22 helpful
6.**The ads are tolerated — and one user does not even see them.** Three reviews say the ads
do not get in the way, and one asserts there are none at all:
> "there are some ads, but it's a little bar that sits at the bottom of the screen, and they
> haven't bothered me at all yet. none that jump you while you're doing your thing."
> "The only other functionality I'd request is the ability to have **two side by side file list
> views which you can swipe between** so you can, for example, easily verify what & where you're
> cut/copy/pasting to/from."
> — Ivan Lewis-Coker, Jul 1 2024 · **392 helpful**
Note the last one carefully: **the second most-helpful review in the sample asks for the
two-pane layout this app already ships** (a master/detail split at ≥ 840 dp, with a single tap
to focus and a double tap to open). It is not a small thing — it is the request of a *Total
Commander* user, i.e. exactly the file-manager power user a paid app should be selling to.
## Markets: where it is strongest
**No public per-country install or revenue figure exists for this app**, and that is the honest
answer to "which countries are best for it". Every source that would give one is gated — Sensor
Tower, data.ai, AppMagic, Apptopia and SimilarWeb are all login/paywall (and their free pages are
JavaScript-only, so a plain fetch returns an empty shell) — which leaves the developer's own Play
Console as the only place that number actually exists. What *is* publicly measurable is where the
app ranks. It is the **first organic result for the query "file manager" in 9 of 10 storefronts**
sampled, and second in the tenth:
| Storefront | Rank for "file manager" | Its rating there |
|---|---|---|
| India | **#1** | 4.6★ |
| United States | **#1** | 4.7★ |
| Brazil | **#1** | 4.8★ |
| Indonesia | **#1** | 4.8★ |
| Philippines | **#1** | 4.7★ |
| Mexico | **#1** | 4.9★ |
| Vietnam | **#1** | 4.5★ |
| Germany | **#1** | 4.8★ |
| South Korea | **#1** | 4.9★ |
| Nigeria | #2*(behind Files by Google)* | 4.7★ |
What that does and does not say:
- It says the app owns the **category query** almost everywhere. For a free, ad-funded app revenue
is installs × ads, and the door to installs is precisely this query — first place in the biggest
Android markets is the whole business.
- It does **not** say which countries install it most. A #1 slot in India and a #1 slot in the
United States are nothing like equal in installs and are wildly unequal in revenue per install.
- Search is personalised and the fetch is unauthenticated from a datacentre, so read the ranks as
"consistently first", not as a fixed position.
- The per-storefront ratings do differ (4.5 Vietnam → 4.9 Mexico and South Korea). That part is
measured — but a rating is sentiment, not market size, and must not be read as share.
**Inference, not measurement:** for a free file manager the largest markets are structurally the
large-population Android markets — India, Brazil, Indonesia, the US, Mexico, Vietnam, the
Philippines, Nigeria — while the highest revenue *per install* sits in the US, Germany, South Korea
and Japan. Where this app's revenue actually leans cannot be established from outside.
One useful side-effect of the same search results, for our own [MONETISATION](MONETISATION.md)
check: the paid rivals show local price points where our €4.49 base and €2.29 launch price assume
they are — **US$6.99** for MiXplorer Silver (and **₩10,000** for the same app in Korea), **R$3.49**
and **MX$13.00** for Simple File Manager Pro, **€0.99** in Germany, **₫52,000 / ₫180,000** in
Vietnam.
## What this means for Plain Files
### Where we already match or beat it
| Their capability | Ours |
|---|---|
| Internal storage / SD card / USB OTG | Same — `VolumeRepository` enumerates mounted volumes (ROADMAP M2) |
| Full action set (copy, cut, delete, rename, compress, decompress, transfer, search) | Same, plus a real **trash with restore** and undo, which their listing does not mention |
| Decompression (their *listed* set: zip, gz, xz, tar) | Broader: **zip, tar, tar.gz/tgz, tar.bz2, tar.xz, gz, bz2, xz, and 7z** — 7z included, on the all-files backend |
| Storage analysis | Same idea, and honest about cost: one listing per directory instead of a `stat` per node, rows streamed as measured (M5/M7) |
| Built-in viewers | Image, text editor, PDF, media, archive list **and** in-archive entry preview, APK info |
| Search | Indexed search everywhere over SQLite, with the index's **age shown** and a rebuild action |
| Two-pane | **Present** — master/detail at ≥ 840 dp. The second most-helpful review in our sample is a request for exactly this |
| Ads / accounts / analytics | **None**, and no `INTERNET` permission at all |
The two rows worth putting in the store copy are the last two: "two-pane on a tablet" answers a
requested feature in the market leader, and the permission line is a claim it cannot make.
### Where we are behind, and each gap's real cost
| Gap | Costs a permission? | Notes |
|---|---|---|
| Automatic category libraries (Images / Audio / Videos / Documents / Downloads / New files / Recent) | **No** | The most-praised organisational feature in the reviews, and the one thing a brand-new user sees before they understand folders. We open on a volume list instead. This is the largest honest gap |
| Duplicate finder | **No** | Explicitly requested, with an offer to pay. Pure local computation over a listing we can already do |
| Batch rename, including continuing an existing numbering sequence | **No** | We have single-item rename only; "Ice (3), Ice (4)" is a stated wish from the top complainant |
| Bookmarks | **No** | Their action list includes it; we have none |
| "Copy again" / "Return to source folder" after a paste | **No** | Shipped in their 3.6.8 and cheap for us — the post-paste moment is when a user most needs a way back |
| New-file dot after an operation | **No** | Their 3.6.5. Cheap, and it answers "what did that 4 GB copy just do?" |
| Date-format setting | **No** | We deliberately leave the timestamp pattern unlocalised (README); a *user setting* is a different thing |
| Music-player engine switch, video gesture toggle | **No** | Deep settings; low priority for a first release |
| Android TV | **No** (form factor, not permission) | Their listing supports it; ours does not |
| App manager | Yes — `QUERY_ALL_PACKAGES` | Already deferred in the ROADMAP |
| Cloud + NAS + FTP/SMB/SFTP, and an FTP server on the phone | Yes — `INTERNET` | Deferred by decision. This is the axis we give up on purpose, and their most enthusiastic review is about it |
### Actionable now: the thumbnail complaint is about us too
We have just made thumbnails bigger. The top complaint about the market leader is thumbnail
latency, so this needs to be pinned down rather than assumed:
1.**Coil 3 sizes its decode to the composable**, so a 96 dp grid tile does not decode a 12 MP
JPEG — that is the mechanism keeping us out of the "loads only when scrolling stops" trap.
Worth writing down as the reason it is safe, and worth not breaking.
2.**No `ImageLoader` is registered** (`FileManagerApplication` builds no Coil component), so
Coil's defaults are what applies: no explicit memory- or disk-cache policy, no placeholder
and no error state for a tile that is still decoding. A placeholder-sized box matters here —
without one, a slow decode is also a layout shift.
3.**Not yet exercised on a hard case.** The enlarged sizes have not been run against a folder
with thousands of images on a device. `docs/EDGE_CASE_TESTING.md` is the right place to record
that pass, with the decode time observed rather than assumed.
4.**Keep the grid size moderate.** 96 dp is a deliberately visible thumbnail; if a large-folder
pass shows jank, the first knob is the grid size, not the list size.
### Themes in their feedback that argue for our existing choices
- *"It's what brought me to this app"* was the storage-analysis screen — we have one.
- *"Simple UI"* plus *"huge amount of powerful functionality"* — the reviews consistently reward
depth that stays out of the way; our details sheet / selection bar split is that shape.
- Their timeout complaint was reported as a **misleading network error**. Our own rule — state
the limit plainly ("7z needs a seekable channel; not available in folder mode") — is the same
lesson learned the other way round.
## What I would do with this
In the ROADMAP's own vocabulary (💡 = candidate, proposed, not scheduled):
@@ -115,6 +115,56 @@ The lesson is the one the phone already taught twice: a green test suite says th
what the tests describe, not that the app does anything on a device. Both times the gap was a
user-visible promise the code no longer kept — once in a dialog, once in a permission.
## The library and duplicate session (M11)
Run **2026-10-17** on the same **Xiaomi M2101K7AG** (Android 12, MIUI), with the debug build
(`de.softwareapp_hb.filemanager.debug`, installed with `adb install`). The device was **cold** for
the index: the app had never run on it, so "the first tap on a library starts a walk" was tested as
a first run rather than as a re-run. First-run permissions were granted from the host
(`appops set … MANAGE_EXTERNAL_STORAGE allow`, `pm grant … READ_EXTERNAL_STORAGE`) rather than
through the prompt, because the prompt was already covered in the M7 session.
| What | Result |
|---|---|
| Home screen, before any index exists | **Correct, and the important part is what is absent.** The **Libraries** section renders above the volume with all six rows and their icons, and **no counts at all** — not a row of zeroes. That is the design: an index that has never run cannot say "you have no photos" |
| Opening a library on a cold index | **Correct.** Title `Images`, no breadcrumbs, no new-folder action, an empty listing, and the honest status row: **"Indexing 300 files…"** with a progress bar and Rebuild beside it. The answer came first and the walk ran behind it |
| The walk finishing | **Correct.** The status row became **"Index updated 2026-10-17 01:01"**, and the library filled with real files and real thumbnails |
| Counts after the walk | **Correct.** Images **6238**, Videos 169, Audio 196, Documents 160, Archives 5, and **Recent uncounted** — the one library that is a window rather than a set, which is what the code says it should be |
| Tapping a file inside a library | **Correct.** A tap opened the image viewer with that file's name in its bar, and `BACK` returned to the library listing with the index row and the scroll position intact |
| The duplicate scan | **Correct.** Tapping *Find duplicates* in the overflow ran the scan and produced real groups: `DOC-20251121-WA0001.zip` and `Schwarzhofen.zip` at 10.5 MB each (identical content in `Android/media/…` and `Download/`), a PDF duplicated at 5.3 MB, another PDF present **three** times at 761.3 KB. Group headers read "2 copies · 10.5 MB" above "10.5 MB could be freed", each row carried its **folder path**, and the groups were ordered by reclaimable space |
| Ticking a copy | **Correct.** The row highlighted and the bar action appeared as **"Delete 1 copy"** — the plural resource agrees with the count |
| Closing the scan | **Correct.**`X` returned to the browser with nothing changed |
| Crashes | **None.**`logcat -b crash` contained no entry for the package across the whole session |
### Two findings worth keeping
- **The library views are device-wide, so they cannot be photographed on a real phone.** The
`Images` list showed the owner's own files — names and thumbnails included — because a library
is not a folder and does not respect one. The M10 rule ("never shoot over a real device's
storage") now applies to the *home screen too*, since its counts expose how many files of each
kind the device holds. See [STORE_LISTING](STORE_LISTING.md).
- **MIUI blocks `adb install` twice over, and both gates are on the device, not in the tooling.**
Developer options needs *Installieren über USB* enabled, and each install then waits for a
confirmation dialog (`com.miui.securitycenter/…AdbInstallActivity`) that times out on its own —
which is what the bare `INSTALL_FAILED_USER_RESTRICTED: Install canceled by user` means. Worth
knowing before blaming a build. A second, cheaper lesson came from this session: **do not run
`assembleDebug` and `adb install` in the same parallel batch.** They raced, the install took the
previous APK, and the new menu entry was missing while the host APK contained it — a confusing
twenty minutes caused entirely by tooling, not by code.
### Deliberately not done on this device
- **The duplicate *delete* was never pressed.** The scan is read-only and safe to run on someone
else's phone; deleting files is not, and the delete itself is the ordinary, already-covered
delete path (trash + undo + the platform dialog where it applies). The selection UI was driven
to the point of the action label and then left untouched.
- **The walk was not timed end to end** on a large volume. It finished inside the 20 seconds this
session waited, but that says nothing about a phone with a far larger library; the M7 note about
a 45–60 s usage walk is the honest precedent.
- **The 7-inch and 10-inch tablet layouts with the new section** were not re-shot or re-checked:
the AVDs have no `/dev/kvm` here, and the two-pane root list is where the libraries now appear
first.
## Not verified, and why
- **Long-press multi-select via adb: it works, but not every time.** `input swipe x y x y 900`
@@ -20,6 +20,8 @@ Legend: ✅ done · 🚧 in progress · ⏳ not started · 💡 candidate (propo
| M8 | Localisation groundwork | ✅ | Every user-visible string is in `res/values/strings.xml` with real `<plurals>`, and `text/TextRes.kt` maps **both**`OperationKind` and `FileKind` onto resources so the screen, the notification and the details sheet cannot drift. The audit found a genuine miss: the details sheet rendered `FileKind.name`, i.e. untranslatable English — now a resource. `isPseudoLocalesEnabled = true` on the debug build makes "did every string come from resources?" mechanically checkable; verified `en-rXA` is present in the debug APK and the flag is debug-only. Byte units and the timestamp pattern are deliberately unlocalised, and the README says why. **142 JVM tests** |
| M9 | Monetisation | ✅ | **Decided: one-time paid — €4.49 base with a 14-day launch price of €2.29**, both set in the Play Console (*Products › App pricing*), so there is **no Billing client, no `com.android.vending.BILLING` permission and no purchase code**. Rationale, market anchors and Console steps: [MONETISATION](MONETISATION.md). Verified against the release APK: zero billing and zero `INTERNET` references. **The price must be set before the first upload** — Play allows paid → free but never free → paid |
| M10 | Store assets | ✅ | Name decided: **Plain Files** (one string; `applicationId` already locked). `tools/make-store-assets.sh` renders the 512×512 **RGBA** icon and the 1024×500 **RGB** feature graphic from the app's own vectors, so the store icon *is* the launcher icon; `tools/make-store-screenshots.sh` normalises `adb` captures to Play's 2:1 cap per form factor (phone crop default 93/130 px, tablets 0). [STORE_LISTING](STORE_LISTING.md) carries length-checked copy (name 11/30, short 67/80, full 2730/4000), the permissions paragraph the `MANAGE_EXTERNAL_STORAGE` declaration requires, and the graphics table. The feature graphic's copy was verified to survive Play's centre-square crop by cropping the rendered PNG. **Screenshots captured:** 5 phone (1080×2160), 4 on a 7-inch tablet (1200×1920) and 4 on a 10-inch tablet (2560×1600), all 24-bit RGB, inside the 2:1 cap. The tablet sets came from AOSP AVDs. The normaliser refuses a capture whose app area is blank, which is how a pure-background capture was caught before it reached the listing |
| M11 | Library views | ✅ | A **Libraries** section on the root screen — Images, Videos, Audio, Documents, Archives, Recent — each row carrying how many files the index knows of that kind, each opening an ordinary listing (thumbnails, sort, multi-select, details, the existing viewers) answered from the index instead of from a directory. `core/FileCategory` groups `FileKind`s and is asserted to leave `DIRECTORY`, `APK` and `OTHER` out deliberately; `SearchIndex` gained `byKind`/`recent`/`kindCounts`, and SQLite schema **v3** adds the derived `kind` column — the migration rebuilds the table *and* drops the volume stamps, because a stamp whose rows have gone would make the next search trust an empty index. A library obeys the indexed search's own two rules: the answer comes first with the walk behind it, and every row is confirmed against the filesystem before it is shown. Not included, and recorded in the CHANGELOG as decisions rather than omissions: Downloads and New files, and restoring a library across process death. **308 JVM tests** (19 added here) |
| M12 | Duplicate finder | ✅ | ***Find duplicates*** **in the overflow.** Three passes, cheapest first: **size** (the index's `duplicateCandidates` query — one query and no reading at all), **the first 64 KB** of every candidate (a filter that may only rule files *out*), and the **whole file** to confirm (`domain/DuplicateFinder`, SHA-256, cancellable between files and between read buffers). Groups are ordered by reclaimable space; each header states the copies and what they cost; every row carries its folder, because identical names with no paths are unactionable; and **nothing is preselected** — the delete is the app's ordinary one, through the trash and the undo snackbar. A file that cannot be read is counted and reported rather than skipped, and the empty state says how many files were compared — or that the index has never run and there was nothing to compare. **323 JVM tests** (15 added). Verified on the Xiaomi phone against four genuine groups in its own storage; the delete was deliberately not pressed on a device that is not ours — see [EDGE_CASE_TESTING](EDGE_CASE_TESTING.md) |
## Key decisions
@@ -39,3 +41,12 @@ Legend: ✅ done · 🚧 in progress · ⏳ not started · 💡 candidate (propo
⚠️ **The home screen and the library views need this more than a folder listing does.** A library is
not a folder — it lists files from *everywhere* — so shooting `Images` on a real phone publishes the
owner's own filenames and thumbnails, and the home screen's counts publish how many files of each
kind the device holds. Both must be shot on a device, or an AVD, whose only storage is the demo
library. This was found by running the libraries on a real phone and seeing its owner's files in
them: [EDGE_CASE_TESTING](EDGE_CASE_TESTING.md).
`tools/make-demo-library.py` builds eight distinct sample images (so the grid view and
thumbnails have something to show), a handful of documents, sized stand-ins for video and
audio so the storage-usage screen has something worth ranking, and a real ZIP so the extract
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.