#!/usr/bin/env bash # CI / 本地共用: 修复 Android 软键盘遮挡输入框 # 在 `tauri android init` 之后、`tauri android build` 之前运行。 # # 背景: # Tauri 2 生成的 MainActivity 调用 enableEdgeToEdge(), 且不处理 IME Insets。 # Android 15+ (targetSdk 35+) 强制 edge-to-edge 后, 系统不再因软键盘缩小窗口, # 清单里的 adjustResize 也随之失效 → 键盘直接盖住 WebView, 底部弹层输入框被遮挡。 # # 做法: # 1. AndroidManifest.xml 的 MainActivity 补 windowSoftInputMode="adjustResize" # (兼容 API < 35 的旧行为) # 2. 重写 MainActivity.kt: 监听 WindowInsetsCompat.Type.ime(), 键盘可见时 # 把 IME 高度作为底部内边距加给内容容器 (等价恢复旧版 adjustResize), # 这样 WebView 真正变矮, 所有 position:fixed / bottom:0 的弹层自动上移。 # # 幂等: 重复执行不会重复注入。 # 注意: gen/android 被 gitignore, CI 每次 init 都重新生成, 所以每次构建都要跑本脚本。 set -euo pipefail MANIFEST="src-tauri/gen/android/app/src/main/AndroidManifest.xml" MAIN_ACTIVITY_DIR="src-tauri/gen/android/app/src/main/java" if [ ! -f "$MANIFEST" ]; then echo "[softinput] ERROR: 找不到 $MANIFEST (请先运行 tauri android init)" >&2 exit 1 fi MAIN_ACTIVITY=$(find "$MAIN_ACTIVITY_DIR" -name MainActivity.kt -print -quit 2>/dev/null || true) if [ -z "$MAIN_ACTIVITY" ]; then echo "[softinput] ERROR: 找不到 MainActivity.kt (请先运行 tauri android init)" >&2 exit 1 fi echo "[softinput] 注入 Android 软键盘避让配置..." python3 - "$MANIFEST" "$MAIN_ACTIVITY" <<'PYEOF' import re import sys manifest_path, activity_path = sys.argv[1], sys.argv[2] # ---------- 1. AndroidManifest.xml: 补 windowSoftInputMode ---------- src = open(manifest_path, encoding='utf-8').read() if 'windowSoftInputMode' not in src: m = re.search(r'', file=sys.stderr) sys.exit(1) src = src[:m.end()] + '\n android:windowSoftInputMode="adjustResize"' + src[m.end():] open(manifest_path, 'w', encoding='utf-8').write(src) print('[softinput] AndroidManifest.xml: 已注入 windowSoftInputMode="adjustResize"') else: print('[softinput] AndroidManifest.xml: 已有 windowSoftInputMode, 跳过') # ---------- 2. MainActivity.kt: IME Insets 处理 ---------- src = open(activity_path, encoding='utf-8').read() if '[iboard] keyboard-insets' in src: print('[softinput] MainActivity.kt: 已注入键盘避让, 跳过') sys.exit(0) m = re.search(r'^\s*package\s+([\w.]+)\s*$', src, flags=re.M) if not m: print('[softinput] ERROR: MainActivity.kt 中找不到 package 声明', file=sys.stderr) sys.exit(1) package = m.group(1) kotlin = f'''package {package} import android.os.Build import android.os.Bundle import android.view.View import androidx.activity.enableEdgeToEdge import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat class MainActivity : TauriActivity() {{ override fun onCreate(savedInstanceState: Bundle?) {{ enableEdgeToEdge() super.onCreate(savedInstanceState) // [iboard] keyboard-insets // Android 15+ 强制 edge-to-edge 后, 系统不再因软键盘缩小窗口, adjustResize 失效, // 键盘会直接盖住 WebView 底部。这里监听 IME Insets, 键盘可见时把键盘高度 // 作为内容容器的底部内边距, 等价恢复旧版 adjustResize 行为。 // API < 30 由清单里的 adjustResize 处理, 避免系统已缩小窗口时重复留白。 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {{ val content = findViewById(android.R.id.content) ViewCompat.setOnApplyWindowInsetsListener(content) {{ view, insets -> // 键盘隐藏时 getInsets(ime).bottom 为 0 (AndroidX 在 API < 30 也做了兼容) val imeBottom = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom view.setPadding(0, 0, 0, imeBottom) insets }} ViewCompat.requestApplyInsets(content) }} }} }} ''' open(activity_path, 'w', encoding='utf-8').write(kotlin) print(f'[softinput] MainActivity.kt: 已注入 IME Insets 处理 ({activity_path})') PYEOF # 断言: 防止注入被模板覆盖或路径写错 if ! grep -q 'windowSoftInputMode' "$MANIFEST"; then echo "[softinput] ERROR: AndroidManifest.xml 注入未生效" >&2 exit 1 fi if ! grep -q '\[iboard\] keyboard-insets' "$MAIN_ACTIVITY"; then echo "[softinput] ERROR: MainActivity.kt 注入未生效" >&2 exit 1 fi echo "[softinput] 完成"