Live watching is now event-driven, no tree polling: - Android: MainActivity registers a MediaStore ContentObserver (Images, notifyForDescendants) and wakes the Rust watcher thread via a JNI call (Java_wow_gmp_gallery_MainActivity_nativeFsChange) — a single channel send, no main-thread work. Covers camera saves, media-scanner indexing, other apps' indexed writes. - Desktop: inotify/FSEvents via notify on the scan roots. - On wake: coalesce + debounce, diff dir mtimes, rescan only the changed subtrees. The 1 s recv timeout doubles as a cheap backstop (raw out-of-band copies the scanner hasn't indexed yet); a 5-minute full pass is the safety net. minSdk 30 (3-arg ContentObserver API). Stale-listing truncation bug found and fixed (reproduced on the emulator's 9p /sdcard; the phone's FUSE does the same): a readdir over a large directory can return a SILENT early EOF — a stale cached listing with NO error. The phone's 9961-file Camera dir truncated at ~8400 once and db::apply deleted ~1700 good rows. Three layers now: 1. scan::read_dir_full — retry whole-dir reads on mid-stream errors; 2. per-directory cross-check in walk_dir: if the enumeration is far below the DB's count for that dir, re-read up to 3x keeping the best listing; whole-walk re-check in rescan keeps the larger of two walks; 3. db::apply mass-delete guard: never delete >20% of the DB in one apply unless a previous walk already saw the low count (two-strike; a meta.walked row tracks it). Genuine gradual deletions still flow through (verified: a 4000-file mass move streams the grid down progressively), and a second consecutive low walk still deletes. Emulator reliability suite (all pass): 3x launch count stability, add +1 / remove -1 in ~2 s, 20-file burst, DB recovery from damage, stale-listing refusal (0 rows lost), two-strike delete, live restore, idle 0.0% dropped frames, deep fling loads thumbnails. A/B vs ttimg (no-backend CSS-scroll control): adb-swipe drop deltas are at the input-injection artifact level, idle identical (0.0%).
90 lines
2.5 KiB
Plaintext
90 lines
2.5 KiB
Plaintext
import java.util.Properties
|
|
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
|
|
|
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 = 37
|
|
namespace = "wow.gmp.gallery"
|
|
defaultConfig {
|
|
manifestPlaceholders["usesCleartextTraffic"] = "false"
|
|
applicationId = "wow.gmp.gallery"
|
|
minSdk = 30
|
|
targetSdk = 37
|
|
versionCode = tauriProperties.getProperty("tauri.android.versionCode", "1").toInt()
|
|
versionName = tauriProperties.getProperty("tauri.android.versionName", "1.0")
|
|
}
|
|
lint {
|
|
// personal-device test builds: debuggable flag is intentional
|
|
abortOnError = false
|
|
checkReleaseBuilds = false
|
|
}
|
|
|
|
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") {
|
|
optimization {
|
|
enable = true
|
|
}
|
|
proguardFiles(
|
|
*fileTree(".") {
|
|
include("**/*.pro")
|
|
exclude("build/**")
|
|
}.files.toTypedArray()
|
|
)
|
|
}
|
|
}
|
|
compileOptions {
|
|
sourceCompatibility = JavaVersion.VERSION_1_8
|
|
targetCompatibility = JavaVersion.VERSION_1_8
|
|
}
|
|
buildFeatures {
|
|
buildConfig = true
|
|
}
|
|
}
|
|
|
|
kotlin {
|
|
compilerOptions {
|
|
jvmTarget = JvmTarget.JVM_1_8
|
|
}
|
|
}
|
|
|
|
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")
|
|
implementation("androidx.lifecycle:lifecycle-process:2.10.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 = file("tauri.build.gradle.kts"))
|