Fixes and updates

This commit is contained in:
soraefir
2026-05-23 12:52:36 +02:00
parent 82ab401862
commit 249b281ae1
18 changed files with 179 additions and 92 deletions

View File

@@ -26,6 +26,10 @@ jobs:
- uses: actions/checkout@v6 - uses: actions/checkout@v6
with: with:
submodules: true submodules: true
- name: Make script executable
run: chmod +x ./patch_submodule.sh
- name: Run patch submodule script
run: ./patch_submodule.sh
- name: set up secrets - name: set up secrets
run: | run: |
echo "${{ secrets.RELEASE_KEYSTORE }}" > keystore.asc echo "${{ secrets.RELEASE_KEYSTORE }}" > keystore.asc
@@ -42,7 +46,7 @@ jobs:
- name: set up JDK - name: set up JDK
uses: actions/setup-java@v5 uses: actions/setup-java@v5
with: with:
java-version: 17 java-version: 21
distribution: "temurin" distribution: "temurin"
cache: 'gradle' cache: 'gradle'
- name: Setup Gradle - name: Setup Gradle

View File

@@ -1,27 +1,38 @@
def getCommitCount() {
try {
def stdout = new ByteArrayOutputStream()
exec {
commandLine 'git', 'rev-list', '--count', 'HEAD'
standardOutput = stdout
}
return stdout.toString().trim().toInteger()
} catch (ignored) {
return 1
}
}
plugins { plugins {
id 'com.android.application' id 'com.android.application'
id 'org.jetbrains.kotlin.android'
id 'org.jetbrains.kotlin.plugin.serialization' version '2.3.21' id 'org.jetbrains.kotlin.plugin.serialization' version '2.3.21'
id 'org.jetbrains.kotlin.plugin.compose' version '2.3.21' id 'org.jetbrains.kotlin.plugin.compose' version '2.3.21'
} }
android { android {
namespace 'net.helcel.fidelity' namespace 'net.helcel.fidelity'
compileSdk 36 compileSdk = 37
defaultConfig { defaultConfig {
applicationId 'net.helcel.fidelity' applicationId 'net.helcel.fidelity'
versionName "1.0d" versionName "1.3"
versionCode getCommitCount()
buildConfigField("String", "APP_NAME", "\"Keepass Fidelity\"") buildConfigField("String", "APP_NAME", "\"Keepass Fidelity\"")
manifestPlaceholders["APP_NAME"] = "Keepass Fidelity" manifestPlaceholders["APP_NAME"] = "Keepass Fidelity"
minSdk 28 minSdk = 28
targetSdk 36 targetSdk = 37
} }
signingConfigs { signingConfigs {
create("release") { register("release") {
try { try {
def keystorePropertiesFile = rootProject.file("app/keystore.properties") def keystorePropertiesFile = rootProject.file("app/keystore.properties")
def keystoreProperties = new Properties() def keystoreProperties = new Properties()
@@ -41,10 +52,12 @@ android {
buildTypes { buildTypes {
debug { debug {
debuggable true debuggable true
initWith(buildTypes.release)
signingConfig signingConfigs.debug
} }
release { release {
minifyEnabled true minifyEnabled true
shrinkResources false shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
} }
signedRelease { signedRelease {
@@ -86,10 +99,8 @@ android {
disable 'UsingMaterialAndMaterial3Libraries' disable 'UsingMaterialAndMaterial3Libraries'
disable 'PreviewAnnotationInFunctionWithParameters' disable 'PreviewAnnotationInFunctionWithParameters'
} }
} }
dependencies { dependencies {
implementation 'androidx.compose.ui:ui' implementation 'androidx.compose.ui:ui'
implementation 'androidx.compose.material3:material3:1.4.0' implementation 'androidx.compose.material3:material3:1.4.0'

View File

@@ -62,6 +62,7 @@ import net.helcel.fidelity.tools.FidelityEntry
import net.helcel.fidelity.tools.FidelityRepository import net.helcel.fidelity.tools.FidelityRepository
import net.helcel.fidelity.tools.FidelityRepository.activeEntry import net.helcel.fidelity.tools.FidelityRepository.activeEntry
import net.helcel.fidelity.tools.FidelityRepository.addEntry import net.helcel.fidelity.tools.FidelityRepository.addEntry
import kotlin.time.Duration.Companion.milliseconds
@Preview @Preview
@@ -81,7 +82,7 @@ fun CreateEntryScreen(navController: NavHostController?) {
LaunchedEffect(entry) { LaunchedEffect(entry) {
isValidBarcode = false isValidBarcode = false
delay(500) delay(500.milliseconds)
if (entry.code.isEmpty()) return@LaunchedEffect if (entry.code.isEmpty()) return@LaunchedEffect
try { try {
val bmp = generateBarcode(entry.code, entry.format, 600) val bmp = generateBarcode(entry.code, entry.format, 600)
@@ -158,8 +159,9 @@ fun CreateEntryScreen(navController: NavHostController?) {
), ),
label = { Text("Code") }, label = { Text("Code") },
isError = errorCode.isNotEmpty(), isError = errorCode.isNotEmpty(),
modifier = Modifier.fillMaxWidth(), maxLines = 5,
singleLine = true singleLine = false,
modifier = Modifier.fillMaxWidth()
) )
if (errorCode.isNotEmpty()) { if (errorCode.isNotEmpty()) {
Text(errorCode, color = MaterialTheme.colors.error) Text(errorCode, color = MaterialTheme.colors.error)
@@ -358,7 +360,13 @@ private fun onSubmitIfValid(
object CreateEntryEventHandler { object CreateEntryEventHandler {
fun onSubmit(navController: NavHostController){ fun onSubmit(navController: NavHostController){
navController.popBackStack() navController.popBackStack()
activeEntry.value = activeEntry.value.copy(null,"","","",false) activeEntry.value = activeEntry.value.copy(
uid = null,
title = "",
code = "",
format = "",
protected = false
)
} }
fun onFileScan(navController: NavHostController){ fun onFileScan(navController: NavHostController){

View File

@@ -1,12 +1,14 @@
package net.helcel.fidelity.activity.fragment package net.helcel.fidelity.activity.fragment
import android.content.Context import android.content.Context
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
@@ -24,9 +26,12 @@ import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.FloatingActionButton import androidx.compose.material.FloatingActionButton
import androidx.compose.material.Icon import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme import androidx.compose.material.MaterialTheme
import androidx.compose.material.OutlinedTextField
import androidx.compose.material.Text import androidx.compose.material.Text
import androidx.compose.material.TextFieldDefaults
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.Edit import androidx.compose.material.icons.filled.Edit
import androidx.compose.material.icons.filled.HideSource import androidx.compose.material.icons.filled.HideSource
import androidx.compose.material.icons.filled.PushPin import androidx.compose.material.icons.filled.PushPin
@@ -36,6 +41,7 @@ import androidx.compose.material3.CardDefaults
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.pulltorefresh.PullToRefreshBox import androidx.compose.material3.pulltorefresh.PullToRefreshBox
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
@@ -44,6 +50,8 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
@@ -51,6 +59,8 @@ import androidx.navigation.NavHostController
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import net.helcel.fidelity.activity.ToastHelper
import net.helcel.fidelity.activity.fragment.LauncherEventHandlers.isSearchVisible
import net.helcel.fidelity.activity.fragment.LauncherEventHandlers.onAdd import net.helcel.fidelity.activity.fragment.LauncherEventHandlers.onAdd
import net.helcel.fidelity.activity.fragment.LauncherEventHandlers.onEdit import net.helcel.fidelity.activity.fragment.LauncherEventHandlers.onEdit
import net.helcel.fidelity.activity.fragment.LauncherEventHandlers.onHide import net.helcel.fidelity.activity.fragment.LauncherEventHandlers.onHide
@@ -58,6 +68,7 @@ import net.helcel.fidelity.activity.fragment.LauncherEventHandlers.onPin
import net.helcel.fidelity.activity.fragment.LauncherEventHandlers.onQuery import net.helcel.fidelity.activity.fragment.LauncherEventHandlers.onQuery
import net.helcel.fidelity.activity.fragment.LauncherEventHandlers.onRefresh import net.helcel.fidelity.activity.fragment.LauncherEventHandlers.onRefresh
import net.helcel.fidelity.activity.fragment.LauncherEventHandlers.onView import net.helcel.fidelity.activity.fragment.LauncherEventHandlers.onView
import net.helcel.fidelity.activity.fragment.LauncherEventHandlers.searchQuery
import net.helcel.fidelity.tools.CredentialResult import net.helcel.fidelity.tools.CredentialResult
import net.helcel.fidelity.tools.FidelityEntry import net.helcel.fidelity.tools.FidelityEntry
import net.helcel.fidelity.tools.FidelityRepository.activeEntry import net.helcel.fidelity.tools.FidelityRepository.activeEntry
@@ -79,9 +90,18 @@ fun LauncherScreen(
var showHidden by remember { mutableStateOf(false) } var showHidden by remember { mutableStateOf(false) }
val context = LocalContext.current val context = LocalContext.current
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
val sortedEntries = remember(entries) { val focusRequester = remember { FocusRequester() }
BackHandler(enabled = isSearchVisible) {
onQuery()
}
val sortedEntries = remember(entries, showHidden, searchQuery) {
derivedStateOf { derivedStateOf {
entries.filter{showHidden || !it.hidden}.sortedWith( entries.filter {
(showHidden || !it.hidden) &&
(searchQuery.isEmpty() || it.title.contains(searchQuery, ignoreCase = true))
}.sortedWith(
compareByDescending<FidelityEntry> { it.pinned } compareByDescending<FidelityEntry> { it.pinned }
.thenBy { it.hidden } .thenBy { it.hidden }
.thenByDescending { it.lastUse } .thenByDescending { it.lastUse }
@@ -105,17 +125,47 @@ fun LauncherScreen(
isRefreshing = isRefreshingState, isRefreshing = isRefreshingState,
modifier = Modifier.fillMaxSize() modifier = Modifier.fillMaxSize()
) { ) {
LazyVerticalGrid( Column(modifier = Modifier.fillMaxSize()) {
columns = GridCells.Fixed(2), if (isSearchVisible) {
modifier = Modifier LaunchedEffect(Unit) {
.fillMaxSize() focusRequester.requestFocus()
.fillMaxSize() }
.padding(16.dp), OutlinedTextField(
verticalArrangement = Arrangement.spacedBy(8.dp), value = searchQuery,
horizontalArrangement = Arrangement.spacedBy(8.dp) onValueChange = { searchQuery = it },
) { colors = TextFieldDefaults.textFieldColors(
items(sortedEntries.value) { entry -> textColor = MaterialTheme.colors.onBackground
FidelityRow(navController, entry) ),
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
.focusRequester(focusRequester),
label = { Text("Search") },
singleLine = true,
trailingIcon = {
Icon(
Icons.Default.Close,
contentDescription = "Clear",
modifier = Modifier.clickable {
searchQuery = ""
onQuery()
}
)
}
)
}
LazyVerticalGrid(
columns = GridCells.Fixed(2),
modifier = Modifier
.fillMaxSize()
.fillMaxSize()
.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(8.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
items(sortedEntries.value) { entry ->
FidelityRow(navController, entry)
}
} }
} }
FloatingActionButton( FloatingActionButton(
@@ -262,21 +312,24 @@ fun FidelityRow(
object LauncherEventHandlers { object LauncherEventHandlers {
var isSearchVisible by mutableStateOf(false)
var searchQuery by mutableStateOf("")
var CRED: CredentialResult.Success? = null
fun onAdd(navController: NavHostController) { fun onAdd(navController: NavHostController) {
navController.navigate("edit") navController.navigate("edit")
} }
fun onQuery() { fun onQuery() {
//TODO isSearchVisible = !isSearchVisible
if (!isSearchVisible) searchQuery = ""
} }
var CRED: CredentialResult.Success? = null
suspend fun onSave(context: Context, navController: NavHostController){ suspend fun onSave(context: Context, navController: NavHostController){
try { try {
if (CRED == null) { if (CRED == null) {
val res = loadCredentials(context) when (val res = loadCredentials(context)) {
when (res) { CredentialResult.AuthFailed, CredentialResult.NoData -> ToastHelper.show(context, "Unable to Load Credentials")
CredentialResult.AuthFailed, CredentialResult.NoData -> null
is CredentialResult.Success -> CRED = res is CredentialResult.Success -> CRED = res
} }
} }
@@ -297,11 +350,9 @@ object LauncherEventHandlers {
suspend fun onRefresh(context: Context, navController: NavHostController) { suspend fun onRefresh(context: Context, navController: NavHostController) {
try { try {
if (CRED == null) { if (CRED == null) {
val res = loadCredentials(context) when (val res = loadCredentials(context)) {
when (res) { CredentialResult.AuthFailed, CredentialResult.NoData -> ToastHelper.show(context, "Unable to Load Credentials")
CredentialResult.AuthFailed, CredentialResult.NoData -> null
is CredentialResult.Success -> CRED = res is CredentialResult.Success -> CRED = res
} }
} }
CRED!! CRED!!
@@ -309,8 +360,8 @@ object LauncherEventHandlers {
genCredentials(context, CRED!!) genCredentials(context, CRED!!)
} }
if (withContext(Dispatchers.IO) { if (withContext(Dispatchers.IO) {
start(context, CRED!!.db, cred) start(context, CRED!!.db, cred)
}) })
importDB(context) importDB(context)
} catch (e: Exception) { } catch (e: Exception) {
println(e.toString()) println(e.toString())

View File

@@ -53,6 +53,7 @@ import net.helcel.fidelity.activity.ToastHelper
import net.helcel.fidelity.activity.fragment.SetupEventHandlers.onOpen import net.helcel.fidelity.activity.fragment.SetupEventHandlers.onOpen
import net.helcel.fidelity.tools.CredentialResult import net.helcel.fidelity.tools.CredentialResult
import net.helcel.fidelity.tools.FidelityRepository.genCredentials import net.helcel.fidelity.tools.FidelityRepository.genCredentials
import net.helcel.fidelity.tools.FidelityRepository.importDB
import net.helcel.fidelity.tools.FidelityRepository.start import net.helcel.fidelity.tools.FidelityRepository.start
import net.helcel.fidelity.tools.KeePassStore.loadCredentials import net.helcel.fidelity.tools.KeePassStore.loadCredentials
import net.helcel.fidelity.tools.KeePassStore.packCredentials import net.helcel.fidelity.tools.KeePassStore.packCredentials
@@ -116,8 +117,8 @@ fun InitialScreen(
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
scope.launch(Dispatchers.Main) { scope.launch(Dispatchers.Main) {
when(val res = loadCredentials(context)) { when(val res = loadCredentials(context)) {
CredentialResult.AuthFailed -> null CredentialResult.AuthFailed -> ToastHelper.show(context, "Unable to Load Credentials")
CredentialResult.NoData -> null CredentialResult.NoData -> ToastHelper.show(context, "Unable to Load Credentials")
is CredentialResult.Success -> { is CredentialResult.Success -> {
if (res.db != null) dbFile = res.db if (res.db != null) dbFile = res.db
if (res.key != null) keyFile = res.key if (res.key != null) keyFile = res.key
@@ -219,13 +220,17 @@ fun InitialScreen(
onClick = { onClick = {
loading = true loading = true
scope.launch { scope.launch {
if(onOpen(context, dbFile!!, password, keyFile)){ val res = onOpen(context, dbFile!!, password, keyFile)
if(res != null){
ToastHelper.show(context, "Successful... Importing")
withContext(Dispatchers.IO) {
start(context, dbFile!!,genCredentials(context, res))
}
importDB(context)
navController!!.popBackStack() navController!!.popBackStack()
navController.navigate("init") navController.navigate("launcher")
}else{ }else{
ToastHelper.show(context, "Auth failed...") ToastHelper.show(context, "Failed... Retry")
navController!!.popBackStack()
navController.navigate("exit")
} }
} }
}, },
@@ -249,7 +254,7 @@ fun InitialScreen(
} }
object SetupEventHandlers { object SetupEventHandlers {
suspend fun onOpen(context: Context, db: Uri, p: String, key: Uri?): Boolean { suspend fun onOpen(context: Context, db: Uri, p: String, key: Uri?): CredentialResult.Success? {
try { try {
val packCred = packCredentials(db, p, key) val packCred = packCredentials(db, p, key)
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
@@ -261,14 +266,14 @@ object SetupEventHandlers {
saveCredentials(context, packCred) saveCredentials(context, packCred)
} }
return when (res) { return when (res) {
CredentialResult.AuthFailed, CredentialResult.NoData -> false CredentialResult.AuthFailed, CredentialResult.NoData -> null
is CredentialResult.Success -> true is CredentialResult.Success -> res
} }
} catch (e: Exception) { } catch (e: Exception) {
ToastHelper.show(context, e.message.toString()) ToastHelper.show(context, e.message.toString())
println("Err${e.toString()}") println("Err${e.toString()}")
println(e.message) println(e.message)
return false return null
} }
} }
} }

View File

@@ -13,7 +13,6 @@ import com.google.zxing.common.HybridBinarizer
import net.helcel.fidelity.tools.BarcodeFormatConverter.formatToString import net.helcel.fidelity.tools.BarcodeFormatConverter.formatToString
import java.util.concurrent.Executors import java.util.concurrent.Executors
@OptIn(ExperimentalGetImage::class) @OptIn(ExperimentalGetImage::class)
object BarcodeScanner { object BarcodeScanner {
@@ -56,6 +55,4 @@ object BarcodeScanner {
fun bitmapUseCase(bitmap: Bitmap, cb: (String?, String?) -> Unit) { fun bitmapUseCase(bitmap: Bitmap, cb: (String?, String?) -> Unit) {
processImage(bitmap, cb) processImage(bitmap, cb)
} }
} }

View File

@@ -113,13 +113,21 @@ suspend fun showBiometricPrompt(activity: FragmentActivity, enc: Boolean): Ciphe
activity, activity,
executor, executor,
object : BiometricPrompt.AuthenticationCallback() { object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) { cont.resume(result.cryptoObject?.cipher) {} } override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
override fun onAuthenticationError(code: Int, msg: CharSequence) { cont.resume(null) {} } cont.resume(result.cryptoObject?.cipher) { _, _, _ -> }
override fun onAuthenticationFailed() { cont.resume(null) {} } }
override fun onAuthenticationError(code: Int, msg: CharSequence) {
cont.resume(null) { _, _, _ -> }
}
override fun onAuthenticationFailed() {
cont.resume(null) { _, _, _ -> }
}
} }
) )
val iv = if(enc) null else prefs[KeePassKeys.IV]?.let { Base64.decode(it, Base64.DEFAULT) } val iv = if(enc) null else prefs[KeePassKeys.IV]?.let { Base64.decode(it, Base64.DEFAULT) }
if (!enc && iv == null) { cont.resume(null) {} } if (!enc && iv == null) {
cont.resume(null) { _, _, _ -> }
}
val cipher = getCipherForDecryption(getOrCreateBiometricKey(), iv) val cipher = getCipherForDecryption(getOrCreateBiometricKey(), iv)
val promptInfo = BiometricPrompt.PromptInfo.Builder() val promptInfo = BiometricPrompt.PromptInfo.Builder()
.setTitle("Unlock KeePass") .setTitle("Unlock KeePass")

View File

@@ -59,7 +59,7 @@ object FidelityRepository {
db.loadData( db.loadData(
bitStream, c, bitStream, c,
{ hardwareKey, seed -> retrieveResponseFromChallenge(hardwareKey, seed) }, { hardwareKey, seed -> retrieveResponseFromChallenge(hardwareKey, seed) },
false, binaryDir!!, readOnly=false, allowUserVerification = false,binaryDir!!,
{ BinaryData.canMemoryBeAllocatedInRAM(ctx, it) }, { BinaryData.canMemoryBeAllocatedInRAM(ctx, it) },
false, null false, null
) )
@@ -84,7 +84,7 @@ object FidelityRepository {
hardwareKey: HardwareKey? = null hardwareKey: HardwareKey? = null
): MasterCredential { ): MasterCredential {
return MasterCredential( return MasterCredential(
cred.password, cred.password.toCharArray(),
cred.key?.let { ctx.contentResolver.openInputStream(cred.key)?.readBytes() }, cred.key?.let { ctx.contentResolver.openInputStream(cred.key)?.readBytes() },
hardwareKey hardwareKey
) )
@@ -103,8 +103,8 @@ object FidelityRepository {
val newEntry = FidelityEntry( val newEntry = FidelityEntry(
uid=it.nodeId.id.toString(), uid=it.nodeId.id.toString(),
title=it.title, title=it.title,
code=code.protectedValue.stringValue, code=code.protectedValue.toString(),
format=format.protectedValue.stringValue, format=format.protectedValue.toString(),
protected=code.protectedValue.isProtected, protected=code.protectedValue.isProtected,
) )
val idx = entries.indexOfFirst { e -> e.uid == newEntry.uid } val idx = entries.indexOfFirst { e -> e.uid == newEntry.uid }
@@ -172,7 +172,7 @@ object FidelityRepository {
putExtraField( putExtraField(
Field( Field(
FidelityKeepassFields.FIDELITYFORMAT, FidelityKeepassFields.FIDELITYFORMAT,
ProtectedString(string= entry.format) ProtectedString(true, entry.format.toCharArray())
) )
) )
if(dbParent!=null) title = entry.title if(dbParent!=null) title = entry.title

View File

@@ -2,4 +2,5 @@
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/> <background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/> <foreground android:drawable="@drawable/ic_launcher_foreground"/>
<monochrome android:drawable="@drawable/ic_launcher_foreground"/>
</adaptive-icon> </adaptive-icon>

View File

@@ -1,11 +1,9 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules. // Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript { buildscript {
// ext.kotlin_version = '1.8.20' ext.joda_time_version = '2.14.1'
// ext.android_core_version = '1.10.1' ext.commons_io_version = '2.21.0'
// ext.android_appcompat_version = '1.6.1' ext.android_test_version = '1.7.0'
// ext.android_material_version = '1.9.0'
ext.android_test_version = '1.5.2'
} }
plugins { plugins {

View File

@@ -22,3 +22,7 @@ kotlin.code.style=official
# resources declared in the library itself and none from the library's dependencies, # resources declared in the library itself and none from the library's dependencies,
# thereby reducing the size of the R class for that library # thereby reducing the size of the R class for that library
android.nonTransitiveRClass=true android.nonTransitiveRClass=true
android.onlyEnableUnitTestForTheTestedBuildType=false
android.uniquePackageNames=false
android.r8.strictFullModeForKeepRules=false
android.dependency.useConstraints=false

Binary file not shown.

View File

@@ -2,6 +2,8 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.1-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.1-bin.zip
networkTimeout=10000 networkTimeout=10000
retries=0
retryBackOffMs=500
validateDistributionUrl=true validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

2
gradlew vendored
View File

@@ -57,7 +57,7 @@
# Darwin, MinGW, and NonStop. # Darwin, MinGW, and NonStop.
# #
# (3) This script is generated from the Groovy template # (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # https://github.com/gradle/gradle/blob/3d91ce3b8caaf77ad09f381f43615b715b53f72c/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project. # within the Gradle project.
# #
# You can find Gradle at https://github.com/gradle/gradle/. # You can find Gradle at https://github.com/gradle/gradle/.

31
gradlew.bat vendored
View File

@@ -23,8 +23,8 @@
@rem @rem
@rem ########################################################################## @rem ##########################################################################
@rem Set local scope for the variables with windows NT shell @rem Set local scope for the variables, and ensure extensions are enabled
if "%OS%"=="Windows_NT" setlocal setlocal EnableExtensions
set DIRNAME=%~dp0 set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=. if "%DIRNAME%"=="" set DIRNAME=.
@@ -51,7 +51,7 @@ echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2 echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2 echo location of your Java installation. 1>&2
goto fail "%COMSPEC%" /c exit 1
:findJavaFromJavaHome :findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=% set JAVA_HOME=%JAVA_HOME:"=%
@@ -65,7 +65,7 @@ echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2 echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2 echo location of your Java installation. 1>&2
goto fail "%COMSPEC%" /c exit 1
:execute :execute
@rem Setup the command line @rem Setup the command line
@@ -73,21 +73,10 @@ goto fail
@rem Execute Gradle @rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* @rem endlocal doesn't take effect until after the line is parsed and variables are expanded
@rem which allows us to clear the local environment before executing the java command
endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel
:end :exitWithErrorLevel
@rem End local scope for the variables with windows NT shell @rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts
if %ERRORLEVEL% equ 0 goto mainEnd "%COMSPEC%" /c exit %ERRORLEVEL%
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

9
patch_submodule.sh Executable file
View File

@@ -0,0 +1,9 @@
#!/bin/bash
for file in external/KeePassDX/{crypto,database}/build.gradle; do
if [ -f "$file" ]; then
sed -i "/id 'kotlin-android'/d" "$file"
sed -i "/apply plugin: 'kotlin-android'/d" "$file"
sed -i '/kotlinOptions {/,/}/d' "$file"
fi
done