Sometimes the network is poor in elevators and high‑speed trains, and it’s frustrating when a mobile device can’t load.
Often browsing on a phone means visiting websites that are entirely composed of static files. However, when you click into the homepage, not all resources are downloaded to the device at once, causing subsequent clicks on internal links to wait for loading.
Is it possible to download all resources beforehand for the browser to browse?
One way is to download all html, css, js files to the phone, then start a web server like darkhttpd, and navigate the browser to localhost:8080, but this is a bit cumbersome.
The cross‑platform framework Tauri v2 provides an easier method, as follows:
Create a new project using the vanilla template
pnpm create tauri-app -m pnpm -t vanilla --identifier app.xjtu.rust-doc xjtu-tauri-app-rust-doc
cd xjtu-tauri-app-rust-doc
pnpm install
pnpm tauri android init
Then copy the static resource directory containing index.html into the src directory:
rsync -av --delete ~/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/share/doc/rust/html/ ./src/
Refer to Android Code Signing | Tauri to modify the following files, adding the credentials needed for release builds:
src-tauri/gen/android/keystore.properties
password=7
keyAlias=xj
storeFile=/app.jks
src-tauri/gen/android/app/build.gradle.kts
import java.util.Properties
import java.io.FileInputStream
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 = 34
namespace = "app.xjtu.static_app"
defaultConfig {
manifestPlaceholders["usesCleartextTraffic"] = "false"
applicationId = "app.xjtu.static_app"
minSdk = 24
targetSdk = 34
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") {
isMinifyEnabled = true
signingConfig = signingConfigs.getByName("release")
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.6.1")
implementation("androidx.appcompat:appcompat:1.6.1")
implementation("com.google.android.material:material:1.8.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")
Build the APK:
tauri android build --apk --target aarch64
Demo:
-
Rails Guides with old UI:
https://assets.xjtu.app/pool/app.xjtu.rails-guides.apk -
Rust Doc (
rustup doc --book)
https://assets.xjtu.app/pool/app.xjtu.rust-doc.apk
Hidden
On the Play Store there are APKs packaged with some unknown advanced method:
https://play.google.com/store/apps/details?id=com.rust_doc.md_ismail_hosen
https://play.google.com/store/apps/details?id=com.rust_book.example