Compare commits
4
Commits
561d3a4ed8
...
d2d713c616
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d2d713c616
|
||
|
|
d7a51a797f
|
||
|
|
de5f9b09a1
|
||
|
|
2ec5c00df1
|
@@ -63,11 +63,17 @@ fun EditBillScreen(
|
|||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val focusRequester = remember { FocusRequester() }
|
val focusRequester = remember { FocusRequester() }
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
// The activity reads the project off the main thread and only then reports whether this is a
|
||||||
if (viewModel.isNewBill) {
|
// new bill, so the flag arrives after the first composition. Keying on it rather than on Unit
|
||||||
|
// is what makes the effect run at all.
|
||||||
|
LaunchedEffect(viewModel.isNewBill) {
|
||||||
|
if (!viewModel.isNewBill) return@LaunchedEffect
|
||||||
|
// The field has to be laid out before it can take focus, which is not yet true on the
|
||||||
|
// pass that composed it. Waiting for the next frame makes this hold whether the flag was
|
||||||
|
// already set or arrived later.
|
||||||
|
withFrameNanos { }
|
||||||
focusRequester.requestFocus()
|
focusRequester.requestFocus()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
StatefulAlertDialog(
|
StatefulAlertDialog(
|
||||||
state = viewModel.dialogState,
|
state = viewModel.dialogState,
|
||||||
@@ -252,8 +258,8 @@ fun BillBasicInfoSection(
|
|||||||
placeholder = { Text(stringResource(R.string.label_what)) },
|
placeholder = { Text(stringResource(R.string.label_what)) },
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
leadingIcon = { Icon(Icons.Default.Title, contentDescription = null) },
|
leadingIcon = { Icon(Icons.Default.Title, contentDescription = null) },
|
||||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next),
|
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
|
||||||
keyboardActions = KeyboardActions(onNext = { focusManager.moveFocus(FocusDirection.Next) })
|
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() })
|
||||||
)
|
)
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
@@ -528,7 +534,10 @@ fun BillAdditionalDetailsSection(
|
|||||||
val context = 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
|
||||||
|
)
|
||||||
|
|
||||||
EditableExposedDropdownMenu(
|
EditableExposedDropdownMenu(
|
||||||
value = selectedCategory?.name ?: "",
|
value = selectedCategory?.name ?: "",
|
||||||
@@ -582,7 +591,10 @@ fun BillAdditionalDetailsSection(
|
|||||||
|
|
||||||
var pmExpanded by remember { mutableStateOf(false) }
|
var pmExpanded by remember { mutableStateOf(false) }
|
||||||
val selectedPm =
|
val selectedPm =
|
||||||
paymentModes.find { it.id == viewModel.paymentModeId } ?: CategoryUtils.getPaymentModeById(context, viewModel.paymentModeId)
|
paymentModes.find { it.id == viewModel.paymentModeId } ?: CategoryUtils.getPaymentModeById(
|
||||||
|
context,
|
||||||
|
viewModel.paymentModeId
|
||||||
|
)
|
||||||
|
|
||||||
EditableExposedDropdownMenu(
|
EditableExposedDropdownMenu(
|
||||||
value = selectedPm?.name ?: "",
|
value = selectedPm?.name ?: "",
|
||||||
|
|||||||
@@ -105,9 +105,9 @@ 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)
|
val keyExtraFeatures = stringResource(R.string.pref_key_extra_features)
|
||||||
var showBetaFeatures by remember(keyBetaFeatures) {
|
var showExtraFeatures by remember(keyExtraFeatures) {
|
||||||
mutableStateOf(sharedPreferences.getBoolean(keyBetaFeatures, false))
|
mutableStateOf(sharedPreferences.getBoolean(keyExtraFeatures, false))
|
||||||
}
|
}
|
||||||
|
|
||||||
StatefulAlertDialog(
|
StatefulAlertDialog(
|
||||||
@@ -165,7 +165,7 @@ fun BillsListScreen(
|
|||||||
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
|
showExtraFeatures = showExtraFeatures
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -334,7 +334,7 @@ fun BillsListScreen(
|
|||||||
},
|
},
|
||||||
actions = {
|
actions = {
|
||||||
if (!isSearchExpanded) {
|
if (!isSearchExpanded) {
|
||||||
if (showBetaFeatures && viewModel.hasUnlabeledBills) {
|
if (showExtraFeatures && viewModel.hasUnlabeledBills) {
|
||||||
IconButton(onClick = onLabelBillsClick) {
|
IconButton(onClick = onLabelBillsClick) {
|
||||||
Icon(
|
Icon(
|
||||||
Icons.Default.Category,
|
Icons.Default.Category,
|
||||||
|
|||||||
@@ -782,7 +782,7 @@ class BillsListViewActivity :
|
|||||||
|
|
||||||
// The account and all-projects refresh belongs to opening the app, throttled by the
|
// The account and all-projects refresh belongs to opening the app, throttled by the
|
||||||
// SyncOnOpen interval so that resuming within the interval does not repeat it.
|
// SyncOnOpen interval so that resuming within the interval does not repeat it.
|
||||||
val intervalMinutes = SyncSettings.intervalMinutes(applicationContext)
|
val intervalMinutes = SyncSettings.OPEN_SYNC_INTERVAL_MINUTES
|
||||||
val lastAccountSync = preferences.getLong(getString(R.string.pref_key_last_account_sync_timestamp), 0L)
|
val lastAccountSync = preferences.getLong(getString(R.string.pref_key_last_account_sync_timestamp), 0L)
|
||||||
val accountSyncDue = trigger == SyncTrigger.APP_OPEN &&
|
val accountSyncDue = trigger == SyncTrigger.APP_OPEN &&
|
||||||
now - lastAccountSync > intervalMinutes * 60 * 1000L
|
now - lastAccountSync > intervalMinutes * 60 * 1000L
|
||||||
@@ -866,10 +866,13 @@ class BillsListViewActivity :
|
|||||||
projectId: Long,
|
projectId: Long,
|
||||||
now: Long
|
now: Long
|
||||||
): Boolean {
|
): Boolean {
|
||||||
if (!preferences.getBoolean(getString(R.string.pref_key_beta_features), false)) return false
|
if (!preferences.getBoolean(getString(R.string.pref_key_extra_features), false)) return false
|
||||||
|
// "Always" means what it says: every manual refresh is a full sync.
|
||||||
|
val delayMinutes = SyncSettings.fullSyncDelayMinutes(applicationContext)
|
||||||
|
if (delayMinutes == 0) return false
|
||||||
val lastFull = preferences.getLong(lastFullSyncKey(projectId), 0L)
|
val lastFull = preferences.getLong(lastFullSyncKey(projectId), 0L)
|
||||||
if (lastFull == 0L) return false
|
if (lastFull == 0L) return false
|
||||||
return now - lastFull < SyncSettings.intervalMinutes(applicationContext) * 60 * 1000L
|
return now - lastFull < delayMinutes * 60 * 1000L
|
||||||
}
|
}
|
||||||
private fun markProjectSynced(preferences: SharedPreferences, projectId: Long, at: Long) {
|
private fun markProjectSynced(preferences: SharedPreferences, projectId: Long, at: Long) {
|
||||||
preferences.edit { putLong(lastProjectSyncKey(projectId), at) }
|
preferences.edit { putLong(lastProjectSyncKey(projectId), at) }
|
||||||
|
|||||||
@@ -11,11 +11,30 @@ import net.helcel.cowspent.R
|
|||||||
import net.helcel.cowspent.model.*
|
import net.helcel.cowspent.model.*
|
||||||
import net.helcel.cowspent.persistence.CowspentSQLiteOpenHelper
|
import net.helcel.cowspent.persistence.CowspentSQLiteOpenHelper
|
||||||
import java.io.InputStreamReader
|
import java.io.InputStreamReader
|
||||||
|
import java.net.URLDecoder
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
object ProjectImportHelper {
|
object ProjectImportHelper {
|
||||||
|
|
||||||
|
private fun optional(line: Array<String>, columns: Map<String, Int>, name: String): String =
|
||||||
|
columns[name]?.takeIf { it < line.size }?.let { line[it] } ?: ""
|
||||||
|
|
||||||
|
/** Parses the `#rrggbb` member colour Cospend writes in its members section. */
|
||||||
|
private fun parseHexColor(value: String): Triple<Int, Int, Int>? {
|
||||||
|
val hex = value.trim().removePrefix("#")
|
||||||
|
if (hex.length != 6) return null
|
||||||
|
return try {
|
||||||
|
Triple(
|
||||||
|
hex.substring(0, 2).toInt(16),
|
||||||
|
hex.substring(2, 4).toInt(16),
|
||||||
|
hex.substring(4, 6).toInt(16)
|
||||||
|
)
|
||||||
|
} catch (_: NumberFormatException) {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressLint("Range")
|
@SuppressLint("Range")
|
||||||
fun getFileName(contentResolver: ContentResolver, uri: Uri): String {
|
fun getFileName(contentResolver: ContentResolver, uri: Uri): String {
|
||||||
var result: String? = null
|
var result: String? = null
|
||||||
@@ -59,6 +78,7 @@ object ProjectImportHelper {
|
|||||||
val bills = mutableListOf<DBBill>()
|
val bills = mutableListOf<DBBill>()
|
||||||
val membersActive = mutableMapOf<String, Boolean>()
|
val membersActive = mutableMapOf<String, Boolean>()
|
||||||
val membersWeight = mutableMapOf<String, Double>()
|
val membersWeight = mutableMapOf<String, Double>()
|
||||||
|
val membersColor = mutableMapOf<String, Triple<Int, Int, Int>>()
|
||||||
val billRemoteIdToPayerName = mutableMapOf<Long, String>()
|
val billRemoteIdToPayerName = mutableMapOf<Long, String>()
|
||||||
val billRemoteIdToOwerStr = mutableMapOf<Long, String>()
|
val billRemoteIdToOwerStr = mutableMapOf<Long, String>()
|
||||||
|
|
||||||
@@ -76,7 +96,10 @@ object ProjectImportHelper {
|
|||||||
|
|
||||||
currentSection = when {
|
currentSection = when {
|
||||||
columns.containsKey("what") && columns.containsKey("amount") -> "bills"
|
columns.containsKey("what") && columns.containsKey("amount") -> "bills"
|
||||||
|
columns.containsKey("name") && columns.containsKey("weight") &&
|
||||||
|
columns.containsKey("active") -> "members"
|
||||||
columns.containsKey("categoryid") && columns.containsKey("categoryname") -> "categories"
|
columns.containsKey("categoryid") && columns.containsKey("categoryname") -> "categories"
|
||||||
|
columns.containsKey("paymentmodeid") && columns.containsKey("paymentmodename") -> "paymentmodes"
|
||||||
columns.containsKey("exchange_rate") && columns.containsKey("currencyname") -> "currencies"
|
columns.containsKey("exchange_rate") && columns.containsKey("currencyname") -> "currencies"
|
||||||
else -> {
|
else -> {
|
||||||
onError(context.getString(R.string.import_error_header, row))
|
onError(context.getString(R.string.import_error_header, row))
|
||||||
@@ -86,11 +109,19 @@ object ProjectImportHelper {
|
|||||||
} else {
|
} else {
|
||||||
previousLineEmpty = false
|
previousLineEmpty = false
|
||||||
when (currentSection) {
|
when (currentSection) {
|
||||||
|
"members" -> {
|
||||||
|
val name = line[columns["name"]!!].trim()
|
||||||
|
if (name.isNotEmpty()) {
|
||||||
|
membersWeight[name] = optional(line, columns, "weight").toDoubleOrNull() ?: 1.0
|
||||||
|
membersActive[name] = optional(line, columns, "active") != "0"
|
||||||
|
parseHexColor(optional(line, columns, "color"))?.let { membersColor[name] = it }
|
||||||
|
}
|
||||||
|
}
|
||||||
"categories" -> {
|
"categories" -> {
|
||||||
categories.add(DBCategory(0, line[columns["categoryid"]!!].toLong(), 0, line[columns["categoryname"]!!], line[columns["icon"]!!], line[columns["color"]!!]))
|
categories.add(DBCategory(0, line[columns["categoryid"]!!].toLong(), 0, line[columns["categoryname"]!!], optional(line, columns, "icon"), optional(line, columns, "color")))
|
||||||
}
|
}
|
||||||
"paymentmodes" -> {
|
"paymentmodes" -> {
|
||||||
paymentModes.add(DBPaymentMode(0, line[columns["categoryid"]!!].toLong(), 0, line[columns["categoryname"]!!], line[columns["icon"]!!], line[columns["color"]!!]))
|
paymentModes.add(DBPaymentMode(0, line[columns["paymentmodeid"]!!].toLong(), 0, line[columns["paymentmodename"]!!], optional(line, columns, "icon"), optional(line, columns, "color")))
|
||||||
}
|
}
|
||||||
"currencies" -> {
|
"currencies" -> {
|
||||||
val name = line[columns["currencyname"]!!]
|
val name = line[columns["currencyname"]!!]
|
||||||
@@ -100,7 +131,22 @@ object ProjectImportHelper {
|
|||||||
}
|
}
|
||||||
"bills" -> {
|
"bills" -> {
|
||||||
val what = if (columns.containsKey("what")) line[columns["what"]!!] else ""
|
val what = if (columns.containsKey("what")) line[columns["what"]!!] else ""
|
||||||
val comment = if (columns.containsKey("comment")) line[columns["comment"]!!] else ""
|
// Cospend url-encodes bill comments on export and marks trashed
|
||||||
|
// bills with a "deleted" column no other dialect has, so that column
|
||||||
|
// doubles as the marker for which comment encoding to expect.
|
||||||
|
val cospendDialect = columns.containsKey("deleted")
|
||||||
|
val comment = if (columns.containsKey("comment")) {
|
||||||
|
val raw = line[columns["comment"]!!]
|
||||||
|
if (cospendDialect) {
|
||||||
|
try {
|
||||||
|
URLDecoder.decode(raw, "UTF-8")
|
||||||
|
} catch (_: Exception) {
|
||||||
|
raw
|
||||||
|
}
|
||||||
|
} else raw
|
||||||
|
} else ""
|
||||||
|
val deleted = cospendDialect &&
|
||||||
|
line[columns["deleted"]!!].trim().let { it.isNotEmpty() && it != "0" }
|
||||||
val amount = if (columns.containsKey("amount")) line[columns["amount"]!!].toDouble() else 0.0
|
val amount = if (columns.containsKey("amount")) line[columns["amount"]!!].toDouble() else 0.0
|
||||||
val timestamp: Long = when {
|
val timestamp: Long = when {
|
||||||
columns.containsKey("timestamp") -> line[columns["timestamp"]!!].toLong()
|
columns.containsKey("timestamp") -> line[columns["timestamp"]!!].toLong()
|
||||||
@@ -121,6 +167,11 @@ object ProjectImportHelper {
|
|||||||
val catId = if (columns.containsKey("categoryid") && line[columns["categoryid"]!!].isNotEmpty()) line[columns["categoryid"]!!].toLong() else 0L
|
val catId = if (columns.containsKey("categoryid") && line[columns["categoryid"]!!].isNotEmpty()) line[columns["categoryid"]!!].toLong() else 0L
|
||||||
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
|
||||||
|
// MoneyBuster's export only carries the legacy one-letter payment
|
||||||
|
// mode. Everything downstream keys off paymentModeId, so translate
|
||||||
|
// it back the same way the sync parser does.
|
||||||
|
val effectivePmId =
|
||||||
|
if (pmId != 0L) pmId else DBBill.oldPmIdToNew[pm] ?: DBBill.PAYMODE_ID_NONE
|
||||||
|
|
||||||
if (payerName.isNotEmpty()) {
|
if (payerName.isNotEmpty()) {
|
||||||
membersActive[payerName] = payerActive
|
membersActive[payerName] = payerActive
|
||||||
@@ -132,7 +183,7 @@ object ProjectImportHelper {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (what != "deleteMeIfYouWant") {
|
if (what != "deleteMeIfYouWant" && !deleted) {
|
||||||
billRemoteIdToOwerStr[row.toLong()] = owersStr
|
billRemoteIdToOwerStr[row.toLong()] = owersStr
|
||||||
val owersArray = owersStr.split(",").map { it.trim() }.filter { it.isNotEmpty() }
|
val owersArray = owersStr.split(",").map { it.trim() }.filter { it.isNotEmpty() }
|
||||||
for (ower in owersArray) {
|
for (ower in owersArray) {
|
||||||
@@ -140,7 +191,7 @@ object ProjectImportHelper {
|
|||||||
membersWeight[ower] = 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, effectivePmId))
|
||||||
billRemoteIdToPayerName[row.toLong()] = payerName
|
billRemoteIdToPayerName[row.toLong()] = payerName
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -168,13 +219,20 @@ object ProjectImportHelper {
|
|||||||
currencies.forEach { db.addCurrency(DBCurrency(0, 0, pid, it.name, it.exchangeRate, DBBill.STATE_OK)) }
|
currencies.forEach { db.addCurrency(DBCurrency(0, 0, pid, it.name, it.exchangeRate, DBBill.STATE_OK)) }
|
||||||
|
|
||||||
membersWeight.keys.forEach { mName ->
|
membersWeight.keys.forEach { mName ->
|
||||||
memberNameToId[mName] = db.addMember(DBMember(0, 0, pid, mName, membersActive[mName] ?: true, membersWeight[mName] ?: 1.0, DBBill.STATE_OK, null, null, null, null, null))
|
val c = membersColor[mName]
|
||||||
|
memberNameToId[mName] = db.addMember(DBMember(0, 0, pid, mName, membersActive[mName] ?: true, membersWeight[mName] ?: 1.0, DBBill.STATE_OK, c?.first, c?.second, c?.third, null, null))
|
||||||
}
|
}
|
||||||
|
|
||||||
bills.forEach { b ->
|
bills.forEach { b ->
|
||||||
val payerId = memberNameToId[billRemoteIdToPayerName[b.remoteId]] ?: 0L
|
val payerId = memberNameToId[billRemoteIdToPayerName[b.remoteId]] ?: 0L
|
||||||
val localCatId = catRemoteToLocal[b.categoryId] ?: 0L
|
// Only custom labels are listed in the categories/paymentmodes sections. Built-in
|
||||||
val localPmId = pmRemoteToLocal[b.paymentModeId] ?: 0L
|
// ones are referenced by their (negative) constant, which the UI resolves on its
|
||||||
|
// own, so those have to be kept rather than reset to "none". Unmapped positive
|
||||||
|
// ids are dropped instead, as they would collide with local ids.
|
||||||
|
val localCatId = catRemoteToLocal[b.categoryId]
|
||||||
|
?: b.categoryId.takeIf { it < 0 } ?: 0L
|
||||||
|
val localPmId = pmRemoteToLocal[b.paymentModeId]
|
||||||
|
?: b.paymentModeId.takeIf { it < 0 } ?: 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(",")?.map { it.trim() }?.filter { it.isNotEmpty() }?.forEach { ower ->
|
billRemoteIdToOwerStr[b.remoteId]?.split(",")?.map { it.trim() }?.filter { it.isNotEmpty() }?.forEach { ower ->
|
||||||
memberNameToId[ower]?.let { owerId -> db.addBillower(billId, owerId) }
|
memberNameToId[ower]?.let { owerId -> db.addBillower(billId, owerId) }
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ fun ProjectOptionsDialogContent(
|
|||||||
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
|
showExtraFeatures: Boolean = false
|
||||||
) {
|
) {
|
||||||
Surface(
|
Surface(
|
||||||
shape = MaterialTheme.shapes.large,
|
shape = MaterialTheme.shapes.large,
|
||||||
@@ -80,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 (showBetaFeatures && (projectType == ProjectType.LOCAL || projectType == ProjectType.COSPEND)) {
|
if (showExtraFeatures && (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))
|
||||||
@@ -183,7 +183,7 @@ fun ProjectOptionsDialogPreview() {
|
|||||||
projectType = ProjectType.COSPEND,
|
projectType = ProjectType.COSPEND,
|
||||||
accessLevel = DBProject.ACCESS_LEVEL_ADMIN,
|
accessLevel = DBProject.ACCESS_LEVEL_ADMIN,
|
||||||
isShareable = true,
|
isShareable = true,
|
||||||
showBetaFeatures = true
|
showExtraFeatures = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -208,7 +208,7 @@ fun ProjectOptionsDialogPreview2() {
|
|||||||
projectType = ProjectType.COSPEND,
|
projectType = ProjectType.COSPEND,
|
||||||
accessLevel = DBProject.ACCESS_LEVEL_ADMIN,
|
accessLevel = DBProject.ACCESS_LEVEL_ADMIN,
|
||||||
isShareable = true,
|
isShareable = true,
|
||||||
showBetaFeatures = true
|
showExtraFeatures = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -232,7 +232,7 @@ fun ProjectOptionsDialogPreview3() {
|
|||||||
projectType = ProjectType.LOCAL,
|
projectType = ProjectType.LOCAL,
|
||||||
accessLevel = DBProject.ACCESS_LEVEL_ADMIN,
|
accessLevel = DBProject.ACCESS_LEVEL_ADMIN,
|
||||||
isShareable = true,
|
isShareable = true,
|
||||||
showBetaFeatures = true
|
showExtraFeatures = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,9 +84,9 @@ 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)
|
val keyExtraFeatures = stringResource(R.string.pref_key_extra_features)
|
||||||
val keyStatsIncludeDeactivated = stringResource(R.string.pref_key_stats_include_deactivated)
|
val keyStatsIncludeDeactivated = stringResource(R.string.pref_key_stats_include_deactivated)
|
||||||
val keyAutoSyncOnOpen = stringResource(R.string.pref_key_auto_sync_on_open)
|
val keyFullSyncDelay = stringResource(R.string.pref_key_full_sync_delay)
|
||||||
val keyFillNewBillFromLast = stringResource(R.string.pref_key_fill_new_bill_from_last)
|
val keyFillNewBillFromLast = stringResource(R.string.pref_key_fill_new_bill_from_last)
|
||||||
val keyLastAccountSync = stringResource(R.string.pref_key_last_account_sync_timestamp)
|
val keyLastAccountSync = stringResource(R.string.pref_key_last_account_sync_timestamp)
|
||||||
|
|
||||||
@@ -128,8 +128,8 @@ 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) {
|
var extraFeatures by remember(keyExtraFeatures) {
|
||||||
mutableStateOf(sharedPreferences.getBoolean(keyBetaFeatures, false))
|
mutableStateOf(sharedPreferences.getBoolean(keyExtraFeatures, false))
|
||||||
}
|
}
|
||||||
var fillNewBillFromLast by remember(keyFillNewBillFromLast) {
|
var fillNewBillFromLast by remember(keyFillNewBillFromLast) {
|
||||||
mutableStateOf(sharedPreferences.getBoolean(keyFillNewBillFromLast, false))
|
mutableStateOf(sharedPreferences.getBoolean(keyFillNewBillFromLast, false))
|
||||||
@@ -138,16 +138,16 @@ fun SettingsScreen(
|
|||||||
mutableStateOf(sharedPreferences.getBoolean(keyStatsIncludeDeactivated, false))
|
mutableStateOf(sharedPreferences.getBoolean(keyStatsIncludeDeactivated, false))
|
||||||
}
|
}
|
||||||
|
|
||||||
val syncIntervals = SyncSettings.INTERVAL_CHOICES_MINUTES
|
val fullSyncDelays = SyncSettings.FULL_SYNC_DELAY_CHOICES_MINUTES
|
||||||
val syncIntervalLabels = listOf(
|
val fullSyncDelayLabels = listOf(
|
||||||
stringResource(R.string.pref_value_sync_1m),
|
stringResource(R.string.pref_value_sync_always),
|
||||||
stringResource(R.string.pref_value_sync_10m),
|
|
||||||
stringResource(R.string.pref_value_sync_1h),
|
stringResource(R.string.pref_value_sync_1h),
|
||||||
stringResource(R.string.pref_value_sync_1d)
|
stringResource(R.string.pref_value_sync_1d),
|
||||||
|
stringResource(R.string.pref_value_sync_1w)
|
||||||
)
|
)
|
||||||
|
|
||||||
var syncInterval by remember(keyAutoSyncOnOpen) {
|
var fullSyncDelay by remember(keyFullSyncDelay) {
|
||||||
mutableIntStateOf(SyncSettings.intervalMinutes(context))
|
mutableIntStateOf(SyncSettings.fullSyncDelayMinutes(context))
|
||||||
}
|
}
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
@@ -173,28 +173,6 @@ fun SettingsScreen(
|
|||||||
// Appearance
|
// Appearance
|
||||||
SettingsCategory(stringResource(R.string.settings_appearance))
|
SettingsCategory(stringResource(R.string.settings_appearance))
|
||||||
|
|
||||||
SettingsSwitchPreference(
|
|
||||||
title = stringResource(R.string.settings_show_archived),
|
|
||||||
icon = Icons.Default.Archive,
|
|
||||||
checked = showArchived,
|
|
||||||
onCheckedChange = {
|
|
||||||
showArchived = it
|
|
||||||
sharedPreferences.edit {
|
|
||||||
putBoolean(keyShowArchived, it)
|
|
||||||
if (!it) {
|
|
||||||
val selectedProjectId = sharedPreferences.getLong("selected_project", 0)
|
|
||||||
if (selectedProjectId != 0L) {
|
|
||||||
val db = CowspentSQLiteOpenHelper.getInstance(context)
|
|
||||||
val project = db.getProject(selectedProjectId)
|
|
||||||
if (project?.isArchived == true) {
|
|
||||||
putLong("selected_project", 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
SettingsListPreference(
|
SettingsListPreference(
|
||||||
title = stringResource(R.string.settings_night_mode),
|
title = stringResource(R.string.settings_night_mode),
|
||||||
icon = Icons.Default.Brightness2,
|
icon = Icons.Default.Brightness2,
|
||||||
@@ -283,19 +261,41 @@ fun SettingsScreen(
|
|||||||
SettingsCategory(stringResource(R.string.settings_other))
|
SettingsCategory(stringResource(R.string.settings_other))
|
||||||
|
|
||||||
SettingsSwitchPreference(
|
SettingsSwitchPreference(
|
||||||
title = stringResource(R.string.settings_beta_features),
|
title = stringResource(R.string.settings_extra_features),
|
||||||
summary = stringResource(R.string.settings_beta_features_summary),
|
summary = stringResource(R.string.settings_extra_features_summary),
|
||||||
icon = Icons.Default.Info,
|
icon = Icons.Default.Info,
|
||||||
checked = betaFeatures,
|
checked = extraFeatures,
|
||||||
onCheckedChange = {
|
onCheckedChange = {
|
||||||
betaFeatures = it
|
extraFeatures = it
|
||||||
sharedPreferences.edit {
|
sharedPreferences.edit {
|
||||||
putBoolean(keyBetaFeatures, it)
|
putBoolean(keyExtraFeatures, it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if (extraFeatures) {
|
||||||
|
SettingsSwitchPreference(
|
||||||
|
title = stringResource(R.string.settings_show_archived),
|
||||||
|
icon = Icons.Default.Archive,
|
||||||
|
checked = showArchived,
|
||||||
|
onCheckedChange = {
|
||||||
|
showArchived = it
|
||||||
|
sharedPreferences.edit {
|
||||||
|
putBoolean(keyShowArchived, it)
|
||||||
|
if (!it) {
|
||||||
|
val selectedProjectId = sharedPreferences.getLong("selected_project", 0)
|
||||||
|
if (selectedProjectId != 0L) {
|
||||||
|
val db = CowspentSQLiteOpenHelper.getInstance(context)
|
||||||
|
val project = db.getProject(selectedProjectId)
|
||||||
|
if (project?.isArchived == true) {
|
||||||
|
putLong("selected_project", 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
if (betaFeatures) {
|
|
||||||
SettingsSwitchPreference(
|
SettingsSwitchPreference(
|
||||||
title = stringResource(R.string.settings_fill_new_bill_from_last),
|
title = stringResource(R.string.settings_fill_new_bill_from_last),
|
||||||
summary = stringResource(R.string.settings_fill_new_bill_from_last_summary),
|
summary = stringResource(R.string.settings_fill_new_bill_from_last_summary),
|
||||||
@@ -322,18 +322,18 @@ fun SettingsScreen(
|
|||||||
)
|
)
|
||||||
|
|
||||||
SettingsSliderPreference(
|
SettingsSliderPreference(
|
||||||
title = stringResource(R.string.settings_auto_sync_on_open),
|
title = stringResource(R.string.settings_full_sync_delay),
|
||||||
summary = stringResource(R.string.settings_auto_sync_on_open_summary),
|
summary = stringResource(R.string.settings_full_sync_delay_summary),
|
||||||
icon = Icons.Default.Sync,
|
icon = Icons.Default.Sync,
|
||||||
value = syncInterval,
|
value = fullSyncDelay,
|
||||||
values = syncIntervals,
|
values = fullSyncDelays,
|
||||||
labels = syncIntervalLabels,
|
labels = fullSyncDelayLabels,
|
||||||
onValueChange = { newInterval ->
|
onValueChange = { newDelay ->
|
||||||
// Slider reports every drag delta, not just the snapped steps.
|
// Slider reports every drag delta, not just the snapped steps.
|
||||||
if (newInterval != syncInterval) {
|
if (newDelay != fullSyncDelay) {
|
||||||
syncInterval = newInterval
|
fullSyncDelay = newDelay
|
||||||
sharedPreferences.edit {
|
sharedPreferences.edit {
|
||||||
putInt(keyAutoSyncOnOpen, newInterval)
|
putInt(keyFullSyncDelay, newDelay)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -144,7 +144,12 @@ class CowspentServerSyncHelper private constructor(private val dbHelper: Cowspen
|
|||||||
* walk it is empty, and nothing may be deleted locally on the strength of it.
|
* walk it is empty, and nothing may be deleted locally on the strength of it.
|
||||||
*/
|
*/
|
||||||
val allIds: List<Long>,
|
val allIds: List<Long>,
|
||||||
val syncTimestamp: Long?
|
val syncTimestamp: Long?,
|
||||||
|
/**
|
||||||
|
* A bill referenced a member, category or payment mode this device does not know. A
|
||||||
|
* partial sync skips re-reading the project, so this is what tells it to go back for it.
|
||||||
|
*/
|
||||||
|
val hasUnresolvedReferences: Boolean = false
|
||||||
)
|
)
|
||||||
|
|
||||||
inner class SyncTask(private val onlyLocalChanges: Boolean, private val project: DBProject, private val forceFullSync: Boolean = false) {
|
inner class SyncTask(private val onlyLocalChanges: Boolean, private val project: DBProject, private val forceFullSync: Boolean = false) {
|
||||||
@@ -188,12 +193,19 @@ class CowspentServerSyncHelper private constructor(private val dbHelper: Cowspen
|
|||||||
if (project.type == ProjectType.COSPEND) {
|
if (project.type == ProjectType.COSPEND) {
|
||||||
nextcloudClient = createNextcloudClient()
|
nextcloudClient = createNextcloudClient()
|
||||||
if (nextcloudClient != null) {
|
if (nextcloudClient != null) {
|
||||||
|
// The server's version only moves when the server itself is upgraded, and
|
||||||
|
// asking costs a whole round trip before any project data moves. Remember it
|
||||||
|
// per server, and re-read it whenever a full sync is asked for.
|
||||||
|
version = if (forceFullSync) null else cachedCospendVersion()
|
||||||
|
if (version == null) {
|
||||||
try {
|
try {
|
||||||
val response = nextcloudClient!!.getCapabilities(project)
|
val response = nextcloudClient!!.getCapabilities(project)
|
||||||
version = response.cospendVersion
|
version = response.cospendVersion
|
||||||
|
rememberCospendVersion(version)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.i(TAG, "Failed to get cospend version when syncing: $e")
|
Log.i(TAG, "Failed to get cospend version when syncing: $e")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else if (preferences.getBoolean(AccountActivity.SETTINGS_USE_SSO, false)) {
|
} else if (preferences.getBoolean(AccountActivity.SETTINGS_USE_SSO, false)) {
|
||||||
return LoginStatus.SSO_TOKEN_MISMATCH
|
return LoginStatus.SSO_TOKEN_MISMATCH
|
||||||
}
|
}
|
||||||
@@ -218,15 +230,32 @@ class CowspentServerSyncHelper private constructor(private val dbHelper: Cowspen
|
|||||||
deferred?.await() ?: LoginStatus.CONNECTION_FAILED
|
deferred?.await() ?: LoginStatus.CONNECTION_FAILED
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Keyed by server rather than by project, since several projects can share one. */
|
||||||
|
private fun versionKey() = "cospend_version_" + (project.serverUrl ?: "")
|
||||||
|
|
||||||
|
private fun cachedCospendVersion(): String? =
|
||||||
|
preferences.getString(versionKey(), null)
|
||||||
|
|
||||||
|
private fun rememberCospendVersion(version: String?) {
|
||||||
|
// A failed lookup is not an answer, and caching it would pin the wrong API dialect
|
||||||
|
// until the next forced sync.
|
||||||
|
if (version.isNullOrEmpty()) return
|
||||||
|
preferences.edit { putString(versionKey(), version) }
|
||||||
|
}
|
||||||
|
|
||||||
private fun pushLocalChanges(): LoginStatus {
|
private fun pushLocalChanges(): LoginStatus {
|
||||||
Log.d(TAG, "PUSH LOCAL CHANGES")
|
Log.d(TAG, "PUSH LOCAL CHANGES")
|
||||||
|
|
||||||
return try {
|
return try {
|
||||||
|
// The remote member list is only needed to match members waiting to be created,
|
||||||
|
// and pullRemoteChanges fetches them again as part of the project anyway, so it
|
||||||
|
// is not worth a round trip of its own when there is nothing to add.
|
||||||
|
val membersToAdd = dbHelper.getMembersOfProjectWithState(project.id, DBBill.STATE_ADDED)
|
||||||
|
if (membersToAdd.isNotEmpty()) {
|
||||||
val membersResponse = client!!.getMembers(project)
|
val membersResponse = client!!.getMembers(project)
|
||||||
val remoteMembers = membersResponse.getMembers(project.id)
|
val remoteMembers = membersResponse.getMembers(project.id)
|
||||||
val remoteMembersNames = remoteMembers.map { it.name }
|
val remoteMembersNames = remoteMembers.map { it.name }
|
||||||
|
|
||||||
val membersToAdd = dbHelper.getMembersOfProjectWithState(project.id, DBBill.STATE_ADDED)
|
|
||||||
for (mToAdd in membersToAdd) {
|
for (mToAdd in membersToAdd) {
|
||||||
val searchIndex = remoteMembersNames.indexOf(mToAdd.name)
|
val searchIndex = remoteMembersNames.indexOf(mToAdd.name)
|
||||||
if (searchIndex != -1) {
|
if (searchIndex != -1) {
|
||||||
@@ -250,6 +279,7 @@ class CowspentServerSyncHelper private constructor(private val dbHelper: Cowspen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val membersToEdit = dbHelper.getMembersOfProjectWithState(project.id, DBBill.STATE_EDITED)
|
val membersToEdit = dbHelper.getMembersOfProjectWithState(project.id, DBBill.STATE_EDITED)
|
||||||
for (mToEdit in membersToEdit) {
|
for (mToEdit in membersToEdit) {
|
||||||
@@ -540,32 +570,53 @@ class CowspentServerSyncHelper private constructor(private val dbHelper: Cowspen
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Brings the local copy of the project in line with the server: its own row, then each of
|
* Reads the project's own row and each of its collections. One request, and everything it
|
||||||
* its collections, then its bills. Each step is applied as it goes, so a failure part way
|
* carries is applied as it goes, so a failure part way through leaves the earlier steps
|
||||||
* through leaves the earlier steps written - the next sync picks up where this one stopped.
|
* written. Returns the members it saw, keyed by remote id.
|
||||||
*/
|
*/
|
||||||
private fun pullRemoteChanges(): LoginStatus {
|
private fun pullProject(): Map<Long, DBMember> {
|
||||||
Log.d(TAG, "pullRemoteChanges($project)")
|
|
||||||
return try {
|
|
||||||
val projResponse = client!!.getProject(project, 0, null)
|
val projResponse = client!!.getProject(project, 0, null)
|
||||||
|
|
||||||
updateLocalProject(projResponse)
|
updateLocalProject(projResponse)
|
||||||
syncPaymentModes(projResponse)
|
syncPaymentModes(projResponse)
|
||||||
syncCategories(projResponse)
|
syncCategories(projResponse)
|
||||||
syncCurrencies(projResponse)
|
syncCurrencies(projResponse)
|
||||||
val remoteMembersByRemoteId = syncMembers(projResponse)
|
return syncMembers(projResponse)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Brings the local copy of the project in line with the server: its own row, then each of
|
||||||
|
* its collections, then its bills. Each step is applied as it goes, so a failure part way
|
||||||
|
* through leaves the earlier steps written - the next sync picks up where this one stopped.
|
||||||
|
*
|
||||||
|
* A partial sync is meant to cost one request. A project's members and labels change far
|
||||||
|
* less often than its bills, so it skips reading them and goes straight for the bills; it
|
||||||
|
* only goes back for the project when a bill turns out to name something this device has
|
||||||
|
* never seen. What that trades away is noticing a rename or a deletion that no bill
|
||||||
|
* refers to - those land on the next full sync.
|
||||||
|
*/
|
||||||
|
private fun pullRemoteChanges(): LoginStatus {
|
||||||
|
Log.d(TAG, "pullRemoteChanges($project)")
|
||||||
|
return try {
|
||||||
|
val localBills = dbHelper.getBillsOfProject(project.id)
|
||||||
|
val localBillsByRemoteId = localBills.associateBy { it.remoteId }
|
||||||
|
val partial = !forceFullSync && localBillsByRemoteId.isNotEmpty()
|
||||||
|
|
||||||
|
var remoteMembersByRemoteId: Map<Long, DBMember>? = null
|
||||||
|
if (!partial) remoteMembersByRemoteId = pullProject()
|
||||||
|
|
||||||
// Bills arrive with the server's ids for their member, category and payment mode,
|
// Bills arrive with the server's ids for their member, category and payment mode,
|
||||||
// so the maps have to be built after those collections are in place.
|
// so the maps have to be built after those collections are in place.
|
||||||
val idMaps = buildRemoteIdMaps()
|
var pulled = fetchRemoteBills(buildRemoteIdMaps(), localBillsByRemoteId)
|
||||||
|
|
||||||
val localBills = dbHelper.getBillsOfProject(project.id)
|
if (pulled.hasUnresolvedReferences && remoteMembersByRemoteId == null) {
|
||||||
val localBillsByRemoteId = localBills.associateBy { it.remoteId }
|
Log.d(TAG, "Bill referenced an unknown member or label; re-reading the project")
|
||||||
val pulled = fetchRemoteBills(idMaps, localBillsByRemoteId)
|
remoteMembersByRemoteId = pullProject()
|
||||||
|
pulled = fetchRemoteBills(buildRemoteIdMaps(), localBillsByRemoteId)
|
||||||
|
}
|
||||||
|
|
||||||
applyRemoteBills(pulled.bills, localBillsByRemoteId)
|
applyRemoteBills(pulled.bills, localBillsByRemoteId)
|
||||||
deleteVanishedBills(pulled, localBills)
|
deleteVanishedBills(pulled, localBills)
|
||||||
deleteVanishedMembers(remoteMembersByRemoteId)
|
remoteMembersByRemoteId?.let { deleteVanishedMembers(it) }
|
||||||
|
|
||||||
dbHelper.updateProject(
|
dbHelper.updateProject(
|
||||||
projId = project.id,
|
projId = project.id,
|
||||||
@@ -825,6 +876,7 @@ class CowspentServerSyncHelper private constructor(private val dbHelper: Cowspen
|
|||||||
var syncTimestamp = project.lastSyncedTimestamp
|
var syncTimestamp = project.lastSyncedTimestamp
|
||||||
var offset = 0
|
var offset = 0
|
||||||
var previousPageIds: List<Long>? = null
|
var previousPageIds: List<Long>? = null
|
||||||
|
var unresolved = false
|
||||||
// Counted across pages, not restarted at each one: a run that begins near the end of
|
// Counted across pages, not restarted at each one: a run that begins near the end of
|
||||||
// a page still finishes on the next.
|
// a page still finishes on the next.
|
||||||
var unchangedRun = 0
|
var unchangedRun = 0
|
||||||
@@ -834,6 +886,7 @@ class CowspentServerSyncHelper private constructor(private val dbHelper: Cowspen
|
|||||||
val page = response.getBillsCospend(
|
val page = response.getBillsCospend(
|
||||||
project.id, idMaps.members, idMaps.categories, idMaps.paymentModes
|
project.id, idMaps.members, idMaps.categories, idMaps.paymentModes
|
||||||
)
|
)
|
||||||
|
if (response.hasUnresolvedReferences) unresolved = true
|
||||||
if (page.isEmpty()) break
|
if (page.isEmpty()) break
|
||||||
|
|
||||||
if (page.first().timestamp < page.last().timestamp) {
|
if (page.first().timestamp < page.last().timestamp) {
|
||||||
@@ -873,7 +926,7 @@ class CowspentServerSyncHelper private constructor(private val dbHelper: Cowspen
|
|||||||
}
|
}
|
||||||
|
|
||||||
// A walk reports no allIds, so it never causes a local deletion.
|
// A walk reports no allIds, so it never causes a local deletion.
|
||||||
return RemoteBills(bills, emptyList(), syncTimestamp)
|
return RemoteBills(bills, emptyList(), syncTimestamp, unresolved)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -888,14 +941,16 @@ class CowspentServerSyncHelper private constructor(private val dbHelper: Cowspen
|
|||||||
val bills = response.getBillsIHM(
|
val bills = response.getBillsIHM(
|
||||||
project.id, idMaps.members, idMaps.categories, idMaps.paymentModes
|
project.id, idMaps.members, idMaps.categories, idMaps.paymentModes
|
||||||
)
|
)
|
||||||
RemoteBills(bills, bills.map { it.remoteId }, 0L)
|
RemoteBills(bills, bills.map { it.remoteId }, 0L, response.hasUnresolvedReferences)
|
||||||
} else {
|
} else {
|
||||||
RemoteBills(
|
val bills = response.getBillsCospend(
|
||||||
response.getBillsCospend(
|
|
||||||
project.id, idMaps.members, idMaps.categories, idMaps.paymentModes
|
project.id, idMaps.members, idMaps.categories, idMaps.paymentModes
|
||||||
),
|
)
|
||||||
|
RemoteBills(
|
||||||
|
bills,
|
||||||
response.allBillIds,
|
response.allBillIds,
|
||||||
response.syncTimestamp
|
response.syncTimestamp,
|
||||||
|
response.hasUnresolvedReferences
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ import net.helcel.cowspent.persistence.CowspentSQLiteOpenHelper
|
|||||||
|
|
||||||
object ExportUtil {
|
object ExportUtil {
|
||||||
|
|
||||||
|
/** Quotes a field the way RFC4180 (and opencsv, and Cospend) expect. */
|
||||||
|
private fun q(value: String?): String = "\"" + (value ?: "").replace("\"", "\"\"") + "\""
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun createExportContent(db: CowspentSQLiteOpenHelper, projectId: Long): String {
|
fun createExportContent(db: CowspentSQLiteOpenHelper, projectId: Long): String {
|
||||||
var fileContent = ""
|
var fileContent = ""
|
||||||
@@ -18,8 +21,8 @@ object ExportUtil {
|
|||||||
}
|
}
|
||||||
val bills = db.getBillsOfProject(projectId).toMutableList()
|
val bills = db.getBillsOfProject(projectId).toMutableList()
|
||||||
|
|
||||||
// write header
|
// write header.
|
||||||
fileContent += "what,amount,date,timestamp,payer_name,payer_weight,payer_active,owers,repeat,categoryid,paymentmode\n"
|
fileContent += "what,amount,date,timestamp,payer_name,payer_weight,payer_active,owers,repeat,categoryid,paymentmode,paymentmodeid,comment\n"
|
||||||
|
|
||||||
// write members
|
// write members
|
||||||
for (m in members) {
|
for (m in members) {
|
||||||
@@ -41,15 +44,11 @@ object ExportUtil {
|
|||||||
val payerName = payer.name
|
val payerName = payer.name
|
||||||
val payerWeight = payer.weight
|
val payerWeight = payer.weight
|
||||||
val payerActive = if (payer.isActivated) 1 else 0
|
val payerActive = if (payer.isActivated) 1 else 0
|
||||||
val billOwers = b.billOwers
|
val owersTxt = b.billOwers.mapNotNull { membersById[it.memberId]?.name }.joinToString(",")
|
||||||
var owersTxt = ""
|
fileContent += "${q(b.what)},${b.amount},${b.date},${b.timestamp},${q(payerName)}," +
|
||||||
for (bo in billOwers) {
|
"$payerWeight,$payerActive,${q(owersTxt)},${b.repeat ?: DBBill.NON_REPEATED}," +
|
||||||
owersTxt += membersById[bo.memberId]?.name + ","
|
"${b.categoryId},${b.paymentMode ?: DBBill.PAYMODE_NONE},${b.paymentModeId}," +
|
||||||
}
|
"${q(b.comment)}\n"
|
||||||
owersTxt = owersTxt.replace(",$".toRegex(), "")
|
|
||||||
fileContent += "\"${b.what}\",${b.amount},${b.date},${b.timestamp},\"$payerName\"," +
|
|
||||||
"$payerWeight,$payerActive,\"$owersTxt\",${b.repeat},${b.categoryId}," +
|
|
||||||
"${b.paymentMode}\n"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// write categories
|
// write categories
|
||||||
@@ -57,7 +56,16 @@ object ExportUtil {
|
|||||||
if (cats.isNotEmpty()) {
|
if (cats.isNotEmpty()) {
|
||||||
fileContent += "\ncategoryname,categoryid,icon,color\n"
|
fileContent += "\ncategoryname,categoryid,icon,color\n"
|
||||||
for (cat in cats) {
|
for (cat in cats) {
|
||||||
fileContent += "\"${cat.name}\",${cat.id},\"${cat.icon}\",\"${cat.color}\"\n"
|
fileContent += "${q(cat.name)},${cat.id},${q(cat.icon)},${q(cat.color)}\n"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// write payment modes
|
||||||
|
val pms = db.getPaymentModes(projectId)
|
||||||
|
if (pms.isNotEmpty()) {
|
||||||
|
fileContent += "\npaymentmodename,paymentmodeid,icon,color\n"
|
||||||
|
for (pm in pms) {
|
||||||
|
fileContent += "${q(pm.name)},${pm.id},${q(pm.icon)},${q(pm.color)}\n"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,9 +75,9 @@ object ExportUtil {
|
|||||||
project.currencyName!!.isNotEmpty() && project.currencyName != "null"
|
project.currencyName!!.isNotEmpty() && project.currencyName != "null"
|
||||||
) {
|
) {
|
||||||
fileContent += "\ncurrencyname,exchange_rate\n"
|
fileContent += "\ncurrencyname,exchange_rate\n"
|
||||||
fileContent += "\"${project.currencyName}\",1\n"
|
fileContent += "${q(project.currencyName)},1\n"
|
||||||
for (cur in curs) {
|
for (cur in curs) {
|
||||||
fileContent += "\"${cur.name}\",${cur.exchangeRate}\n"
|
fileContent += "${q(cur.name)},${cur.exchangeRate}\n"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,33 @@ open class ServerResponse(
|
|||||||
) {
|
) {
|
||||||
private val sdf = SimpleDateFormat("yyyy-MM-dd", Locale.ROOT)
|
private val sdf = SimpleDateFormat("yyyy-MM-dd", Locale.ROOT)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set while parsing bills when one referenced a member, category or payment mode that none of
|
||||||
|
* the supplied maps knew about. A partial sync does not re-read the project, so this is how it
|
||||||
|
* learns that its copy of those collections has gone stale and has to be refreshed.
|
||||||
|
*/
|
||||||
|
var hasUnresolvedReferences = false
|
||||||
|
private set
|
||||||
|
|
||||||
|
/** Members are only ever addressed by the server's own id, so an unknown one is stale state. */
|
||||||
|
private fun resolveMember(map: Map<Long, Long>, remoteId: Long): Long {
|
||||||
|
map[remoteId]?.let { return it }
|
||||||
|
if (remoteId != 0L) hasUnresolvedReferences = true
|
||||||
|
return 0L
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Built-in labels are negative constants with no row of their own on a remote project, so they
|
||||||
|
* never appear in these maps and are carried through as-is. An unknown *positive* id is a real
|
||||||
|
* gap - it is dropped rather than kept, as it would collide with a local id.
|
||||||
|
*/
|
||||||
|
private fun resolveLabel(map: Map<Long, Long>, remoteId: Long): Long {
|
||||||
|
map[remoteId]?.let { return it }
|
||||||
|
if (remoteId < 0) return remoteId
|
||||||
|
if (remoteId > 0) hasUnresolvedReferences = true
|
||||||
|
return 0L
|
||||||
|
}
|
||||||
|
|
||||||
class NotModifiedException : IOException()
|
class NotModifiedException : IOException()
|
||||||
|
|
||||||
protected val content: String
|
protected val content: String
|
||||||
@@ -744,10 +771,10 @@ open class ServerResponse(
|
|||||||
}
|
}
|
||||||
if (!json.isNull("payer_id")) {
|
if (!json.isNull("payer_id")) {
|
||||||
payerRemoteId = json.getLong("payer_id")
|
payerRemoteId = json.getLong("payer_id")
|
||||||
payerId = memberRemoteIdToId[payerRemoteId] ?: 0
|
payerId = resolveMember(memberRemoteIdToId, payerRemoteId)
|
||||||
} else if (!json.isNull("payer")) {
|
} else if (!json.isNull("payer")) {
|
||||||
payerRemoteId = json.getLong("payer")
|
payerRemoteId = json.getLong("payer")
|
||||||
payerId = memberRemoteIdToId[payerRemoteId] ?: 0
|
payerId = resolveMember(memberRemoteIdToId, payerRemoteId)
|
||||||
}
|
}
|
||||||
if (!json.isNull("amount")) {
|
if (!json.isNull("amount")) {
|
||||||
amount = json.getDouble("amount")
|
amount = json.getDouble("amount")
|
||||||
@@ -794,8 +821,8 @@ open class ServerResponse(
|
|||||||
paymentModeRemoteId = DBBill.oldPmIdToNew[paymentMode] ?: DBBill.PAYMODE_ID_NONE
|
paymentModeRemoteId = DBBill.oldPmIdToNew[paymentMode] ?: DBBill.PAYMODE_ID_NONE
|
||||||
}
|
}
|
||||||
|
|
||||||
val categoryId = catRemoteIdToId[categoryRemoteId] ?: 0L
|
val categoryId = resolveLabel(catRemoteIdToId, categoryRemoteId)
|
||||||
val paymentModeId = pmRemoteIdToId[paymentModeRemoteId] ?: 0L
|
val paymentModeId = resolveLabel(pmRemoteIdToId, paymentModeRemoteId)
|
||||||
|
|
||||||
val bill = DBBill(
|
val bill = DBBill(
|
||||||
0, remoteId, projId, payerId, amount, timestamp, what,
|
0, remoteId, projId, payerId, amount, timestamp, what,
|
||||||
@@ -822,7 +849,7 @@ open class ServerResponse(
|
|||||||
} else {
|
} else {
|
||||||
jsonOs.getLong(i)
|
jsonOs.getLong(i)
|
||||||
}
|
}
|
||||||
val memberLocalId = memberRemoteIdToId[memberRemoteId] ?: 0
|
val memberLocalId = resolveMember(memberRemoteIdToId, memberRemoteId)
|
||||||
billOwers.add(DBBillOwer(0, 0, memberLocalId))
|
billOwers.add(DBBillOwer(0, 0, memberLocalId))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,29 +5,45 @@ import androidx.preference.PreferenceManager
|
|||||||
import net.helcel.cowspent.R
|
import net.helcel.cowspent.R
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The SyncOnOpen preference: how often opening the app refreshes the account and every project.
|
* The two independent delays that govern syncing.
|
||||||
*
|
*
|
||||||
* The choices and the default live here rather than in the settings screen so that the screen and
|
* They used to be one setting, which meant the app-open throttle and the full-sync delay could
|
||||||
* the sync trigger cannot disagree about what is in effect.
|
* not be tuned apart even though they answer different questions. The choices and defaults live
|
||||||
|
* here rather than in the settings screen so the screen and the sync triggers cannot disagree
|
||||||
|
* about what is in effect.
|
||||||
*/
|
*/
|
||||||
object SyncSettings {
|
object SyncSettings {
|
||||||
|
|
||||||
/** Steps offered by the slider, in minutes. */
|
/**
|
||||||
val INTERVAL_CHOICES_MINUTES = listOf(1, 10, 60, 1440)
|
* How long opening the app waits before refreshing the account and every project again, so
|
||||||
|
* that resuming within the interval does not repeat the work. Not user-facing: it governs
|
||||||
const val DEFAULT_INTERVAL_MINUTES = 10
|
* background catch-up, not anything the user asked for.
|
||||||
|
*/
|
||||||
|
const val OPEN_SYNC_INTERVAL_MINUTES = 10
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The configured interval in minutes. A stored value that is not one of the offered steps —
|
* Steps offered for the full sync delay, in minutes. 0 means every manual refresh is a full
|
||||||
* from a restored backup, or a build that changed the steps — falls back to the default
|
* sync; anything else lets a refresh inside the window settle for fetching just the changes.
|
||||||
* rather than being displayed as the first step while a different value drives the sync.
|
|
||||||
*/
|
*/
|
||||||
fun intervalMinutes(context: Context): Int {
|
val FULL_SYNC_DELAY_CHOICES_MINUTES = listOf(0, 60, 1440, 10080)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only consulted once Extra Features is on - with it off every pull-to-refresh is a full
|
||||||
|
* sync, whatever is stored here.
|
||||||
|
*/
|
||||||
|
const val DEFAULT_FULL_SYNC_DELAY_MINUTES = 1440
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The configured delay in minutes. A stored value that is not one of the offered steps - from
|
||||||
|
* a restored backup, or a build that changed the steps - falls back to the default rather
|
||||||
|
* than being displayed as one step while a different value drives the sync.
|
||||||
|
*/
|
||||||
|
fun fullSyncDelayMinutes(context: Context): Int {
|
||||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
val stored = prefs.getInt(
|
val stored = prefs.getInt(
|
||||||
context.getString(R.string.pref_key_auto_sync_on_open),
|
context.getString(R.string.pref_key_full_sync_delay),
|
||||||
DEFAULT_INTERVAL_MINUTES
|
DEFAULT_FULL_SYNC_DELAY_MINUTES
|
||||||
)
|
)
|
||||||
return if (stored in INTERVAL_CHOICES_MINUTES) stored else DEFAULT_INTERVAL_MINUTES
|
return if (stored in FULL_SYNC_DELAY_CHOICES_MINUTES) stored else DEFAULT_FULL_SYNC_DELAY_MINUTES
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -144,14 +144,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_fill_new_bill_from_last">Auto-fill from last bill</string>
|
<string name="settings_fill_new_bill_from_last">Auto-fill from last bill</string>
|
||||||
<string name="settings_fill_new_bill_from_last_summary">Pre-fill payer, category, mode and owers from the last bill created in the project.</string>
|
<string name="settings_fill_new_bill_from_last_summary">Pre-fill payer, category, mode and owers from the last bill created in the project.</string>
|
||||||
<string name="settings_auto_sync_on_open">Sync interval</string>
|
|
||||||
<string name="settings_auto_sync_on_open_summary">How often to refresh the account and all projects when opening the app.</string>
|
|
||||||
<string name="pref_value_sync_1m">1 minute</string>
|
|
||||||
<string name="pref_value_sync_10m">10 minutes</string>
|
|
||||||
<string name="pref_value_sync_1h">1 hour</string>
|
<string name="pref_value_sync_1h">1 hour</string>
|
||||||
<string name="pref_value_sync_1d">1 day</string>
|
<string name="pref_value_sync_1d">1 day</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>
|
||||||
|
|||||||
@@ -144,14 +144,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_fill_new_bill_from_last">Auto-fill from last bill</string>
|
<string name="settings_fill_new_bill_from_last">Auto-fill from last bill</string>
|
||||||
<string name="settings_fill_new_bill_from_last_summary">Pre-fill payer, category, mode and owers from the last bill created in the project.</string>
|
<string name="settings_fill_new_bill_from_last_summary">Pre-fill payer, category, mode and owers from the last bill created in the project.</string>
|
||||||
<string name="settings_auto_sync_on_open">Sync interval</string>
|
|
||||||
<string name="settings_auto_sync_on_open_summary">How often to refresh the account and all projects when opening the app.</string>
|
|
||||||
<string name="pref_value_sync_1m">1 minute</string>
|
|
||||||
<string name="pref_value_sync_10m">10 minutes</string>
|
|
||||||
<string name="pref_value_sync_1h">1 hour</string>
|
<string name="pref_value_sync_1h">1 hour</string>
|
||||||
<string name="pref_value_sync_1d">1 day</string>
|
<string name="pref_value_sync_1d">1 day</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>
|
||||||
|
|||||||
@@ -144,14 +144,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_fill_new_bill_from_last">Auto-fill from last bill</string>
|
<string name="settings_fill_new_bill_from_last">Auto-fill from last bill</string>
|
||||||
<string name="settings_fill_new_bill_from_last_summary">Pre-fill payer, category, mode and owers from the last bill created in the project.</string>
|
<string name="settings_fill_new_bill_from_last_summary">Pre-fill payer, category, mode and owers from the last bill created in the project.</string>
|
||||||
<string name="settings_auto_sync_on_open">Sync interval</string>
|
|
||||||
<string name="settings_auto_sync_on_open_summary">How often to refresh the account and all projects when opening the app.</string>
|
|
||||||
<string name="pref_value_sync_1m">1 minute</string>
|
|
||||||
<string name="pref_value_sync_10m">10 minutes</string>
|
|
||||||
<string name="pref_value_sync_1h">1 hour</string>
|
<string name="pref_value_sync_1h">1 hour</string>
|
||||||
<string name="pref_value_sync_1d">1 day</string>
|
<string name="pref_value_sync_1d">1 day</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>
|
||||||
|
|||||||
@@ -144,14 +144,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_fill_new_bill_from_last">Auto-fill from last bill</string>
|
<string name="settings_fill_new_bill_from_last">Auto-fill from last bill</string>
|
||||||
<string name="settings_fill_new_bill_from_last_summary">Pre-fill payer, category, mode and owers from the last bill created in the project.</string>
|
<string name="settings_fill_new_bill_from_last_summary">Pre-fill payer, category, mode and owers from the last bill created in the project.</string>
|
||||||
<string name="settings_auto_sync_on_open">Sync interval</string>
|
|
||||||
<string name="settings_auto_sync_on_open_summary">How often to refresh the account and all projects when opening the app.</string>
|
|
||||||
<string name="pref_value_sync_1m">1 minute</string>
|
|
||||||
<string name="pref_value_sync_10m">10 minutes</string>
|
|
||||||
<string name="pref_value_sync_1h">1 hour</string>
|
<string name="pref_value_sync_1h">1 hour</string>
|
||||||
<string name="pref_value_sync_1d">1 day</string>
|
<string name="pref_value_sync_1d">1 day</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>
|
||||||
|
|||||||
@@ -144,14 +144,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_fill_new_bill_from_last">Auto-fill from last bill</string>
|
<string name="settings_fill_new_bill_from_last">Auto-fill from last bill</string>
|
||||||
<string name="settings_fill_new_bill_from_last_summary">Pre-fill payer, category, mode and owers from the last bill created in the project.</string>
|
<string name="settings_fill_new_bill_from_last_summary">Pre-fill payer, category, mode and owers from the last bill created in the project.</string>
|
||||||
<string name="settings_auto_sync_on_open">Sync interval</string>
|
|
||||||
<string name="settings_auto_sync_on_open_summary">How often to refresh the account and all projects when opening the app.</string>
|
|
||||||
<string name="pref_value_sync_1m">1 minute</string>
|
|
||||||
<string name="pref_value_sync_10m">10 minutes</string>
|
|
||||||
<string name="pref_value_sync_1h">1 hour</string>
|
<string name="pref_value_sync_1h">1 hour</string>
|
||||||
<string name="pref_value_sync_1d">1 day</string>
|
<string name="pref_value_sync_1d">1 day</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>
|
||||||
|
|||||||
@@ -138,14 +138,8 @@
|
|||||||
<string name="settings_color_custom">Eigene Farbe</string>
|
<string name="settings_color_custom">Eigene Farbe</string>
|
||||||
<string name="settings_color_mode">Farbauswahl</string>
|
<string name="settings_color_mode">Farbauswahl</string>
|
||||||
<string name="settings_show_archived">Archivierte Projekte anzeigen</string>
|
<string name="settings_show_archived">Archivierte Projekte anzeigen</string>
|
||||||
<string name="settings_beta_features">Beta-Funktionen</string>
|
|
||||||
<string name="settings_beta_features_summary">Experimentelle Funktionen aktivieren. Benutzung auf eigene Gefahr.</string>
|
|
||||||
<string name="settings_fill_new_bill_from_last">Aus letzter Rechnung vorausfüllen</string>
|
<string name="settings_fill_new_bill_from_last">Aus letzter Rechnung vorausfüllen</string>
|
||||||
<string name="settings_fill_new_bill_from_last_summary">Zahler, Kategorie, Zahlungsart und Beteiligte aus der zuletzt erstellten Rechnung des Projekts übernehmen.</string>
|
<string name="settings_fill_new_bill_from_last_summary">Zahler, Kategorie, Zahlungsart und Beteiligte aus der zuletzt erstellten Rechnung des Projekts übernehmen.</string>
|
||||||
<string name="settings_auto_sync_on_open">Synchronisierungsintervall</string>
|
|
||||||
<string name="settings_auto_sync_on_open_summary">Wie oft Konto und alle Projekte beim Öffnen der App aktualisiert werden.</string>
|
|
||||||
<string name="pref_value_sync_1m">1 Minute</string>
|
|
||||||
<string name="pref_value_sync_10m">10 Minuten</string>
|
|
||||||
<string name="pref_value_sync_1h">1 Stunde</string>
|
<string name="pref_value_sync_1h">1 Stunde</string>
|
||||||
<string name="pref_value_sync_1d">1 Tag</string>
|
<string name="pref_value_sync_1d">1 Tag</string>
|
||||||
<string name="settings_url_warn_http">WARNUNG: \"http\" ist unsicher. Verwende \"https\".</string>
|
<string name="settings_url_warn_http">WARNUNG: \"http\" ist unsicher. Verwende \"https\".</string>
|
||||||
|
|||||||
@@ -144,14 +144,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_fill_new_bill_from_last">Auto-fill from last bill</string>
|
<string name="settings_fill_new_bill_from_last">Auto-fill from last bill</string>
|
||||||
<string name="settings_fill_new_bill_from_last_summary">Pre-fill payer, category, mode and owers from the last bill created in the project.</string>
|
<string name="settings_fill_new_bill_from_last_summary">Pre-fill payer, category, mode and owers from the last bill created in the project.</string>
|
||||||
<string name="settings_auto_sync_on_open">Sync interval</string>
|
|
||||||
<string name="settings_auto_sync_on_open_summary">How often to refresh the account and all projects when opening the app.</string>
|
|
||||||
<string name="pref_value_sync_1m">1 minute</string>
|
|
||||||
<string name="pref_value_sync_10m">10 minutes</string>
|
|
||||||
<string name="pref_value_sync_1h">1 hour</string>
|
<string name="pref_value_sync_1h">1 hour</string>
|
||||||
<string name="pref_value_sync_1d">1 day</string>
|
<string name="pref_value_sync_1d">1 day</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>
|
||||||
|
|||||||
@@ -137,14 +137,8 @@
|
|||||||
<string name="settings_color_custom">Color personalizado</string>
|
<string name="settings_color_custom">Color personalizado</string>
|
||||||
<string name="settings_color_mode">Selección de color</string>
|
<string name="settings_color_mode">Selección de color</string>
|
||||||
<string name="settings_show_archived">Mostrar los proyectos archivados</string>
|
<string name="settings_show_archived">Mostrar los proyectos archivados</string>
|
||||||
<string name="settings_beta_features">Funciones beta</string>
|
|
||||||
<string name="settings_beta_features_summary">Activar las funciones experimentales. Úsalas bajo tu propia responsabilidad.</string>
|
|
||||||
<string name="settings_fill_new_bill_from_last">Rellenar desde la última factura</string>
|
<string name="settings_fill_new_bill_from_last">Rellenar desde la última factura</string>
|
||||||
<string name="settings_fill_new_bill_from_last_summary">Reutilizar el pagador, la categoría, el modo y los participantes de la última factura creada en el proyecto.</string>
|
<string name="settings_fill_new_bill_from_last_summary">Reutilizar el pagador, la categoría, el modo y los participantes de la última factura creada en el proyecto.</string>
|
||||||
<string name="settings_auto_sync_on_open">Intervalo de sincronización</string>
|
|
||||||
<string name="settings_auto_sync_on_open_summary">Con qué frecuencia se actualizan la cuenta y todos los proyectos al abrir la aplicación.</string>
|
|
||||||
<string name="pref_value_sync_1m">1 minuto</string>
|
|
||||||
<string name="pref_value_sync_10m">10 minutos</string>
|
|
||||||
<string name="pref_value_sync_1h">1 hora</string>
|
<string name="pref_value_sync_1h">1 hora</string>
|
||||||
<string name="pref_value_sync_1d">1 día</string>
|
<string name="pref_value_sync_1d">1 día</string>
|
||||||
<string name="settings_url_warn_http">ADVERTENCIA: \"http\" no es seguro. Usa \"https\".</string>
|
<string name="settings_url_warn_http">ADVERTENCIA: \"http\" no es seguro. Usa \"https\".</string>
|
||||||
|
|||||||
@@ -144,14 +144,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_fill_new_bill_from_last">Auto-fill from last bill</string>
|
<string name="settings_fill_new_bill_from_last">Auto-fill from last bill</string>
|
||||||
<string name="settings_fill_new_bill_from_last_summary">Pre-fill payer, category, mode and owers from the last bill created in the project.</string>
|
<string name="settings_fill_new_bill_from_last_summary">Pre-fill payer, category, mode and owers from the last bill created in the project.</string>
|
||||||
<string name="settings_auto_sync_on_open">Sync interval</string>
|
|
||||||
<string name="settings_auto_sync_on_open_summary">How often to refresh the account and all projects when opening the app.</string>
|
|
||||||
<string name="pref_value_sync_1m">1 minute</string>
|
|
||||||
<string name="pref_value_sync_10m">10 minutes</string>
|
|
||||||
<string name="pref_value_sync_1h">1 hour</string>
|
<string name="pref_value_sync_1h">1 hour</string>
|
||||||
<string name="pref_value_sync_1d">1 day</string>
|
<string name="pref_value_sync_1d">1 day</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>
|
||||||
|
|||||||
@@ -138,14 +138,8 @@
|
|||||||
<string name="settings_color_custom">Couleur personnalisée</string>
|
<string name="settings_color_custom">Couleur personnalisée</string>
|
||||||
<string name="settings_color_mode">Sélection de la couleur</string>
|
<string name="settings_color_mode">Sélection de la couleur</string>
|
||||||
<string name="settings_show_archived">Afficher les projets archivés</string>
|
<string name="settings_show_archived">Afficher les projets archivés</string>
|
||||||
<string name="settings_beta_features">Fonctionnalités bêta</string>
|
|
||||||
<string name="settings_beta_features_summary">Activer les fonctionnalités expérimentales. À utiliser à vos risques et périls.</string>
|
|
||||||
<string name="settings_fill_new_bill_from_last">Pré-remplir depuis la dernière facture</string>
|
<string name="settings_fill_new_bill_from_last">Pré-remplir depuis la dernière facture</string>
|
||||||
<string name="settings_fill_new_bill_from_last_summary">Reprendre le payeur, la catégorie, le mode et les participants de la dernière facture créée dans le projet.</string>
|
<string name="settings_fill_new_bill_from_last_summary">Reprendre le payeur, la catégorie, le mode et les participants de la dernière facture créée dans le projet.</string>
|
||||||
<string name="settings_auto_sync_on_open">Intervalle de synchronisation</string>
|
|
||||||
<string name="settings_auto_sync_on_open_summary">Fréquence de rafraîchissement du compte et de tous les projets à l\'ouverture de l\'application.</string>
|
|
||||||
<string name="pref_value_sync_1m">1 minute</string>
|
|
||||||
<string name="pref_value_sync_10m">10 minutes</string>
|
|
||||||
<string name="pref_value_sync_1h">1 heure</string>
|
<string name="pref_value_sync_1h">1 heure</string>
|
||||||
<string name="pref_value_sync_1d">1 jour</string>
|
<string name="pref_value_sync_1d">1 jour</string>
|
||||||
<string name="settings_url_warn_http">AVERTISSEMENT : \"http\" n\'est pas sûr. Utilisez \"https\".</string>
|
<string name="settings_url_warn_http">AVERTISSEMENT : \"http\" n\'est pas sûr. Utilisez \"https\".</string>
|
||||||
|
|||||||
@@ -144,14 +144,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_fill_new_bill_from_last">Auto-fill from last bill</string>
|
<string name="settings_fill_new_bill_from_last">Auto-fill from last bill</string>
|
||||||
<string name="settings_fill_new_bill_from_last_summary">Pre-fill payer, category, mode and owers from the last bill created in the project.</string>
|
<string name="settings_fill_new_bill_from_last_summary">Pre-fill payer, category, mode and owers from the last bill created in the project.</string>
|
||||||
<string name="settings_auto_sync_on_open">Sync interval</string>
|
|
||||||
<string name="settings_auto_sync_on_open_summary">How often to refresh the account and all projects when opening the app.</string>
|
|
||||||
<string name="pref_value_sync_1m">1 minute</string>
|
|
||||||
<string name="pref_value_sync_10m">10 minutes</string>
|
|
||||||
<string name="pref_value_sync_1h">1 hour</string>
|
<string name="pref_value_sync_1h">1 hour</string>
|
||||||
<string name="pref_value_sync_1d">1 day</string>
|
<string name="pref_value_sync_1d">1 day</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>
|
||||||
|
|||||||
@@ -144,14 +144,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_fill_new_bill_from_last">Auto-fill from last bill</string>
|
<string name="settings_fill_new_bill_from_last">Auto-fill from last bill</string>
|
||||||
<string name="settings_fill_new_bill_from_last_summary">Pre-fill payer, category, mode and owers from the last bill created in the project.</string>
|
<string name="settings_fill_new_bill_from_last_summary">Pre-fill payer, category, mode and owers from the last bill created in the project.</string>
|
||||||
<string name="settings_auto_sync_on_open">Sync interval</string>
|
|
||||||
<string name="settings_auto_sync_on_open_summary">How often to refresh the account and all projects when opening the app.</string>
|
|
||||||
<string name="pref_value_sync_1m">1 minute</string>
|
|
||||||
<string name="pref_value_sync_10m">10 minutes</string>
|
|
||||||
<string name="pref_value_sync_1h">1 hour</string>
|
<string name="pref_value_sync_1h">1 hour</string>
|
||||||
<string name="pref_value_sync_1d">1 day</string>
|
<string name="pref_value_sync_1d">1 day</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>
|
||||||
|
|||||||
@@ -144,14 +144,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_fill_new_bill_from_last">Auto-fill from last bill</string>
|
<string name="settings_fill_new_bill_from_last">Auto-fill from last bill</string>
|
||||||
<string name="settings_fill_new_bill_from_last_summary">Pre-fill payer, category, mode and owers from the last bill created in the project.</string>
|
<string name="settings_fill_new_bill_from_last_summary">Pre-fill payer, category, mode and owers from the last bill created in the project.</string>
|
||||||
<string name="settings_auto_sync_on_open">Sync interval</string>
|
|
||||||
<string name="settings_auto_sync_on_open_summary">How often to refresh the account and all projects when opening the app.</string>
|
|
||||||
<string name="pref_value_sync_1m">1 minute</string>
|
|
||||||
<string name="pref_value_sync_10m">10 minutes</string>
|
|
||||||
<string name="pref_value_sync_1h">1 hour</string>
|
<string name="pref_value_sync_1h">1 hour</string>
|
||||||
<string name="pref_value_sync_1d">1 day</string>
|
<string name="pref_value_sync_1d">1 day</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>
|
||||||
|
|||||||
@@ -144,14 +144,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_fill_new_bill_from_last">Auto-fill from last bill</string>
|
<string name="settings_fill_new_bill_from_last">Auto-fill from last bill</string>
|
||||||
<string name="settings_fill_new_bill_from_last_summary">Pre-fill payer, category, mode and owers from the last bill created in the project.</string>
|
<string name="settings_fill_new_bill_from_last_summary">Pre-fill payer, category, mode and owers from the last bill created in the project.</string>
|
||||||
<string name="settings_auto_sync_on_open">Sync interval</string>
|
|
||||||
<string name="settings_auto_sync_on_open_summary">How often to refresh the account and all projects when opening the app.</string>
|
|
||||||
<string name="pref_value_sync_1m">1 minute</string>
|
|
||||||
<string name="pref_value_sync_10m">10 minutes</string>
|
|
||||||
<string name="pref_value_sync_1h">1 hour</string>
|
<string name="pref_value_sync_1h">1 hour</string>
|
||||||
<string name="pref_value_sync_1d">1 day</string>
|
<string name="pref_value_sync_1d">1 day</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>
|
||||||
|
|||||||
@@ -144,14 +144,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_fill_new_bill_from_last">Auto-fill from last bill</string>
|
<string name="settings_fill_new_bill_from_last">Auto-fill from last bill</string>
|
||||||
<string name="settings_fill_new_bill_from_last_summary">Pre-fill payer, category, mode and owers from the last bill created in the project.</string>
|
<string name="settings_fill_new_bill_from_last_summary">Pre-fill payer, category, mode and owers from the last bill created in the project.</string>
|
||||||
<string name="settings_auto_sync_on_open">Sync interval</string>
|
|
||||||
<string name="settings_auto_sync_on_open_summary">How often to refresh the account and all projects when opening the app.</string>
|
|
||||||
<string name="pref_value_sync_1m">1 minute</string>
|
|
||||||
<string name="pref_value_sync_10m">10 minutes</string>
|
|
||||||
<string name="pref_value_sync_1h">1 hour</string>
|
<string name="pref_value_sync_1h">1 hour</string>
|
||||||
<string name="pref_value_sync_1d">1 day</string>
|
<string name="pref_value_sync_1d">1 day</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>
|
||||||
|
|||||||
@@ -144,14 +144,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_fill_new_bill_from_last">Auto-fill from last bill</string>
|
<string name="settings_fill_new_bill_from_last">Auto-fill from last bill</string>
|
||||||
<string name="settings_fill_new_bill_from_last_summary">Pre-fill payer, category, mode and owers from the last bill created in the project.</string>
|
<string name="settings_fill_new_bill_from_last_summary">Pre-fill payer, category, mode and owers from the last bill created in the project.</string>
|
||||||
<string name="settings_auto_sync_on_open">Sync interval</string>
|
|
||||||
<string name="settings_auto_sync_on_open_summary">How often to refresh the account and all projects when opening the app.</string>
|
|
||||||
<string name="pref_value_sync_1m">1 minute</string>
|
|
||||||
<string name="pref_value_sync_10m">10 minutes</string>
|
|
||||||
<string name="pref_value_sync_1h">1 hour</string>
|
<string name="pref_value_sync_1h">1 hour</string>
|
||||||
<string name="pref_value_sync_1d">1 day</string>
|
<string name="pref_value_sync_1d">1 day</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>
|
||||||
|
|||||||
@@ -144,14 +144,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_fill_new_bill_from_last">Auto-fill from last bill</string>
|
<string name="settings_fill_new_bill_from_last">Auto-fill from last bill</string>
|
||||||
<string name="settings_fill_new_bill_from_last_summary">Pre-fill payer, category, mode and owers from the last bill created in the project.</string>
|
<string name="settings_fill_new_bill_from_last_summary">Pre-fill payer, category, mode and owers from the last bill created in the project.</string>
|
||||||
<string name="settings_auto_sync_on_open">Sync interval</string>
|
|
||||||
<string name="settings_auto_sync_on_open_summary">How often to refresh the account and all projects when opening the app.</string>
|
|
||||||
<string name="pref_value_sync_1m">1 minute</string>
|
|
||||||
<string name="pref_value_sync_10m">10 minutes</string>
|
|
||||||
<string name="pref_value_sync_1h">1 hour</string>
|
<string name="pref_value_sync_1h">1 hour</string>
|
||||||
<string name="pref_value_sync_1d">1 day</string>
|
<string name="pref_value_sync_1d">1 day</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>
|
||||||
|
|||||||
@@ -144,14 +144,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_fill_new_bill_from_last">Auto-fill from last bill</string>
|
<string name="settings_fill_new_bill_from_last">Auto-fill from last bill</string>
|
||||||
<string name="settings_fill_new_bill_from_last_summary">Pre-fill payer, category, mode and owers from the last bill created in the project.</string>
|
<string name="settings_fill_new_bill_from_last_summary">Pre-fill payer, category, mode and owers from the last bill created in the project.</string>
|
||||||
<string name="settings_auto_sync_on_open">Sync interval</string>
|
|
||||||
<string name="settings_auto_sync_on_open_summary">How often to refresh the account and all projects when opening the app.</string>
|
|
||||||
<string name="pref_value_sync_1m">1 minute</string>
|
|
||||||
<string name="pref_value_sync_10m">10 minutes</string>
|
|
||||||
<string name="pref_value_sync_1h">1 hour</string>
|
<string name="pref_value_sync_1h">1 hour</string>
|
||||||
<string name="pref_value_sync_1d">1 day</string>
|
<string name="pref_value_sync_1d">1 day</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>
|
||||||
|
|||||||
@@ -144,14 +144,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_fill_new_bill_from_last">Auto-fill from last bill</string>
|
<string name="settings_fill_new_bill_from_last">Auto-fill from last bill</string>
|
||||||
<string name="settings_fill_new_bill_from_last_summary">Pre-fill payer, category, mode and owers from the last bill created in the project.</string>
|
<string name="settings_fill_new_bill_from_last_summary">Pre-fill payer, category, mode and owers from the last bill created in the project.</string>
|
||||||
<string name="settings_auto_sync_on_open">Sync interval</string>
|
|
||||||
<string name="settings_auto_sync_on_open_summary">How often to refresh the account and all projects when opening the app.</string>
|
|
||||||
<string name="pref_value_sync_1m">1 minute</string>
|
|
||||||
<string name="pref_value_sync_10m">10 minutes</string>
|
|
||||||
<string name="pref_value_sync_1h">1 hour</string>
|
<string name="pref_value_sync_1h">1 hour</string>
|
||||||
<string name="pref_value_sync_1d">1 day</string>
|
<string name="pref_value_sync_1d">1 day</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>
|
||||||
|
|||||||
@@ -144,14 +144,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_fill_new_bill_from_last">Auto-fill from last bill</string>
|
<string name="settings_fill_new_bill_from_last">Auto-fill from last bill</string>
|
||||||
<string name="settings_fill_new_bill_from_last_summary">Pre-fill payer, category, mode and owers from the last bill created in the project.</string>
|
<string name="settings_fill_new_bill_from_last_summary">Pre-fill payer, category, mode and owers from the last bill created in the project.</string>
|
||||||
<string name="settings_auto_sync_on_open">Sync interval</string>
|
|
||||||
<string name="settings_auto_sync_on_open_summary">How often to refresh the account and all projects when opening the app.</string>
|
|
||||||
<string name="pref_value_sync_1m">1 minute</string>
|
|
||||||
<string name="pref_value_sync_10m">10 minutes</string>
|
|
||||||
<string name="pref_value_sync_1h">1 hour</string>
|
<string name="pref_value_sync_1h">1 hour</string>
|
||||||
<string name="pref_value_sync_1d">1 day</string>
|
<string name="pref_value_sync_1d">1 day</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>
|
||||||
|
|||||||
@@ -144,14 +144,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_fill_new_bill_from_last">Auto-fill from last bill</string>
|
<string name="settings_fill_new_bill_from_last">Auto-fill from last bill</string>
|
||||||
<string name="settings_fill_new_bill_from_last_summary">Pre-fill payer, category, mode and owers from the last bill created in the project.</string>
|
<string name="settings_fill_new_bill_from_last_summary">Pre-fill payer, category, mode and owers from the last bill created in the project.</string>
|
||||||
<string name="settings_auto_sync_on_open">Sync interval</string>
|
|
||||||
<string name="settings_auto_sync_on_open_summary">How often to refresh the account and all projects when opening the app.</string>
|
|
||||||
<string name="pref_value_sync_1m">1 minute</string>
|
|
||||||
<string name="pref_value_sync_10m">10 minutes</string>
|
|
||||||
<string name="pref_value_sync_1h">1 hour</string>
|
<string name="pref_value_sync_1h">1 hour</string>
|
||||||
<string name="pref_value_sync_1d">1 day</string>
|
<string name="pref_value_sync_1d">1 day</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>
|
||||||
|
|||||||
@@ -144,14 +144,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_fill_new_bill_from_last">Auto-fill from last bill</string>
|
<string name="settings_fill_new_bill_from_last">Auto-fill from last bill</string>
|
||||||
<string name="settings_fill_new_bill_from_last_summary">Pre-fill payer, category, mode and owers from the last bill created in the project.</string>
|
<string name="settings_fill_new_bill_from_last_summary">Pre-fill payer, category, mode and owers from the last bill created in the project.</string>
|
||||||
<string name="settings_auto_sync_on_open">Sync interval</string>
|
|
||||||
<string name="settings_auto_sync_on_open_summary">How often to refresh the account and all projects when opening the app.</string>
|
|
||||||
<string name="pref_value_sync_1m">1 minute</string>
|
|
||||||
<string name="pref_value_sync_10m">10 minutes</string>
|
|
||||||
<string name="pref_value_sync_1h">1 hour</string>
|
<string name="pref_value_sync_1h">1 hour</string>
|
||||||
<string name="pref_value_sync_1d">1 day</string>
|
<string name="pref_value_sync_1d">1 day</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>
|
||||||
|
|||||||
@@ -144,14 +144,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_fill_new_bill_from_last">Auto-fill from last bill</string>
|
<string name="settings_fill_new_bill_from_last">Auto-fill from last bill</string>
|
||||||
<string name="settings_fill_new_bill_from_last_summary">Pre-fill payer, category, mode and owers from the last bill created in the project.</string>
|
<string name="settings_fill_new_bill_from_last_summary">Pre-fill payer, category, mode and owers from the last bill created in the project.</string>
|
||||||
<string name="settings_auto_sync_on_open">Sync interval</string>
|
|
||||||
<string name="settings_auto_sync_on_open_summary">How often to refresh the account and all projects when opening the app.</string>
|
|
||||||
<string name="pref_value_sync_1m">1 minute</string>
|
|
||||||
<string name="pref_value_sync_10m">10 minutes</string>
|
|
||||||
<string name="pref_value_sync_1h">1 hour</string>
|
<string name="pref_value_sync_1h">1 hour</string>
|
||||||
<string name="pref_value_sync_1d">1 day</string>
|
<string name="pref_value_sync_1d">1 day</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>
|
||||||
|
|||||||
@@ -144,14 +144,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_fill_new_bill_from_last">Auto-fill from last bill</string>
|
<string name="settings_fill_new_bill_from_last">Auto-fill from last bill</string>
|
||||||
<string name="settings_fill_new_bill_from_last_summary">Pre-fill payer, category, mode and owers from the last bill created in the project.</string>
|
<string name="settings_fill_new_bill_from_last_summary">Pre-fill payer, category, mode and owers from the last bill created in the project.</string>
|
||||||
<string name="settings_auto_sync_on_open">Sync interval</string>
|
|
||||||
<string name="settings_auto_sync_on_open_summary">How often to refresh the account and all projects when opening the app.</string>
|
|
||||||
<string name="pref_value_sync_1m">1 minute</string>
|
|
||||||
<string name="pref_value_sync_10m">10 minutes</string>
|
|
||||||
<string name="pref_value_sync_1h">1 hour</string>
|
<string name="pref_value_sync_1h">1 hour</string>
|
||||||
<string name="pref_value_sync_1d">1 day</string>
|
<string name="pref_value_sync_1d">1 day</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>
|
||||||
|
|||||||
@@ -144,14 +144,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_fill_new_bill_from_last">Auto-fill from last bill</string>
|
<string name="settings_fill_new_bill_from_last">Auto-fill from last bill</string>
|
||||||
<string name="settings_fill_new_bill_from_last_summary">Pre-fill payer, category, mode and owers from the last bill created in the project.</string>
|
<string name="settings_fill_new_bill_from_last_summary">Pre-fill payer, category, mode and owers from the last bill created in the project.</string>
|
||||||
<string name="settings_auto_sync_on_open">Sync interval</string>
|
|
||||||
<string name="settings_auto_sync_on_open_summary">How often to refresh the account and all projects when opening the app.</string>
|
|
||||||
<string name="pref_value_sync_1m">1 minute</string>
|
|
||||||
<string name="pref_value_sync_10m">10 minutes</string>
|
|
||||||
<string name="pref_value_sync_1h">1 hour</string>
|
<string name="pref_value_sync_1h">1 hour</string>
|
||||||
<string name="pref_value_sync_1d">1 day</string>
|
<string name="pref_value_sync_1d">1 day</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>
|
||||||
|
|||||||
@@ -144,14 +144,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_fill_new_bill_from_last">Auto-fill from last bill</string>
|
<string name="settings_fill_new_bill_from_last">Auto-fill from last bill</string>
|
||||||
<string name="settings_fill_new_bill_from_last_summary">Pre-fill payer, category, mode and owers from the last bill created in the project.</string>
|
<string name="settings_fill_new_bill_from_last_summary">Pre-fill payer, category, mode and owers from the last bill created in the project.</string>
|
||||||
<string name="settings_auto_sync_on_open">Sync interval</string>
|
|
||||||
<string name="settings_auto_sync_on_open_summary">How often to refresh the account and all projects when opening the app.</string>
|
|
||||||
<string name="pref_value_sync_1m">1 minute</string>
|
|
||||||
<string name="pref_value_sync_10m">10 minutes</string>
|
|
||||||
<string name="pref_value_sync_1h">1 hour</string>
|
<string name="pref_value_sync_1h">1 hour</string>
|
||||||
<string name="pref_value_sync_1d">1 day</string>
|
<string name="pref_value_sync_1d">1 day</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>
|
||||||
|
|||||||
@@ -144,14 +144,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_fill_new_bill_from_last">Auto-fill from last bill</string>
|
<string name="settings_fill_new_bill_from_last">Auto-fill from last bill</string>
|
||||||
<string name="settings_fill_new_bill_from_last_summary">Pre-fill payer, category, mode and owers from the last bill created in the project.</string>
|
<string name="settings_fill_new_bill_from_last_summary">Pre-fill payer, category, mode and owers from the last bill created in the project.</string>
|
||||||
<string name="settings_auto_sync_on_open">Sync interval</string>
|
|
||||||
<string name="settings_auto_sync_on_open_summary">How often to refresh the account and all projects when opening the app.</string>
|
|
||||||
<string name="pref_value_sync_1m">1 minute</string>
|
|
||||||
<string name="pref_value_sync_10m">10 minutes</string>
|
|
||||||
<string name="pref_value_sync_1h">1 hour</string>
|
<string name="pref_value_sync_1h">1 hour</string>
|
||||||
<string name="pref_value_sync_1d">1 day</string>
|
<string name="pref_value_sync_1d">1 day</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>
|
||||||
|
|||||||
@@ -144,14 +144,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_fill_new_bill_from_last">Auto-fill from last bill</string>
|
<string name="settings_fill_new_bill_from_last">Auto-fill from last bill</string>
|
||||||
<string name="settings_fill_new_bill_from_last_summary">Pre-fill payer, category, mode and owers from the last bill created in the project.</string>
|
<string name="settings_fill_new_bill_from_last_summary">Pre-fill payer, category, mode and owers from the last bill created in the project.</string>
|
||||||
<string name="settings_auto_sync_on_open">Sync interval</string>
|
|
||||||
<string name="settings_auto_sync_on_open_summary">How often to refresh the account and all projects when opening the app.</string>
|
|
||||||
<string name="pref_value_sync_1m">1 minute</string>
|
|
||||||
<string name="pref_value_sync_10m">10 minutes</string>
|
|
||||||
<string name="pref_value_sync_1h">1 hour</string>
|
<string name="pref_value_sync_1h">1 hour</string>
|
||||||
<string name="pref_value_sync_1d">1 day</string>
|
<string name="pref_value_sync_1d">1 day</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>
|
||||||
|
|||||||
@@ -144,14 +144,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_fill_new_bill_from_last">Auto-fill from last bill</string>
|
<string name="settings_fill_new_bill_from_last">Auto-fill from last bill</string>
|
||||||
<string name="settings_fill_new_bill_from_last_summary">Pre-fill payer, category, mode and owers from the last bill created in the project.</string>
|
<string name="settings_fill_new_bill_from_last_summary">Pre-fill payer, category, mode and owers from the last bill created in the project.</string>
|
||||||
<string name="settings_auto_sync_on_open">Sync interval</string>
|
|
||||||
<string name="settings_auto_sync_on_open_summary">How often to refresh the account and all projects when opening the app.</string>
|
|
||||||
<string name="pref_value_sync_1m">1 minute</string>
|
|
||||||
<string name="pref_value_sync_10m">10 minutes</string>
|
|
||||||
<string name="pref_value_sync_1h">1 hour</string>
|
<string name="pref_value_sync_1h">1 hour</string>
|
||||||
<string name="pref_value_sync_1d">1 day</string>
|
<string name="pref_value_sync_1d">1 day</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>
|
||||||
|
|||||||
@@ -139,17 +139,17 @@
|
|||||||
<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_extra_features">Extra Features</string>
|
||||||
<string name="settings_beta_features_summary">Enable experimental features. Use at your own risk.</string>
|
<string name="settings_extra_features_summary">Experimental and advanced settings. Use at your own risk.</string>
|
||||||
<string name="settings_fill_new_bill_from_last">Auto-fill from last bill</string>
|
<string name="settings_fill_new_bill_from_last">Auto-fill from last bill</string>
|
||||||
<string name="settings_fill_new_bill_from_last_summary">Pre-fill payer, category, mode and owers from the last bill created in the project.</string>
|
<string name="settings_fill_new_bill_from_last_summary">Pre-fill payer, category, mode and owers from the last bill created in the project.</string>
|
||||||
<string name="settings_stats_include_deactivated">Include deactivated members in stats</string>
|
<string name="settings_stats_include_deactivated">Include deactivated members in stats</string>
|
||||||
<string name="settings_auto_sync_on_open">Sync interval</string>
|
<string name="settings_full_sync_delay">Full sync delay</string>
|
||||||
<string name="settings_auto_sync_on_open_summary">How often to refresh the account and all projects when opening the app.</string>
|
<string name="settings_full_sync_delay_summary">How long a pull-to-refresh waits between two full syncs. In between, it only fetches what changed, which is much faster.</string>
|
||||||
<string name="pref_value_sync_1m">1 minute</string>
|
<string name="pref_value_sync_always">Always</string>
|
||||||
<string name="pref_value_sync_10m">10 minutes</string>
|
|
||||||
<string name="pref_value_sync_1h">1 hour</string>
|
<string name="pref_value_sync_1h">1 hour</string>
|
||||||
<string name="pref_value_sync_1d">1 day</string>
|
<string name="pref_value_sync_1d">1 day</string>
|
||||||
|
<string name="pref_value_sync_1w">1 week</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>
|
||||||
|
|
||||||
@@ -170,9 +170,9 @@
|
|||||||
<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_key_extra_features" translatable="false">betaFeatures</string>
|
||||||
<string name="pref_key_stats_include_deactivated" translatable="false">statsIncludeDeactivated</string>
|
<string name="pref_key_stats_include_deactivated" translatable="false">statsIncludeDeactivated</string>
|
||||||
<string name="pref_key_auto_sync_on_open" translatable="false">autoSyncOnOpen</string>
|
<string name="pref_key_full_sync_delay" translatable="false">fullSyncDelayMinutes</string>
|
||||||
<string name="pref_key_last_account_sync_timestamp" translatable="false">lastAccountSyncTimestamp</string>
|
<string name="pref_key_last_account_sync_timestamp" translatable="false">lastAccountSyncTimestamp</string>
|
||||||
<string name="pref_key_fill_new_bill_from_last" translatable="false">fillNewBillFromLast</string>
|
<string name="pref_key_fill_new_bill_from_last" translatable="false">fillNewBillFromLast</string>
|
||||||
<string name="pref_value_night_mode_no" translatable="false">1</string>
|
<string name="pref_value_night_mode_no" translatable="false">1</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user