Compare commits

...

2 Commits

Author SHA1 Message Date
0e159900b1 Dropped iOS (not paying 99 dollars lol) 2026-05-13 08:53:29 +02:00
04c0cdc1eb Overlay 2026-05-13 08:50:19 +02:00
12 changed files with 218 additions and 236 deletions

8
.gitignore vendored
View File

@@ -2,7 +2,13 @@ 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

View File

@@ -1,10 +1,10 @@
**NOTE: This is a vibe-ported HARD FORK of the cinny-desktop repo and iOS support is untested. Have fun.**
**NOTE: This is a vibe-ported HARD FORK of the cinny-desktop repo and is now Android-only. Have fun.**
---
# Cinny Mobile
Cinny is a Matrix client focused on a simple, elegant, and secure interface. This fork ports the Tauri host from desktop packaging to Tauris Android and iOS targets while keeping the upstream Cinny web app as the frontend.
Cinny is a Matrix client focused on a simple, elegant, and secure interface. This fork ports the Tauri host from desktop packaging to Tauris Android target while keeping the upstream Cinny web app as the frontend.
## Project Layout
@@ -14,7 +14,7 @@ Cinny is a Matrix client focused on a simple, elegant, and secure interface. Thi
## Local Development
Follow the official [Tauri prerequisites](https://v2.tauri.app/start/prerequisites/) first. For phone targets, also install the Android and iOS toolchains described there.
Follow the official [Tauri prerequisites](https://v2.tauri.app/start/prerequisites/) first. For phone builds, also install the Android toolchain described there.
Then install dependencies:
@@ -29,14 +29,11 @@ Then install dependencies:
- `npm run android:init`
- `npm run android:dev`
- `npm run android:build`
- `npm run ios:init`
- `npm run ios:dev`
- `npm run ios:build`
`npm run tauri dev` is still useful for validating the shared Tauri host on the current machine before deploying to a handset or simulator.
## Notes
- The Tauri entrypoint lives in src-tauri/src/lib.rs and is shared by desktop dev runs plus Android and iOS builds.
- The Tauri entrypoint lives in src-tauri/src/lib.rs and is shared by desktop dev runs plus Android builds.
- Production assets are served through `tauri-plugin-localhost` so the wrapped web app can continue using its existing browser-oriented runtime behavior.
- Android scaffolding has been regenerated in this workspace. iOS setup is intentionally deferred for now and still requires the usual Apple-side tooling plus a valid development team for signed runs.
- Android scaffolding has been regenerated in this workspace and is the only mobile target maintained by this fork.

View File

@@ -12,10 +12,7 @@
"android:sync-icons": "npm run android:prepare",
"android:init": "sh ./scripts/with-android-java.sh npm run tauri android init -- --ci --skip-targets-install && npm run android:prepare",
"android:dev": "npm run android:prepare && sh ./scripts/with-android-java.sh npm run tauri android dev",
"android:build": "npm run android:prepare && sh ./scripts/with-android-java.sh npm run tauri android build",
"ios:init": "npm run tauri ios init -- --ci --skip-targets-install",
"ios:dev": "npm run tauri ios dev",
"ios:build": "npm run tauri ios build"
"android:build": "npm run android:prepare && sh ./scripts/with-android-java.sh npm run tauri android build"
},
"keywords": [],
"author": "Ajay Bura",

View File

@@ -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.view.View>(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'
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="cinny_system_bar_background">#1A1A1A</color>
</resources>
EOF
cat > "$ANDROID_VALUES_NIGHT_DIR/colors.xml" <<'EOF'
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="cinny_system_bar_background">#1A1A1A</color>
</resources>
EOF
cat > "$ANDROID_VALUES_DIR/themes.xml" <<'EOF'
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Theme.cinny" parent="Theme.MaterialComponents.DayNight.NoActionBar">
<item name="android:statusBarColor">@color/cinny_system_bar_background</item>
<item name="android:navigationBarColor">@color/cinny_system_bar_background</item>
<item name="android:windowLightStatusBar">false</item>
<item name="android:windowLightNavigationBar">false</item>
</style>
</resources>
EOF
cat > "$ANDROID_VALUES_NIGHT_DIR/themes.xml" <<'EOF'
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Theme.cinny" parent="Theme.MaterialComponents.DayNight.NoActionBar">
<item name="android:statusBarColor">@color/cinny_system_bar_background</item>
<item name="android:navigationBarColor">@color/cinny_system_bar_background</item>
<item name="android:windowLightStatusBar">false</item>
<item name="android:windowLightNavigationBar">false</item>
</style>
</resources>
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

View File

@@ -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")

View File

@@ -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.view.View>(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
}
}
}

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="cinny_system_bar_background">#1A1A1A</color>
</resources>

View File

@@ -0,0 +1,9 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Theme.cinny" parent="Theme.MaterialComponents.DayNight.NoActionBar">
<item name="android:statusBarColor">@color/cinny_system_bar_background</item>
<item name="android:navigationBarColor">@color/cinny_system_bar_background</item>
<item name="android:windowLightStatusBar">false</item>
<item name="android:windowLightNavigationBar">false</item>
</style>
</resources>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="cinny_system_bar_background">#1A1A1A</color>
</resources>

View File

@@ -0,0 +1,9 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Theme.cinny" parent="Theme.MaterialComponents.DayNight.NoActionBar">
<item name="android:statusBarColor">@color/cinny_system_bar_background</item>
<item name="android:navigationBarColor">@color/cinny_system_bar_background</item>
<item name="android:windowLightStatusBar">false</item>
<item name="android:windowLightNavigationBar">false</item>
</style>
</resources>

View File

@@ -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()
}
}

View File

@@ -15,9 +15,6 @@
"longDescription": "Phone-focused fork of Cinny built with Tauri mobile.",
"android": {
"minSdkVersion": 24
},
"iOS": {
"minimumSystemVersion": "14.0"
}
},
"build": {