Skip to content

Infotify 3.0 — Compose rewrite, proxied API, site and store assets - #16

Merged
gabriel-TheCode merged 6 commits into
masterfrom
main
Sep 3, 2026
Merged

gabriel-TheCode merged 6 commits into
masterfrom
main

Conversation

@gabriel-TheCode

Copy link
Copy Markdown
Owner

Brings master up to date with the six commits already on main. master is a strict
ancestor of main, so this is a fast-forward — no history is rewritten and nothing is
dropped.

What is in it

  • Compose rewrite, no ComposeView. Clean Architecture with MVI on the screens that have
    a real state machine; single Activity, typed Navigation Compose routes, Room v3 with
    migrations that preserve existing bookmarks.
  • The provider key left the app. The app carries no key at all: it talks to
    infotify-api.nativia.co, a caching PHP proxy that holds the key server-side. This was
    not optional — NewsData.io's free tier is 200 credits/day for the whole app, not per user.
  • Build modernised. AGP 9 with built-in Kotlin, KSP instead of kapt, a Gradle version
    catalog as the single source of versions, targetSdk 37, and every unused dependency
    removed after checking the sources rather than assuming.
  • Marketing site at infotify.nativia.co, with privacy and support pages the Play
    listing can point at.
  • Play Store assets — icon, feature graphic, eight phone screenshots, and the script
    that generates them from real captures of the R8-minified build.

Known, and deliberately left open

  • No tablet screenshots yet; Play wants 7-inch and 10-inch sets before it shows the app as
    tablet-ready.
  • screenshot-2-for-you.png shows one unresolved thumbnail. That is the app's real current
    behaviour: a publisher image that never resolves leaves the placeholder in place instead
    of collapsing as ArticleCard intends, because Coil reports no Error state for it. Seen
    only on an emulator whose clock is visibly broken, which is enough to stop OkHttp's
    timeouts firing — it needs confirming on a real device before it is treated as an app bug.
  • Release signing still uses the debug key in releaseTest, by design.

🤖 Generated with Claude Code

gabriel-TheCode and others added 6 commits September 3, 2026 18:48
The build had drifted into a state that could not compile: the Kotlin toolchain
was declared as 2.0.20 in the root file, 2.4.10 for two compiler plugins and
2.2.10 for the standard library. `ksp` was used for Room without the plugin ever
being applied, and turbine had been dropped while a test still imported it.

Version catalog first, because inline literals are what allowed that drift.
gradle/libs.versions.toml is now the only place a version is written; there is
not one version literal left in any build file except versionName. The three
Kotlin compiler plugins — Compose, serialization, KSP — are pinned together on
purpose, since they must track the Kotlin the build uses rather than float.

