Skip to content

Repository files navigation

Gemini Watermark Remover — Android App

An offline-capable Android application wrapper designed to host a high-performance, local JavaScript watermark-removal engine. It integrates a Jetpack Compose native container hosting a full-screen, customized WebView that executes local asset files.

For development guidelines, see CLAUDE.md.


🏗️ Architecture Overview

The project is structured as a hybrid application split into three main layers:

graph TD
    A[Native Android Container] -->|Hosts full screen| B[WebView UI]
    B -->|Bridges JavaScript API| A
    C[android-js-src/android-app.js] -->|Bundled into| D[assets/app.js]
    E[gemini-watermark-remover SDK] -->|Imported by| C
    B -->|Loads local assets| D
Loading
  1. Native Android Shell (android/):

    • Built using Jetpack Compose.
    • Consists of a single MainActivity hosting a WebView utilizing explicit layout parameters to ensure perfect sizing.
    • Exposes a native JS bridge class WebAppInterface named AndroidBridge to support saving images to gallery via MediaStore, copying images to clipboard, and native chooser sharing via FileProvider.
  2. Web Front-end (android/app/src/main/assets/):

    • Contains the UI framework files: index.html and style.css.
    • Serves the compiled asset bundle app.js locally.
    • Do not edit app.js directly; it is compiled automatically during the Gradle build pipeline.
  3. JS Entry Point & Core Submodule (android-js-src/ & gemini-watermark-remover/):

    • The JS source file android-app.js orchestrates application logic, UI interaction, and touch gestures.
    • Imports core watermark-removal capabilities from the gemini-watermark-remover submodule.

✨ Features

  • Offline Processing: All watermark-removal computations are run entirely client-side using browser canvas APIs.
  • Interactive Comparison: Toggle pills allow seamless switching between the original and cleaned version of images.
  • Pinch-to-Zoom & Pan: Fullscreen modal supporting smooth multi-touch pinch gestures, drag/pan, and double-tap gestures to reset zoom.
  • Native Operations:
    • 💾 Save to Gallery: Saves the processed image to Pictures/GeminiWatermarkRemover via Android's MediaStore API.
    • 📋 Copy to Clipboard: Copies the processed PNG to the system clipboard.
    • 📤 Share: Invokes the native Android share sheet to send the image to other apps.
  • Build Timestamping: Displays a build time string at the bottom-right corner of the web UI to verify the active Javascript build.

🛠️ Build Pipeline & Setup

Prerequisites

  1. Android SDK: Set up Android SDK, build tools (target SDK 36), and JDK 17.
  2. Node.js Dependencies: The bundling tool esbuild needs to be initialized. Run the following command inside the submodule directory:
    cd gemini-watermark-remover && pnpm install

Building the Project

The project is configured to bundle the web assets automatically whenever the Android app is compiled. This dependency is defined in the build.gradle.kts preBuild task:

tasks.register<Exec>("bundleJsAssets") { ... }
tasks.named("preBuild") { dependsOn("bundleJsAssets") }
  • Compile Debug APK:
    cd android
    .\gradlew.bat assembleDebug
  • Install on Connected Device:
    adb install -r android\app\build\outputs\apk\debug\app-debug.apk

⚠️ Important Development & WebView Gotchas

Before modifying layout styling or gesture logic, review these critical gotchas:

1. Viewport Height (vh/vw) Units

Inside Android's WebView, vh units can resolve to 0 if the WebView is measured before its final height is set.

  • Rule: Never use vh or vw units for critical layout elements in style.css.
  • Solution: Use absolute/fixed layouts with inset: 0 or percentage heights (100%) to enforce correct layout sizing. Ensure the native AndroidView in Compose configures its layoutParams explicitly with MATCH_PARENT.

2. Multi-Touch Interception

To allow custom JavaScript pinch-to-zoom to operate correctly without parent layout interference, the WebView listens to touches. When event.pointerCount > 1, it executes v.parent?.requestDisallowInterceptTouchEvent(true).


🐛 Live Debugging on Device

You can debug the running web context inside the Android WebView by using Google Chrome/Chromium DevTools:

  1. Retrieve the process ID of the app:
    adb shell pidof me.wanghuan.geminiwatermarkremover
  2. Forward the debug socket to your local machine (replace <pid> with the PID from the previous step):
    adb forward tcp:9222 localabstract:webview_devtools_remote_<pid>
  3. Verify connection:
    curl -m 5 http://localhost:9222/json
  4. Connect from Chrome DevTools or a Playwright script targeting http://127.0.0.1:9222.

Releases

Packages

Contributors

Languages