Skip to content

Add native Android app on the shared ripper engine - #2194

Open
soloturn wants to merge 2 commits into
compose-gui-scaffoldfrom
soloturn-android
Open

Add native Android app on the shared ripper engine#2194
soloturn wants to merge 2 commits into
compose-gui-scaffoldfrom
soloturn-android

Conversation

@soloturn

Copy link
Copy Markdown
Collaborator

Native Android app. Same ripper engine as the desktop build, not a fork.

Stacked on #2193 (compose-gui-scaffold). Base that, not main.

Layout

android/ is a separate Gradle build. The root build never sees it. gradle build at the root still passes.

  • :core — java-library. Compiles the same Java sources as the desktop build (../../src/main/java) through a Sync-filtered copy at -release 17. 111 album rippers + 8 video rippers.
  • :app — AGP 9.3.1 + Jetpack Compose. Rip / Queue / History / Log / Settings, foreground service, MediaStore export, light + dark.

Three problems porting the engine

Core→GUI coupling is three symbols. MainWindow.addUrlToQueue, App.stringToAppendToFoldername, UpdateUtils.getThisJarVersion. Three shim classes replace them. Everything else the rippers import from ui is already Swing-free.

Ripper discovery scans the classpath. Finds nothing in a DEX. A Gradle task regenerates the list from source each build, so it stays in sync as rippers get added.

Config dir resolves under $HOME. Not writable on Android. One patch to shared code: a ripme.config.dir override in Utils.getConfigDir(). That is the only change under src/.

Two findings

log4j-core cannot be compileOnly. HotSpot verifies Utils as a whole class, so it resolves configureLogger()'s log4j-core references just to load Utils — even though nothing calls that method. First reflective ripper construction dies with NoClassDefFoundError. ART verifies per-method and soft-fails instead, so :app excludes log4j-core again. Confirmed on-device across ~119 reflective ripper constructions, not assumed. Full evidence chain in android/app/build.gradle.kts.

Service race left an un-stoppable notification. A URL that fails to resolve never sets busy=true, so the stop collector fired on a freshly created service. A real rip right after got delivered to the dying instance. Reproduced, then fixed by debouncing the stop decision only.

Gaps

  • No InstagramRipper. GraalVM JS will not dex.
  • No in-app updates. Replacing a jar makes no sense on Android.
  • No SAF picker. Rips go to getExternalFilesDir(DIRECTORY_DOWNLOADS)/rips; a user-chosen directory needs the core's file IO abstracted first.
  • No R8. The registry resolves rippers reflectively and would need keep rules.
  • Export needs API 29+ for MediaStore.Downloads.

Two pre-existing shared-code issues surfaced. Both hit the desktop build the same way, both left alone since they are outside the one sanctioned patch:

  • HistoryEntry.toJSON() never writes dir, though fromJSON() reads it. A history row loses its folder across a restart.
  • After Stop/Panic nothing resumes the queue except re-adding a URL.

Verified

  • gradle -p android :core:jar, :core:test (offline, deterministic), :app:assembleDebug
  • gradle build at the repo root
  • Emulator API 36.1: imgur rip downloaded a real file, history row written, export landed at Download/RipMe/imgur_qbfcLyG/qbfcLyG.jpeg
  • All five screens in light and dark mode

Build and run instructions, pinned-version constraints, and the full gap list are in android/README.md.

🤖 Generated with Claude Code

https://claude.ai/code/session_01TRPnr275L8rtX5pbNMjLUx

soloturn and others added 2 commits August 16, 2026 07:48
Separate Gradle build under android/. Root build never sees it.

:core compiles the same Java sources as the desktop build
(../../src/main/java) through a Sync-filtered copy at -release 17.
111 album rippers + 8 video rippers, shared not forked. :app is
AGP 9.3.1 + Jetpack Compose.

Three shims cover the only core->GUI coupling there is:
MainWindow.addUrlToQueue, App.stringToAppendToFoldername,
UpdateUtils.getThisJarVersion.

Ripper discovery scans the classpath on desktop, which finds nothing
in a DEX. A Gradle task regenerates the list from source each build.

One patch to shared code: ripme.config.dir override in
Utils.getConfigDir(). $HOME/.config is not writable on Android.

UI is rip, queue, history, log, settings. Foreground service keeps
rips alive in the background. Export copies a rip to Download/RipMe/
via MediaStore, since app-scoped storage dies on uninstall.

Verified on emulator API 36.1: imgur rip downloaded, history row
written, export landed in Download/RipMe/imgur_qbfcLyG.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TRPnr275L8rtX5pbNMjLUx
…ndings

Both were only in the PR description before this. Pull them into the
repo so they survive after the PR closes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TRPnr275L8rtX5pbNMjLUx
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