Android picture-in-picture, and fix three dead Android config files

Add PiP for native (ExoPlayer) video on Android. Video renders into a
SurfaceView behind the WebView, so PiP is driven by the Activity shrinking
into a floating window rather than the HTML5 PiP API (which WebKitGTK does
not implement, hence Android-only).

- PictureInPictureManager.kt: enter PiP with the video's aspect ratio
  (clamped to the 1:2.39-2.39:1 range Android accepts, outside which it
  throws), plus a play/pause RemoteAction. Hides the WebView while in PiP -
  it is opaque and sits above the surface, so it would otherwise occlude the
  video entirely - and re-fits the surface on exit.
- MainActivity.kt: onUserLeaveHint auto-PiP, onPictureInPictureModeChanged,
  and an AndroidPictureInPicture JS interface following the existing
  AndroidAudioFocus pattern.
- pictureInPicture.ts + VideoPlayer.svelte: PiP button, rendered only when
  the native bridge reports support.
- proguard: keep rules for @JavascriptInterface methods, which are only
  referenced from JS and would be stripped in minified release builds.

Casting needs no special handling: canEnterPip() checks natively that a
local video surface is attached and playing, which a remote session lacks.

While wiring the manifest, found that three tracked files under
src-tauri/android/ were never reaching any build. Gradle reads only
gen/android/app/src/main/, and sync-android-sources.sh did not copy them:

- src/main/AndroidManifest.xml was a partial <application> fragment written
  as if Tauri merged it. It does not - there is no manifest-merger hook
  here, so its hardwareAccelerated flag never reached an APK. Promoted to
  the complete authoritative manifest (folding in that flag) and synced.
- src/main/res/values/themes.xml (transparent status bar, fitsSystemWindows)
  was never copied; the sync only globbed mipmap-*. Now synced.
- build.gradle.kts was a leftover com.android.library module config with
  stale media3 1.5.1 deps. The live deps are in app/build.gradle.kts at
  1.5.0. Deleted.

Verified: merged manifest now carries hardwareAccelerated,
supportsPictureInPicture, resizeableActivity and the density configChange;
themes.xml compiles into merged resources; Kotlin builds warning-free;
svelte-check clean; 537 frontend tests pass.

Not verified: PiP behaviour on a device, and the release keep rules against
a minified build. assembleUniversalDebug cannot complete in this
environment - the Rust step wants a dev-server addr file that only exists
under `tauri android dev`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-18 12:59:39 +02:00
co-authored by Claude Opus 4.8
parent 7b8a8f66e5
commit 1fa5aa46f9
8 changed files with 569 additions and 41 deletions
+22
View File
@@ -41,6 +41,19 @@ if [ -f "$APP_GRADLE_SRC" ]; then
echo " Copied: app/build.gradle.kts"
fi
# AndroidManifest.xml. `tauri android init` regenerates gen/android from
# tauri.conf.json and would drop our hand-maintained entries (media playback
# service + permissions, hardware acceleration, picture-in-picture attributes
# on MainActivity), so this tracked copy is the source of truth and must be
# restored after a regen. Gradle reads ONLY the gen/ copy - there is no
# manifest-merger hook here, so this must be the complete manifest.
MANIFEST_SRC="$PROJECT_ROOT/src-tauri/android/src/main/AndroidManifest.xml"
MANIFEST_DST="$PROJECT_ROOT/src-tauri/gen/android/app/src/main/AndroidManifest.xml"
if [ -f "$MANIFEST_SRC" ]; then
cp "$MANIFEST_SRC" "$MANIFEST_DST"
echo " Copied: app/src/main/AndroidManifest.xml"
fi
# Custom ProGuard/R8 keep rules. Required for minified release builds:
# the player/ and security/ Kotlin classes are loaded by name via JNI from
# Rust, so R8 can't see the references and would strip them without this.
@@ -65,6 +78,15 @@ if [ -d "$RES_SRC" ]; then
cp "$dir"/* "$RES_DST/$name/"
echo " Copied res: $name"
done
# values/ (themes.xml): status-bar styling that `tauri android init` does
# not generate. Previously this directory was tracked but never copied, so
# the theme customizations below never reached a build.
if [ -d "$RES_SRC/values" ]; then
mkdir -p "$RES_DST/values"
cp "$RES_SRC"/values/*.xml "$RES_DST/values/"
echo " Copied res: values"
fi
# We ship only the color adaptive icon (background + foreground). Drop any
# monochrome layer Tauri may generate: the themed-icon monochrome doesn't
# render well, and our adaptive-icon xml no longer references it, so a stray