From 7bffc7ffdaed7e631eb46b1f2a9105939ab784b7 Mon Sep 17 00:00:00 2001 From: Greg Pomerantz Date: Mon, 12 Oct 2026 16:43:10 -0400 Subject: [PATCH] Fix activity-recreation crash: give Permissions a no-arg constructor On any activity recreation with saved state (config changes such as night mode), the FragmentManager restores the Permissions fragment reflectively and requires a no-arg constructor; it only had Permissions(View), so the activity failed to start with Fragment$InstantiationException. Also regenerate cmd/pad/Permissions.jar from the .java source in both build scripts: gogio does not compile the app's own .java files - it silently merges any *.jar in the Go package dirs into the dex, so the stale Aug-16 jar shadowed source edits (which is how this bug went unnoticed). This crash is separate from the black-screen hang after relaunch, which is fixed in the gioui fork (app/os_android.go: skip the window handshake ack on re-attach). --- cmd/pad/Permissions.java | 11 +++++++++++ scripts/build_emu.sh | 17 +++++++++++++++++ scripts/build_phone.sh | 17 +++++++++++++++++ 3 files changed, 45 insertions(+) diff --git a/cmd/pad/Permissions.java b/cmd/pad/Permissions.java index 310bb89..0d96172 100644 --- a/cmd/pad/Permissions.java +++ b/cmd/pad/Permissions.java @@ -25,6 +25,17 @@ public class Permissions extends Fragment { Handler handler; final int PERMISSIONS_REQUEST = 1; + // No-arg constructor required by the FragmentManager when restoring + // this fragment from the activity's saved state (activity recreation + // on config changes). Without it the restore throws + // Fragment$InstantiationException and the activity fails to start. + // Only the first-creation path uses the (View) constructor below; + // on restore the framework adds the fragment itself, and ctx/handler + // stay null — onAttach uses its own context parameter, so nothing on + // the restore path needs them. + public Permissions() { + } + public Permissions(View view) { Log.d("gio", "Permissions()"); this.ctx = view.getContext(); diff --git a/scripts/build_emu.sh b/scripts/build_emu.sh index 0fb2a26..8e8f36f 100755 --- a/scripts/build_emu.sh +++ b/scripts/build_emu.sh @@ -54,6 +54,23 @@ fi WORK=$(mktemp -d) trap 'rm -rf "$WORK"' EXIT +echo "=== compile Permissions.java -> Permissions.jar ===" +# gogio does NOT compile the app's own .java files; it silently merges any +# *.jar found in the Go package dirs (extraJars) into the dex. The +# Permissions fragment is therefore shipped from cmd/pad/Permissions.jar. +# Regenerate it from the .java source on every build, or source edits are +# silently ignored (the stale jar wins) — that is how the missing no-arg +# constructor (Fragment$InstantiationException on activity recreation) went +# unnoticed. +ANDROID_JAR="$ANDROID_HOME/platforms/android-35/android.jar" +[ -f "$ANDROID_JAR" ] || die "android.jar missing: $ANDROID_JAR" +PERMCLS=$(mktemp -d) +javac -source 1.8 -target 1.8 -nowarn \ + -bootclasspath "$ANDROID_JAR" \ + -d "$PERMCLS" "$REPO/cmd/pad/Permissions.java" || die "Permissions.java failed to compile" +jar cf "$REPO/cmd/pad/Permissions.jar" -C "$PERMCLS" st +rm -rf "$PERMCLS" + echo "=== gogio (Go -> APK) ===" # targetSdk 34: targeting 35 makes Android 15 force edge-to-edge. We keep 34 # and instead consume the IME insets ourselves (PadInsetsListener patch below); diff --git a/scripts/build_phone.sh b/scripts/build_phone.sh index 79fb282..eff462d 100755 --- a/scripts/build_phone.sh +++ b/scripts/build_phone.sh @@ -52,6 +52,23 @@ fi WORK=$(mktemp -d) trap 'rm -rf "$WORK"' EXIT +echo "=== compile Permissions.java -> Permissions.jar ===" +# gogio does NOT compile the app's own .java files; it silently merges any +# *.jar found in the Go package dirs (extraJars) into the dex. The +# Permissions fragment is therefore shipped from cmd/pad/Permissions.jar. +# Regenerate it from the .java source on every build, or source edits are +# silently ignored (the stale jar wins) — that is how the missing no-arg +# constructor (Fragment$InstantiationException on activity recreation) went +# unnoticed. +ANDROID_JAR="$ANDROID_HOME/platforms/android-35/android.jar" +[ -f "$ANDROID_JAR" ] || die "android.jar missing: $ANDROID_JAR" +PERMCLS=$(mktemp -d) +javac -source 1.8 -target 1.8 -nowarn \ + -bootclasspath "$ANDROID_JAR" \ + -d "$PERMCLS" "$REPO/cmd/pad/Permissions.java" || die "Permissions.java failed to compile" +jar cf "$REPO/cmd/pad/Permissions.jar" -C "$PERMCLS" st +rm -rf "$PERMCLS" + echo "=== gogio (Go -> APK, arm64+arm) ===" # targetSdk 34: targeting 35 makes Android 15 force edge-to-edge. We keep 34 # and instead consume the IME insets ourselves (PadInsetsListener patch below);