From 04c0cdc1ebe4f16c22e4ed16c9c62f8737f43277 Mon Sep 17 00:00:00 2001 From: sneedmaster Date: Wed, 13 May 2026 08:50:19 +0200 Subject: [PATCH] Overlay --- .gitignore | 7 + scripts/prepare-android-gen.sh | 232 +----------------- .../android-overlay/app/build.gradle.kts | 87 +++++++ .../main/java/in/cinny/app/MainActivity.kt | 24 ++ .../app/src/main/res/values-night/colors.xml | 4 + .../app/src/main/res/values-night/themes.xml | 9 + .../app/src/main/res/values/colors.xml | 4 + .../app/src/main/res/values/themes.xml | 9 + .../java/in/cinny/app/kotlin/BuildTask.kt | 56 +++++ 9 files changed, 212 insertions(+), 220 deletions(-) create mode 100644 src-tauri/android-overlay/app/build.gradle.kts create mode 100644 src-tauri/android-overlay/app/src/main/java/in/cinny/app/MainActivity.kt create mode 100644 src-tauri/android-overlay/app/src/main/res/values-night/colors.xml create mode 100644 src-tauri/android-overlay/app/src/main/res/values-night/themes.xml create mode 100644 src-tauri/android-overlay/app/src/main/res/values/colors.xml create mode 100644 src-tauri/android-overlay/app/src/main/res/values/themes.xml create mode 100644 src-tauri/android-overlay/buildSrc/src/main/java/in/cinny/app/kotlin/BuildTask.kt diff --git a/.gitignore b/.gitignore index c5d1839..a21902b 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,14 @@ experiment dist node_modules devAssets + +# Tauri mobile outputs are regenerated from init + the tracked overlay below. src-tauri/gen/android src-tauri/gen/ios +src-tauri/gen/schemas + +# Tracked Android overlay reapplied by scripts/prepare-android-gen.sh +!src-tauri/android-overlay/ +!src-tauri/android-overlay/** .DS_Store diff --git a/scripts/prepare-android-gen.sh b/scripts/prepare-android-gen.sh index fc5cddc..cab1b36 100755 --- a/scripts/prepare-android-gen.sh +++ b/scripts/prepare-android-gen.sh @@ -6,27 +6,26 @@ SCRIPT_DIR=$(CDPATH= cd -- "$(dirname "$0")" && pwd) REPO_ROOT=$(CDPATH= cd -- "$SCRIPT_DIR/.." && pwd) SOURCE_ICON="$REPO_ROOT/src-tauri/icons/icon.png" +ANDROID_OVERLAY_DIR="$REPO_ROOT/src-tauri/android-overlay" ANDROID_APP_DIR="$REPO_ROOT/src-tauri/gen/android/app" ANDROID_RES_DIR="$ANDROID_APP_DIR/src/main/res" -ANDROID_VALUES_DIR="$ANDROID_RES_DIR/values" -ANDROID_VALUES_NIGHT_DIR="$ANDROID_RES_DIR/values-night" -ANDROID_GRADLE_FILE="$ANDROID_APP_DIR/build.gradle.kts" -ANDROID_MAIN_ACTIVITY="$ANDROID_APP_DIR/src/main/java/in/cinny/app/MainActivity.kt" -ANDROID_BUILDTASK_FILE="$REPO_ROOT/src-tauri/gen/android/buildSrc/src/main/java/in/cinny/app/kotlin/BuildTask.kt" if [ ! -f "$SOURCE_ICON" ]; then echo "Missing source icon: $SOURCE_ICON" >&2 exit 1 fi +if [ ! -d "$ANDROID_OVERLAY_DIR" ]; then + echo "Missing Android overlay directory: $ANDROID_OVERLAY_DIR" >&2 + exit 1 +fi + if [ ! -d "$ANDROID_RES_DIR" ]; then echo "Missing Android resources directory: $ANDROID_RES_DIR" >&2 echo "Run android init first." >&2 exit 1 fi -mkdir -p "$ANDROID_VALUES_DIR" "$ANDROID_VALUES_NIGHT_DIR" - resize_icon() { size="$1" output="$2" @@ -56,216 +55,9 @@ do resize_icon "$1" "$ANDROID_RES_DIR/$2/ic_launcher_foreground.png" done -cat > "$ANDROID_GRADLE_FILE" <<'EOF' -import java.io.FileInputStream -import java.util.Properties - -plugins { - id("com.android.application") - id("org.jetbrains.kotlin.android") - id("rust") -} - -val tauriProperties = Properties().apply { - val propFile = file("tauri.properties") - if (propFile.exists()) { - propFile.inputStream().use { load(it) } - } -} - -android { - compileSdk = 36 - namespace = "in.cinny.app" - defaultConfig { - manifestPlaceholders["usesCleartextTraffic"] = "true" - applicationId = "in.cinny.app" - minSdk = 24 - targetSdk = 36 - versionCode = tauriProperties.getProperty("tauri.android.versionCode", "1").toInt() - versionName = tauriProperties.getProperty("tauri.android.versionName", "1.0") - } - signingConfigs { - create("release") { - val keystorePropertiesFile = rootProject.file("keystore.properties") - val keystoreProperties = Properties() - if (keystorePropertiesFile.exists()) { - keystoreProperties.load(FileInputStream(keystorePropertiesFile)) - } - - keyAlias = keystoreProperties["keyAlias"] as String - keyPassword = keystoreProperties["password"] as String - storeFile = file(keystoreProperties["storeFile"] as String) - storePassword = keystoreProperties["password"] as String - } - } - buildTypes { - getByName("debug") { - manifestPlaceholders["usesCleartextTraffic"] = "true" - isDebuggable = true - isJniDebuggable = true - isMinifyEnabled = false - packaging { - jniLibs.keepDebugSymbols.add("*/arm64-v8a/*.so") - jniLibs.keepDebugSymbols.add("*/armeabi-v7a/*.so") - jniLibs.keepDebugSymbols.add("*/x86/*.so") - jniLibs.keepDebugSymbols.add("*/x86_64/*.so") - } - } - getByName("release") { - signingConfig = signingConfigs.getByName("release") - isMinifyEnabled = true - proguardFiles( - *fileTree(".") { include("**/*.pro") } - .plus(getDefaultProguardFile("proguard-android-optimize.txt")) - .toList().toTypedArray() - ) - } - } - kotlinOptions { - jvmTarget = "1.8" - } - buildFeatures { - buildConfig = true - } -} - -rust { - rootDirRel = "../../../" -} - -dependencies { - implementation("androidx.webkit:webkit:1.14.0") - implementation("androidx.appcompat:appcompat:1.7.1") - implementation("androidx.activity:activity-ktx:1.10.1") - implementation("com.google.android.material:material:1.12.0") - testImplementation("junit:junit:4.13.2") - androidTestImplementation("androidx.test.ext:junit:1.1.4") - androidTestImplementation("androidx.test.espresso:espresso-core:3.5.0") -} - -apply(from = "tauri.build.gradle.kts") -EOF - -cat > "$ANDROID_MAIN_ACTIVITY" <<'EOF' -package `in`.cinny.app - -import android.os.Bundle -import androidx.core.view.ViewCompat -import androidx.core.view.WindowInsetsCompat -import androidx.core.view.updatePadding - -class MainActivity : TauriActivity() { - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - val content = findViewById(android.R.id.content) - ViewCompat.setOnApplyWindowInsetsListener(content) { view, windowInsets -> - val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) - view.updatePadding( - left = insets.left, - top = insets.top, - right = insets.right, - bottom = insets.bottom, - ) - windowInsets - } - } -} -EOF - -cat > "$ANDROID_BUILDTASK_FILE" <<'EOF' -import java.io.File -import org.apache.tools.ant.taskdefs.condition.Os -import org.gradle.api.DefaultTask -import org.gradle.api.GradleException -import org.gradle.api.logging.LogLevel -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.TaskAction - -open class BuildTask : DefaultTask() { - @Input - var rootDirRel: String? = null - @Input - var target: String? = null - @Input - var release: Boolean? = null - - @TaskAction - fun assemble() { - val executable = """node""" - try { - runTauriCli(executable) - } catch (e: Exception) { - if (Os.isFamily(Os.FAMILY_WINDOWS)) { - runTauriCli("$executable.cmd") - } else { - throw e - } - } - } - - fun runTauriCli(executable: String) { - val rootDirRel = rootDirRel ?: throw GradleException("rootDirRel cannot be null") - val target = target ?: throw GradleException("target cannot be null") - val release = release ?: throw GradleException("release cannot be null") - val tauriCli = File( - project.projectDir, - "${rootDirRel}/../node_modules/@tauri-apps/cli/tauri.js" - ).canonicalPath - val args = listOf(tauriCli, "android", "android-studio-script") - - project.exec { - workingDir(File(project.projectDir, rootDirRel)) - executable(executable) - args(args) - if (project.logger.isEnabled(LogLevel.DEBUG)) { - args("-vv") - } else if (project.logger.isEnabled(LogLevel.INFO)) { - args("-v") - } - if (release) { - args("--release") - } - args(listOf("--target", target)) - }.assertNormalExitValue() - } -} -EOF - -cat > "$ANDROID_VALUES_DIR/colors.xml" <<'EOF' - - - #1A1A1A - -EOF - -cat > "$ANDROID_VALUES_NIGHT_DIR/colors.xml" <<'EOF' - - - #1A1A1A - -EOF - -cat > "$ANDROID_VALUES_DIR/themes.xml" <<'EOF' - - - - -EOF - -cat > "$ANDROID_VALUES_NIGHT_DIR/themes.xml" <<'EOF' - - - - -EOF +find "$ANDROID_OVERLAY_DIR" -type f | while IFS= read -r overlay_file; do + relative_path=${overlay_file#"$ANDROID_OVERLAY_DIR/"} + destination="$REPO_ROOT/src-tauri/gen/android/$relative_path" + mkdir -p "$(dirname "$destination")" + cp "$overlay_file" "$destination" +done diff --git a/src-tauri/android-overlay/app/build.gradle.kts b/src-tauri/android-overlay/app/build.gradle.kts new file mode 100644 index 0000000..a514107 --- /dev/null +++ b/src-tauri/android-overlay/app/build.gradle.kts @@ -0,0 +1,87 @@ +import java.io.FileInputStream +import java.util.Properties + +plugins { + id("com.android.application") + id("org.jetbrains.kotlin.android") + id("rust") +} + +val tauriProperties = Properties().apply { + val propFile = file("tauri.properties") + if (propFile.exists()) { + propFile.inputStream().use { load(it) } + } +} + +android { + compileSdk = 36 + namespace = "in.cinny.app" + defaultConfig { + manifestPlaceholders["usesCleartextTraffic"] = "true" + applicationId = "in.cinny.app" + minSdk = 24 + targetSdk = 36 + versionCode = tauriProperties.getProperty("tauri.android.versionCode", "1").toInt() + versionName = tauriProperties.getProperty("tauri.android.versionName", "1.0") + } + signingConfigs { + create("release") { + val keystorePropertiesFile = rootProject.file("keystore.properties") + val keystoreProperties = Properties() + if (keystorePropertiesFile.exists()) { + keystoreProperties.load(FileInputStream(keystorePropertiesFile)) + } + + keyAlias = keystoreProperties["keyAlias"] as String + keyPassword = keystoreProperties["password"] as String + storeFile = file(keystoreProperties["storeFile"] as String) + storePassword = keystoreProperties["password"] as String + } + } + buildTypes { + getByName("debug") { + manifestPlaceholders["usesCleartextTraffic"] = "true" + isDebuggable = true + isJniDebuggable = true + isMinifyEnabled = false + packaging { + jniLibs.keepDebugSymbols.add("*/arm64-v8a/*.so") + jniLibs.keepDebugSymbols.add("*/armeabi-v7a/*.so") + jniLibs.keepDebugSymbols.add("*/x86/*.so") + jniLibs.keepDebugSymbols.add("*/x86_64/*.so") + } + } + getByName("release") { + signingConfig = signingConfigs.getByName("release") + isMinifyEnabled = true + proguardFiles( + *fileTree(".") { include("**/*.pro") } + .plus(getDefaultProguardFile("proguard-android-optimize.txt")) + .toList().toTypedArray() + ) + } + } + kotlinOptions { + jvmTarget = "1.8" + } + buildFeatures { + buildConfig = true + } +} + +rust { + rootDirRel = "../../../" +} + +dependencies { + implementation("androidx.webkit:webkit:1.14.0") + implementation("androidx.appcompat:appcompat:1.7.1") + implementation("androidx.activity:activity-ktx:1.10.1") + implementation("com.google.android.material:material:1.12.0") + testImplementation("junit:junit:4.13.2") + androidTestImplementation("androidx.test.ext:junit:1.1.4") + androidTestImplementation("androidx.test.espresso:espresso-core:3.5.0") +} + +apply(from = "tauri.build.gradle.kts") diff --git a/src-tauri/android-overlay/app/src/main/java/in/cinny/app/MainActivity.kt b/src-tauri/android-overlay/app/src/main/java/in/cinny/app/MainActivity.kt new file mode 100644 index 0000000..3afb69e --- /dev/null +++ b/src-tauri/android-overlay/app/src/main/java/in/cinny/app/MainActivity.kt @@ -0,0 +1,24 @@ +package `in`.cinny.app + +import android.os.Bundle +import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat +import androidx.core.view.updatePadding + +class MainActivity : TauriActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + val content = findViewById(android.R.id.content) + ViewCompat.setOnApplyWindowInsetsListener(content) { view, windowInsets -> + val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) + view.updatePadding( + left = insets.left, + top = insets.top, + right = insets.right, + bottom = insets.bottom, + ) + windowInsets + } + } +} diff --git a/src-tauri/android-overlay/app/src/main/res/values-night/colors.xml b/src-tauri/android-overlay/app/src/main/res/values-night/colors.xml new file mode 100644 index 0000000..2ab8bb1 --- /dev/null +++ b/src-tauri/android-overlay/app/src/main/res/values-night/colors.xml @@ -0,0 +1,4 @@ + + + #1A1A1A + diff --git a/src-tauri/android-overlay/app/src/main/res/values-night/themes.xml b/src-tauri/android-overlay/app/src/main/res/values-night/themes.xml new file mode 100644 index 0000000..c95c2cb --- /dev/null +++ b/src-tauri/android-overlay/app/src/main/res/values-night/themes.xml @@ -0,0 +1,9 @@ + + + + diff --git a/src-tauri/android-overlay/app/src/main/res/values/colors.xml b/src-tauri/android-overlay/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..2ab8bb1 --- /dev/null +++ b/src-tauri/android-overlay/app/src/main/res/values/colors.xml @@ -0,0 +1,4 @@ + + + #1A1A1A + diff --git a/src-tauri/android-overlay/app/src/main/res/values/themes.xml b/src-tauri/android-overlay/app/src/main/res/values/themes.xml new file mode 100644 index 0000000..c95c2cb --- /dev/null +++ b/src-tauri/android-overlay/app/src/main/res/values/themes.xml @@ -0,0 +1,9 @@ + + + + diff --git a/src-tauri/android-overlay/buildSrc/src/main/java/in/cinny/app/kotlin/BuildTask.kt b/src-tauri/android-overlay/buildSrc/src/main/java/in/cinny/app/kotlin/BuildTask.kt new file mode 100644 index 0000000..55b2590 --- /dev/null +++ b/src-tauri/android-overlay/buildSrc/src/main/java/in/cinny/app/kotlin/BuildTask.kt @@ -0,0 +1,56 @@ +import java.io.File +import org.apache.tools.ant.taskdefs.condition.Os +import org.gradle.api.DefaultTask +import org.gradle.api.GradleException +import org.gradle.api.logging.LogLevel +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.TaskAction + +open class BuildTask : DefaultTask() { + @Input + var rootDirRel: String? = null + @Input + var target: String? = null + @Input + var release: Boolean? = null + + @TaskAction + fun assemble() { + val executable = """node""" + try { + runTauriCli(executable) + } catch (e: Exception) { + if (Os.isFamily(Os.FAMILY_WINDOWS)) { + runTauriCli("$executable.cmd") + } else { + throw e + } + } + } + + fun runTauriCli(executable: String) { + val rootDirRel = rootDirRel ?: throw GradleException("rootDirRel cannot be null") + val target = target ?: throw GradleException("target cannot be null") + val release = release ?: throw GradleException("release cannot be null") + val tauriCli = File( + project.projectDir, + "${rootDirRel}/../node_modules/@tauri-apps/cli/tauri.js" + ).canonicalPath + val args = listOf(tauriCli, "android", "android-studio-script") + + project.exec { + workingDir(File(project.projectDir, rootDirRel)) + executable(executable) + args(args) + if (project.logger.isEnabled(LogLevel.DEBUG)) { + args("-vv") + } else if (project.logger.isEnabled(LogLevel.INFO)) { + args("-v") + } + if (release) { + args("--release") + } + args(listOf("--target", target)) + }.assertNormalExitValue() + } +}