M10: store assets - Plain Files naming, generated icon/feature graphic, listing copy
This commit is contained in:
@@ -31,6 +31,21 @@ upload needs a **higher `versionCode`** than every previous upload.
|
|||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
- **M10 — store assets.** The app is named **Plain Files** (one string; the `applicationId`
|
||||||
|
was already locked in M0). `tools/make-store-assets.sh` renders the 512×512 RGBA icon and
|
||||||
|
the 1024×500 RGB feature graphic from the app's own icon vectors, so the store icon *is*
|
||||||
|
the launcher icon rather than a lookalike that drifts after the next rebrand.
|
||||||
|
`tools/make-store-screenshots.sh` normalises `adb` captures to Play's 2:1 limit per form
|
||||||
|
factor — a phone at its native 1080×2400 is 2.22:1 and would be rejected outright.
|
||||||
|
[docs/STORE_LISTING.md](docs/STORE_LISTING.md) has the ready-to-paste copy, length-checked
|
||||||
|
against Play's limits, plus the permissions paragraph that the `MANAGE_EXTERNAL_STORAGE`
|
||||||
|
declaration requires. The feature graphic's copy was verified to survive Play's
|
||||||
|
centre-square crop by cropping the rendered PNG, not by trusting the arithmetic.
|
||||||
|
|
||||||
|
**Not finished:** the screenshots themselves. The generator and the raw-capture
|
||||||
|
directories are in place, but no captures have been taken, so `store-assets/screenshots/`
|
||||||
|
is still empty. The listing is publishable except for that.
|
||||||
|
|
||||||
- **M9 — monetisation.** Decided: a **one-time paid app at €4.49, with a 14-day launch price
|
- **M9 — monetisation.** Decided: a **one-time paid app at €4.49, with a 14-day launch price
|
||||||
of €2.29**, both set in the Play Console. No ads, no subscription and no in-app purchases —
|
of €2.29**, both set in the Play Console. No ads, no subscription and no in-app purchases —
|
||||||
which means no Billing client, no `com.android.vending.BILLING` permission and no purchase
|
which means no Billing client, no `com.android.vending.BILLING` permission and no purchase
|
||||||
|
|||||||
@@ -12,10 +12,10 @@ no advertising or tracking SDKs.
|
|||||||
|
|
||||||
## Status
|
## Status
|
||||||
|
|
||||||
**M9 — monetisation: done.** One-time paid at €4.49 (€2.29 for the first 14 days), set in
|
**M10 — store assets: mostly done.** The app is named **Plain Files**, the icon and feature
|
||||||
the Play Console — so the APK carries no Billing client and no `BILLING` permission, which
|
graphic are generated from the app's own vectors, the screenshot normaliser is written, and
|
||||||
keeps the offline claim true of the shipped artefact. See
|
[docs/STORE_LISTING.md](docs/STORE_LISTING.md) holds length-checked listing copy. The
|
||||||
[docs/MONETISATION.md](docs/MONETISATION.md).
|
screenshots are still outstanding — the generator is ready but no captures exist yet.
|
||||||
|
|
||||||
## Tech stack
|
## Tech stack
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,10 @@
|
|||||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
<!--
|
<!--
|
||||||
All user-visible text lives here. English is currently the only locale, but keeping the
|
The Play listing name. Deliberately not "File Manager": dozens of apps are literally
|
||||||
text in resources is what makes a translation possible later at all — Play's automatic
|
called that, and a distinctive name is what makes the listing findable and
|
||||||
translation covers the store listing, not the app UI. Plurals are declared properly so a
|
rememberable. "Plain" carries the pitch — no ads, no account, no clutter.
|
||||||
language with more than two plural forms needs no code change.
|
|
||||||
|
|
||||||
NOTE: `app_name` is a working title. The Play listing needs a distinctive name before
|
|
||||||
the first upload (it is changeable afterwards, unlike the applicationId).
|
|
||||||
-->
|
-->
|
||||||
<string name="app_name">File Manager</string>
|
<string name="app_name">Plain Files</string>
|
||||||
|
|
||||||
<!-- Progress notification for long file operations -->
|
<!-- Progress notification for long file operations -->
|
||||||
<string name="operation_copy_title">Copying files</string>
|
<string name="operation_copy_title">Copying files</string>
|
||||||
|
|||||||
+1
-1
@@ -19,7 +19,7 @@ Legend: ✅ done · 🚧 in progress · ⏳ not started · 💡 candidate (propo
|
|||||||
| M7 | On-device validation & hardening | ✅ | Run on a real **Xiaomi M2101K7AG** (Android 12 / MIUI V140) — see [EDGE_CASE_TESTING](EDGE_CASE_TESTING.md). Verified on hardware: the first-run permission prompt, granting "All files access" while the app is alive (picked up on resume), browsing real storage, the text editor's read **and** write path (independently checked with `adb shell cat`), the storage-usage screen, landscape layout, and **graceful degradation when access is revoked mid-use**. Found and fixed two real defects: misleading first-run copy ("Full access was declined" on a device where nothing had been declined), and a usage screen that stayed blank for **~45–60 s** on a 55 GB volume — now one listing per directory instead of a `stat` per node, with rows streamed out as they are measured. **142 JVM tests** |
|
| M7 | On-device validation & hardening | ✅ | Run on a real **Xiaomi M2101K7AG** (Android 12 / MIUI V140) — see [EDGE_CASE_TESTING](EDGE_CASE_TESTING.md). Verified on hardware: the first-run permission prompt, granting "All files access" while the app is alive (picked up on resume), browsing real storage, the text editor's read **and** write path (independently checked with `adb shell cat`), the storage-usage screen, landscape layout, and **graceful degradation when access is revoked mid-use**. Found and fixed two real defects: misleading first-run copy ("Full access was declined" on a device where nothing had been declined), and a usage screen that stayed blank for **~45–60 s** on a 55 GB volume — now one listing per directory instead of a `stat` per node, with rows streamed out as they are measured. **142 JVM tests** |
|
||||||
| 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** |
|
| 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 |
|
| 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 | ⏳ | icon/feature-graphic generators, per-form-factor screenshots, [STORE_LISTING](STORE_LISTING.md); display name confirmed |
|
| 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 1881/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. **Outstanding: screenshots** — the tooling and directories are in place but no captures exist yet |
|
||||||
|
|
||||||
## Key decisions
|
## Key decisions
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,120 @@
|
|||||||
|
# Play Store listing (M10)
|
||||||
|
|
||||||
|
Ready-to-paste copy for the *Main store listing* page, plus the graphics that go with it.
|
||||||
|
Every graphic is generated from the app's own vectors, so a rebrand is a re-run rather than
|
||||||
|
a redraw.
|
||||||
|
|
||||||
|
See [PLAY_RELEASE_CHECKLIST](PLAY_RELEASE_CHECKLIST.md) § 6 for where this fits in the
|
||||||
|
release.
|
||||||
|
|
||||||
|
## App name
|
||||||
|
|
||||||
|
**Plain Files**
|
||||||
|
|
||||||
|
Deliberately not "File Manager": dozens of apps are literally called that, and a distinctive
|
||||||
|
name is what makes a listing findable and rememberable. It is one string
|
||||||
|
(`app_name` in `values/strings.xml`); the `applicationId` is separate and already locked.
|
||||||
|
|
||||||
|
## Graphics
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./tools/make-store-assets.sh # icon + feature graphic -> store-assets/
|
||||||
|
./tools/make-store-screenshots.sh # phone -> screenshots/phone/
|
||||||
|
FORM_FACTOR=tablet-7in ./tools/make-store-screenshots.sh
|
||||||
|
FORM_FACTOR=tablet-10in ./tools/make-store-screenshots.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
| Asset | Play's spec | Result | Source |
|
||||||
|
|---|---|---|---|
|
||||||
|
| App icon | 512×512, 32-bit PNG with alpha, ≤ 1 MB | `store-assets/icon-512.png` — 512×512, **RGBA**, ~5 KB | `tools/store-assets/icon.svg`, mirroring `ic_launcher_background.xml` + `ic_launcher_foreground.xml`. Background, paths and opacities are identical, so **the store icon is the launcher icon** rather than a lookalike that drifts. |
|
||||||
|
| Feature graphic | 1024×500, JPEG or 24-bit PNG **without** alpha | `store-assets/feature-graphic-1024x500.png` — 1024×500, **RGB** | `tools/store-assets/feature-graphic.svg` |
|
||||||
|
| Phone screenshots | no alpha, shorter side ≥ 320, longer side ≤ 3840, **longer side ≤ 2× shorter** | `store-assets/screenshots/phone/` (1080×2160) | `tools/make-store-screenshots.sh`, from `adb` captures in `store-assets/raw-screenshots/phone/` |
|
||||||
|
|
||||||
|
Two details worth keeping:
|
||||||
|
|
||||||
|
- **The icon is full bleed.** The background rect covers all 108 dp, so every pixel is
|
||||||
|
opaque and Play's mask can crop to any shape without showing a gap.
|
||||||
|
- **A phone at its native 1080×2400 is 2.22:1 and is therefore rejected.** The normaliser
|
||||||
|
crops the status and navigation bars and then clamps to 1080×2160 — system chrome only,
|
||||||
|
never app content. Tablets are already inside the 2:1 cap, so their crop defaults to 0.
|
||||||
|
|
||||||
|
### The feature graphic's safe area
|
||||||
|
|
||||||
|
Play crops the feature graphic to a **centre square** for some placements, so all of the copy
|
||||||
|
sits inside **x 262…762** and the motif — which does not survive — is decoration. Verified by
|
||||||
|
actually cropping the rendered PNG rather than by trusting the arithmetic:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
magick store-assets/feature-graphic-1024x500.png -gravity Center -crop 500x500+0+0 +repage /tmp/square.png
|
||||||
|
```
|
||||||
|
|
||||||
|
## Short description (80 characters max)
|
||||||
|
|
||||||
|
```
|
||||||
|
Browse, organise and manage your files. Ad-free, offline, pay once.
|
||||||
|
```
|
||||||
|
|
||||||
|
## Full description (4000 characters max)
|
||||||
|
|
||||||
|
```
|
||||||
|
Plain Files is a straightforward file manager for Android — no ads, no account, no
|
||||||
|
subscription, and no network access at all. You buy it once and it is yours.
|
||||||
|
|
||||||
|
BROWSE ANYTHING
|
||||||
|
• Internal storage, SD cards and USB drives, side by side
|
||||||
|
• List or grid view, with sorting by name, size, date or type
|
||||||
|
• Breadcrumb navigation and an in-folder search
|
||||||
|
• Hidden files on request
|
||||||
|
|
||||||
|
MANAGE YOUR FILES
|
||||||
|
• Copy, move, rename and delete, one item or many at once
|
||||||
|
• Create folders and edit text files in the built-in editor
|
||||||
|
• See the details of anything: size, type, location, modified date
|
||||||
|
• Open a file in any app, or share it
|
||||||
|
|
||||||
|
FIND OUT WHERE THE SPACE WENT
|
||||||
|
• A storage-usage view that measures every folder and ranks it largest first
|
||||||
|
• Spot the folder that is actually eating your storage, then act on it
|
||||||
|
|
||||||
|
ARCHIVES
|
||||||
|
• Create ZIP archives, and extract ZIP, TAR, TAR.GZ, GZ, BZ2 and XZ
|
||||||
|
• RAR and 7z are not supported — they need native code this app does not ship, and it says
|
||||||
|
so rather than failing with a strange error
|
||||||
|
|
||||||
|
PRIVATE BY DEFAULT
|
||||||
|
• No INTERNET permission. The app cannot send your data anywhere, because it has no way to.
|
||||||
|
• No analytics, no advertising SDKs, no third-party trackers
|
||||||
|
• No account, no sign-in, nothing to remember
|
||||||
|
• Your files stay on your device, always
|
||||||
|
|
||||||
|
SOLD ONCE
|
||||||
|
• One purchase, every feature, no ads and nothing to buy inside the app
|
||||||
|
• No subscription
|
||||||
|
|
||||||
|
PERMISSIONS, EXPLAINED
|
||||||
|
Plain Files asks for "All files access" because that is what browsing and managing files
|
||||||
|
anywhere on a device requires — the app's core purpose. Google Play only grants this
|
||||||
|
permission to app types that genuinely need it, and a file manager is one of them. If you
|
||||||
|
would rather not grant it, the app still works: it can browse a folder you pick yourself
|
||||||
|
through Android's own folder picker, and it remembers that choice.
|
||||||
|
|
||||||
|
Read the privacy policy: https://example.invalid/plain-files/privacy
|
||||||
|
```
|
||||||
|
|
||||||
|
> Replace the privacy-policy URL with the real public URL before publishing — a repository
|
||||||
|
> file is not acceptable to Play. [PRIVACY.md](../PRIVACY.md) is the source text.
|
||||||
|
|
||||||
|
### Why the permission sentence matters
|
||||||
|
|
||||||
|
Play requires that an app's **core functionality be prominently documented and promoted in
|
||||||
|
the app's description** when it declares `MANAGE_EXTERNAL_STORAGE`. The "PERMISSIONS,
|
||||||
|
EXPLAINED" section is not marketing copy — it is the evidence the Permissions Declaration
|
||||||
|
Form asks for.
|
||||||
|
|
||||||
|
## Still to do before publishing
|
||||||
|
|
||||||
|
- **Screenshots.** `tools/make-store-screenshots.sh` is written and the raw-screenshot
|
||||||
|
directories are in place, but no captures have been taken yet, so
|
||||||
|
`store-assets/screenshots/` is empty. Capture against a **synthetic** library — never a
|
||||||
|
real one, since published screenshots would expose real file names.
|
||||||
|
- **Tablet screenshots.** Play has a 7-inch and a 10-inch slot, each wanting 4+.
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 307 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 5.1 KiB |
Executable
+41
@@ -0,0 +1,41 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# Generates the Google Play store graphics from the app's own icon vectors.
|
||||||
|
#
|
||||||
|
# ./tools/make-store-assets.sh [output-dir] # default: ./store-assets
|
||||||
|
#
|
||||||
|
# Produces, matching Play's asset specs:
|
||||||
|
# icon-512.png 512 x 512 32-bit PNG *with* alpha (ours is full-bleed,
|
||||||
|
# so every pixel ends up opaque anyway)
|
||||||
|
# feature-graphic-1024x500.png 1024 x 500 24-bit PNG, no alpha
|
||||||
|
#
|
||||||
|
# Output lands in ./store-assets/ *inside* the repo, never in $HOME: the editable sources are
|
||||||
|
# the SVGs in tools/store-assets/ and these PNGs are the files that get uploaded to the Play
|
||||||
|
# Console, so they belong with the release they were made for. Pass a directory to override.
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
here="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
repo="$(dirname "$here")"
|
||||||
|
out="${1:-$repo/store-assets}"
|
||||||
|
mkdir -p "$out"
|
||||||
|
|
||||||
|
render() { # <svg> <width> <height> <filename> <keep-alpha|flatten>
|
||||||
|
local svg="$1" width="$2" height="$3" name="$4" mode="$5"
|
||||||
|
inkscape "$svg" \
|
||||||
|
--export-type=png --export-width="$width" --export-height="$height" \
|
||||||
|
--export-filename="$out/$name" >/dev/null 2>&1
|
||||||
|
if [ "$mode" = keep-alpha ]; then
|
||||||
|
# Play asks for a 32-bit PNG here. The SVG's background rect covers the whole
|
||||||
|
# canvas, so the alpha channel is fully opaque — no accidental transparency.
|
||||||
|
magick "$out/$name" -alpha on PNG32:"$out/$name"
|
||||||
|
else
|
||||||
|
magick "$out/$name" -background '#1565C0' -alpha remove -alpha off PNG24:"$out/$name"
|
||||||
|
fi
|
||||||
|
printf ' %s ' "$out/$name"
|
||||||
|
magick identify -format '%wx%h %[channels] %b\n' "$out/$name"
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "Writing store assets to $out"
|
||||||
|
render "$here/store-assets/icon.svg" 512 512 icon-512.png keep-alpha
|
||||||
|
render "$here/store-assets/feature-graphic.svg" 1024 500 feature-graphic-1024x500.png flatten
|
||||||
|
echo "Done. Screenshots: ./tools/make-store-screenshots.sh # reads store-assets/raw-screenshots/phone/"
|
||||||
Executable
+59
@@ -0,0 +1,59 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# Normalises `adb exec-out screencap` captures into the files that get uploaded to Play.
|
||||||
|
#
|
||||||
|
# ./tools/make-store-screenshots.sh # phone -> 1080x2160
|
||||||
|
# FORM_FACTOR=tablet-7in ./tools/make-store-screenshots.sh # 7-inch -> 1200x1920
|
||||||
|
# FORM_FACTOR=tablet-10in ./tools/make-store-screenshots.sh # 10-inch -> 1600x2560
|
||||||
|
#
|
||||||
|
# store-assets/raw-screenshots/<form>/ the raw captures (git-ignored)
|
||||||
|
# store-assets/screenshots/<form>/ what gets uploaded (committed)
|
||||||
|
#
|
||||||
|
# Play's rule that actually bites: JPEG or 24-bit PNG **without alpha**, shorter side >= 320,
|
||||||
|
# longer side <= 3840, and **longer side <= 2x the shorter side**. A phone at its native
|
||||||
|
# 1080x2400 is 2.22:1 and is therefore REJECTED, which is why the phone path crops system
|
||||||
|
# chrome (status + navigation bars) before clamping to 2:1. A tablet frame is already inside
|
||||||
|
# the 2:1 cap on every profile the SDK ships, so its crop defaults to 0 and is opt-in.
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
here="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
repo="$(dirname "$here")"
|
||||||
|
form="${FORM_FACTOR:-phone}"
|
||||||
|
|
||||||
|
case "$form" in
|
||||||
|
phone) target_w=1080; target_h=2160; default_status=93; default_nav=130 ;;
|
||||||
|
tablet-7in) target_w=1200; target_h=1920; default_status=0; default_nav=0 ;;
|
||||||
|
tablet-10in) target_w=1600; target_h=2560; default_status=0; default_nav=0 ;;
|
||||||
|
*) echo "Unknown FORM_FACTOR '$form' (phone|tablet-7in|tablet-10in)" >&2; exit 1 ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
status_px="${STATUS_BAR_PX:-$default_status}"
|
||||||
|
nav_px="${NAV_BAR_PX:-$default_nav}"
|
||||||
|
|
||||||
|
raw="$repo/store-assets/raw-screenshots/$form"
|
||||||
|
out="$repo/store-assets/screenshots/$form"
|
||||||
|
mkdir -p "$out"
|
||||||
|
|
||||||
|
shopt -s nullglob
|
||||||
|
captures=("$raw"/*.png)
|
||||||
|
if [ ${#captures[@]} -eq 0 ]; then
|
||||||
|
echo "No captures in $raw — take some with:" >&2
|
||||||
|
echo " adb exec-out screencap -p > $raw/01-roots.png" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Normalising ${#captures[@]} capture(s) from $raw -> $out"
|
||||||
|
for capture in "${captures[@]}"; do
|
||||||
|
name="$(basename "$capture")"
|
||||||
|
magick "$capture" \
|
||||||
|
-gravity North -chop 0x"$status_px" \
|
||||||
|
-gravity South -chop 0x"$nav_px" \
|
||||||
|
+repage \
|
||||||
|
-resize "${target_w}x${target_h}^" \
|
||||||
|
-gravity Center -extent "${target_w}x${target_h}" \
|
||||||
|
-alpha remove -alpha off PNG24:"$out/$name"
|
||||||
|
printf ' %s ' "$out/$name"
|
||||||
|
magick identify -format '%wx%h %[channels] %b\n' "$out/$name"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "Done."
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="500" viewBox="0 0 1024 500">
|
||||||
|
<!--
|
||||||
|
Play feature graphic (1024 x 500, no transparency).
|
||||||
|
|
||||||
|
Same palette as the app icon (#1565C0) with the icon motif on the left and the store copy
|
||||||
|
on the right. Text stays inside the middle band because Play crops this graphic to a
|
||||||
|
square for some placements, and the outer edges are what get cut.
|
||||||
|
-->
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="bg" x1="0" y1="0" x2="1" y2="1">
|
||||||
|
<stop offset="0" stop-color="#0D3F7A" />
|
||||||
|
<stop offset="0.55" stop-color="#1565C0" />
|
||||||
|
<stop offset="1" stop-color="#2E7DD1" />
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<rect x="0" y="0" width="1024" height="500" fill="url(#bg)" />
|
||||||
|
|
||||||
|
<!--
|
||||||
|
The icon motif, on the left. The icon's own content spans roughly x 24..84 and y 38..77,
|
||||||
|
so it is centred on (54, 57.5).
|
||||||
|
-->
|
||||||
|
<g transform="translate(160,250) scale(3.4) translate(-54,-57.5)">
|
||||||
|
<path d="M34,38 h14 l5,5 h27 a4,4 0 0 1 4,4 v20 a4,4 0 0 1 -4,4 h-46 a4,4 0 0 1 -4,-4 v-25 a4,4 0 0 1 4,-4 z"
|
||||||
|
fill="#FFFFFF" fill-opacity="0.45" />
|
||||||
|
<path d="M28,44 h14 l5,5 h27 a4,4 0 0 1 4,4 v20 a4,4 0 0 1 -4,4 h-46 a4,4 0 0 1 -4,-4 v-25 a4,4 0 0 1 4,-4 z"
|
||||||
|
fill="#FFFFFF" />
|
||||||
|
<path d="M44,56 h20 a2,2 0 0 1 2,2 v12 a2,2 0 0 1 -2,2 h-20 a2,2 0 0 1 -2,-2 v-12 a2,2 0 0 1 2,-2 z"
|
||||||
|
fill="#1565C0" />
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Store copy. Everything here stays inside x 262..762 — the centre square Play crops to for
|
||||||
|
some placements — so the name and the tagline survive the crop even though the motif does
|
||||||
|
not. Sizes were chosen by measuring the strings, not by eye.
|
||||||
|
-->
|
||||||
|
<text x="300" y="215" font-family="DejaVu Sans" font-weight="bold" font-size="50"
|
||||||
|
fill="#FFFFFF">Plain Files</text>
|
||||||
|
<text x="302" y="265" font-family="DejaVu Sans" font-size="20"
|
||||||
|
fill="#FFFFFF" fill-opacity="0.92">Browse and manage your files</text>
|
||||||
|
<text x="302" y="298" font-family="DejaVu Sans" font-size="20"
|
||||||
|
fill="#FFFFFF" fill-opacity="0.92">Ad-free, offline, no account. Pay once.</text>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.0 KiB |
@@ -0,0 +1,26 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 108 108">
|
||||||
|
<!--
|
||||||
|
Play app icon (512 x 512, 32-bit PNG with alpha).
|
||||||
|
|
||||||
|
This mirrors the app's own launcher icon exactly: `ic_launcher_background.xml` plus the
|
||||||
|
three paths from `ic_launcher_foreground.xml`. Background, paths and opacities are
|
||||||
|
identical, so the store icon *is* the launcher icon rather than a lookalike that drifts
|
||||||
|
after the next rebrand.
|
||||||
|
|
||||||
|
The blue rect covers the whole 108 dp canvas, which is what makes the PNG full bleed:
|
||||||
|
Play's mask can crop to any shape without revealing a gap.
|
||||||
|
-->
|
||||||
|
<rect x="0" y="0" width="108" height="108" fill="#1565C0" />
|
||||||
|
|
||||||
|
<!-- Back folder, offset up and to the right -->
|
||||||
|
<path d="M34,38 h14 l5,5 h27 a4,4 0 0 1 4,4 v20 a4,4 0 0 1 -4,4 h-46 a4,4 0 0 1 -4,-4 v-25 a4,4 0 0 1 4,-4 z"
|
||||||
|
fill="#FFFFFF" fill-opacity="0.45" />
|
||||||
|
|
||||||
|
<!-- Front folder -->
|
||||||
|
<path d="M28,44 h14 l5,5 h27 a4,4 0 0 1 4,4 v20 a4,4 0 0 1 -4,4 h-46 a4,4 0 0 1 -4,-4 v-25 a4,4 0 0 1 4,-4 z"
|
||||||
|
fill="#FFFFFF" />
|
||||||
|
|
||||||
|
<!-- Document tab -->
|
||||||
|
<path d="M44,56 h20 a2,2 0 0 1 2,2 v12 a2,2 0 0 1 -2,2 h-20 a2,2 0 0 1 -2,-2 v-12 a2,2 0 0 1 2,-2 z"
|
||||||
|
fill="#1565C0" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.2 KiB |
Reference in New Issue
Block a user