AGP 8.9.3 -> 9.2.1 (the newest the user's Android Studio supports), Gradle
8.14.5 -> 9.7.1, JDK 17. That upgrade was not optional: Hilt 2.60.1 refuses any
AGP below 9.0.0, and compileSdk 37 needs it too.

AGP 9 brings built-in Kotlin, so:
- the kotlin-android plugin is gone; AGP compiles Kotlin itself
- kotlin-kapt is incompatible with it, so Room and Hilt both moved to KSP
- android.kotlinOptions{} no longer exists; jvmTarget now follows
  compileOptions.targetCompatibility
- the Groovy space-assignment DSL is replaced by explicit assignment

Kotlin 2.3.21 rather than 2.4.10: KSP is only published up to 2.3.11, so 2.4
would have meant keeping kapt. Given the goal was to reach KSP, Kotlin gives way.

Removed as unused — each checked against the sources, not assumed:
appcompat and material (no View-system widget or theme remains), lifecycle-process
(ProcessLifecycleOwner went with the old Application class), mockk (the tests use
hand-written fakes), and espresso, test-ext-junit and compose ui-test (there are
no androidTest sources at all).

Two fixes the upgrade forced:
- ic_notification.xml referenced ?attr/colorControlNormal, which came from
  AppCompat and stopped resolving once that was dropped. Notification icons are
  tinted by the system anyway, so the attribute was doing nothing.
- RelativeTime used LocalContext.current.getString, which the newer Compose lint
  rejects as not configuration-aware: a locale change would have left stale text
  on screen. It uses stringResource now.

ktlint moved from the abandoned com.pinterest:ktlint 0.50 to ktlint-cli 1.8.0,
lost its own repositories block (repositories are centralised in settings.gradle),
and uses mainClass instead of main, which Gradle 9 removed.

Verified: assembleDebug, 38 unit tests, lintDebug and assembleReleaseTest all
green. The R8 APK is 2.66 MB, down from 3.47 MB.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…p a dead thumbnail

The site had no icon at all, and its screenshots were visibly wide.

The distortion was baked into the image files, not the CSS: the pair was 620x1217
for a 1080x2220 screen, a 4.7% horizontal stretch that no stylesheet could undo.
Retaken from the app and resized to 620x1274, which is the device ratio to four
decimal places. The markup's width/height attributes and the CSS aspect-ratio
were updated to match, so the reserved box and the image agree.

Favicons are generated from the same geometry as the launcher icon — ink ground,
paper stem, ember dot — as favicon.ico, an SVG for modern browsers, and a
180px apple-touch-icon, all linked from the three pages.

One of the old screenshots showed a card with an empty grey thumbnail. Chasing
it turned out to be worth doing: list cards kept their 96dp placeholder when an
image failed, while the featured card already dropped its own. They now behave
the same, and the headline takes the full width instead. Publishers' image hosts
fail often enough that this is a common state, not an edge case.

Worth recording, since it is easy to misread: a grey box is also the *loading*
placeholder. Coil reports Error, not Loading, to onState, so a slow image looks
exactly like a failed one for as long as it takes. The screenshot that prompted
this was taken 25s in on an emulator with slow reach to publisher CDNs; the
images did eventually arrive.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The screenshots stayed visibly wide after the files were corrected, and the
reason was mine: .phone img carried `aspect-ratio: 620 / 1274`. A declared ratio
overrides the file's own, so the box was fixed and whatever image landed in it
was squashed to fit. I had added it to reserve space during loading; it was the
fixed height causing the very problem it sat next to.

Removed. `width: 100%` with `height: auto` and nothing else, so the file's
intrinsic ratio always wins. The width and height attributes in the markup
reserve the space instead, which is what they are for.

Two compounding causes fixed with it:
- The image filenames never changed, so browsers kept serving the old 620x1217
  files against the new declared ratio — the fix looked like no fix at all.
  Assets are versioned in the filename now (for-you-2.png, explore-2.png).
- HTML was cacheable, so a stale page kept pointing at the old filenames.
  .htaccess now sends no-cache for HTML and a year for versioned assets.

Also added the phone frame that was asked for: an ink bezel with the screen
inset, a speaker slot and a side button, drawn entirely in CSS. Nothing is baked
into the PNGs, so the screenshots stay honest captures and can be replaced
without redrawing any chrome.

Verified against what the server actually returns: the image is 620x1274, the
markup declares the same, the stylesheet contains no aspect-ratio declaration,
and the frame's pseudo-elements are present. I could not view the rendered page
— the browser pane refuses this origin by policy — so this is verified by
measurement, not by eye.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The listing had an icon and a feature graphic and nothing else. This adds the eight
phone screenshots Play shows in the carousel, and the script that produces all of it,
so a capture can be replaced without anyone reopening an image editor.

The icon changes. It was a separate drawing from the launcher icon — stem 15px off
its axis and 50px too tall — so the tile in the store and the tile on the home screen
were not the same mark. generate.py now renders the geometry straight out of
ic_launcher_foreground.xml, and both come from one source.

The captures are honest: the R8-minified releaseTest build on a Pixel 3a emulator
against the live proxy, with SystemUI demo mode so the status bar carries no real
device state. The phone chassis is drawn around each PNG rather than baked in, and
the phone's width is fixed while its height follows the file's own ratio — the same
mistake that stretched the website screenshots cannot happen here.

Two things are deliberately left open, both recorded in playstore/README.md: there
are no tablet screenshots, and screenshot-2 shows one empty grey thumbnail. That grey
box is the app's real behaviour today — a publisher image that never resolves leaves
the placeholder in place instead of collapsing as ArticleCard intends, because Coil
reports no Error state for it. Seen only on this emulator so far, whose clock is
visibly broken ("time of day goes back"), which is enough to stop OkHttp's timeouts
firing — so it needs confirming on a real device before it is treated as an app bug.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The live listing still describes NewsAPI and seven languages, and points its privacy
policy at the GitHub repository. This is the replacement text for the English and
French listings, with every claim taken from the source rather than from memory:
fifteen topics capped at five (Topic.MAX_SELECTED), four regions, nine languages,
three non-sensitive permissions, and no analytics or advertising library in the app.

Every field is inside its Play limit — names 28/30, short descriptions 66 and 60 of
80, full descriptions 1880 and 2144 of 4000.

The data-safety section records one open question rather than answering it: the API
keeps a rate-limit counter per hashed IP for under a day, which is not "ephemeral" by
Play's definition, and Play has no IP category to declare it under.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The two images in the README had been broken since the website fix: renaming
site/img/for-you.png to for-you-2.png for cache busting left the README pointing at
files that no longer exist, so GitHub was rendering two dead images rather than the
stretched ones they replaced.

They now use the Play Store tiles, which are captures of the R8-minified build
against the live API, and neither carries an unresolved thumbnail.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@gabriel-TheCode
gabriel-TheCode merged commit 34d6708 into master Sep 3, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant