Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/build-vault-android.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ jobs:
- name: Setup Android SDK
uses: android-actions/setup-android@v3
with:
cmdline-tools-version: 10406996
cmdline-tools-version: 15859902
accept-android-sdk-licenses: true
log-accepted-android-sdk-licenses: false
- name: Setup Android NDK
run: |
sdkmanager "ndk;26.0.10792818"
sdkmanager "ndk;30.0.15729638"
- uses: actions/cache@v3
with:
path: ~/.cargo/registry
Expand All @@ -44,7 +44,7 @@ jobs:
run: |
cd vault-android
echo 'sdk.dir=/usr/local/lib/android/sdk' >> local.properties
echo 'android.ndkVersion=26.0.10792818' >> local.properties
echo 'android.ndkVersion=30.0.15729638' >> local.properties

./gradlew generateUniFFIBindings

Expand Down
2 changes: 2 additions & 0 deletions vault-android/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,5 @@
.cxx
/.profile
/keystores
/.kotlin

158 changes: 73 additions & 85 deletions vault-android/app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,27 +1,68 @@
import com.android.build.api.dsl.ApplicationExtension
import com.android.build.gradle.internal.cxx.configure.gradleLocalProperties
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import java.io.ByteArrayOutputStream

plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.ksp)
alias(libs.plugins.hilt.android)
alias(libs.plugins.compose.compiler)
alias(libs.plugins.kotlinx.serialization)
alias(libs.plugins.rust.android)
}

val localProperties = gradleLocalProperties(rootDir, providers)

