Compare commits
10
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
741e3cf861
|
||
|
|
8c7dfd8568
|
||
|
|
da9d2a2f74
|
||
|
|
d654e5f0f3
|
||
|
|
1742da65df
|
||
|
|
f515e5cd5e
|
||
|
|
4c2edc77ca
|
||
|
|
e2d9088dea
|
||
|
|
0633dbe2ca
|
||
|
|
3586a16f5f
|
@@ -49,12 +49,21 @@ jobs:
|
|||||||
- name: Setup Gradle
|
- name: Setup Gradle
|
||||||
uses: gradle/actions/setup-gradle@v6
|
uses: gradle/actions/setup-gradle@v6
|
||||||
|
|
||||||
|
- name: Run unit tests
|
||||||
|
run: ./gradlew testDebugUnitTest
|
||||||
|
|
||||||
- name: Build APK
|
- name: Build APK
|
||||||
run: |
|
run: |
|
||||||
VERSION_CODE=$(git rev-list --count HEAD)
|
VERSION_CODE=$(git rev-list --count HEAD)
|
||||||
VERSION_NAME=$(git describe --tags --always)
|
VERSION_NAME=$(git describe --tags --always)
|
||||||
./gradlew assembleSignedRelease -PVERSION_CODE=$VERSION_CODE -PVERSION_NAME=$VERSION_NAME
|
./gradlew assembleSignedRelease -PVERSION_CODE=$VERSION_CODE -PVERSION_NAME=$VERSION_NAME
|
||||||
|
|
||||||
|
- name: Upload APK
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: Cowspent-APK
|
||||||
|
path: app/build/outputs/apk/signedRelease/app-signedRelease.apk
|
||||||
|
|
||||||
- name: Release
|
- name: Release
|
||||||
uses: softprops/action-gh-release@v3
|
uses: softprops/action-gh-release@v3
|
||||||
if: startsWith(github.ref, 'refs/tags/')
|
if: startsWith(github.ref, 'refs/tags/')
|
||||||
|
|||||||
+43
-10
@@ -1,7 +1,8 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id 'com.android.application' version '9.2.1'
|
id 'com.android.application' version '9.3.2'
|
||||||
id 'org.jetbrains.kotlin.plugin.serialization' version '2.4.0'
|
id 'org.jetbrains.kotlin.plugin.serialization' version '2.4.10'
|
||||||
id 'org.jetbrains.kotlin.plugin.compose' version '2.4.0'
|
id 'org.jetbrains.kotlin.plugin.compose' version '2.4.10'
|
||||||
|
id 'jacoco'
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
@@ -40,6 +41,7 @@ android {
|
|||||||
debuggable true
|
debuggable true
|
||||||
initWith(buildTypes.release)
|
initWith(buildTypes.release)
|
||||||
signingConfig = signingConfigs.debug
|
signingConfig = signingConfigs.debug
|
||||||
|
enableUnitTestCoverage true
|
||||||
}
|
}
|
||||||
release {
|
release {
|
||||||
minifyEnabled true
|
minifyEnabled true
|
||||||
@@ -69,6 +71,12 @@ android {
|
|||||||
compose = true
|
compose = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
testOptions {
|
||||||
|
unitTests {
|
||||||
|
includeAndroidResources = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
jvmToolchain(21)
|
jvmToolchain(21)
|
||||||
}
|
}
|
||||||
@@ -96,21 +104,21 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'androidx.compose.foundation:foundation:1.11.4'
|
implementation 'androidx.compose.foundation:foundation:1.12.0'
|
||||||
implementation 'androidx.compose.runtime:runtime:1.11.4'
|
implementation 'androidx.compose.runtime:runtime:1.12.0'
|
||||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.5'
|
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.5'
|
||||||
|
|
||||||
implementation 'androidx.preference:preference-ktx:1.2.1'
|
implementation 'androidx.preference:preference-ktx:1.2.1'
|
||||||
implementation 'androidx.work:work-runtime-ktx:2.11.2'
|
implementation 'androidx.work:work-runtime-ktx:2.11.2'
|
||||||
|
|
||||||
implementation 'com.google.zxing:core:3.5.4'
|
implementation 'com.google.zxing:core:3.5.4'
|
||||||
implementation 'androidx.camera:camera-camera2:1.6.1'
|
implementation 'androidx.camera:camera-camera2:1.6.2'
|
||||||
implementation 'androidx.camera:camera-lifecycle:1.6.1'
|
implementation 'androidx.camera:camera-lifecycle:1.6.2'
|
||||||
implementation 'androidx.camera:camera-view:1.6.1'
|
implementation 'androidx.camera:camera-view:1.6.2'
|
||||||
implementation 'com.github.nextcloud:Android-SingleSignOn:1.1.0'
|
implementation 'com.github.nextcloud:Android-SingleSignOn:1.1.0'
|
||||||
implementation 'com.opencsv:opencsv:5.12.0'
|
implementation 'com.opencsv:opencsv:5.12.0'
|
||||||
|
|
||||||
implementation platform('androidx.compose:compose-bom:2026.06.01')
|
implementation platform('androidx.compose:compose-bom:2026.08.00')
|
||||||
implementation 'androidx.compose.ui:ui'
|
implementation 'androidx.compose.ui:ui'
|
||||||
implementation 'androidx.compose.material:material'
|
implementation 'androidx.compose.material:material'
|
||||||
implementation 'androidx.compose.material:material-icons-extended'
|
implementation 'androidx.compose.material:material-icons-extended'
|
||||||
@@ -121,5 +129,30 @@ dependencies {
|
|||||||
implementation 'androidx.security:security-crypto:1.1.0'
|
implementation 'androidx.security:security-crypto:1.1.0'
|
||||||
implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.11.0'
|
implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.11.0'
|
||||||
implementation "androidx.datastore:datastore-preferences:1.2.1"
|
implementation "androidx.datastore:datastore-preferences:1.2.1"
|
||||||
implementation "com.google.crypto.tink:tink-android:1.22.0"
|
implementation "com.google.crypto.tink:tink-android:1.23.0"
|
||||||
|
|
||||||
|
testImplementation 'junit:junit:4.13.2'
|
||||||
|
testImplementation 'io.mockk:mockk:1.14.11'
|
||||||
|
testImplementation 'org.robolectric:robolectric:4.16.1'
|
||||||
|
testImplementation 'androidx.test:core:1.7.0'
|
||||||
|
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.11.0'
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.register('jacocoTestReport', JacocoReport) {
|
||||||
|
dependsOn 'testDebugUnitTest'
|
||||||
|
|
||||||
|
reports {
|
||||||
|
xml.required = true
|
||||||
|
html.required = true
|
||||||
|
}
|
||||||
|
|
||||||
|
def fileFilter = ['**/R.class', '**/R$*.class', '**/BuildConfig.*', '**/Manifest*.*', '**/*Test*.*']
|
||||||
|
def debugTree = fileTree(dir: "${project.layout.buildDirectory.asFile.get()}/intermediates/built_in_kotlinc/debug/compileDebugKotlin/classes", excludes: fileFilter)
|
||||||
|
def mainSrc = "${project.projectDir}/src/main/java"
|
||||||
|
|
||||||
|
sourceDirectories.from = files([mainSrc])
|
||||||
|
classDirectories.from = files([debugTree])
|
||||||
|
executionData.from = fileTree(dir: project.layout.buildDirectory.asFile.get(), includes: [
|
||||||
|
'jacoco/testDebugUnitTest.exec', 'outputs/unit_test_code_coverage/debugUnitTest/testDebugUnitTest.exec'
|
||||||
|
])
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -151,8 +151,9 @@ class EditBillActivity : AppCompatActivity() {
|
|||||||
val billIdToDuplicate = intent.getLongExtra(PARAM_BILL_ID_TO_DUPLICATE, 0)
|
val billIdToDuplicate = intent.getLongExtra(PARAM_BILL_ID_TO_DUPLICATE, 0)
|
||||||
val timeNowSeconds = System.currentTimeMillis() / 1000
|
val timeNowSeconds = System.currentTimeMillis() / 1000
|
||||||
if (billIdToDuplicate == 0L) {
|
if (billIdToDuplicate == 0L) {
|
||||||
|
val project = db.getProject(projectId)
|
||||||
bill = DBBill(
|
bill = DBBill(
|
||||||
0, 0, projectId, 0, 0.0, timeNowSeconds,
|
0, 0, projectId, project?.lastPayerId ?: 0, 0.0, timeNowSeconds,
|
||||||
"", DBBill.STATE_ADDED, DBBill.NON_REPEATED,
|
"", DBBill.STATE_ADDED, DBBill.NON_REPEATED,
|
||||||
DBBill.PAYMODE_NONE, DBBill.CATEGORY_NONE, "", DBBill.PAYMODE_ID_NONE
|
DBBill.PAYMODE_NONE, DBBill.CATEGORY_NONE, "", DBBill.PAYMODE_ID_NONE
|
||||||
)
|
)
|
||||||
@@ -324,16 +325,26 @@ class EditBillActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
private suspend fun saveBill(): Long = withContext(Dispatchers.IO) {
|
private suspend fun saveBill(): Long = withContext(Dispatchers.IO) {
|
||||||
val groupedBillIds = intent.getLongArrayExtra(PARAM_GROUPED_BILL_IDS)
|
val groupedBillIds = intent.getLongArrayExtra(PARAM_GROUPED_BILL_IDS)
|
||||||
val isCustomSplit = viewModel.isCustomSplit
|
val splitMode = viewModel.splitMode
|
||||||
|
|
||||||
if (isCustomSplit) {
|
if (splitMode != SplitMode.EVEN) {
|
||||||
val splits: Map<Long, Double> = viewModel.owersCustomSplit.filter { (id, amountStr) ->
|
val splits: Map<Long, Double> = if (splitMode == SplitMode.CUSTOM) {
|
||||||
viewModel.owersSelection[id] == true && (amountStr.replace(',', '.').toDoubleOrNull()
|
viewModel.owersCustomSplit.filter { (id, amountStr) ->
|
||||||
?: 0.0) > 0
|
viewModel.owersSelection[id] == true && viewModel.parseAmountFromUi(amountStr) > 0
|
||||||
}.mapValues {
|
}.mapValues {
|
||||||
val uiAmount = it.value.replace(',', '.').toDoubleOrNull() ?: 0.0
|
val uiAmount = viewModel.parseAmountFromUi(it.value)
|
||||||
SupportUtil.round2(uiAmount / viewModel.selectedCurrencyRate)
|
SupportUtil.round2(uiAmount / viewModel.selectedCurrencyRate)
|
||||||
}
|
}
|
||||||
|
} else { // PERCENT
|
||||||
|
val totalAmount = viewModel.amountAsDouble
|
||||||
|
viewModel.owersPercentSplit.filter { (id, percentStr) ->
|
||||||
|
viewModel.owersSelection[id] == true && viewModel.parseAmountFromUi(percentStr) > 0
|
||||||
|
}.mapValues {
|
||||||
|
val percent = viewModel.parseAmountFromUi(it.value)
|
||||||
|
val uiAmount = SupportUtil.round2(totalAmount * percent / 100.0)
|
||||||
|
SupportUtil.round2(uiAmount / viewModel.selectedCurrencyRate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (splits.isEmpty()) return@withContext 0L
|
if (splits.isEmpty()) return@withContext 0L
|
||||||
|
|
||||||
@@ -405,16 +416,8 @@ class EditBillActivity : AppCompatActivity() {
|
|||||||
val proj = db.getProject(bill.projectId)
|
val proj = db.getProject(bill.projectId)
|
||||||
if (proj != null) db.syncIfRemote(proj)
|
if (proj != null) db.syncIfRemote(proj)
|
||||||
db.updateProject(
|
db.updateProject(
|
||||||
bill.projectId,
|
projId = bill.projectId,
|
||||||
null,
|
newLastPayerId = viewModel.payerId
|
||||||
null,
|
|
||||||
null,
|
|
||||||
viewModel.payerId,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return@withContext firstSavedId
|
return@withContext firstSavedId
|
||||||
@@ -458,16 +461,8 @@ class EditBillActivity : AppCompatActivity() {
|
|||||||
newOwersIds.forEach { newBill.billOwers += DBBillOwer(0, 0, it) }
|
newOwersIds.forEach { newBill.billOwers += DBBillOwer(0, 0, it) }
|
||||||
val newBillId = db.addBill(newBill)
|
val newBillId = db.addBill(newBill)
|
||||||
db.updateProject(
|
db.updateProject(
|
||||||
bill.projectId,
|
projId = bill.projectId,
|
||||||
null,
|
newLastPayerId = viewModel.payerId
|
||||||
null,
|
|
||||||
null,
|
|
||||||
viewModel.payerId,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null
|
|
||||||
)
|
)
|
||||||
val proj = db.getProject(bill.projectId)
|
val proj = db.getProject(bill.projectId)
|
||||||
if (proj != null) db.syncIfRemote(proj)
|
if (proj != null) db.syncIfRemote(proj)
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
package net.helcel.cowspent.android.bill_edit
|
package net.helcel.cowspent.android.bill_edit
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.text.BasicTextField
|
import androidx.compose.foundation.text.BasicTextField
|
||||||
|
import androidx.compose.foundation.text.KeyboardActions
|
||||||
import androidx.compose.foundation.text.KeyboardOptions
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material.*
|
import androidx.compose.material.*
|
||||||
@@ -16,12 +18,18 @@ import androidx.compose.material.icons.filled.*
|
|||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.draw.scale
|
import androidx.compose.ui.draw.scale
|
||||||
|
import androidx.compose.ui.focus.FocusDirection
|
||||||
|
import androidx.compose.ui.focus.FocusRequester
|
||||||
|
import androidx.compose.ui.focus.focusRequester
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.SolidColor
|
import androidx.compose.ui.graphics.SolidColor
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.platform.LocalFocusManager
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.text.input.ImeAction
|
||||||
import androidx.compose.ui.text.input.KeyboardType
|
import androidx.compose.ui.text.input.KeyboardType
|
||||||
import androidx.compose.ui.text.input.VisualTransformation
|
import androidx.compose.ui.text.input.VisualTransformation
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
@@ -53,6 +61,13 @@ fun EditBillScreen(
|
|||||||
) {
|
) {
|
||||||
val canEdit = accessLevel == DBProject.ACCESS_LEVEL_UNKNOWN || accessLevel >= DBProject.ACCESS_LEVEL_PARTICIPANT
|
val canEdit = accessLevel == DBProject.ACCESS_LEVEL_UNKNOWN || accessLevel >= DBProject.ACCESS_LEVEL_PARTICIPANT
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
val focusRequester = remember { FocusRequester() }
|
||||||
|
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
if (viewModel.isNewBill) {
|
||||||
|
focusRequester.requestFocus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
StatefulAlertDialog(
|
StatefulAlertDialog(
|
||||||
state = viewModel.dialogState,
|
state = viewModel.dialogState,
|
||||||
@@ -128,7 +143,8 @@ fun EditBillScreen(
|
|||||||
viewModel = viewModel,
|
viewModel = viewModel,
|
||||||
canEdit = canEdit,
|
canEdit = canEdit,
|
||||||
onDateClick = onDateClick,
|
onDateClick = onDateClick,
|
||||||
onTimeClick = onTimeClick
|
onTimeClick = onTimeClick,
|
||||||
|
amountFocusRequester = focusRequester
|
||||||
)
|
)
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
@@ -164,7 +180,8 @@ fun BillBasicInfoSection(
|
|||||||
viewModel: EditBillViewModel,
|
viewModel: EditBillViewModel,
|
||||||
canEdit: Boolean,
|
canEdit: Boolean,
|
||||||
onDateClick: () -> Unit,
|
onDateClick: () -> Unit,
|
||||||
onTimeClick: () -> Unit
|
onTimeClick: () -> Unit,
|
||||||
|
amountFocusRequester: FocusRequester
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = "GENERAL",
|
text = "GENERAL",
|
||||||
@@ -175,29 +192,20 @@ fun BillBasicInfoSection(
|
|||||||
)
|
)
|
||||||
|
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
val focusManager = LocalFocusManager.current
|
||||||
val currencyDialogTitle =
|
val currencyDialogTitle =
|
||||||
stringResource(R.string.currency_dialog_title, viewModel.mainCurrencyName)
|
stringResource(R.string.currency_dialog_title, viewModel.mainCurrencyName)
|
||||||
|
|
||||||
OutlinedTextField(
|
|
||||||
value = viewModel.what,
|
|
||||||
onValueChange = { viewModel.what = it },
|
|
||||||
enabled = canEdit,
|
|
||||||
placeholder = { Text(stringResource(R.string.label_what)) },
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
leadingIcon = { Icon(Icons.Default.Title, contentDescription = null) }
|
|
||||||
)
|
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
|
||||||
|
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
value = viewModel.amount,
|
value = viewModel.amount,
|
||||||
onValueChange = {
|
onValueChange = { nv->
|
||||||
viewModel.amount = it
|
val filteredValue = nv.filter { it in "0123456789.+-*/" }
|
||||||
|
viewModel.amount = filteredValue
|
||||||
viewModel.updateSplits()
|
viewModel.updateSplits()
|
||||||
},
|
},
|
||||||
enabled = canEdit,
|
enabled = canEdit,
|
||||||
placeholder = { Text("0") },
|
placeholder = { Text("0") },
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth().focusRequester(amountFocusRequester),
|
||||||
leadingIcon = {
|
leadingIcon = {
|
||||||
val currencyToShow = viewModel.selectedCurrencyName.ifEmpty {
|
val currencyToShow = viewModel.selectedCurrencyName.ifEmpty {
|
||||||
viewModel.mainCurrencyName.ifEmpty { "$" }
|
viewModel.mainCurrencyName.ifEmpty { "$" }
|
||||||
@@ -231,7 +239,21 @@ fun BillBasicInfoSection(
|
|||||||
Icon(Icons.Default.SwapHoriz, contentDescription = "Change Currency")
|
Icon(Icons.Default.SwapHoriz, contentDescription = "Change Currency")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number)
|
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Phone, imeAction = ImeAction.Next),
|
||||||
|
keyboardActions = KeyboardActions(onNext = { focusManager.moveFocus(FocusDirection.Next) })
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
|
|
||||||
|
OutlinedTextField(
|
||||||
|
value = viewModel.what,
|
||||||
|
onValueChange = { viewModel.what = it },
|
||||||
|
enabled = canEdit,
|
||||||
|
placeholder = { Text(stringResource(R.string.label_what)) },
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
leadingIcon = { Icon(Icons.Default.Title, contentDescription = null) },
|
||||||
|
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next),
|
||||||
|
keyboardActions = KeyboardActions(onNext = { focusManager.moveFocus(FocusDirection.Next) })
|
||||||
)
|
)
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
@@ -357,35 +379,55 @@ fun OwerSelectionSection(
|
|||||||
)
|
)
|
||||||
Spacer(Modifier.weight(1f))
|
Spacer(Modifier.weight(1f))
|
||||||
|
|
||||||
if (viewModel.isCustomSplit) {
|
if (viewModel.splitMode != SplitMode.EVEN) {
|
||||||
val diff = viewModel.getDiffSplit()
|
val diff = viewModel.getDiffSplit()
|
||||||
if (abs(diff) > 0.01) {
|
if (abs(diff) > 0.01) {
|
||||||
|
val unit = if (viewModel.splitMode == SplitMode.PERCENT) "%" else ""
|
||||||
val diffText =
|
val diffText =
|
||||||
if (diff > 0) "Missing: ${SupportUtil.normalNumberFormat.format(diff)}" else "Excess: ${
|
if (diff > 0) "Missing: ${SupportUtil.normalNumberFormat.format(diff)}$unit" else "Excess: ${
|
||||||
SupportUtil.normalNumberFormat.format(-diff)
|
SupportUtil.normalNumberFormat.format(-diff)
|
||||||
}"
|
}$unit"
|
||||||
Text(
|
Text(
|
||||||
diffText,
|
diffText,
|
||||||
color = MaterialTheme.colors.error,
|
color = MaterialTheme.colors.error,
|
||||||
fontSize = 12.sp,
|
fontSize = 12.sp,
|
||||||
modifier = Modifier.padding(horizontal = 16.dp)
|
modifier = Modifier.padding(horizontal = 4.dp)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
Text("Even Split", fontSize = 12.sp)
|
|
||||||
}
|
}
|
||||||
Switch(
|
|
||||||
checked = !viewModel.isCustomSplit,
|
val splitOptions = listOf(SplitMode.EVEN, SplitMode.CUSTOM, SplitMode.PERCENT)
|
||||||
enabled = canEdit,
|
Row(
|
||||||
onCheckedChange = {
|
modifier = Modifier
|
||||||
viewModel.isCustomSplit = !it
|
.padding(start = 8.dp)
|
||||||
|
.background(MaterialTheme.colors.onSurface.copy(alpha = 0.05f), MaterialTheme.shapes.small)
|
||||||
|
) {
|
||||||
|
splitOptions.forEach { mode ->
|
||||||
|
val isSelected = viewModel.splitMode == mode
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.clip(MaterialTheme.shapes.small)
|
||||||
|
.background(if (isSelected) MaterialTheme.colors.primary else Color.Transparent)
|
||||||
|
.clickable(enabled = canEdit) {
|
||||||
|
viewModel.splitMode = mode
|
||||||
viewModel.updateSplits()
|
viewModel.updateSplits()
|
||||||
|
}
|
||||||
|
.padding(horizontal = 8.dp, vertical = 4.dp)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = when (mode) {
|
||||||
|
SplitMode.EVEN -> "="
|
||||||
|
SplitMode.CUSTOM -> "#"
|
||||||
|
SplitMode.PERCENT -> "%"
|
||||||
},
|
},
|
||||||
colors = SwitchDefaults.colors(
|
color = if (isSelected) MaterialTheme.colors.onPrimary else MaterialTheme.colors.onSurface,
|
||||||
uncheckedThumbColor = MaterialTheme.colors.onSurface,
|
fontSize = 14.sp,
|
||||||
)
|
fontWeight = FontWeight.Bold
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
viewModel.members.forEach { member ->
|
viewModel.members.forEach { member ->
|
||||||
val isSelected = viewModel.owersSelection[member.id] ?: false
|
val isSelected = viewModel.owersSelection[member.id] ?: false
|
||||||
@@ -410,21 +452,34 @@ fun OwerSelectionSection(
|
|||||||
Spacer(modifier = Modifier.width(8.dp))
|
Spacer(modifier = Modifier.width(8.dp))
|
||||||
Text(member.name, modifier = Modifier.weight(1f))
|
Text(member.name, modifier = Modifier.weight(1f))
|
||||||
|
|
||||||
if (isSelected || viewModel.isCustomSplit) {
|
if (isSelected || viewModel.splitMode != SplitMode.EVEN) {
|
||||||
|
val focusManager = LocalFocusManager.current
|
||||||
val interactionSource = remember { MutableInteractionSource() }
|
val interactionSource = remember { MutableInteractionSource() }
|
||||||
|
val value = if (viewModel.splitMode == SplitMode.PERCENT) {
|
||||||
|
viewModel.owersPercentSplit[member.id] ?: ""
|
||||||
|
} else {
|
||||||
|
viewModel.owersCustomSplit[member.id] ?: ""
|
||||||
|
}
|
||||||
|
|
||||||
BasicTextField(
|
BasicTextField(
|
||||||
value = viewModel.owersCustomSplit[member.id] ?: "",
|
value = value,
|
||||||
onValueChange = {
|
onValueChange = { nv ->
|
||||||
viewModel.owersCustomSplit[member.id] = it
|
val filteredValue = nv.filter { it in "0123456789.+-*/" }
|
||||||
viewModel.owersSelection[member.id] = (it != "")
|
if (viewModel.splitMode == SplitMode.PERCENT) {
|
||||||
|
viewModel.owersPercentSplit[member.id] = filteredValue
|
||||||
|
} else {
|
||||||
|
viewModel.owersCustomSplit[member.id] = filteredValue
|
||||||
|
}
|
||||||
|
viewModel.owersSelection[member.id] = (filteredValue != "")
|
||||||
},
|
},
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.width(80.dp)
|
.width(80.dp)
|
||||||
.height(46.dp),
|
.height(46.dp),
|
||||||
interactionSource = interactionSource,
|
interactionSource = interactionSource,
|
||||||
singleLine = true,
|
singleLine = true,
|
||||||
enabled = viewModel.isCustomSplit && canEdit,
|
enabled = viewModel.splitMode != SplitMode.EVEN && canEdit,
|
||||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
|
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Next),
|
||||||
|
keyboardActions = KeyboardActions(onNext = { focusManager.moveFocus(FocusDirection.Next) }),
|
||||||
cursorBrush = SolidColor(MaterialTheme.colors.onSurface),
|
cursorBrush = SolidColor(MaterialTheme.colors.onSurface),
|
||||||
textStyle = LocalTextStyle.current.copy(
|
textStyle = LocalTextStyle.current.copy(
|
||||||
textAlign = TextAlign.Right,
|
textAlign = TextAlign.Right,
|
||||||
@@ -432,13 +487,13 @@ fun OwerSelectionSection(
|
|||||||
),
|
),
|
||||||
) { innerTextField ->
|
) { innerTextField ->
|
||||||
TextFieldDefaults.OutlinedTextFieldDecorationBox(
|
TextFieldDefaults.OutlinedTextFieldDecorationBox(
|
||||||
value = viewModel.owersCustomSplit[member.id] ?: "",
|
value = value,
|
||||||
visualTransformation = VisualTransformation.None,
|
visualTransformation = VisualTransformation.None,
|
||||||
innerTextField = innerTextField,
|
innerTextField = innerTextField,
|
||||||
singleLine = true,
|
singleLine = true,
|
||||||
enabled = viewModel.isCustomSplit && canEdit,
|
enabled = viewModel.splitMode != SplitMode.EVEN && canEdit,
|
||||||
interactionSource = interactionSource,
|
interactionSource = interactionSource,
|
||||||
contentPadding = PaddingValues(horizontal = 12.dp, vertical = 0.dp),
|
contentPadding = PaddingValues(horizontal = 8.dp, vertical = 0.dp),
|
||||||
colors = TextFieldDefaults.textFieldColors(
|
colors = TextFieldDefaults.textFieldColors(
|
||||||
cursorColor = MaterialTheme.colors.onSurface,
|
cursorColor = MaterialTheme.colors.onSurface,
|
||||||
backgroundColor = Color.Transparent,
|
backgroundColor = Color.Transparent,
|
||||||
|
|||||||
@@ -16,6 +16,10 @@ import net.helcel.cowspent.model.DBMember
|
|||||||
import net.helcel.cowspent.util.SupportUtil
|
import net.helcel.cowspent.util.SupportUtil
|
||||||
import net.helcel.cowspent.util.evalMath
|
import net.helcel.cowspent.util.evalMath
|
||||||
|
|
||||||
|
enum class SplitMode {
|
||||||
|
EVEN, CUSTOM, PERCENT
|
||||||
|
}
|
||||||
|
|
||||||
class EditBillViewModel : ViewModel() {
|
class EditBillViewModel : ViewModel() {
|
||||||
var what by mutableStateOf("")
|
var what by mutableStateOf("")
|
||||||
var amount by mutableStateOf("")
|
var amount by mutableStateOf("")
|
||||||
@@ -34,27 +38,36 @@ class EditBillViewModel : ViewModel() {
|
|||||||
var members by mutableStateOf<List<DBMember>>(emptyList())
|
var members by mutableStateOf<List<DBMember>>(emptyList())
|
||||||
|
|
||||||
var owersSelection = mutableStateMapOf<Long, Boolean>()
|
var owersSelection = mutableStateMapOf<Long, Boolean>()
|
||||||
var isCustomSplit by mutableStateOf(false)
|
var splitMode by mutableStateOf(SplitMode.EVEN)
|
||||||
var owersCustomSplit = mutableStateMapOf<Long, String>()
|
var owersCustomSplit = mutableStateMapOf<Long, String>()
|
||||||
|
var owersPercentSplit = mutableStateMapOf<Long, String>()
|
||||||
|
|
||||||
var dialogState by mutableStateOf<DialogState?>(null)
|
var dialogState by mutableStateOf<DialogState?>(null)
|
||||||
|
|
||||||
|
fun parseAmountFromUi(input: String): Double {
|
||||||
|
if (input.isBlank()) return 0.0
|
||||||
|
val sanitized = input.replace(',', '.')
|
||||||
|
// If it contains any math operator, try evalMath first
|
||||||
|
if (sanitized.any { it in "+-*/" }) {
|
||||||
|
try {
|
||||||
|
val result = evalMath(sanitized)
|
||||||
|
if (result != 0.0) return SupportUtil.round2(result)
|
||||||
|
} catch (_: Exception) {}
|
||||||
|
}
|
||||||
|
val parsed = parseAmount(input) ?: 0.0
|
||||||
|
return SupportUtil.round2(parsed)
|
||||||
|
}
|
||||||
|
|
||||||
val amountAsDouble: Double
|
val amountAsDouble: Double
|
||||||
get() {
|
get() = parseAmountFromUi(amount)
|
||||||
return parseAmount(amount) ?: try {
|
|
||||||
evalMath(amount.replace(',', '.'))
|
|
||||||
} catch (_: Exception) {
|
|
||||||
0.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getEvenSplit(): Double {
|
fun getEvenSplit(): Double {
|
||||||
val selectedOwersCount = owersSelection.count { it.value }
|
val selectedOwersCount = owersSelection.count { it.value }
|
||||||
return if (selectedOwersCount > 0) amountAsDouble / selectedOwersCount else 0.0
|
return if (selectedOwersCount > 0) SupportUtil.round2(amountAsDouble / selectedOwersCount) else 0.0
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateSplits() {
|
fun updateSplits() {
|
||||||
if (!isCustomSplit) {
|
if (splitMode == SplitMode.EVEN) {
|
||||||
val even = getEvenSplit()
|
val even = getEvenSplit()
|
||||||
val evenStr = if (even == 0.0) "" else SupportUtil.round2(even).toString()
|
val evenStr = if (even == 0.0) "" else SupportUtil.round2(even).toString()
|
||||||
members.forEach { m ->
|
members.forEach { m ->
|
||||||
@@ -64,18 +77,36 @@ class EditBillViewModel : ViewModel() {
|
|||||||
owersCustomSplit.remove(m.id)
|
owersCustomSplit.remove(m.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (splitMode == SplitMode.PERCENT) {
|
||||||
|
val selectedCount = owersSelection.count { it.value }
|
||||||
|
if (selectedCount > 0) {
|
||||||
|
val evenPercent = 100.0 / selectedCount
|
||||||
|
val evenPercentStr = SupportUtil.round2(evenPercent).toString()
|
||||||
|
members.forEach { m ->
|
||||||
|
if (owersSelection[m.id] == true) {
|
||||||
|
if (owersPercentSplit[m.id].isNullOrEmpty()) {
|
||||||
|
owersPercentSplit[m.id] = evenPercentStr
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
owersPercentSplit.remove(m.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun toggleMember(id: Long, selected: Boolean) {
|
fun toggleMember(id: Long, selected: Boolean) {
|
||||||
owersSelection[id] = selected
|
owersSelection[id] = selected
|
||||||
if (isCustomSplit) {
|
if (splitMode != SplitMode.EVEN) {
|
||||||
if (selected) {
|
if (selected) {
|
||||||
if (owersCustomSplit[id].isNullOrEmpty()) {
|
if (splitMode == SplitMode.CUSTOM && owersCustomSplit[id].isNullOrEmpty()) {
|
||||||
owersCustomSplit[id] = "0"
|
owersCustomSplit[id] = "0"
|
||||||
|
} else if (splitMode == SplitMode.PERCENT && owersPercentSplit[id].isNullOrEmpty()) {
|
||||||
|
owersPercentSplit[id] = "0"
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
owersCustomSplit.remove(id)
|
owersCustomSplit.remove(id)
|
||||||
|
owersPercentSplit.remove(id)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
updateSplits()
|
updateSplits()
|
||||||
@@ -83,10 +114,17 @@ class EditBillViewModel : ViewModel() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun getDiffSplit(): Double {
|
fun getDiffSplit(): Double {
|
||||||
|
return if (splitMode == SplitMode.PERCENT) {
|
||||||
|
val customTotal = owersPercentSplit.entries
|
||||||
|
.filter { owersSelection[it.key] == true }
|
||||||
|
.sumOf { parseAmountFromUi(it.value) }
|
||||||
|
SupportUtil.round2(100.0 - customTotal)
|
||||||
|
} else {
|
||||||
val customTotal = owersCustomSplit.entries
|
val customTotal = owersCustomSplit.entries
|
||||||
.filter { owersSelection[it.key] == true }
|
.filter { owersSelection[it.key] == true }
|
||||||
.sumOf { it.value.replace(',', '.').toDoubleOrNull() ?: 0.0 }
|
.sumOf { parseAmountFromUi(it.value) }
|
||||||
return amountAsDouble - customTotal
|
SupportUtil.round2(amountAsDouble - customTotal)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getOwersIds(): List<Long> {
|
fun getOwersIds(): List<Long> {
|
||||||
@@ -204,9 +242,10 @@ class EditBillViewModel : ViewModel() {
|
|||||||
|
|
||||||
owersSelection.clear()
|
owersSelection.clear()
|
||||||
owersCustomSplit.clear()
|
owersCustomSplit.clear()
|
||||||
|
owersPercentSplit.clear()
|
||||||
|
|
||||||
if (customSplits != null) {
|
if (customSplits != null) {
|
||||||
isCustomSplit = true
|
splitMode = SplitMode.CUSTOM
|
||||||
for (member in members) {
|
for (member in members) {
|
||||||
val selected = customSplits.containsKey(member.id)
|
val selected = customSplits.containsKey(member.id)
|
||||||
owersSelection[member.id] = selected
|
owersSelection[member.id] = selected
|
||||||
@@ -222,6 +261,7 @@ class EditBillViewModel : ViewModel() {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Even split logic
|
// Even split logic
|
||||||
|
splitMode = SplitMode.EVEN
|
||||||
val billOwerIds = bill.billOwersIds
|
val billOwerIds = bill.billOwersIds
|
||||||
val selectedCount = billOwerIds.size
|
val selectedCount = billOwerIds.size
|
||||||
|
|
||||||
|
|||||||
+8
-4
@@ -84,9 +84,8 @@ class ManageCurrenciesActivity : AppCompatActivity() {
|
|||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
db!!.updateProject(
|
db!!.updateProject(
|
||||||
selectedProjectID, null, null, null,
|
projId = selectedProjectID,
|
||||||
null, null, newMainCurrencyName,
|
newCurrencyName = newMainCurrencyName
|
||||||
null, null, null
|
|
||||||
)
|
)
|
||||||
val project = db!!.getProject(selectedProjectID)
|
val project = db!!.getProject(selectedProjectID)
|
||||||
if (project != null) {
|
if (project != null) {
|
||||||
@@ -94,7 +93,12 @@ class ManageCurrenciesActivity : AppCompatActivity() {
|
|||||||
if (project.type == ProjectType.COSPEND) {
|
if (project.type == ProjectType.COSPEND) {
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
if (!db!!.cowspentServerSyncHelper
|
if (!db!!.cowspentServerSyncHelper
|
||||||
.editRemoteProject(selectedProjectID, project.name, null, null, newMainCurrencyName, editMainCurrencyCallBack)
|
.editRemoteProject(
|
||||||
|
projId = selectedProjectID,
|
||||||
|
newName = project.name,
|
||||||
|
newMainCurrencyName = newMainCurrencyName,
|
||||||
|
callback = editMainCurrencyCallBack
|
||||||
|
)
|
||||||
) {
|
) {
|
||||||
showToast(this@ManageCurrenciesActivity, getString(R.string.remote_project_operation_no_network), Toast.LENGTH_LONG)
|
showToast(this@ManageCurrenciesActivity, getString(R.string.remote_project_operation_no_network), Toast.LENGTH_LONG)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,8 +4,10 @@ import android.annotation.SuppressLint
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
@@ -417,6 +419,9 @@ fun BillsListScreen(
|
|||||||
is SectionItem -> SectionHeader(item.title)
|
is SectionItem -> SectionHeader(item.title)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
item {
|
||||||
|
Spacer(modifier = Modifier.height(64.dp).fillMaxWidth())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -88,6 +88,7 @@ object BillsListUtils {
|
|||||||
shareIntent,
|
shareIntent,
|
||||||
context.getString(R.string.title_settle)
|
context.getString(R.string.title_settle)
|
||||||
)
|
)
|
||||||
|
chooserIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
context.startActivity(chooserIntent)
|
context.startActivity(chooserIntent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -427,19 +427,26 @@ class BillsListViewActivity :
|
|||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
db.updateProject(
|
db.updateProject(
|
||||||
projId = projectId,
|
projId = projectId,
|
||||||
newName = null,
|
|
||||||
newEmail = null,
|
|
||||||
newPassword = null,
|
|
||||||
newLastPayerId = null,
|
|
||||||
newLastSyncedTimestamp = null,
|
|
||||||
newCurrencyName = null,
|
|
||||||
newDeletionDisabled = null,
|
|
||||||
newMyAccessLevel = null,
|
|
||||||
newBearerToken = null,
|
|
||||||
newArchivedTs = newArchivedTs
|
newArchivedTs = newArchivedTs
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!proj.isLocal) {
|
||||||
|
db.cowspentServerSyncHelper.editRemoteProject(
|
||||||
|
projId = projectId,
|
||||||
|
newArchivedTs = newArchivedTs,
|
||||||
|
callback = object : ICallback {
|
||||||
|
override fun onFinish() {}
|
||||||
|
override fun onFinish(result: String, message: String) {
|
||||||
|
if (message.isNotEmpty()) {
|
||||||
|
showToast(this@BillsListViewActivity, getString(R.string.error_sync, message))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
override fun onScheduled() {}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
setupDrawerProjects()
|
setupDrawerProjects()
|
||||||
refreshLists()
|
refreshLists()
|
||||||
|
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ object ProjectImportHelper {
|
|||||||
}
|
}
|
||||||
else -> 0
|
else -> 0
|
||||||
}
|
}
|
||||||
val payerName = if (columns.containsKey("payer_name")) line[columns["payer_name"]!!] else ""
|
val payerName = if (columns.containsKey("payer_name")) line[columns["payer_name"]!!].trim() else ""
|
||||||
val payerWeight = if (columns.containsKey("payer_weight")) line[columns["payer_weight"]!!].toDouble() else 1.0
|
val payerWeight = if (columns.containsKey("payer_weight")) line[columns["payer_weight"]!!].toDouble() else 1.0
|
||||||
val owersStr = if (columns.containsKey("owers")) line[columns["owers"]!!] else ""
|
val owersStr = if (columns.containsKey("owers")) line[columns["owers"]!!] else ""
|
||||||
val payerActive = columns.containsKey("payer_active") && line[columns["payer_active"]!!] == "1"
|
val payerActive = columns.containsKey("payer_active") && line[columns["payer_active"]!!] == "1"
|
||||||
@@ -122,8 +122,10 @@ object ProjectImportHelper {
|
|||||||
val pmId = if (columns.containsKey("paymentmodeid") && line[columns["paymentmodeid"]!!].isNotEmpty()) line[columns["paymentmodeid"]!!].toLong() else 0L
|
val pmId = if (columns.containsKey("paymentmodeid") && line[columns["paymentmodeid"]!!].isNotEmpty()) line[columns["paymentmodeid"]!!].toLong() else 0L
|
||||||
val pm = if (columns.containsKey("paymentmode")) line[columns["paymentmode"]!!] else null
|
val pm = if (columns.containsKey("paymentmode")) line[columns["paymentmode"]!!] else null
|
||||||
|
|
||||||
|
if (payerName.isNotEmpty()) {
|
||||||
membersActive[payerName] = payerActive
|
membersActive[payerName] = payerActive
|
||||||
membersWeight[payerName] = payerWeight
|
membersWeight[payerName] = payerWeight
|
||||||
|
}
|
||||||
|
|
||||||
if (owersStr.trim().isEmpty()) {
|
if (owersStr.trim().isEmpty()) {
|
||||||
onError(context.getString(R.string.import_error_owers, row))
|
onError(context.getString(R.string.import_error_owers, row))
|
||||||
@@ -132,10 +134,10 @@ object ProjectImportHelper {
|
|||||||
|
|
||||||
if (what != "deleteMeIfYouWant") {
|
if (what != "deleteMeIfYouWant") {
|
||||||
billRemoteIdToOwerStr[row.toLong()] = owersStr
|
billRemoteIdToOwerStr[row.toLong()] = owersStr
|
||||||
val owersArray = owersStr.split(", ").filter { it.isNotEmpty() }
|
val owersArray = owersStr.split(",").map { it.trim() }.filter { it.isNotEmpty() }
|
||||||
for (ower in owersArray) {
|
for (ower in owersArray) {
|
||||||
if (!membersWeight.containsKey(ower.trim())) {
|
if (!membersWeight.containsKey(ower)) {
|
||||||
membersWeight[ower.trim()] = 1.0
|
membersWeight[ower] = 1.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
bills.add(DBBill(0, row.toLong(), 0, 0, amount, timestamp, what, DBBill.STATE_OK, "n", pm, catId, comment, pmId))
|
bills.add(DBBill(0, row.toLong(), 0, 0, amount, timestamp, what, DBBill.STATE_OK, "n", pm, catId, comment, pmId))
|
||||||
@@ -171,8 +173,8 @@ object ProjectImportHelper {
|
|||||||
val localCatId = catRemoteToLocal[b.categoryId] ?: 0L
|
val localCatId = catRemoteToLocal[b.categoryId] ?: 0L
|
||||||
val localPmId = pmRemoteToLocal[b.paymentModeId] ?: 0L
|
val localPmId = pmRemoteToLocal[b.paymentModeId] ?: 0L
|
||||||
val billId = db.addBill(DBBill(0, 0, pid, payerId, b.amount, b.timestamp, b.what, DBBill.STATE_OK, b.repeat, b.paymentMode, localCatId, b.comment, localPmId))
|
val billId = db.addBill(DBBill(0, 0, pid, payerId, b.amount, b.timestamp, b.what, DBBill.STATE_OK, b.repeat, b.paymentMode, localCatId, b.comment, localPmId))
|
||||||
billRemoteIdToOwerStr[b.remoteId]?.split(", ")?.filter { it.isNotEmpty() }?.forEach { ower ->
|
billRemoteIdToOwerStr[b.remoteId]?.split(",")?.map { it.trim() }?.filter { it.isNotEmpty() }?.forEach { ower ->
|
||||||
memberNameToId[ower.trim()]?.let { owerId -> db.addBillower(billId, owerId) }
|
memberNameToId[ower]?.let { owerId -> db.addBillower(billId, owerId) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onSuccess(pid)
|
onSuccess(pid)
|
||||||
|
|||||||
@@ -85,10 +85,11 @@ class EditProjectActivity : AppCompatActivity() {
|
|||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
db.updateProject(
|
db.updateProject(
|
||||||
project.id, newName, newEmail, targetPwd,
|
projId = project.id,
|
||||||
null, project.type, null,
|
newName = newName,
|
||||||
null, null,
|
newEmail = newEmail,
|
||||||
null, null
|
newPassword = targetPwd,
|
||||||
|
projectType = project.type
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
closeOnEdit(project.id)
|
closeOnEdit(project.id)
|
||||||
@@ -103,18 +104,21 @@ class EditProjectActivity : AppCompatActivity() {
|
|||||||
project.password = currentPwd
|
project.password = currentPwd
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
db.updateProject(project.id, null, null, currentPwd, null, project.type, null, null, null, null, null)
|
db.updateProject(
|
||||||
|
projId = project.id,
|
||||||
|
newPassword = currentPwd,
|
||||||
|
projectType = project.type
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!db.cowspentServerSyncHelper.editRemoteProject(
|
if (!db.cowspentServerSyncHelper.editRemoteProject(
|
||||||
project.id,
|
projId = project.id,
|
||||||
newName,
|
newName = newName,
|
||||||
newEmail,
|
newEmail = newEmail,
|
||||||
if (pwdChanged) newPwd else null,
|
newPassword = if (pwdChanged) newPwd else null,
|
||||||
null,
|
callback = editCallBack
|
||||||
editCallBack
|
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
showToast(this, getString(R.string.remote_project_operation_no_network), Toast.LENGTH_LONG)
|
showToast(this, getString(R.string.remote_project_operation_no_network), Toast.LENGTH_LONG)
|
||||||
@@ -124,10 +128,9 @@ class EditProjectActivity : AppCompatActivity() {
|
|||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
db.updateProject(
|
db.updateProject(
|
||||||
project.id, null, null, currentPwd,
|
projId = project.id,
|
||||||
null, project.type, null,
|
newPassword = currentPwd,
|
||||||
null, null,
|
projectType = project.type
|
||||||
null, null
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
closeOnEdit(project.id)
|
closeOnEdit(project.id)
|
||||||
|
|||||||
@@ -271,12 +271,18 @@ class CowspentSQLiteOpenHelper private constructor(val context: Context) :
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun updateProject(
|
fun updateProject(
|
||||||
projId: Long, newName: String?, newEmail: String?,
|
projId: Long,
|
||||||
newPassword: String?, newLastPayerId: Long?,
|
newName: String? = null,
|
||||||
newLastSyncedTimestamp: Long?,
|
newEmail: String? = null,
|
||||||
newCurrencyName: String?, newDeletionDisabled: Boolean?,
|
newPassword: String? = null,
|
||||||
newMyAccessLevel: Int?, newBearerToken: String?,
|
newLastPayerId: Long? = null,
|
||||||
newArchivedTs: Long? = null
|
newLastSyncedTimestamp: Long? = null,
|
||||||
|
newCurrencyName: String? = null,
|
||||||
|
newDeletionDisabled: Boolean? = null,
|
||||||
|
newMyAccessLevel: Int? = null,
|
||||||
|
newBearerToken: String? = null,
|
||||||
|
newArchivedTs: Long? = null,
|
||||||
|
projectType: ProjectType? = null
|
||||||
) {
|
) {
|
||||||
val db = writableDatabase
|
val db = writableDatabase
|
||||||
val values = ContentValues()
|
val values = ContentValues()
|
||||||
@@ -290,47 +296,7 @@ class CowspentSQLiteOpenHelper private constructor(val context: Context) :
|
|||||||
if (newDeletionDisabled != null) values.put(key_deletionDisabled, if (newDeletionDisabled) 1 else 0)
|
if (newDeletionDisabled != null) values.put(key_deletionDisabled, if (newDeletionDisabled) 1 else 0)
|
||||||
if (newMyAccessLevel != null) values.put(key_myAccessLevel, newMyAccessLevel)
|
if (newMyAccessLevel != null) values.put(key_myAccessLevel, newMyAccessLevel)
|
||||||
if (newArchivedTs != null) values.put(key_archived, newArchivedTs)
|
if (newArchivedTs != null) values.put(key_archived, newArchivedTs)
|
||||||
if (values.size() > 0) {
|
if (projectType != null) values.put(key_type, projectType.id)
|
||||||
db.update(table_projects, values, "$key_id = ?", arrayOf(projId.toString()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun updateProject(
|
|
||||||
projId: Long, newName: String?, newEmail: String?,
|
|
||||||
newPassword: String?, newLastPayerId: Long?,
|
|
||||||
projectType: ProjectType, newLastSyncedTimestamp: Long?,
|
|
||||||
newCurrencyName: String?, newDeletionDisabled: Boolean?,
|
|
||||||
newMyAccessLevel: Int?, newBearerToken: String?,
|
|
||||||
newArchivedTs: Long? = null
|
|
||||||
) {
|
|
||||||
val db = writableDatabase
|
|
||||||
updateProject(
|
|
||||||
projId, newName, newEmail, newPassword, newLastPayerId, projectType,
|
|
||||||
newLastSyncedTimestamp, newCurrencyName, newDeletionDisabled, newMyAccessLevel,
|
|
||||||
newBearerToken, newArchivedTs, db
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun updateProject(
|
|
||||||
projId: Long, newName: String?, newEmail: String?,
|
|
||||||
newPassword: String?, newLastPayerId: Long?,
|
|
||||||
projectType: ProjectType, newLastSyncedTimestamp: Long?,
|
|
||||||
newCurrencyName: String?, newDeletionDisabled: Boolean?,
|
|
||||||
newMyAccessLevel: Int?, newBearerToken: String?,
|
|
||||||
newArchivedTs: Long?, db: SQLiteDatabase
|
|
||||||
) {
|
|
||||||
val values = ContentValues()
|
|
||||||
if (newName != null) values.put(key_name, newName)
|
|
||||||
if (newEmail != null) values.put(key_email, newEmail)
|
|
||||||
if (newPassword != null) SecureStorage.savePasswordSync(context, "ProjectPassword_$projId", newPassword)
|
|
||||||
if (newBearerToken != null) values.put(key_bearer_token, newBearerToken)
|
|
||||||
if (newLastPayerId != null) values.put(key_lastPayerId, newLastPayerId)
|
|
||||||
if (newLastSyncedTimestamp != null) values.put(key_lastSyncTimestamp, newLastSyncedTimestamp)
|
|
||||||
if (newCurrencyName != null) values.put(key_currencyName, newCurrencyName)
|
|
||||||
if (newDeletionDisabled != null) values.put(key_deletionDisabled, if (newDeletionDisabled) 1 else 0)
|
|
||||||
if (newMyAccessLevel != null) values.put(key_myAccessLevel, newMyAccessLevel)
|
|
||||||
if (newArchivedTs != null) values.put(key_archived, newArchivedTs)
|
|
||||||
values.put(key_type, projectType.id)
|
|
||||||
if (values.size() > 0) {
|
if (values.size() > 0) {
|
||||||
db.update(table_projects, values, "$key_id = ?", arrayOf(projId.toString()))
|
db.update(table_projects, values, "$key_id = ?", arrayOf(projId.toString()))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,7 +85,8 @@ class CowspentServerSyncHelper private constructor(private val dbHelper: Cowspen
|
|||||||
val isSyncPossible: Boolean
|
val isSyncPossible: Boolean
|
||||||
get() {
|
get() {
|
||||||
updateNetworkStatus()
|
updateNetworkStatus()
|
||||||
return networkConnected
|
val offlineMode = preferences.getBoolean(appContext.getString(R.string.pref_key_offline_mode), false)
|
||||||
|
return networkConnected && !offlineMode
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addCallbackPull(callback: ICallback) {
|
fun addCallbackPull(callback: ICallback) {
|
||||||
@@ -817,10 +818,8 @@ class CowspentServerSyncHelper private constructor(private val dbHelper: Cowspen
|
|||||||
}
|
}
|
||||||
|
|
||||||
dbHelper.updateProject(
|
dbHelper.updateProject(
|
||||||
project.id, null, null,
|
projId = project.id,
|
||||||
null, null, serverSyncTimestamp,
|
newLastSyncedTimestamp = serverSyncTimestamp
|
||||||
null, null, null,
|
|
||||||
null
|
|
||||||
)
|
)
|
||||||
LoginStatus.OK
|
LoginStatus.OK
|
||||||
} catch (_: ServerResponse.NotModifiedException) {
|
} catch (_: ServerResponse.NotModifiedException) {
|
||||||
@@ -935,11 +934,18 @@ class CowspentServerSyncHelper private constructor(private val dbHelper: Cowspen
|
|||||||
return isCospend && projUrl == accountUrl
|
return isCospend && projUrl == accountUrl
|
||||||
}
|
}
|
||||||
|
|
||||||
fun editRemoteProject(projId: Long, newName: String?, newEmail: String?,
|
fun editRemoteProject(
|
||||||
newPassword: String?, newMainCurrencyName: String?, callback: ICallback): Boolean {
|
projId: Long,
|
||||||
|
newName: String? = null,
|
||||||
|
newEmail: String? = null,
|
||||||
|
newPassword: String? = null,
|
||||||
|
newMainCurrencyName: String? = null,
|
||||||
|
newArchivedTs: Long? = null,
|
||||||
|
callback: ICallback
|
||||||
|
): Boolean {
|
||||||
updateNetworkStatus()
|
updateNetworkStatus()
|
||||||
if (isSyncPossible) {
|
if (isSyncPossible) {
|
||||||
EditRemoteProjectTask(projId, newName, newEmail, newPassword, newMainCurrencyName, callback).execute()
|
EditRemoteProjectTask(projId, newName, newEmail, newPassword, newMainCurrencyName, newArchivedTs, callback).execute()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
@@ -951,6 +957,7 @@ class CowspentServerSyncHelper private constructor(private val dbHelper: Cowspen
|
|||||||
private val newEmail: String?,
|
private val newEmail: String?,
|
||||||
private val newPassword: String?,
|
private val newPassword: String?,
|
||||||
private val newMainCurrencyName: String?,
|
private val newMainCurrencyName: String?,
|
||||||
|
private val newArchivedTs: Long?,
|
||||||
private val callback: ICallback
|
private val callback: ICallback
|
||||||
) {
|
) {
|
||||||
private val project: DBProject? = dbHelper.getProject(projId)
|
private val project: DBProject? = dbHelper.getProject(projId)
|
||||||
@@ -985,7 +992,7 @@ class CowspentServerSyncHelper private constructor(private val dbHelper: Cowspen
|
|||||||
var status = LoginStatus.OK
|
var status = LoginStatus.OK
|
||||||
try {
|
try {
|
||||||
val response = client!!.editRemoteProject(
|
val response = client!!.editRemoteProject(
|
||||||
project!!, newName, newEmail, newPassword, newMainCurrencyName
|
project!!, newName, newEmail, newPassword, newMainCurrencyName, newArchivedTs
|
||||||
)
|
)
|
||||||
if (BillsListViewActivity.DEBUG) {
|
if (BillsListViewActivity.DEBUG) {
|
||||||
Log.i(TAG, "RESPONSE edit remote project : ${response.stringContent}")
|
Log.i(TAG, "RESPONSE edit remote project : ${response.stringContent}")
|
||||||
@@ -1025,8 +1032,11 @@ class CowspentServerSyncHelper private constructor(private val dbHelper: Cowspen
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
dbHelper.updateProject(
|
dbHelper.updateProject(
|
||||||
project!!.id, newName, newEmail, newPassword,
|
projId = project!!.id,
|
||||||
null, null, null, null, null, null
|
newName = newName,
|
||||||
|
newEmail = newEmail,
|
||||||
|
newPassword = newPassword,
|
||||||
|
newArchivedTs = newArchivedTs
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
callback.onFinish(newName ?: "", errorString)
|
callback.onFinish(newName ?: "", errorString)
|
||||||
@@ -1205,7 +1215,7 @@ class CowspentServerSyncHelper private constructor(private val dbHelper: Cowspen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun hasChanged(localBill: DBBill, remoteBill: DBBill): Boolean {
|
internal fun hasChanged(localBill: DBBill, remoteBill: DBBill): Boolean {
|
||||||
if (localBill.payerId == remoteBill.payerId &&
|
if (localBill.payerId == remoteBill.payerId &&
|
||||||
localBill.amount == remoteBill.amount &&
|
localBill.amount == remoteBill.amount &&
|
||||||
localBill.timestamp == remoteBill.timestamp &&
|
localBill.timestamp == remoteBill.timestamp &&
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ class VersatileProjectSyncClient(
|
|||||||
@Throws(IOException::class, TokenMismatchException::class, NextcloudHttpRequestFailedException::class)
|
@Throws(IOException::class, TokenMismatchException::class, NextcloudHttpRequestFailedException::class)
|
||||||
fun editRemoteProject(
|
fun editRemoteProject(
|
||||||
project: DBProject, newName: String?, newEmail: String?, newPassword: String?,
|
project: DBProject, newName: String?, newEmail: String?, newPassword: String?,
|
||||||
newMainCurrencyName: String?
|
newMainCurrencyName: String?, newArchivedTs: Long? = null
|
||||||
): ServerResponse.EditRemoteProjectResponse {
|
): ServerResponse.EditRemoteProjectResponse {
|
||||||
val paramKeys: MutableList<String> = ArrayList()
|
val paramKeys: MutableList<String> = ArrayList()
|
||||||
val paramValues: MutableList<String> = ArrayList()
|
val paramValues: MutableList<String> = ArrayList()
|
||||||
@@ -130,6 +130,10 @@ class VersatileProjectSyncClient(
|
|||||||
paramKeys.add("password")
|
paramKeys.add("password")
|
||||||
paramValues.add(newPassword)
|
paramValues.add(newPassword)
|
||||||
}
|
}
|
||||||
|
if (newArchivedTs != null) {
|
||||||
|
paramKeys.add("archived_ts")
|
||||||
|
paramValues.add(newArchivedTs.toString())
|
||||||
|
}
|
||||||
|
|
||||||
var target: String
|
var target: String
|
||||||
var username: String? = null
|
var username: String? = null
|
||||||
|
|||||||
@@ -5,9 +5,11 @@ fun evalMath(expression: String): Double {
|
|||||||
var result = 0.0
|
var result = 0.0
|
||||||
var db: SQLiteDatabase? = null
|
var db: SQLiteDatabase? = null
|
||||||
try {
|
try {
|
||||||
|
// Force floating point division by replacing / with * 1.0 /
|
||||||
|
val forcedRealExpr = expression.replace("/", "* 1.0 /")
|
||||||
// Opens a temporary, in-memory system database block
|
// Opens a temporary, in-memory system database block
|
||||||
db = SQLiteDatabase.create(null)
|
db = SQLiteDatabase.create(null)
|
||||||
val cursor = db.rawQuery("SELECT ($expression);", null)
|
val cursor = db.rawQuery("SELECT ($forcedRealExpr);", null)
|
||||||
if (cursor.moveToFirst()) {
|
if (cursor.moveToFirst()) {
|
||||||
result = cursor.getDouble(0)
|
result = cursor.getDouble(0)
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
BIN
Binary file not shown.
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.6.1-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-9.7.1-bin.zip
|
||||||
networkTimeout=10000
|
networkTimeout=10000
|
||||||
retries=0
|
retries=0
|
||||||
retryBackOffMs=500
|
retryBackOffMs=500
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
#
|
#
|
||||||
# Copyright © 2015-2021 the original authors.
|
# Copyright © 2015 the original authors.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
#
|
#
|
||||||
# Gradle start up script for POSIX generated by Gradle.
|
# gradlew start up script for POSIX generated by Gradle.
|
||||||
#
|
#
|
||||||
# Important for running:
|
# Important for running:
|
||||||
#
|
#
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
# bash, then to run this script, type that shell name before the whole
|
# bash, then to run this script, type that shell name before the whole
|
||||||
# command line, like:
|
# command line, like:
|
||||||
#
|
#
|
||||||
# ksh Gradle
|
# ksh gradlew
|
||||||
#
|
#
|
||||||
# Busybox and similar reduced shells will NOT work, because this script
|
# Busybox and similar reduced shells will NOT work, because this script
|
||||||
# requires all of these POSIX shell features:
|
# requires all of these POSIX shell features:
|
||||||
@@ -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/.
|
||||||
@@ -114,7 +114,6 @@ case "$( uname )" in #(
|
|||||||
NONSTOP* ) nonstop=true ;;
|
NONSTOP* ) nonstop=true ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
|
||||||
|
|
||||||
|
|
||||||
# Determine the Java command to use to start the JVM.
|
# Determine the Java command to use to start the JVM.
|
||||||
@@ -172,7 +171,6 @@ fi
|
|||||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||||
if "$cygwin" || "$msys" ; then
|
if "$cygwin" || "$msys" ; then
|
||||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
|
||||||
|
|
||||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||||
|
|
||||||
@@ -212,8 +210,7 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
|||||||
|
|
||||||
set -- \
|
set -- \
|
||||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||||
-classpath "$CLASSPATH" \
|
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
|
||||||
org.gradle.wrapper.GradleWrapperMain \
|
|
||||||
"$@"
|
"$@"
|
||||||
|
|
||||||
# Stop when "xargs" is not available.
|
# Stop when "xargs" is not available.
|
||||||
|
|||||||
Vendored
+12
-24
@@ -19,12 +19,12 @@
|
|||||||
@if "%DEBUG%"=="" @echo off
|
@if "%DEBUG%"=="" @echo off
|
||||||
@rem ##########################################################################
|
@rem ##########################################################################
|
||||||
@rem
|
@rem
|
||||||
@rem Gradle startup script for Windows
|
@rem gradlew startup script for Windows
|
||||||
@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,30 +65,18 @@ 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
|
||||||
|
|
||||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
|
||||||
|
|
||||||
|
|
||||||
@rem Execute Gradle
|
@rem Execute gradlew
|
||||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
@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
|
|
||||||
|
|||||||
@@ -6,6 +6,9 @@ pluginManagement {
|
|||||||
maven { url = 'https://jitpack.io' }
|
maven { url = 'https://jitpack.io' }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
plugins {
|
||||||
|
id 'org.gradle.toolchains.foojay-resolver-convention' version '1.0.0'
|
||||||
|
}
|
||||||
dependencyResolutionManagement {
|
dependencyResolutionManagement {
|
||||||
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
||||||
repositories {
|
repositories {
|
||||||
|
|||||||
Reference in New Issue
Block a user