Fix sync and add beta features

This commit is contained in:
soraefir
2026-07-09 19:22:04 +02:00
parent 47d0471b48
commit 106c4e27fc
13 changed files with 54 additions and 31 deletions
+1 -1
View File
@@ -14,7 +14,7 @@ android {
applicationId "net.helcel.cowspent" applicationId "net.helcel.cowspent"
minSdk = 26 minSdk = 26
targetSdk = 37 targetSdk = 37
versionName project.hasProperty('VERSION_NAME') ? project.property('VERSION_NAME') : "1" versionName project.hasProperty('VERSION_NAME') ? project.property('VERSION_NAME') : "1.4"
versionCode project.hasProperty('VERSION_CODE') ? project.property('VERSION_CODE').toInteger() : 1 versionCode project.hasProperty('VERSION_CODE') ? project.property('VERSION_CODE').toInteger() : 1
} }
@@ -467,7 +467,7 @@ fun BillAdditionalDetailsSection(
modifier = Modifier.padding(bottom = 8.dp, top = 16.dp) modifier = Modifier.padding(bottom = 8.dp, top = 16.dp)
) )
val context = androidx.compose.ui.platform.LocalContext.current val context = LocalContext.current
var categoryExpanded by remember { mutableStateOf(false) } var categoryExpanded by remember { mutableStateOf(false) }
val selectedCategory = val selectedCategory =
categories.find { it.id == viewModel.categoryId } ?: CategoryUtils.getCategoryById(context, viewModel.categoryId) categories.find { it.id == viewModel.categoryId } ?: CategoryUtils.getCategoryById(context, viewModel.categoryId)
@@ -2,22 +2,20 @@ package net.helcel.cowspent.android.bill_edit
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableDoubleStateOf import androidx.compose.runtime.mutableDoubleStateOf
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableLongStateOf import androidx.compose.runtime.mutableLongStateOf
import androidx.compose.runtime.mutableStateMapOf import androidx.compose.runtime.mutableStateMapOf
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import net.helcel.cowspent.model.DBBill
import net.helcel.cowspent.model.DBMember
import net.helcel.cowspent.android.helper.DialogState import net.helcel.cowspent.android.helper.DialogState
import net.helcel.cowspent.android.helper.parseAmount import net.helcel.cowspent.android.helper.parseAmount
import net.helcel.cowspent.model.DBBill
import net.helcel.cowspent.model.DBCurrency
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
import net.helcel.cowspent.model.DBCurrency
import androidx.compose.ui.graphics.vector.ImageVector
class EditBillViewModel : ViewModel() { class EditBillViewModel : ViewModel() {
var what by mutableStateOf("") var what by mutableStateOf("")
var amount by mutableStateOf("") var amount by mutableStateOf("")
@@ -103,6 +103,10 @@ fun BillsListScreen(
val context = LocalContext.current val context = LocalContext.current
val sharedPreferences = remember { PreferenceManager.getDefaultSharedPreferences(context) } val sharedPreferences = remember { PreferenceManager.getDefaultSharedPreferences(context) }
val showArchived = sharedPreferences.getBoolean(stringResource(R.string.pref_key_show_archived), false) val showArchived = sharedPreferences.getBoolean(stringResource(R.string.pref_key_show_archived), false)
val keyBetaFeatures = stringResource(R.string.pref_key_beta_features)
var showBetaFeatures by remember(keyBetaFeatures) {
mutableStateOf(sharedPreferences.getBoolean(keyBetaFeatures, false))
}
StatefulAlertDialog( StatefulAlertDialog(
state = viewModel.dialogState, state = viewModel.dialogState,
@@ -158,7 +162,8 @@ fun BillsListScreen(
isArchived = proj?.isArchived == true, isArchived = proj?.isArchived == true,
projectType = proj?.type ?: ProjectType.LOCAL, projectType = proj?.type ?: ProjectType.LOCAL,
accessLevel = proj?.myAccessLevel ?: DBProject.ACCESS_LEVEL_ADMIN, accessLevel = proj?.myAccessLevel ?: DBProject.ACCESS_LEVEL_ADMIN,
isShareable = proj?.isShareable() ?: true isShareable = proj?.isShareable() ?: true,
showBetaFeatures = showBetaFeatures
) )
} }
} }
@@ -327,7 +332,7 @@ fun BillsListScreen(
}, },
actions = { actions = {
if (!isSearchExpanded) { if (!isSearchExpanded) {
if (viewModel.hasUnlabeledBills) { if (showBetaFeatures && viewModel.hasUnlabeledBills) {
IconButton(onClick = onLabelBillsClick) { IconButton(onClick = onLabelBillsClick) {
Icon( Icon(
Icons.Default.Category, Icons.Default.Category,
@@ -35,7 +35,8 @@ fun ProjectOptionsDialogContent(
isArchived: Boolean = false, isArchived: Boolean = false,
projectType: ProjectType = ProjectType.LOCAL, projectType: ProjectType = ProjectType.LOCAL,
accessLevel: Int = DBProject.ACCESS_LEVEL_ADMIN, accessLevel: Int = DBProject.ACCESS_LEVEL_ADMIN,
isShareable: Boolean = true isShareable: Boolean = true,
showBetaFeatures: Boolean = false
) { ) {
Surface( Surface(
shape = MaterialTheme.shapes.large, shape = MaterialTheme.shapes.large,
@@ -79,7 +80,7 @@ fun ProjectOptionsDialogContent(
// Row 2: Manage Member, Manage Labels, Manage Currencies // Row 2: Manage Member, Manage Labels, Manage Currencies
if (!isArchived && isMaintainer) { if (!isArchived && isMaintainer) {
row2.add(ProjectOption(stringResource(R.string.action_members), Icons.Default.Group, onManageMembers)) row2.add(ProjectOption(stringResource(R.string.action_members), Icons.Default.Group, onManageMembers))
if (false && (projectType == ProjectType.LOCAL || projectType == ProjectType.COSPEND)) { if (showBetaFeatures && (projectType == ProjectType.LOCAL || projectType == ProjectType.COSPEND)) {
row2.add(ProjectOption(stringResource(R.string.action_labels), Icons.AutoMirrored.Filled.Label, onManageLabels)) row2.add(ProjectOption(stringResource(R.string.action_labels), Icons.AutoMirrored.Filled.Label, onManageLabels))
} }
row2.add(ProjectOption(stringResource(R.string.action_currencies), Icons.Default.MonetizationOn, onManageCurrencies)) row2.add(ProjectOption(stringResource(R.string.action_currencies), Icons.Default.MonetizationOn, onManageCurrencies))
@@ -181,7 +182,8 @@ fun ProjectOptionsDialogPreview() {
isArchived = false, isArchived = false,
projectType = ProjectType.COSPEND, projectType = ProjectType.COSPEND,
accessLevel = DBProject.ACCESS_LEVEL_ADMIN, accessLevel = DBProject.ACCESS_LEVEL_ADMIN,
isShareable = true isShareable = true,
showBetaFeatures = true
) )
} }
} }
@@ -205,7 +207,8 @@ fun ProjectOptionsDialogPreview2() {
isArchived = true, isArchived = true,
projectType = ProjectType.COSPEND, projectType = ProjectType.COSPEND,
accessLevel = DBProject.ACCESS_LEVEL_ADMIN, accessLevel = DBProject.ACCESS_LEVEL_ADMIN,
isShareable = true isShareable = true,
showBetaFeatures = true
) )
} }
} }
@@ -228,7 +231,8 @@ fun ProjectOptionsDialogPreview3() {
isArchived = false, isArchived = false,
projectType = ProjectType.LOCAL, projectType = ProjectType.LOCAL,
accessLevel = DBProject.ACCESS_LEVEL_ADMIN, accessLevel = DBProject.ACCESS_LEVEL_ADMIN,
isShareable = true isShareable = true,
showBetaFeatures = true
) )
} }
} }
@@ -79,6 +79,7 @@ fun SettingsScreen(
val keyColor = stringResource(R.string.pref_key_color) val keyColor = stringResource(R.string.pref_key_color)
val keyOfflineMode = stringResource(R.string.pref_key_offline_mode) val keyOfflineMode = stringResource(R.string.pref_key_offline_mode)
val keyShowArchived = stringResource(R.string.pref_key_show_archived) val keyShowArchived = stringResource(R.string.pref_key_show_archived)
val keyBetaFeatures = stringResource(R.string.pref_key_beta_features)
// States for preferences // States for preferences
var nightMode by remember(keyNightMode) { var nightMode by remember(keyNightMode) {
@@ -113,6 +114,9 @@ fun SettingsScreen(
var showArchived by remember(keyShowArchived) { var showArchived by remember(keyShowArchived) {
mutableStateOf(sharedPreferences.getBoolean(keyShowArchived, false)) mutableStateOf(sharedPreferences.getBoolean(keyShowArchived, false))
} }
var betaFeatures by remember(keyBetaFeatures) {
mutableStateOf(sharedPreferences.getBoolean(keyBetaFeatures, false))
}
Scaffold( Scaffold(
topBar = { topBar = {
@@ -242,6 +246,19 @@ fun SettingsScreen(
// Other // Other
SettingsCategory(stringResource(R.string.settings_other)) SettingsCategory(stringResource(R.string.settings_other))
SettingsSwitchPreference(
title = stringResource(R.string.settings_beta_features),
summary = stringResource(R.string.settings_beta_features_summary),
icon = Icons.Default.Info,
checked = betaFeatures,
onCheckedChange = {
betaFeatures = it
sharedPreferences.edit {
putBoolean(keyBetaFeatures, it)
}
}
)
SettingsPreference( SettingsPreference(
title = stringResource(R.string.title_about), title = stringResource(R.string.title_about),
icon = Icons.Default.Info, icon = Icons.Default.Info,
@@ -1,6 +1,5 @@
package net.helcel.cowspent.model package net.helcel.cowspent.model
import android.util.Log
import java.io.Serializable import java.io.Serializable
import java.util.Calendar import java.util.Calendar
import java.util.Locale import java.util.Locale
@@ -7,16 +7,22 @@ import android.database.Cursor
import android.database.sqlite.SQLiteDatabase import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteOpenHelper import android.database.sqlite.SQLiteOpenHelper
import android.text.TextUtils import android.text.TextUtils
import android.util.Log
import androidx.annotation.WorkerThread import androidx.annotation.WorkerThread
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import net.helcel.cowspent.R import net.helcel.cowspent.R
import net.helcel.cowspent.android.main.BillsListViewActivity import net.helcel.cowspent.model.DBAccountProject
import net.helcel.cowspent.model.* import net.helcel.cowspent.model.DBBill
import net.helcel.cowspent.model.DBBillOwer
import net.helcel.cowspent.model.DBCategory
import net.helcel.cowspent.model.DBCurrency
import net.helcel.cowspent.model.DBMember
import net.helcel.cowspent.model.DBPaymentMode
import net.helcel.cowspent.model.DBProject
import net.helcel.cowspent.model.ProjectType
import net.helcel.cowspent.util.CategoryUtils import net.helcel.cowspent.util.CategoryUtils
import net.helcel.cowspent.util.SecureStorage import net.helcel.cowspent.util.SecureStorage
import net.helcel.cowspent.util.SupportUtil import net.helcel.cowspent.util.SupportUtil
import java.util.* import java.util.Locale
/** /**
* Helps to add, get, update and delete bills, members, projects with the option to trigger a sync with the server. * Helps to add, get, update and delete bills, members, projects with the option to trigger a sync with the server.
@@ -1177,7 +1183,6 @@ class CowspentSQLiteOpenHelper private constructor(val context: Context) :
@Suppress("ConstPropertyName") @Suppress("ConstPropertyName")
companion object { companion object {
private val TAG = CowspentSQLiteOpenHelper::class.java.simpleName
private const val database_version = 1 private const val database_version = 1
private const val database_name = "COWSPENT" private const val database_name = "COWSPENT"
private const val table_members = "MEMBERS" private const val table_members = "MEMBERS"
@@ -29,11 +29,9 @@ import net.helcel.cowspent.android.account.AccountActivity
import net.helcel.cowspent.android.main.BillsListViewActivity import net.helcel.cowspent.android.main.BillsListViewActivity
import net.helcel.cowspent.android.main.MainConstants import net.helcel.cowspent.android.main.MainConstants
import net.helcel.cowspent.model.DBBill import net.helcel.cowspent.model.DBBill
import net.helcel.cowspent.model.DBCategory
import net.helcel.cowspent.model.DBProject import net.helcel.cowspent.model.DBProject
import net.helcel.cowspent.model.ProjectType import net.helcel.cowspent.model.ProjectType
import net.helcel.cowspent.util.CospendClientUtil.LoginStatus import net.helcel.cowspent.util.CospendClientUtil.LoginStatus
import net.helcel.cowspent.util.CategoryUtils
import net.helcel.cowspent.util.ICallback import net.helcel.cowspent.util.ICallback
import net.helcel.cowspent.util.IProjectCreationCallback import net.helcel.cowspent.util.IProjectCreationCallback
import net.helcel.cowspent.util.NextcloudClient import net.helcel.cowspent.util.NextcloudClient
@@ -103,9 +103,6 @@ class NextcloudClient(
val acceptHeader: MutableList<String> = ArrayList() val acceptHeader: MutableList<String> = ArrayList()
acceptHeader.add("application/json") acceptHeader.add("application/json")
headers["Accept"] = acceptHeader headers["Accept"] = acceptHeader
val ocsHeader: MutableList<String> = ArrayList()
ocsHeader.add("true")
headers["OCS-APIRequest"] = ocsHeader
} }
val nextcloudRequest: NextcloudRequest = if (params == null) { val nextcloudRequest: NextcloudRequest = if (params == null) {
NextcloudRequest.Builder() NextcloudRequest.Builder()
@@ -68,7 +68,7 @@ open class ServerResponse(
} }
val rawData = JSONObject(content) val rawData = JSONObject(content)
val data = rawData.getJSONObject("ocs") val data = rawData.getJSONObject("ocs")
return data.get("data").toString() return data.getString("data")
} }
class ProjectResponse(response: VersatileProjectSyncClient.ResponseData, isOcsResponse: Boolean) : class ProjectResponse(response: VersatileProjectSyncClient.ResponseData, isOcsResponse: Boolean) :
@@ -88,7 +88,7 @@ class VersatileProjectSyncClient(
"/ocs/v2.php/apps/cospend/api/v1/projects/" + project.remoteId "/ocs/v2.php/apps/cospend/api/v1/projects/" + project.remoteId
else else
"/index.php/apps/cospend/api-priv/projects/" + project.remoteId "/index.php/apps/cospend/api-priv/projects/" + project.remoteId
return ServerResponse.ProjectResponse(requestServerWithSSO(nextcloudAPI!!, target, METHOD_GET, null, null, true), true) return ServerResponse.ProjectResponse(requestServerWithSSO(nextcloudAPI!!, target, METHOD_GET, null, null, cospendVersionGT161), cospendVersionGT161)
} else { } else {
useOcsApiRequest = cospendVersionGT161 useOcsApiRequest = cospendVersionGT161
target = if (cospendVersionGT161) target = if (cospendVersionGT161)
@@ -1348,9 +1348,6 @@ class VersatileProjectSyncClient(
val acceptHeader: MutableList<String> = ArrayList() val acceptHeader: MutableList<String> = ArrayList()
acceptHeader.add("application/json") acceptHeader.add("application/json")
headers["Accept"] = acceptHeader headers["Accept"] = acceptHeader
val ocsHeader: MutableList<String> = ArrayList()
ocsHeader.add("true")
headers["OCS-APIRequest"] = ocsHeader
} }
val nextcloudRequest: NextcloudRequest = if (params == null) { val nextcloudRequest: NextcloudRequest = if (params == null) {
NextcloudRequest.Builder() NextcloudRequest.Builder()
+3
View File
@@ -135,6 +135,8 @@
<string name="settings_color_custom">Custom color</string> <string name="settings_color_custom">Custom color</string>
<string name="settings_color_mode">Color Selection</string> <string name="settings_color_mode">Color Selection</string>
<string name="settings_show_archived">Show archived projects</string> <string name="settings_show_archived">Show archived projects</string>
<string name="settings_beta_features">Beta Features</string>
<string name="settings_beta_features_summary">Enable experimental features. Use at your own risk.</string>
<string name="settings_url_warn_http">WARNING: "http" is unsafe. Use "https".</string> <string name="settings_url_warn_http">WARNING: "http" is unsafe. Use "https".</string>
<string name="settings_colorpicker_title">Choose Color</string> <string name="settings_colorpicker_title">Choose Color</string>
@@ -155,6 +157,7 @@
<string name="pref_key_color_mode" translatable="false">colorMode</string> <string name="pref_key_color_mode" translatable="false">colorMode</string>
<string name="pref_key_offline_mode" translatable="false">offlineMode</string> <string name="pref_key_offline_mode" translatable="false">offlineMode</string>
<string name="pref_key_show_archived" translatable="false">showArchived</string> <string name="pref_key_show_archived" translatable="false">showArchived</string>
<string name="pref_key_beta_features" translatable="false">betaFeatures</string>
<string name="pref_value_night_mode_no" translatable="false">1</string> <string name="pref_value_night_mode_no" translatable="false">1</string>
<string name="pref_value_night_mode_yes" translatable="false">2</string> <string name="pref_value_night_mode_yes" translatable="false">2</string>
<string name="pref_value_night_mode_system" translatable="false">-1</string> <string name="pref_value_night_mode_system" translatable="false">-1</string>