android {
val uniFFIBindingsPath = "generated/source/uniffi/java"
val uniFFIBindingsDir = layout.buildDirectory.dir(uniFFIBindingsPath)

tasks.register<Exec>("generateUniFFIBindings") {
description = "generates UniFFI bindings for native rust libs"

inputs.file("${project.projectDir}/../../vault-mobile/src/vault-mobile.udl")
outputs.dir(uniFFIBindingsDir)

workingDir = file("${project.projectDir}/../../vault-mobile/uniffi-bindgen")
commandLine(
"cargo",
"run",
"generate",
"../src/vault-mobile.udl",
"--language",
"kotlin",
"--no-format",
"--out-dir",
uniFFIBindingsDir.get().asFile,
)

doLast {
println("UniFFI bindings generated successfully!")
}
}

kotlin {
compilerOptions {
jvmTarget = JvmTarget.JVM_17
}
}

extensions.configure<ApplicationExtension> {
namespace = "net.koofr.vault"
compileSdk = 35
compileSdk = 37

sourceSets {
named("main").get().kotlin {
directories.add(uniFFIBindingsDir.get().asFile.absolutePath)
}
}

defaultConfig {
applicationId = "net.koofr.vault"
minSdk = 24
targetSdk = 35
versionCode = 125001
versionName = "0.1.25"
minSdk = 26
targetSdk = 37
versionCode = 126001
versionName = "0.1.26"

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
Expand Down Expand Up @@ -86,12 +127,10 @@ android {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = "17"
}
buildFeatures {
compose = true
buildConfig = true
resValues = true
}
composeOptions {
kotlinCompilerExtensionVersion = "1.5.3"
Expand All @@ -102,14 +141,14 @@ android {
}
}
ndkVersion = localProperties.getProperty("android.ndkVersion")
sourceSets {
getByName("debug") {
jniLibs.srcDir(layout.buildDirectory.dir("rustJniLibs/android"))
}
getByName("release") {
jniLibs.srcDir(layout.buildDirectory.dir("rustJniLibs/android"))
}
}
}

val rustJniLibsDir = layout.buildDirectory.dir("rustJniLibs/android").get()
tasks.matching { it.name.matches(Regex("merge.*JniLibFolders")) }.configureEach {
inputs.dir(rustJniLibsDir)
// NOTE dependency wastes precious time each build, you could omit this and run cargo build
// manually before android build
// dependsOn("cargoBuild")
}

dependencies {
Expand Down Expand Up @@ -160,96 +199,54 @@ dependencies {
debugImplementation(libs.androidx.compose.ui.test.manifest)
}

val uniFFIBindingsDir = layout.buildDirectory.dir("generated/source/uniffi/java")

tasks.register<Exec>("generateUniFFIBindings") {
inputs.file("${project.projectDir}/../../vault-mobile/src/vault-mobile.udl")
outputs.dir(uniFFIBindingsDir)

workingDir = file("${project.projectDir}/../../vault-mobile/uniffi-bindgen")
commandLine(
"cargo",
"run",
"generate",
"../src/vault-mobile.udl",
"--language",
"kotlin",
"--out-dir",
uniFFIBindingsDir.get().asFile,
)

doLast {
println("UniFFI bindings generated successfully!")
}
}

kotlin {
sourceSets {
main {
kotlin.srcDir(uniFFIBindingsDir)
}
}
interface ExecuteOps {
@get:Inject val execOps: ExecOperations
}

apply(plugin = "org.mozilla.rust-android-gradle.rust-android")

fun getGitRevision(): String {
val x = project.objects.newInstance<ExecuteOps>()
val stdout = ByteArrayOutputStream()
project.exec {
x.execOps.exec {
commandLine("git", "rev-parse", "--short", "HEAD")
standardOutput = stdout
}
return String(stdout.toByteArray()).trim()
val rev = String(stdout.toByteArray()).trim()
return rev
}

fun getGitRelease(): String {
val x = project.objects.newInstance<ExecuteOps>()
val stdout = ByteArrayOutputStream()
project.exec {
x.execOps.exec {
commandLine("git", "describe", "--tags", "--exact-match")
standardOutput = stdout
isIgnoreExitValue = true
}
return String(stdout.toByteArray()).trim()
val rel = String(stdout.toByteArray()).trim()
return rel
}

extensions.configure(com.nishtahir.CargoExtension::class) {
cargo {
module = "../../vault-mobile"
libname = "vault_mobile"
targets = listOf("arm", "arm64", "x86", "x86_64")
// targets = listOf("x86")
targetDirectory = "../../target"
pythonCommand = "python3"
profile = System.getenv("GRADLE_CARGO_PROFILE") ?: "release"
exec = { spec, _ ->
spec.environment("GIT_REVISION", getGitRevision())
spec.environment("GIT_RELEASE", getGitRelease())

// Support 16 KB page sizes
// https://github.com/mozilla/rust-android-gradle/pull/151#issuecomment-2931056842
spec.environment(
"RUST_ANDROID_GRADLE_CC_LINK_ARG",
"-Wl,-z,max-page-size=16384,-soname,libvault_mobile.so",
)
}
environmentalOverrides["GIT_REVISION"] = getGitRevision()
environmentalOverrides["GIT_RELEASE"] = getGitRelease()
environmentalOverrides["RUST_ANDROID_GRADLE_CC_LINK_ARG"] = "-Wl,-z,max-page-size=16384,-soname,libvault_mobile.so"
}

//tasks.whenTaskAdded {
// if (name == "javaPreCompileDebug" || name == "javaPreCompileRelease") {
// dependsOn("cargoBuild")
// dependsOn("generateUniFFIBindings")
// }
// if (name == "kaptGenerateStubsDebugKotlin" || name == "kaptGenerateStubsReleaseKotlin") {
// dependsOn("generateUniFFIBindings")
// }
//}

val mergedJniLibsDir = layout.buildDirectory.dir("intermediates/merged_jni_libs")

// mergeDebugNativeLibs and mergeReleaseNativeLibs don't update the .so files in
// build/intermediates/merged_jni_libs. if we manually delete this folder before
// cargoBuild the new libraries will be copied correctly without needing to run
// clean task
tasks.register<Delete>("cleanupMergedJniLibs") {
description = "cleans merged JNI libs so the shared object files get updated"

delete(mergedJniLibsDir)

doLast {
Expand All @@ -262,12 +259,3 @@ tasks.whenTaskAdded {
dependsOn("cleanupMergedJniLibs")
}
}

task("printJniLibs") {
doLast {
println("debug")
println(android.sourceSets["debug"].jniLibs)
println("release")
println(android.sourceSets["release"].jniLibs)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import net.koofr.vault.IntlOwnership
import net.koofr.vault.MobileVault
import org.json.JSONObject
import java.io.Closeable
import kotlin.collections.iterator

class Fixture constructor(
val fakeRemote: FakeRemote,
Expand Down
2 changes: 1 addition & 1 deletion vault-android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/Theme.Vault"
tools:targetApi="33">
tools:targetApi="37">
<activity
android:name=".MainActivity"
android:exported="true"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.compositionLocalOf
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.ViewModel
import androidx.lifecycle.compose.LocalLifecycleOwner
import dagger.hilt.android.lifecycle.HiltViewModel
import net.koofr.vault.features.dialogs.Dialogs
import net.koofr.vault.features.notifications.NotificationHandler
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
package net.koofr.vault.composables

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
import androidx.compose.material3.pulltorefresh.PullToRefreshDefaults.Indicator
import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import net.koofr.vault.Status
import net.koofr.vault.composables.pullrefresh.PullRefreshIndicator
import net.koofr.vault.composables.pullrefresh.pullRefresh
import net.koofr.vault.composables.pullrefresh.rememberPullRefreshState

@Composable
fun RefreshableList(
Expand All @@ -29,19 +29,26 @@ fun RefreshableList(

val refreshing = pullRefreshing.value && status is Status.Loading && status.loaded

val pullRefreshState = rememberPullRefreshState(
refreshing = refreshing,
val state = rememberPullToRefreshState()

PullToRefreshBox(
isRefreshing = refreshing,
onRefresh = {
pullRefreshing.value = true

onRefresh()
},
)

Box(
modifier = modifier
.fillMaxSize()
.pullRefresh(pullRefreshState),
modifier = modifier.fillMaxSize(),
state = state,
indicator = {
Indicator(
state = state,
isRefreshing = refreshing,
modifier = Modifier.align(Alignment.TopCenter),
containerColor = MaterialTheme.colorScheme.background,
color = MaterialTheme.colorScheme.primary
)
}
) {
LazyColumn(modifier = Modifier.fillMaxSize()) {
when {
Expand Down Expand Up @@ -77,12 +84,6 @@ fun RefreshableList(
}
}

PullRefreshIndicator(
refreshing = refreshing,
state = pullRefreshState,
modifier = Modifier.align(Alignment.TopCenter),
)

if (status is Status.Loading && !status.loaded) {
LoadingView()
}
Expand Down
Loading
Loading