-
Phandroid v2.0.79-dev Pre-release
released this
2026-10-15 22:31:56 +02:00 | 0 commits to main since this releaseChanged
- Merged upstream Phanpy — 2 commit(s); no Android-fork code changes (see
CHANGELOG.phanpy.mdfor upstream detail):b4804d90Fix notification dot color36f9f259Try remove @babel/plugin-transform-runtime this time
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
- Merged upstream Phanpy — 2 commit(s); no Android-fork code changes (see
-
Phandroid v2.0.78-dev Pre-release
released this
2026-10-15 04:30:55 +02:00 | 4 commits to main since this releaseChanged
- Merged upstream Phanpy — 1 commit(s); no Android-fork code changes (see
CHANGELOG.phanpy.mdfor upstream detail):2fb534dbExperiment: pagination dots for boosts carousel
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
- Merged upstream Phanpy — 1 commit(s); no Android-fork code changes (see
-
Phandroid v2.0.77-dev Pre-release
released this
2026-10-14 10:32:00 +02:00 | 7 commits to main since this releaseChanged
- Merged upstream Phanpy — 4 commit(s); no Android-fork code changes (see
CHANGELOG.phanpy.mdfor upstream detail):56a8e3fcAdd one more 'View Quotes' menufa9ba209Reduce gap between boosts in carouselee4bd4e5Try macroTransform1e4e7d06i18n updates (fi-FI,tr-TR) (#1613)
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
- Merged upstream Phanpy — 4 commit(s); no Android-fork code changes (see
-
Phandroid v2.0.76-dev Pre-release
released this
2026-10-13 10:30:54 +02:00 | 13 commits to main since this releaseChanged
- Merged upstream Phanpy — 6 commit(s); no Android-fork code changes (see
CHANGELOG.phanpy.mdfor upstream detail):6dcd33cfFix style for long polls058c5c6aless dashes7180c006Migrate to action-crowdin-contributorsd7b3dd7fUpdate README.mddc6b6c9aUse more defaults, rebuild table when yml changes7f83a066Update README.md
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
- Merged upstream Phanpy — 6 commit(s); no Android-fork code changes (see
-
Phandroid v2.0.75-dev Pre-release
released this
2026-10-12 16:30:48 +02:00 | 21 commits to main since this releaseChanged
- Merged upstream Phanpy — 6 commit(s); no Android-fork code changes (see
CHANGELOG.phanpy.mdfor upstream detail):d69189f0Save is in96c8e5eeStyle changes for polls, QPs, thread badges, alt badgese41b2c70Fix nested QPs not updating0842a013Simplify translation block styles5c912dc9i18n updates (ar-SA,ca-ES,cs-CZ,de-DE,eo-UY,es-ES,eu-ES,fa-IR,fi-FI,fr-FR,gl-ES,he-IL,hu-HU,it-IT,ja-JP,kab,ko-KR,lt-LT,nb-NO,nl-NL,oc-FR,pl-PL,pt-BR,pt-PT,ru-RU,th-TH,tok,tr-TR,uk-UA,zh-CN,zh-TW) (#1612)c3281effSharper underlines
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
- Merged upstream Phanpy — 6 commit(s); no Android-fork code changes (see
-
Phandroid v2.0.24 Stable
released this
2026-10-11 23:17:52 +02:00 | 4484 commits to main since this releaseSame fixes as dev 2.0.74 — the findings of the 2026-10-11 whole-project review.
Fixed
- Camera capture works again. The native camera component never forwarded the
refthat the composer clicks, so "Take photo or video" and the toolbar button
did nothing at all — while the listing advertised the feature. The component is
patched in place now instead of overridden, so it also produces upstream's current
attachment shape and cannot silently drift behind upstream again. - No more source maps in the APK. Upstream builds them for crash-report
symbolication, which Phandroid does not ship; Capacitor copied them into the
assets anyway, so every APK carried its own complete unminified source. They are
excluded now and the release verifies on the artifact that none remain. - Sharing into the running app works, and a share no longer repeats itself. The
event that signals a share to an already-running app was dispatched on iOS only,
and the share intent stayed the activity's current intent for its whole life — so
a warm share did nothing, while every WebView reload re-opened the composer with
the same old share (by then without its image). - A share into a logged-out app waits instead of vanishing. It is kept until you
log in, and a toast says why nothing happened yet. - The status bar follows the theme. It was set once at startup, so a light system
with a dark app theme gave same-coloured icons on a same-coloured header. - Multi-account push no longer kills the other accounts. Turning push off for one
account unregistered the endpoint every account shares, an endpoint rotation healed
only the current account, and logging out left the preference behind — so a later
login silently re-enabled push. - Account export: a cancelled share sheet is no longer reported as "Export
failed", and the cache file is cleaned up by the next export instead of racing
the app you shared to. - German F-Droid clients get a "What's New" text (English content under a German
heading) instead of an empty update note.
Added
- A crash shows a recovery screen instead of a blank app, with a Reload button and
the stack behind "Error details" — the app has no telemetry, so a screenshot of that
screen is the only diagnostic you can send. - Failures you can act on now say so: camera failure, an unreadable shared image
and failed push (re-)subscribes show a toast instead of only a console message that
release builds strip.
Security
- The FileProvider no longer exposes all of external storage — only the two
app-private roots the camera and the export actually use. - A shared item's name can no longer make Phandroid delete arbitrary files in its
private directory; only names that look like the plugin's own image copy are removed.
Changed
- Pipeline hardening (no user-visible effect): no secrets while
apt-getinstalls
fdroidserver, concurrency guards so overlapping runs cannot corrupt each other, job
timeouts, the signer fingerprint checked before publishing, failure reporting for the
iPhanpy watcher, and correct release-note labelling.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
- Camera capture works again. The native camera component never forwarded the
-
Phandroid v2.0.74-dev Pre-release
released this
2026-10-11 18:18:29 +02:00 | 29 commits to main since this releaseChanged
- CI hardening batch (six independent fixes, pipeline-only): the
upstream-sync's instance guard moved to the first step — the public instance
no longer clones the repo and fetches github.com 4x/day just to refuse; both
build workflows assert that all four fork workflows exist in
.forgejo/workflows/(Forgejo prefers it over.github/workflows, and
losing it would silently activate upstream's unguarded workflows);
iphanpy-watchreports its failures as issues like every other workflow (it
exits non-zero at three points nobody would ever hear about); every job now
carries atimeout-minutes(the single runner hosts the keys and is also the
work machine — a hung job blocked everything, invisibly); the failure-dedup
innotify-failure.shpaginates beyond the first 50 open issues (same bug
class the release-pruning script fixed); and the fdroid config'ssdk_path
comes from$ANDROID_HOMEvia a template placeholder instead of a hardcoded
runner-image path.
Added
- German F-Droid clients now get a "What's New" text. The promote wrote
en-US/changelogs/<versionCode>.txtonly, sode-DEhad none at all
(measured live in the index for both apps, while the German title and
descriptions are maintained) — German users saw an empty update note.
CHANGELOG.mdis English-only and translating every release is not
feasible, so de-DE now gets the English text under an honest German
heading ("Änderungen (englisch):"), fitted to a smaller budget because
fdroidserver cuts whatsNew at 500 characters. Verified by running the
real block against the real changelog: en-US 256, de-DE 281 characters. - A crash now shows a recovery screen instead of a blank app. A render error
used to replace the whole app tree with nothing — and since release builds
strip everyconsole.*and the WebView ships with debugging disabled, the
error vanished without a trace; the only way out was killing the app.<App />
is now wrapped in a fork error boundary: a "Something broke" screen with a
Reload button, and the stack behind an expandable "Error details" — visible in
release builds too, deliberately, because the app has no telemetry: a
screenshot of that screen is the only diagnostic a user can send. Verified by
injecting a render crash in the test environment. - Failures that matter to the user now say so. Camera capture failure,
an unreadable shared image, and failed push (re-)subscribes used to be
console.erroronly — stripped in release builds, i.e. invisible exactly
where they happen. They now also show a toast (a cancelled camera pick stays
silent, that is not an error). - Diagnosis builds can keep console output.
PHANPY_KEEP_CONSOLE=1 npm run buildskips Vite'sremoveConsole, so console output survives for on-device
debugging viachrome://inspect(combined with temporarily enabling
webContentsDebuggingEnabled, which should be reverted afterwards — see the
handover §13.6). Release builds are unchanged: verified 0console.*in the
normal bundle, output present with the flag set.
Fixed
- The status bar now follows the theme instead of being set once. It was
Style.Defaultat mount and never again: Phanpy's own light/dark/auto setting
is independent of the system's, andAndroidManifestlistsuiModein
configChanges, so neither an in-app theme switch nor a system theme change
recreated the activity — a light system with a dark app theme (or the reverse)
gave same-coloured icons on a same-coloured header, which the fork's own frosted
deck header makes plainly visible. The style is now derived from the effective
theme (app setting, falling back to the system scheme in auto mode) and
re-applied on both sources of change: aprefers-color-schemelistener and a
class-attribute observer on<html>— the attributesettings.jsxtoggles, so
no upstream file needed a patch.Style.Dark/Style.Lightare set explicitly,
and their meaning was verified in the plugin's Java source
(setAppearanceLightStatusBars(!style.equals("DARK")), i.e. DARK = light icons)
rather than assumed, since inverting them would be worse than not setting them.
Four Playwright tests cover dark, light, runtime switching and auto; all four
fail against the old behaviour. PHANDROID_CUSTOMIZATIONS.mdtells the truth again. The fork's entry
document had drifted into fiction in nine places: it referenced a
check-oauth-scheme.shthat does not exist, claimed every change was gated on
Capacitor.isNativePlatform()(the timeline cap, error boundary, share-intent
cleanup and build switch are not), described push with anenablePush()that
does not exist and a VAPID restriction that is deliberately absent, called push
auto-enabled on login (it is opt-in through the settings), claimed the pipeline
lives in a separate private repository (it lives here, in
.forgejo/workflows/), gave an incomplete one-time setup that fails on the
first merge (two of three merge drivers were missing), had an unpaired code
fence that rendered half the document as code, and an override-file inventory
missing three files plus the whole "modified upstream files" list being 5
files short. All corrected; the file now also carries the rule that every new
patch site and override file is registered in the same commit.- Sharing into the running app works, and a share no longer repeats itself.
Two bugs in the share target (#378): (1) thesendIntentReceivedevent that
signals a share into an already-running app is dispatched by thesend-intent
plugin only on iOS — on Android the warm-start listener never fired, sharing
into the running app did nothing until it was killed.MainActivity.onNewIntent
now dispatches the event itself and callssetIntent(). (2) AOSP does not
update the activity's intent on its own, so the SEND intent stayed current for
the activity's whole life — every WebView reload (OAuth redirect, process
restore, crash) read the same old share and re-opened the composer with it, by
then without its image (the plugin's file copy had been deleted). The web layer
now consumes the intent after handing a share to the composer, via a small
ShareIntentPluginthat replaces it with a plain ACTION_MAIN (send-intent's
ownfinish()closes the whole app, which is why it exists). Covered by
Playwright tests for the cold start, the warm start, and the reload. - Multi-account push no longer kills other accounts. The UnifiedPush endpoint
is app-wide — exactly one for all accounts, like the browser's single
PushSubscription — but the native paths had none of upstream's multi-account
protections: turning push off for one account unregistered the endpoint and
silently killed delivery for every other account; an endpoint rotation healed
only the current account, leaving the others' instances pushing into the dead
endpoint; and logout/account-removal never cleaned up (upstream's
removeAccountPushSettingsread the browser-shaped settings, found nothing,
returned early) — the removed account's pref survived, and a re-login silently
re-subscribed push without the user ever enabling it. The per-account prefs now
mirror upstream'spushSubscriptionSettingsshape (one localStorage blob keyed
by account NS, endpoint recorded): push is only unregistered when no other
account uses the endpoint, rotations re-subscribe every subscribed account with
its own instance, and account removal drops the pref, removes the instance-side
subscription while the token still works, and unregisters only as the last
user — where "user" means an account that still exists, so prefs left behind
by removals from before this fix cannot keep the endpoint registered
forever. Covered by Playwright tests against two logged-in accounts. Still open
(needs a product decision, see the handover): notifications carry no account
identity — a tap always opens the notifications of whichever account is
current, and per-notification routing is not built. - Overlapping pipeline runs can no longer corrupt each other.
release.yml,
promote.ymlandupstream-sync.ymlareworkflow_dispatch-driven, and Forgejo
only manages concurrency automatically forpush/pull_requestevents — two
simultaneous release dispatches would prune each other's release assets, two
promotes would force-push the F-Droid pages branch concurrently (one index
lost), and an overlapping sync's push would fail as non-fast-forward, losing
its merge. All three workflows now carry explicit concurrency groups with
cancel-in-progress: false— nothing is ever cancelled mid-signing or
mid-merge; overlapping runs queue instead. - The F-Droid deploy no longer runs apt-get with all the release secrets in the
environment.promote.yml's F-Droid step installed fdroidserver viaapt-get
(unpinned, from the Debian mirror, postinst scripts as root) while carrying the
F-Droid keystore, both of its passwords AND the write token for
code.umrath.dev — exactly the mixing thatrelease.yml's security model
refuses for the APK key. The step is now split into four secret-scoped steps:
install (no secrets), prepare (git only, code.umrath.dev token), update
(fdroidserver with the F-Droid secrets only — a compromised fdroidserver can no
longer write to code.umrath.dev and replace the APK users install), deploy
(git only). A failed promote now also trips the leftover-dir part of the leak
check, because the working tree survives mid-pipeline failures until the
deploy step's cleanup — key material itself is still shredded by the update
step's trap in every case. The real fix, pinning fdroidserver in the runner
image, stays open (handover §13.5). - The web app's service worker no longer runs in the native app. Upstream's
PWA service worker registered unguarded in the WebView: it laid a second,
app-update-surviving cache layer (Workbox + IndexedDB, coupled to Workbox
internals) over assets that already ship inside the APK, ran push and
share-target handlers that can never fire there, and a broken SW state
has no user-facing way out in a native app — "clear app data" also wipes
every account and token. VitePWA injects the registration at build time,
so a guard in index.html was impossible; a post-order transform strips
the injected script from the app build (verified in the built HTML, and
the full Playwright suite runs against the stripped build). Installs that
already registered a worker are cleaned up too: stripping the
registration does not unregister one that exists, so on native the app
now unregisters any leftover worker and drops its caches once at startup
— otherwise the second cache layer would survive for the rest of that
install's life. The WebView's own HTTP cache still covers remote images. - The share-intent delete path only accepts names the plugin's image copy
could have. Animage/*share still let any installed app make
Phandroid overwrite and then delete a file of any single-segment name in
the app's private files dir (confused deputy — harmless today only
because nothing fork-owned lives there). The name check now sits on the
DELETE, not on the read: attaching is safe because the content is
validated (a blob that is not an image is dropped, the sender's declared
MIME is never trusted) and the path is confined to the app's own files
dir — while deleting is the operation a foreign app must not get to
choose. So an image attaches under any name, and only copies whose name
looks like an image are removed; anything else stays behind as bounded
litter. A malformed share (URI without a display name — the plugin's Java
side throws on those) now shows a toast instead of silently doing
nothing. Covered by Playwright tests for all three cases (extensionless
name attaches, image name attaches and is cleaned up, foreign-chosen name
attaches and is NOT deleted). - The FileProvider no longer exposes all of external storage.
file_paths.xmlmapped<external-path path="."/>— everything on the
shared storage — plus the cache dir. Any JS path handing a foreign
file://to@capacitor/sharewould have been a cross-storage file read
with Phandroid's UID. What the fork actually needs was derived from the
plugin sources rather than guessed:cache-pathforgetCacheDir()
(save-file.jsexports via Directory.Cache, plus CameraPlugin's own cache
temp file) andexternal-files-pathscoped toPictures/for
getExternalFilesDir(DIRECTORY_PICTURES), which is where
CameraUtils.createImageFile()puts the photo that CameraPlugin then hands
through THIS app's FileProvider. An earlier, narrower version of this fix
omitted the second entry and would have broken camera capture with an
IllegalArgumentExceptionat runtime — invisible to the web-layer tests,
which is why the file now documents which plugin needs which root. Both
roots are the app's own private directories, not shared storage.
@capacitor/filesystem(plainfile://URIs) andsend-intent
(Uri.fromFile) need no entry. Camera capture and export are on the
device re-test list (PHANDROID_CUSTOMIZATIONS.md). - A share into a logged-out app waits instead of vanishing. The composer
only renders when logged in, so a share while logged out deleted the
image copy and set shared data nobody would ever see — lost without a
word. The share is now kept (the intent is not consumed): after logging
in it lands in the composer — the OAuth reload re-runs the handler — and
until then a toast says why nothing happened. Covered by a Playwright
test for the logged-out state. - Export: a cancelled share sheet is no longer an error, and the cache file
is cleaned up. The system share sheet runs as a full activity, so
cancelling usually resolves cleanly — but OEM sheets that are dialogs
reject with "Share canceled", and the export showed "Export failed" with
the modal stuck open even though the file was written fine. The cancel
case is now swallowed; everything else still throws. The cache file is
cleaned up by the NEXT export instead of immediately: the sheet resolves
as soon as the chooser returns, which is exactly when the chosen app
starts reading the file, so deleting right away would race the receiver
("Save to Files", Nextcloud and mail all read lazily) and could hand it a
dead URI. At most one stale file sits in the cache dir, and it carries no
tokens either way (the export strips the accessToken). - Notification taps no longer depend on a hashCode for their pending intent.
Android caches PendingIntents by (requestCode, Intent); with all
notifications carrying the same extras this was harmless, but the moment
they gain notification-specific paths (per-account routing, still open), a
hashCode-derived requestCode would make one notification's extras overwrite
another's. The requestCode is now a constant, and the comment claiming
hashCode collisions could silently replace pushes — while the code did
exactly that hashCode thing — is corrected: the notification's identity is
the (tag, id) pair, and it always was. - Release changelogs are labelled correctly now. The automatic upstream-merge
entry was inserted directly after the## [Unreleased]heading — in front of
whatever hand-written entries sat there — so those entries were silently
absorbed into the release while its header claimed "no Android-fork code
changes" (2.0.66 shipped the whole test infrastructure under that claim, 2.0.69
the whatsNew tests; the contradiction reached users through the release notes
and the F-Droid "What's New" text). The newphandroid-changelog-bump.py
inserts the section behind the Unreleased block and moves hand-written entries
into the release, dropping the "no Android-fork code changes" claim when they
are present. It also refuses to run when the[Unreleased]heading is missing
or the version section already exists — the awk it replaced exited 0 in both
cases and shipped releases without their changelog entry. Unit-tested in the
test gate (10 tests, alongside the whatsnew converter's). - No more source maps in the shipped APK. Upstream builds with
sourcemap: true(Rollbar symbolication, which Phandroid does not ship), and
Capacitor copiesdist/into the APK's assets verbatim — 5.7 MB of.mapfiles
(55 % of the web payload, the complete unminified source including comments) were
riding along in every signed APK.ignoreAssetsPatternnow excludes*.mapat
the aapt level, which also covers local builds, and the release verification
additionally asserts on the signed artifact itself that it contains no.map
entries — so the claim is machine-checked, not assumed. - Native camera capture works again. The camera capture had been dead in the app:
the fork's override ofcamera-capture-inputnever forwarded therefthat
compose.jsx clicks, so the "Take photo or video" menu entry and toolbar button did
nothing (Preact handsrefto function components as a plain prop — the override
ignored it). The override had also missed three upstream refactors since 2.0.66
(thefileData/fileNameattachment shape, the Mobile Safari MIME filter, the
hidden 1×1 styling), which is the structural problem: an override drifts silently.
The override is now gone. Upstream's owncamera-capture-input.jsxis patched in
place (// PHANDROID:) with a native branch: a hidden trigger button — with the
forwardedref— that opens the camera via@capacitor/cameraand hands the photo
to the composer in upstream's attachment shape. Upstream refactors of the file now
merge through instead of drifting, and a Playwright test clicks the composer's
camera menu entry against the bridge and asserts theCamera.getPhotocall and the
resulting attachment, so this class of breakage cannot ship silently again.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
- CI hardening batch (six independent fixes, pipeline-only): the
-
Phandroid v2.0.73-dev Pre-release
released this
2026-10-10 10:30:03 +02:00 | 78 commits to main since this releaseChanged
- Merged upstream Phanpy — 3 commit(s); no Android-fork code changes (see
CHANGELOG.phanpy.mdfor upstream detail):96917632Hide excessive br(s) AFTER .quote-inlineca95af2dFixes for ALT folder tabsdbb0e55eAdjust indents
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
- Merged upstream Phanpy — 3 commit(s); no Android-fork code changes (see
-
Phandroid v2.0.72-dev Pre-release
released this
2026-10-09 22:30:28 +02:00 | 83 commits to main since this releaseChanged
- Merged upstream Phanpy — 4 commit(s); no Android-fork code changes (see
CHANGELOG.phanpy.mdfor upstream detail):362918d3It's time to change things a little2f21e3a3Better grids for sections in Settings3a32083dExperiment with this rotated ALT badgeb5a0f8cbSlight adjustments
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
- Merged upstream Phanpy — 4 commit(s); no Android-fork code changes (see
-
Phandroid v2.0.71-dev Pre-release
released this
2026-10-09 04:41:00 +02:00 | 89 commits to main since this releaseChanged
- Merged upstream Phanpy — 2 commit(s); no Android-fork code changes (see
CHANGELOG.phanpy.mdfor upstream detail):f588710dMore ignoreModifiers, ughh4179c986[autofix.ci] apply automated fixes
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
- Merged upstream Phanpy — 2 commit(s); no Android-fork code changes (see