From: Damyan Ivanov Date: Sun, 9 Jun 2024 19:19:26 +0000 (+0300) Subject: bump androidx.constraintlayout library version X-Git-Url: https://git.ktnx.net/?a=commitdiff_plain;ds=inline;h=HEAD;hp=dd7e3b83b17546504605f93b3a462d1a1be3b382;p=mobile-ledger.git bump androidx.constraintlayout library version --- diff --git a/CHANGES.md b/CHANGES.md index c7dc45db..02f84284 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,39 @@ # Changes -## [0.21.2] = 2022-04-04 +## [0.21.7] = 2024-03-19 + +* FIXES: + + allow user certificates in network security config +* OTHERS: + + bump gradle version + +## [0.21.6] - 2023-06-20 + +* FIXES + + fix sending transations to hledger-web 1.23+ + +## [0.21.5] - 2022-09-03 + +* FIXES + + fix cloud backup + +## [0.21.4] - 2022-06-18 + +* FIXES + + fix compatibility wuth hledger-web 1.23+ when submitting new transactions. Thanks to Faye Duxovni for the patch! + + fix a crash when deleting templates + + fix a rare crash when submitting transactions with multiple accounts with no amounts with zero remaining balance + +## [0.21.3] - 2022-04-06 + +* FIXES + + sync gradle version requirements +* OTHERS + + bump version of several dependent libraries + + bump SDK version to 31 + + adjust deprecated constructor usage + +## [0.21.2] - 2022-04-04 * FIXES + fix crash when auto-balancing multi currency transaction diff --git a/TODO.txt b/TODO.txt new file mode 100644 index 00000000..e229e17d --- /dev/null +++ b/TODO.txt @@ -0,0 +1,7 @@ +* Easy way to add tag:value pairs to transactions and transaction accounts + +* Filter by tag:value pairs + +* Refresh button in account/transaction list + +* Top button in account/transaction list diff --git a/app/build.gradle b/app/build.gradle index 58dfbabf..2d2659e1 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,5 +1,5 @@ /* - * Copyright © 2022 Damyan Ivanov. + * Copyright © 2023 Damyan Ivanov. * This file is part of MoLe. * MoLe is free software: you can distribute it and/or modify it * under the term of the GNU General Public License as published by @@ -18,14 +18,14 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 31 + compileSdkVersion 33 defaultConfig { applicationId "net.ktnx.mobileledger" minSdkVersion 22 - targetSdkVersion 31 + targetSdkVersion 33 vectorDrawables.useSupportLibrary true - versionCode 51 - versionName '0.21.2' + versionCode 56 + versionName '0.21.7' testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" javaCompileOptions { annotationProcessorOptions { @@ -62,6 +62,7 @@ android { } buildFeatures.viewBinding = true buildToolsVersion '30.0.3' + namespace 'net.ktnx.mobileledger' } dependencies { @@ -74,17 +75,17 @@ dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') implementation 'androidx.legacy:legacy-support-v4:1.0.0' implementation 'com.google.android.material:material:1.5.0' - implementation 'androidx.constraintlayout:constraintlayout:2.1.3' + implementation 'androidx.constraintlayout:constraintlayout:2.1.4' implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' implementation 'androidx.recyclerview:recyclerview:1.2.1' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test:runner:1.5.0-alpha02' androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.0-alpha05' - implementation 'org.jetbrains:annotations:23.0.0' - implementation 'com.fasterxml.jackson.module:jackson-modules-java8:2.13.2' + implementation 'org.jetbrains:annotations:24.1.0' + implementation 'com.fasterxml.jackson.module:jackson-modules-java8:2.17.1' implementation "androidx.navigation:navigation-fragment:$nav_version" implementation "androidx.navigation:navigation-ui:$nav_version" - implementation 'androidx.appcompat:appcompat:1.6.0-alpha01' + implementation 'androidx.appcompat:appcompat:1.6.1' } allprojects { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 47f9b1d4..1674610e 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,5 +1,5 @@ + xmlns:tools="http://schemas.android.com/tools"> @@ -24,13 +23,13 @@ android:name=".App" android:allowBackup="true" android:appCategory="productivity" - android:fullBackupContent="@xml/backup_descriptor" android:icon="@drawable/app_icon" android:label="@string/app_name" android:networkSecurityConfig="@xml/network_security_config" android:roundIcon="@drawable/app_icon_round" android:supportsRtl="true" - android:backupAgent="net.ktnx.mobileledger.backup.MobileLedgerBackupAgent" + android:backupAgent=".backup.MobileLedgerBackupAgent" + android:enableOnBackInvokedCallback="true" tools:ignore="GoogleAppIndexingWarning"> { - taskCallback.onTransactionSaveDone(error, transaction); - }); + Misc.onMainThread(() -> taskCallback.onTransactionSaveDone(error, transaction)); } private void legacySendOkWithRetry() throws IOException { int tried = 0; diff --git a/app/src/main/java/net/ktnx/mobileledger/backup/MobileLedgerBackupAgent.java b/app/src/main/java/net/ktnx/mobileledger/backup/MobileLedgerBackupAgent.java index ea09e514..66538435 100644 --- a/app/src/main/java/net/ktnx/mobileledger/backup/MobileLedgerBackupAgent.java +++ b/app/src/main/java/net/ktnx/mobileledger/backup/MobileLedgerBackupAgent.java @@ -1,5 +1,5 @@ /* - * Copyright © 2021 Damyan Ivanov. + * Copyright © 2022 Damyan Ivanov. * This file is part of MoLe. * MoLe is free software: you can distribute it and/or modify it * under the term of the GNU General Public License as published by @@ -17,7 +17,7 @@ package net.ktnx.mobileledger.backup; -import android.app.backup.BackupAgentHelper; +import android.app.backup.BackupAgent; import android.app.backup.BackupDataInput; import android.app.backup.BackupDataOutput; import android.os.ParcelFileDescriptor; @@ -29,13 +29,13 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; -class MobileLedgerBackupAgent extends BackupAgentHelper { +public class MobileLedgerBackupAgent extends BackupAgent { private static final int READ_BUF_LEN = 10; public static String SETTINGS_KEY = "settings"; @Override public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, ParcelFileDescriptor newState) throws IOException { - super.onBackup(oldState, data, newState); + Logger.debug("backup", "onBackup()"); backupSettings(data); newState.close(); } diff --git a/app/src/main/java/net/ktnx/mobileledger/backup/RawConfigReader.java b/app/src/main/java/net/ktnx/mobileledger/backup/RawConfigReader.java index 45a593d7..95b27b32 100644 --- a/app/src/main/java/net/ktnx/mobileledger/backup/RawConfigReader.java +++ b/app/src/main/java/net/ktnx/mobileledger/backup/RawConfigReader.java @@ -20,6 +20,7 @@ package net.ktnx.mobileledger.backup; import android.util.JsonReader; import android.util.JsonToken; +import net.ktnx.mobileledger.App; import net.ktnx.mobileledger.backup.ConfigIO.Keys; import net.ktnx.mobileledger.dao.CurrencyDAO; import net.ktnx.mobileledger.dao.ProfileDAO; @@ -30,6 +31,8 @@ import net.ktnx.mobileledger.db.Profile; import net.ktnx.mobileledger.db.TemplateAccount; import net.ktnx.mobileledger.db.TemplateHeader; import net.ktnx.mobileledger.db.TemplateWithAccounts; +import net.ktnx.mobileledger.model.Data; +import net.ktnx.mobileledger.utils.Logger; import java.io.BufferedReader; import java.io.IOException; @@ -337,6 +340,7 @@ public class RawConfigReader { restoreCommodities(); restoreProfiles(); restoreTemplates(); + restoreCurrentProfile(); } private void restoreTemplates() { if (templates == null) @@ -374,4 +378,24 @@ public class RawConfigReader { dao.insert(c); } } + private void restoreCurrentProfile() { + if (currentProfile == null) { + Logger.debug("backup", "Not restoring current profile (not present in backup)"); + return; + } + + ProfileDAO dao = DB.get() + .getProfileDAO(); + + Profile p = dao.getByUuidSync(currentProfile); + + if (p != null) { + Logger.debug("backup", "Restoring current profile "+p.getName()); + Data.postCurrentProfile(p); + App.storeStartupProfileAndTheme(p.getId(), p.getTheme()); + } + else { + Logger.debug("backup", "Not restoring profile "+currentProfile+": not found in DB"); + } + } } diff --git a/app/src/main/java/net/ktnx/mobileledger/dao/AccountDAO.java b/app/src/main/java/net/ktnx/mobileledger/dao/AccountDAO.java index 0c581787..5ad4c90f 100644 --- a/app/src/main/java/net/ktnx/mobileledger/dao/AccountDAO.java +++ b/app/src/main/java/net/ktnx/mobileledger/dao/AccountDAO.java @@ -1,5 +1,5 @@ /* - * Copyright © 2021 Damyan Ivanov. + * Copyright © 2024 Damyan Ivanov. * This file is part of MoLe. * MoLe is free software: you can distribute it and/or modify it * under the term of the GNU General Public License as published by @@ -78,12 +78,19 @@ public abstract class AccountDAO extends BaseDAO { @Query("DELETE FROM accounts") public abstract void deleteAllSync(); - @Query("SELECT * FROM accounts WHERE profile_id=:profileId ORDER BY name") - public abstract LiveData> getAll(long profileId); + @Query("SELECT * FROM accounts WHERE profile_id=:profileId AND IIF(:includeZeroBalances=1, 1," + + " (EXISTS(SELECT 1 FROM account_values av WHERE av.account_id=accounts.id AND av.value" + + " <> 0) OR EXISTS(SELECT 1 FROM accounts a WHERE a.parent_name = accounts.name))) " + + "ORDER BY name") + public abstract LiveData> getAll(long profileId, boolean includeZeroBalances); @Transaction - @Query("SELECT * FROM accounts WHERE profile_id = :profileId ORDER BY name") - public abstract LiveData> getAllWithAmounts(long profileId); + @Query("SELECT * FROM accounts WHERE profile_id = :profileId AND IIF(:includeZeroBalances=1, " + + "1, (EXISTS(SELECT 1 FROM account_values av WHERE av.account_id=accounts.id AND av" + + ".value <> 0) OR EXISTS(SELECT 1 FROM accounts a WHERE a.parent_name = accounts.name))" + + ") ORDER BY name") + public abstract LiveData> getAllWithAmounts(long profileId, + boolean includeZeroBalances); @Query("SELECT * FROM accounts WHERE id=:id") public abstract Account getByIdSync(long id); diff --git a/app/src/main/java/net/ktnx/mobileledger/json/v1_23/ParsedLedgerTransaction.java b/app/src/main/java/net/ktnx/mobileledger/json/v1_23/ParsedLedgerTransaction.java index fe0a2809..59d17635 100644 --- a/app/src/main/java/net/ktnx/mobileledger/json/v1_23/ParsedLedgerTransaction.java +++ b/app/src/main/java/net/ktnx/mobileledger/json/v1_23/ParsedLedgerTransaction.java @@ -43,6 +43,12 @@ public class ParsedLedgerTransaction implements net.ktnx.mobileledger.json.Parse private List> ttags = new ArrayList<>(); private List tsourcepos = new ArrayList<>(); public ParsedLedgerTransaction() { + ParsedSourcePos startPos = new ParsedSourcePos(); + ParsedSourcePos endPos = new ParsedSourcePos(); + endPos.setSourceLine(2); + + tsourcepos.add(startPos); + tsourcepos.add(endPos); } public static ParsedLedgerTransaction fromLedgerTransaction(LedgerTransaction tr) { ParsedLedgerTransaction result = new ParsedLedgerTransaction(); diff --git a/app/src/main/java/net/ktnx/mobileledger/json/v1_23/ParsedPosting.java b/app/src/main/java/net/ktnx/mobileledger/json/v1_23/ParsedPosting.java index 8f0cdbcd..e60bd193 100644 --- a/app/src/main/java/net/ktnx/mobileledger/json/v1_23/ParsedPosting.java +++ b/app/src/main/java/net/ktnx/mobileledger/json/v1_23/ParsedPosting.java @@ -59,7 +59,7 @@ public class ParsedPosting extends net.ktnx.mobileledger.json.ParsedPosting { ParsedStyle style = new ParsedStyle(); style.setAscommodityside(getCommoditySide()); style.setAscommodityspaced(getCommoditySpaced()); - style.setAsprecision(new ParsedPrecision(2)); + style.setAsprecision(2); style.setAsdecimalpoint('.'); amt.setAstyle(style); if (acc.getCurrency() != null) diff --git a/app/src/main/java/net/ktnx/mobileledger/json/v1_23/ParsedPrecision.java b/app/src/main/java/net/ktnx/mobileledger/json/v1_23/ParsedPrecision.java deleted file mode 100644 index ffaac7db..00000000 --- a/app/src/main/java/net/ktnx/mobileledger/json/v1_23/ParsedPrecision.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright © 2020 Damyan Ivanov. - * This file is part of MoLe. - * MoLe is free software: you can distribute it and/or modify it - * under the term of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your opinion), any later version. - * - * MoLe is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License terms for details. - * - * You should have received a copy of the GNU General Public License - * along with MoLe. If not, see . - */ - -package net.ktnx.mobileledger.json.v1_23; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; - -@JsonIgnoreProperties(ignoreUnknown = true) -class ParsedPrecision { - private int contents; - private String tag; - ParsedPrecision() { - tag = "NaturalPrecision"; - } - ParsedPrecision(int contents) { - this.contents = contents; - tag = "Precision"; - } - public int getContents() { - return contents; - } - public void setContents(int contents) { - this.contents = contents; - } - public String getTag() { - return tag; - } - public void setTag(String tag) { - this.tag = tag; - } -} diff --git a/app/src/main/java/net/ktnx/mobileledger/json/v1_23/ParsedSourcePos.java b/app/src/main/java/net/ktnx/mobileledger/json/v1_23/ParsedSourcePos.java index 6a62f8dd..b3ea5db6 100644 --- a/app/src/main/java/net/ktnx/mobileledger/json/v1_23/ParsedSourcePos.java +++ b/app/src/main/java/net/ktnx/mobileledger/json/v1_23/ParsedSourcePos.java @@ -18,9 +18,9 @@ package net.ktnx.mobileledger.json.v1_23; class ParsedSourcePos { - private String sourceName; - private int sourceLine; - private int sourceColumn; + private String sourceName = ""; + private int sourceLine = 1; + private int sourceColumn = 1; public ParsedSourcePos() { } public String getSourceName() { diff --git a/app/src/main/java/net/ktnx/mobileledger/json/v1_23/ParsedStyle.java b/app/src/main/java/net/ktnx/mobileledger/json/v1_23/ParsedStyle.java index c9bf8bef..d3a0a133 100644 --- a/app/src/main/java/net/ktnx/mobileledger/json/v1_23/ParsedStyle.java +++ b/app/src/main/java/net/ktnx/mobileledger/json/v1_23/ParsedStyle.java @@ -21,13 +21,13 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @JsonIgnoreProperties(ignoreUnknown = true) public class ParsedStyle extends net.ktnx.mobileledger.json.ParsedStyle { - private ParsedPrecision asprecision; + private int asprecision; public ParsedStyle() { } - public ParsedPrecision getAsprecision() { + public int getAsprecision() { return asprecision; } - public void setAsprecision(ParsedPrecision asprecision) { + public void setAsprecision(int asprecision) { this.asprecision = asprecision; } } diff --git a/app/src/main/java/net/ktnx/mobileledger/model/AccountListItem.java b/app/src/main/java/net/ktnx/mobileledger/model/AccountListItem.java index dfd3d982..807e93d3 100644 --- a/app/src/main/java/net/ktnx/mobileledger/model/AccountListItem.java +++ b/app/src/main/java/net/ktnx/mobileledger/model/AccountListItem.java @@ -1,5 +1,5 @@ /* - * Copyright © 2021 Damyan Ivanov. + * Copyright © 2024 Damyan Ivanov. * This file is part of MoLe. * MoLe is free software: you can distribute it and/or modify it * under the term of the GNU General Public License as published by @@ -34,12 +34,19 @@ public abstract class AccountListItem { else throw new RuntimeException("Unsupported sub-class " + this); } - @NotNull - public LedgerAccount getAccount() { - if (this instanceof Account) - return ((Account) this).account; - - throw new IllegalStateException(String.format("Item type is not Account, but %s", this)); + public boolean isAccount() { + return this instanceof Account; + } + public Account toAccount() { + assert isAccount(); + return ((Account) this); + } + public boolean isHeader() { + return this instanceof Header; + } + public Header toHeader() { + assert isHeader(); + return ((Header) this); } public enum Type {ACCOUNT, HEADER} @@ -59,6 +66,13 @@ public abstract class AccountListItem { ((Account) other).account.getAmountsString() .equals(account.getAmountsString()); } + @NotNull + public LedgerAccount getAccount() { + return account; + } + public boolean allAmountsAreZero() { + return account.allAmountsAreZero(); + } } public static class Header extends AccountListItem { diff --git a/app/src/main/java/net/ktnx/mobileledger/model/LedgerAccount.java b/app/src/main/java/net/ktnx/mobileledger/model/LedgerAccount.java index 1be684cf..c2e62772 100644 --- a/app/src/main/java/net/ktnx/mobileledger/model/LedgerAccount.java +++ b/app/src/main/java/net/ktnx/mobileledger/model/LedgerAccount.java @@ -1,5 +1,5 @@ /* - * Copyright © 2021 Damyan Ivanov. + * Copyright © 2024 Damyan Ivanov. * This file is part of MoLe. * MoLe is free software: you can distribute it and/or modify it * under the term of the GNU General Public License as published by @@ -201,13 +201,21 @@ public class LedgerAccount { if (amounts != null) amounts.clear(); } - public boolean amountsExpanded() { return amountsExpanded; } - public void setAmountsExpanded(boolean flag) { amountsExpanded = flag; } - public void toggleAmountsExpanded() { amountsExpanded = !amountsExpanded; } + public boolean amountsExpanded() {return amountsExpanded;} + public void setAmountsExpanded(boolean flag) {amountsExpanded = flag;} + public void toggleAmountsExpanded() {amountsExpanded = !amountsExpanded;} public void propagateAmountsTo(LedgerAccount acc) { for (LedgerAmount a : amounts) a.propagateToAccount(acc); } + public boolean allAmountsAreZero() { + for (LedgerAmount a : amounts) { + if (a.getAmount() != 0) + return false; + } + + return true; + } public List getAmounts() { return amounts; } diff --git a/app/src/main/java/net/ktnx/mobileledger/ui/MainModel.java b/app/src/main/java/net/ktnx/mobileledger/ui/MainModel.java index 329109fd..a8b957dc 100644 --- a/app/src/main/java/net/ktnx/mobileledger/ui/MainModel.java +++ b/app/src/main/java/net/ktnx/mobileledger/ui/MainModel.java @@ -1,5 +1,5 @@ /* - * Copyright © 2021 Damyan Ivanov. + * Copyright © 2024 Damyan Ivanov. * This file is part of MoLe. * MoLe is free software: you can distribute it and/or modify it * under the term of the GNU General Public License as published by @@ -38,6 +38,7 @@ import java.util.Locale; public class MainModel extends ViewModel { public final MutableLiveData foundTransactionItemIndex = new MutableLiveData<>(null); private final MutableLiveData updatingFlag = new MutableLiveData<>(false); + private final MutableLiveData showZeroBalanceAccounts = new MutableLiveData<>(true); private final MutableLiveData accountFilter = new MutableLiveData<>(null); private final MutableLiveData> displayedTransactions = new MutableLiveData<>(new ArrayList<>()); @@ -45,7 +46,6 @@ public class MainModel extends ViewModel { private SimpleDate firstTransactionDate; private SimpleDate lastTransactionDate; transient private RetrieveTransactionsTask retrieveTransactionsTask; - transient private Thread displayedAccountsUpdater; private TransactionsDisplayedFilter displayedTransactionsUpdater; public LiveData getUpdatingFlag() { return updatingFlag; @@ -66,6 +66,7 @@ public class MainModel extends ViewModel { public void setFirstTransactionDate(SimpleDate earliestDate) { this.firstTransactionDate = earliestDate; } + public MutableLiveData getShowZeroBalanceAccounts() {return showZeroBalanceAccounts;} public MutableLiveData getAccountFilter() { return accountFilter; } @@ -81,6 +82,7 @@ public class MainModel extends ViewModel { return; } Profile profile = Data.getProfile(); + assert profile != null; retrieveTransactionsTask = new RetrieveTransactionsTask(profile); Logger.debug("db", "Created a background transaction retrieval task"); diff --git a/app/src/main/java/net/ktnx/mobileledger/ui/account_summary/AccountSummaryAdapter.java b/app/src/main/java/net/ktnx/mobileledger/ui/account_summary/AccountSummaryAdapter.java index d7da335d..dcc16f36 100644 --- a/app/src/main/java/net/ktnx/mobileledger/ui/account_summary/AccountSummaryAdapter.java +++ b/app/src/main/java/net/ktnx/mobileledger/ui/account_summary/AccountSummaryAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright © 2021 Damyan Ivanov. + * Copyright © 2024 Damyan Ivanov. * This file is part of MoLe. * MoLe is free software: you can distribute it and/or modify it * under the term of the GNU General Public License as published by @@ -17,6 +17,8 @@ package net.ktnx.mobileledger.ui.account_summary; +import static net.ktnx.mobileledger.utils.Logger.debug; + import android.content.res.Resources; import android.view.LayoutInflater; import android.view.View; @@ -48,8 +50,6 @@ import org.jetbrains.annotations.NotNull; import java.util.List; import java.util.Locale; -import static net.ktnx.mobileledger.utils.Logger.debug; - public class AccountSummaryAdapter extends RecyclerView.Adapter { public static final int AMOUNT_LIMIT = 3; private static final int ITEM_TYPE_HEADER = 1; @@ -66,8 +66,10 @@ public class AccountSummaryAdapter extends RecyclerView.Adapter payloads) { - LedgerAccount acc = item.getAccount(); + LedgerAccount acc = item.toAccount() + .getAccount(); Change changes = new Change(); if (payloads != null) { diff --git a/app/src/main/java/net/ktnx/mobileledger/ui/account_summary/AccountSummaryFragment.java b/app/src/main/java/net/ktnx/mobileledger/ui/account_summary/AccountSummaryFragment.java index 35a9edb7..02d685ce 100644 --- a/app/src/main/java/net/ktnx/mobileledger/ui/account_summary/AccountSummaryFragment.java +++ b/app/src/main/java/net/ktnx/mobileledger/ui/account_summary/AccountSummaryFragment.java @@ -1,5 +1,5 @@ /* - * Copyright © 2021 Damyan Ivanov. + * Copyright © 2024 Damyan Ivanov. * This file is part of MoLe. * MoLe is free software: you can distribute it and/or modify it * under the term of the GNU General Public License as published by @@ -17,9 +17,14 @@ package net.ktnx.mobileledger.ui.account_summary; +import static net.ktnx.mobileledger.utils.Logger.debug; + import android.content.Context; import android.os.Bundle; import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; @@ -31,6 +36,8 @@ import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; +import net.ktnx.mobileledger.App; +import net.ktnx.mobileledger.R; import net.ktnx.mobileledger.async.GeneralBackgroundTasks; import net.ktnx.mobileledger.databinding.AccountSummaryFragmentBinding; import net.ktnx.mobileledger.db.AccountWithAmounts; @@ -51,11 +58,11 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; -import static net.ktnx.mobileledger.utils.Logger.debug; - public class AccountSummaryFragment extends MobileLedgerListFragment { public AccountSummaryAdapter modelAdapter; private AccountSummaryFragmentBinding b; + private MenuItem menuShowZeroBalances; + private MainModel model; @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -82,7 +89,7 @@ public class AccountSummaryFragment extends MobileLedgerListFragment { debug("flow", "AccountSummaryFragment.onActivityCreated()"); super.onViewCreated(view, savedInstanceState); - MainModel model = new ViewModelProvider(requireActivity()).get(MainModel.class); + model = new ViewModelProvider(requireActivity()).get(MainModel.class); Data.backgroundTasksRunning.observe(this.getViewLifecycleOwner(), this::onBackgroundTaskRunningChanged); @@ -100,8 +107,7 @@ public class AccountSummaryFragment extends MobileLedgerListFragment { mainActivity.fabShouldShow(); - if (mainActivity instanceof FabManager.FabHandler) - FabManager.handle(mainActivity, b.accountRoot); + FabManager.handle(mainActivity, b.accountRoot); Colors.themeWatch.observe(getViewLifecycleOwner(), this::themeChanged); b.accountSwipeRefreshLayout.setOnRefreshListener(() -> { @@ -109,15 +115,43 @@ public class AccountSummaryFragment extends MobileLedgerListFragment { model.scheduleTransactionListRetrieval(); }); - Data.observeProfile(this, this::onProfileChanged); + Data.observeProfile(this, profile -> onProfileChanged(profile, Boolean.TRUE.equals( + model.getShowZeroBalanceAccounts() + .getValue()))); + model.getShowZeroBalanceAccounts() + .setValue(App.getShowZeroBalanceAccounts()); + } + @Override + public void onCreateOptionsMenu(@NotNull Menu menu, @NotNull MenuInflater inflater) { + inflater.inflate(R.menu.account_list, menu); + + menuShowZeroBalances = menu.findItem(R.id.menu_account_list_show_zero_balances); + if ((menuShowZeroBalances == null)) + throw new AssertionError(); + + menuShowZeroBalances.setOnMenuItemClickListener(menuItem -> { + model.getShowZeroBalanceAccounts() + .setValue(Boolean.FALSE.equals(model.getShowZeroBalanceAccounts() + .getValue())); + return true; + }); + + model.getShowZeroBalanceAccounts() + .observe(this, v -> { + menuShowZeroBalances.setChecked(v); + onProfileChanged(Data.getProfile(), v); + App.storeShowZeroBalanceAccounts(v); + }); + + super.onCreateOptionsMenu(menu, inflater); } - private void onProfileChanged(Profile profile) { + private void onProfileChanged(Profile profile, boolean showZeroBalanceAccounts) { if (profile == null) return; DB.get() .getAccountDAO() - .getAllWithAmounts(profile.getId()) + .getAllWithAmounts(profile.getId(), showZeroBalanceAccounts) .observe(getViewLifecycleOwner(), list -> GeneralBackgroundTasks.run(() -> { List adapterList = new ArrayList<>(); adapterList.add(new AccountListItem.Header(Data.lastAccountsUpdateText)); @@ -134,8 +168,57 @@ public class AccountSummaryFragment extends MobileLedgerListFragment { adapterList.add(new AccountListItem.Account(account)); accMap.put(dbAcc.account.getName(), account); } + + if (!showZeroBalanceAccounts) { + removeZeroAccounts(adapterList); + } modelAdapter.setAccounts(adapterList); Data.lastUpdateAccountCount.postValue(adapterList.size() - 1); })); } + private void removeZeroAccounts(List list) { + boolean removed = true; + + while (removed) { + AccountListItem last = null; + removed = false; + List newList = new ArrayList<>(); + + for (AccountListItem item : list) { + if (last == null) { + last = item; + continue; + } + + if (!last.isAccount() || !last.toAccount() + .allAmountsAreZero() || last.toAccount() + .getAccount() + .isParentOf( + item.toAccount() + .getAccount())) + { + newList.add(last); + } + else { + removed = true; + } + + last = item; + } + + if (last != null) { + if (!last.isAccount() || !last.toAccount() + .allAmountsAreZero()) + { + newList.add(last); + } + else { + removed = true; + } + } + + list.clear(); + list.addAll(newList); + } + } } diff --git a/app/src/main/java/net/ktnx/mobileledger/ui/new_transaction/NewTransactionModel.java b/app/src/main/java/net/ktnx/mobileledger/ui/new_transaction/NewTransactionModel.java index d8f387b7..51bcd306 100644 --- a/app/src/main/java/net/ktnx/mobileledger/ui/new_transaction/NewTransactionModel.java +++ b/app/src/main/java/net/ktnx/mobileledger/ui/new_transaction/NewTransactionModel.java @@ -482,27 +482,28 @@ public class NewTransactionModel extends ViewModel { if (emptyAmountAccounts.size() > 0) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { emptyAmountAccounts.forEach((currency, accounts) -> { - if (accounts.size() != 1) + final Float balance = emptyAmountAccountBalance.get(currency); + + if (balance != null && !Misc.isZero(balance) && accounts.size() != 1) { throw new RuntimeException(String.format(Locale.US, - "Should not happen: approved transaction has %d accounts for " + - "currency %s", accounts.size(), currency)); - accounts.get(0) - .setAmount(-Objects.requireNonNull( - emptyAmountAccountBalance.get(currency))); + "Should not happen: approved transaction has %d accounts " + + "without amounts for currency '%s'", accounts.size(), currency)); + } + accounts.forEach(acc -> acc.setAmount(balance == null ? 0 : -balance)); }); } else { for (String currency : emptyAmountAccounts.keySet()) { List accounts = Objects.requireNonNull(emptyAmountAccounts.get(currency)); - - if (accounts.size() != 1) + final Float balance = emptyAmountAccountBalance.get(currency); + if (balance != null && !Misc.isZero(balance) && accounts.size() != 1) throw new RuntimeException(String.format(Locale.US, "Should not happen: approved transaction has %d accounts for " + "currency %s", accounts.size(), currency)); - accounts.get(0) - .setAmount(-Objects.requireNonNull( - emptyAmountAccountBalance.get(currency))); + for (LedgerTransactionAccount acc : accounts) { + acc.setAmount(balance == null ? 0 : -balance); + } } } } diff --git a/app/src/main/java/net/ktnx/mobileledger/ui/templates/TemplateListDivider.java b/app/src/main/java/net/ktnx/mobileledger/ui/templates/TemplateListDivider.java index 9860e804..f841ad76 100644 --- a/app/src/main/java/net/ktnx/mobileledger/ui/templates/TemplateListDivider.java +++ b/app/src/main/java/net/ktnx/mobileledger/ui/templates/TemplateListDivider.java @@ -1,5 +1,5 @@ /* - * Copyright © 2021 Damyan Ivanov. + * Copyright © 2022 Damyan Ivanov. * This file is part of MoLe. * MoLe is free software: you can distribute it and/or modify it * under the term of the GNU General Public License as published by @@ -15,23 +15,6 @@ * along with MoLe. If not, see . */ -// -// Substantial portions taken from DividerItemDecoration subject to the following license terms: -// -// Copyright 2018 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// package net.ktnx.mobileledger.ui.templates; import android.content.Context; @@ -94,7 +77,8 @@ class TemplateListDivider extends DividerItemDecoration { for (int i = 0; i < childCount; i++) { final View child = parent.getChildAt(i); final int childAdapterPosition = parent.getChildAdapterPosition(child); - if (adapter.getItemViewType(childAdapterPosition) == + if (childAdapterPosition == RecyclerView.NO_POSITION || + adapter.getItemViewType(childAdapterPosition) == TemplatesRecyclerViewAdapter.ITEM_TYPE_DIVIDER || childAdapterPosition + 1 < itemCount && adapter.getItemViewType(childAdapterPosition + 1) == @@ -110,6 +94,10 @@ class TemplateListDivider extends DividerItemDecoration { } private void drawHorizontal(Canvas canvas, RecyclerView parent) { + final RecyclerView.LayoutManager layoutManager = parent.getLayoutManager(); + if (layoutManager == null) + return; + canvas.save(); final int top; final int bottom; @@ -133,14 +121,16 @@ class TemplateListDivider extends DividerItemDecoration { for (int i = 0; i < childCount; i++) { final View child = parent.getChildAt(i); final int childAdapterPosition = parent.getChildAdapterPosition(child); - if (adapter.getItemViewType(childAdapterPosition) == + if (childAdapterPosition == RecyclerView.NO_POSITION || + adapter.getItemViewType(childAdapterPosition) == TemplatesRecyclerViewAdapter.ITEM_TYPE_DIVIDER || childAdapterPosition + 1 < itemCount && adapter.getItemViewType(childAdapterPosition + 1) == TemplatesRecyclerViewAdapter.ITEM_TYPE_DIVIDER) + { continue; - parent.getLayoutManager() - .getDecoratedBoundsWithMargins(child, mBounds); + } + layoutManager.getDecoratedBoundsWithMargins(child, mBounds); final int right = mBounds.right + Math.round(child.getTranslationX()); final int left = right - divider.getIntrinsicWidth(); divider.setBounds(left, top, right, bottom); @@ -156,7 +146,8 @@ class TemplateListDivider extends DividerItemDecoration { (TemplatesRecyclerViewAdapter) Objects.requireNonNull(parent.getAdapter()); final int itemCount = adapter.getItemCount(); - if (adapter.getItemViewType(childAdapterPosition) == + if (childAdapterPosition == RecyclerView.NO_POSITION || + adapter.getItemViewType(childAdapterPosition) == TemplatesRecyclerViewAdapter.ITEM_TYPE_DIVIDER || childAdapterPosition + 1 < itemCount && adapter.getItemViewType(childAdapterPosition + 1) == diff --git a/app/src/main/java/net/ktnx/mobileledger/ui/transaction_list/TransactionListDelimiterRowHolder.java b/app/src/main/java/net/ktnx/mobileledger/ui/transaction_list/TransactionListDelimiterRowHolder.java index 390b4941..b5fe883d 100644 --- a/app/src/main/java/net/ktnx/mobileledger/ui/transaction_list/TransactionListDelimiterRowHolder.java +++ b/app/src/main/java/net/ktnx/mobileledger/ui/transaction_list/TransactionListDelimiterRowHolder.java @@ -1,5 +1,5 @@ /* - * Copyright © 2021 Damyan Ivanov. + * Copyright © 2024 Damyan Ivanov. * This file is part of MoLe. * MoLe is free software: you can distribute it and/or modify it * under the term of the GNU General Public License as published by @@ -19,9 +19,12 @@ package net.ktnx.mobileledger.ui.transaction_list; import android.view.View; +import androidx.constraintlayout.widget.ConstraintLayout; + import net.ktnx.mobileledger.App; import net.ktnx.mobileledger.databinding.TransactionDelimiterBinding; import net.ktnx.mobileledger.model.TransactionListItem; +import net.ktnx.mobileledger.utils.DimensionUtils; import net.ktnx.mobileledger.utils.Globals; import net.ktnx.mobileledger.utils.SimpleDate; @@ -46,15 +49,21 @@ class TransactionListDelimiterRowHolder extends TransactionRowHolderBase { b.transactionDelimiterMonth.setText( Globals.monthNames[cal.get(GregorianCalendar.MONTH)]); b.transactionDelimiterMonth.setVisibility(View.VISIBLE); - // holder.vDelimiterLine.setBackgroundResource(R.drawable - // .dashed_border_8dp); b.transactionDelimiterThick.setVisibility(View.VISIBLE); + ConstraintLayout.LayoutParams lp = + (ConstraintLayout.LayoutParams) b.transactionDelimiterThick.getLayoutParams(); + lp.height = DimensionUtils.dp2px(b.getRoot() + .getContext(), 4); + b.transactionDelimiterThick.setLayoutParams(lp); } else { b.transactionDelimiterMonth.setVisibility(View.GONE); - // holder.vDelimiterLine.setBackgroundResource(R.drawable - // .dashed_border_1dp); - b.transactionDelimiterThick.setVisibility(View.GONE); + ConstraintLayout.LayoutParams lp = + (ConstraintLayout.LayoutParams) b.transactionDelimiterThick.getLayoutParams(); + lp.height = DimensionUtils.dp2px(b.getRoot() + .getContext(), 1.3f); + b.transactionDelimiterThick.setLayoutParams(lp); + b.transactionDelimiterThick.setVisibility(View.VISIBLE); } } diff --git a/app/src/main/java/net/ktnx/mobileledger/ui/transaction_list/TransactionRowHolder.java b/app/src/main/java/net/ktnx/mobileledger/ui/transaction_list/TransactionRowHolder.java index 876430f5..e5161150 100644 --- a/app/src/main/java/net/ktnx/mobileledger/ui/transaction_list/TransactionRowHolder.java +++ b/app/src/main/java/net/ktnx/mobileledger/ui/transaction_list/TransactionRowHolder.java @@ -40,12 +40,9 @@ import net.ktnx.mobileledger.model.TransactionListItem; import net.ktnx.mobileledger.utils.Colors; import net.ktnx.mobileledger.utils.Misc; -import java.util.Observer; - class TransactionRowHolder extends TransactionRowHolderBase { private final TransactionListRowBinding b; TransactionListItem.Type lastType; - private Observer lastUpdateObserver; public TransactionRowHolder(@NonNull TransactionListRowBinding binding) { super(binding.getRoot()); b = binding; diff --git a/app/src/main/java/net/ktnx/mobileledger/utils/Colors.java b/app/src/main/java/net/ktnx/mobileledger/utils/Colors.java index 7cf710ac..348a5598 100644 --- a/app/src/main/java/net/ktnx/mobileledger/utils/Colors.java +++ b/app/src/main/java/net/ktnx/mobileledger/utils/Colors.java @@ -1,5 +1,5 @@ /* - * Copyright © 2021 Damyan Ivanov. + * Copyright © 2024 Damyan Ivanov. * This file is part of MoLe. * MoLe is free software: you can distribute it and/or modify it * under the term of the GNU General Public License as published by @@ -17,6 +17,8 @@ package net.ktnx.mobileledger.utils; +import static net.ktnx.mobileledger.utils.Logger.debug; + import android.app.Activity; import android.content.res.ColorStateList; import android.content.res.Resources; @@ -38,8 +40,6 @@ import java.util.List; import java.util.Locale; import java.util.Objects; -import static net.ktnx.mobileledger.utils.Logger.debug; - public class Colors { public static final int DEFAULT_HUE_DEG = 261; public static final MutableLiveData themeWatch = new MutableLiveData<>(0); @@ -76,7 +76,7 @@ public class Colors { TypedValue tv = new TypedValue(); theme.resolveAttribute(R.attr.table_row_dark_bg, tv, true); tableRowDarkBG = tv.data; - theme.resolveAttribute(R.attr.colorPrimary, tv, true); + theme.resolveAttribute(androidx.appcompat.R.attr.colorPrimary, tv, true); primary = tv.data; if (themePrimaryColor.size() == 0) { @@ -84,7 +84,7 @@ public class Colors { Resources.Theme tmpTheme = theme.getResources() .newTheme(); tmpTheme.applyStyle(themeId, true); - tmpTheme.resolveAttribute(R.attr.colorPrimary, tv, false); + tmpTheme.resolveAttribute(androidx.appcompat.R.attr.colorPrimary, tv, false); themePrimaryColor.put(themeId, tv.data); } } @@ -180,7 +180,7 @@ public class Colors { huesSB.append(", "); huesSB.append(h); } - debug("profiles", String.format("used hues: %s", huesSB.toString())); + debug("profiles", String.format("used hues: %s", huesSB)); } hues.add(hues.get(0)); diff --git a/app/src/main/java/net/ktnx/mobileledger/utils/ObservableAtomicInteger.java b/app/src/main/java/net/ktnx/mobileledger/utils/ObservableAtomicInteger.java deleted file mode 100644 index f804464c..00000000 --- a/app/src/main/java/net/ktnx/mobileledger/utils/ObservableAtomicInteger.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright © 2020 Damyan Ivanov. - * This file is part of MoLe. - * MoLe is free software: you can distribute it and/or modify it - * under the term of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your opinion), any later version. - * - * MoLe is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License terms for details. - * - * You should have received a copy of the GNU General Public License - * along with MoLe. If not, see . - */ - -package net.ktnx.mobileledger.utils; - -import android.os.Build; - -import androidx.annotation.RequiresApi; - -import java.util.Observable; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.IntBinaryOperator; -import java.util.function.IntUnaryOperator; - -public class ObservableAtomicInteger extends Observable { - private final AtomicInteger holder; - ObservableAtomicInteger() { - super(); - holder = new AtomicInteger(); - } - public ObservableAtomicInteger(int initialValue) { - this(); - holder.set(initialValue); - } - public int get() { - return holder.get(); - } - public void set(int newValue) { -// debug("atomic", "set"); - holder.set(newValue); - forceNotify(); - } - private void forceNotify() { - setChanged(); -// debug("atomic", String.format("notifying %d observers", countObservers())); - notifyObservers(); - } -// public void lazySet(int newValue) { -// holder.lazySet(newValue); -// forceNotify(); -// } - public int getAndSet(int newValue) { - int result = holder.getAndSet(newValue); - forceNotify(); - return result; - } - public boolean compareAndSet(int expect, int update) { - boolean result = holder.compareAndSet(expect, update); - if (result) forceNotify(); - return result; - } -// public boolean weakCompareAndSet(int expect, int update) { -// boolean result = holder.weakCompareAndSet(expect, update); -// if (result) forceNotify(); -// return result; -// } - public int getAndIncrement() { - int result = holder.getAndIncrement(); - forceNotify(); - return result; - } - public int getAndDecrement() { - int result = holder.getAndDecrement(); - forceNotify(); - return result; - } - public int getAndAdd(int delta) { - int result = holder.getAndAdd(delta); - forceNotify(); - return result; - } - public int incrementAndGet() { -// debug("atomic", "incrementAndGet"); - int result = holder.incrementAndGet(); - forceNotify(); - return result; - } - public int decrementAndGet() { -// debug("atomic", "decrementAndGet"); - int result = holder.decrementAndGet(); - forceNotify(); - return result; - } - public int addAndGet(int delta) { - int result = holder.addAndGet(delta); - forceNotify(); - return result; - } - @RequiresApi(Build.VERSION_CODES.N) - public int getAndUpdate(IntUnaryOperator updateFunction) { - int result = holder.getAndUpdate(updateFunction); - forceNotify(); - return result; - } - @RequiresApi(api = Build.VERSION_CODES.N) - public int updateAndGet(IntUnaryOperator updateFunction) { - int result = holder.updateAndGet(updateFunction); - forceNotify(); - return result; - } - @RequiresApi(api = Build.VERSION_CODES.N) - public int getAndAccumulate(int x, IntBinaryOperator accumulatorFunction) { - int result = holder.getAndAccumulate(x, accumulatorFunction); - forceNotify(); - return result; - } - @RequiresApi(api = Build.VERSION_CODES.N) - public int accumulateAndGet(int x, IntBinaryOperator accumulatorFunction) { - int result = holder.accumulateAndGet(x, accumulatorFunction); - forceNotify(); - return result; - } - public int intValue() { - return holder.intValue(); - } - public long longValue() { - return holder.longValue(); - } - public float floatValue() { - return holder.floatValue(); - } - public double doubleValue() { - return holder.doubleValue(); - } - public byte byteValue() { - return holder.byteValue(); - } - public short shortValue() { - return holder.shortValue(); - } -} diff --git a/app/src/main/java/net/ktnx/mobileledger/utils/ObservableList.java b/app/src/main/java/net/ktnx/mobileledger/utils/ObservableList.java deleted file mode 100644 index 4ca3e695..00000000 --- a/app/src/main/java/net/ktnx/mobileledger/utils/ObservableList.java +++ /dev/null @@ -1,325 +0,0 @@ -/* - * Copyright © 2020 Damyan Ivanov. - * This file is part of MoLe. - * MoLe is free software: you can distribute it and/or modify it - * under the term of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your opinion), any later version. - * - * MoLe is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License terms for details. - * - * You should have received a copy of the GNU General Public License - * along with MoLe. If not, see . - */ - -package net.ktnx.mobileledger.utils; - -import android.os.Build; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.RequiresApi; - -import org.jetbrains.annotations.NotNull; - -import java.util.Collection; -import java.util.Comparator; -import java.util.Iterator; -import java.util.List; -import java.util.ListIterator; -import java.util.Observable; -import java.util.Spliterator; -import java.util.concurrent.locks.ReentrantReadWriteLock; -import java.util.function.Consumer; -import java.util.function.Predicate; -import java.util.function.UnaryOperator; -import java.util.stream.Stream; - -import static net.ktnx.mobileledger.utils.Logger.debug; - -public class ObservableList extends Observable implements List { - private List list; - private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); - private int notificationBlocks = 0; - private boolean notificationWasBlocked = false; - public ObservableList(List list) { - this.list = list; - } - private void forceNotify() { - if (notificationBlocked()) return; - setChanged(); - notifyObservers(); - } - private boolean notificationBlocked() { - return notificationWasBlocked = (notificationBlocks > 0); - } - private void forceNotify(int index) { - if (notificationBlocked()) return; - setChanged(); - notifyObservers(index); - } - public int size() { - try (LockHolder lh = lockForReading()) { - return list.size(); - } - } - public boolean isEmpty() { - try (LockHolder lh = lockForReading()) { - return list.isEmpty(); - } - } - public boolean contains(@Nullable Object o) { - try (LockHolder lh = lockForReading()) { - return list.contains(o); - } - } - @NonNull - public Iterator iterator() { - throw new RuntimeException("Iterators break encapsulation and ignore locking"); -// return list.iterator(); - } - @NotNull - public Object[] toArray() { - try (LockHolder lh = lockForReading()) { - return list.toArray(); - } - } - @Override - @NotNull - public T1[] toArray(@Nullable T1[] a) { - try (LockHolder lh = lockForReading()) { - return list.toArray(a); - } - } - public boolean add(T t) { - try (LockHolder lh = lockForWriting()) { - boolean result = list.add(t); - lh.downgrade(); - if (result) - forceNotify(); - return result; - } - } - public boolean remove(@Nullable Object o) { - try (LockHolder lh = lockForWriting()) { - boolean result = list.remove(o); - lh.downgrade(); - if (result) forceNotify(); - return result; - } - } - public T removeQuietly(int index) { - return list.remove(index); - } - public boolean containsAll(@NonNull Collection c) { - try (LockHolder lh = lockForReading()) { - return list.containsAll(c); - } - } - public boolean addAll(@NonNull Collection c) { - try (LockHolder lh = lockForWriting()) { - boolean result = list.addAll(c); - lh.downgrade(); - if (result) forceNotify(); - return result; - } - } - public boolean addAll(int index, @NonNull Collection c) { - try (LockHolder lh = lockForWriting()) { - boolean result = list.addAll(index, c); - lh.downgrade(); - if (result) forceNotify(); - return result; - } - } - public boolean addAllQuietly(int index, Collection c) { - return list.addAll(index, c); - } - public boolean removeAll(@NonNull Collection c) { - try (LockHolder lh = lockForWriting()) { - boolean result = list.removeAll(c); - lh.downgrade(); - if (result) forceNotify(); - return result; - } - } - public boolean retainAll(@NonNull Collection c) { - try (LockHolder lh = lockForWriting()) { - boolean result = list.retainAll(c); - lh.downgrade(); - if (result) forceNotify(); - return result; - } - } - @RequiresApi(api = Build.VERSION_CODES.N) - public void replaceAll(@NonNull UnaryOperator operator) { - try (LockHolder lh = lockForWriting()) { - list.replaceAll(operator); - lh.downgrade(); - forceNotify(); - } - } - @RequiresApi(api = Build.VERSION_CODES.N) - public void sort(@Nullable Comparator c) { - try (LockHolder lh = lockForWriting()) { - lock.writeLock().lock(); - list.sort(c); - lh.downgrade(); - forceNotify(); - } - } - public void clear() { - try (LockHolder lh = lockForWriting()) { - boolean wasEmpty = list.isEmpty(); - list.clear(); - lh.downgrade(); - if (!wasEmpty) forceNotify(); - } - } - public T get(int index) { - try (LockHolder lh = lockForReading()) { - return list.get(index); - } - } - public T set(int index, T element) { - try (LockHolder lh = lockForWriting()) { - T result = list.set(index, element); - lh.downgrade(); - forceNotify(); - return result; - } - } - public void add(int index, T element) { - try (LockHolder lh = lockForWriting()) { - list.add(index, element); - lh.downgrade(); - forceNotify(); - } - } - public T remove(int index) { - try (LockHolder lh = lockForWriting()) { - T result = list.remove(index); - lh.downgrade(); - forceNotify(); - return result; - } - } - public int indexOf(@Nullable Object o) { - try (LockHolder lh = lockForReading()) { - return list.indexOf(o); - } - } - public int lastIndexOf(@Nullable Object o) { - try (LockHolder lh = lockForReading()) { - return list.lastIndexOf(o); - } - } - @NotNull - public ListIterator listIterator() { - if (!lock.isWriteLockedByCurrentThread()) throw new RuntimeException( - "Iterators break encapsulation and ignore locking. Write-lock first"); - return list.listIterator(); - } - @NotNull - public ListIterator listIterator(int index) { - if (!lock.isWriteLockedByCurrentThread()) throw new RuntimeException( - "Iterators break encapsulation and ignore locking. Write-lock first"); - return list.listIterator(index); - } - @NotNull - public List subList(int fromIndex, int toIndex) { - try (LockHolder lh = lockForReading()) { - return list.subList(fromIndex, toIndex); - } - } - @NotNull - @RequiresApi(api = Build.VERSION_CODES.N) - public Spliterator spliterator() { - if (!lock.isWriteLockedByCurrentThread()) - throw new RuntimeException( - "Iterators break encapsulation and ignore locking. Write-lock first"); - return list.spliterator(); - } - @RequiresApi(api = Build.VERSION_CODES.N) - public boolean removeIf(@NotNull Predicate filter) { - try (LockHolder lh = lockForWriting()) { - boolean result = list.removeIf(filter); - lh.downgrade(); - if (result) - forceNotify(); - return result; - } - } - @NotNull - @RequiresApi(api = Build.VERSION_CODES.N) - public Stream stream() { - if (!lock.isWriteLockedByCurrentThread()) throw new RuntimeException( - "Iterators break encapsulation and ignore locking. Write-lock first"); - return list.stream(); - } - @NotNull - @RequiresApi(api = Build.VERSION_CODES.N) - public Stream parallelStream() { - if (!lock.isWriteLockedByCurrentThread()) - throw new RuntimeException( - "Iterators break encapsulation and ignore locking. Write-lock first"); - return list.parallelStream(); - } - @RequiresApi(api = Build.VERSION_CODES.N) - public void forEach(@NotNull Consumer action) { - try (LockHolder lh = lockForReading()) { - list.forEach(action); - } - } - public List getList() { - if (!lock.isWriteLockedByCurrentThread()) - throw new RuntimeException( - "Direct list access breaks encapsulation and ignore locking. Write-lock first"); - return list; - } - public void setList(List aList) { - try (LockHolder lh = lockForWriting()) { - list = aList; - lh.downgrade(); - forceNotify(); - } - } - public void triggerItemChangedNotification(T item) { - try (LockHolder lh = lockForReading()) { - int index = list.indexOf(item); - if (index == -1) { - debug("ObList", "??? not sending notifications for item not found in the list"); - return; - } - debug("ObList", "Notifying item change observers"); - triggerItemChangedNotification(index); - } - } - public void triggerItemChangedNotification(int index) { - forceNotify(index); - } - public LockHolder lockForWriting() { - ReentrantReadWriteLock.WriteLock wLock = lock.writeLock(); - wLock.lock(); - - ReentrantReadWriteLock.ReadLock rLock = lock.readLock(); - rLock.lock(); - - return new LockHolder(rLock, wLock); - } - public LockHolder lockForReading() { - ReentrantReadWriteLock.ReadLock rLock = lock.readLock(); - rLock.lock(); - return new LockHolder(rLock); - } - public void blockNotifications() { - notificationBlocks++; - } - public void unblockNotifications() { - notificationBlocks--; - if ((notificationBlocks == 0) && notificationWasBlocked) notifyObservers(); - } -} \ No newline at end of file diff --git a/app/src/main/java/net/ktnx/mobileledger/utils/ObservableValue.java b/app/src/main/java/net/ktnx/mobileledger/utils/ObservableValue.java deleted file mode 100644 index 326be072..00000000 --- a/app/src/main/java/net/ktnx/mobileledger/utils/ObservableValue.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright © 2020 Damyan Ivanov. - * This file is part of MoLe. - * MoLe is free software: you can distribute it and/or modify it - * under the term of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your opinion), any later version. - * - * MoLe is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License terms for details. - * - * You should have received a copy of the GNU General Public License - * along with MoLe. If not, see . - */ - -package net.ktnx.mobileledger.utils; - -import java.util.Observable; -import java.util.Observer; - -public class ObservableValue { - private final ObservableValueImpl impl = new ObservableValueImpl<>(); - public ObservableValue() {} - public ObservableValue(T initialValue) { - impl.setValue(initialValue, false); - } - public void set(T newValue) { - impl.setValue(newValue); - } - public T get() { - return impl.getValue(); - } - public void addObserver(Observer o) { - impl.addObserver(o); - } - public void deleteObserver(Observer o) { - impl.deleteObserver(o); - } - public void notifyObservers() { - impl.notifyObservers(); - } - public void notifyObservers(T arg) { - impl.notifyObservers(arg); - } - public void deleteObservers() { - impl.deleteObservers(); - } - public boolean hasChanged() { - return impl.hasChanged(); - } - public int countObservers() { - return impl.countObservers(); - } - public void forceNotifyObservers() { - impl.setChanged(); - impl.notifyObservers(); - } - private static class ObservableValueImpl extends Observable { - protected T value; - public void setValue(T newValue) { - setValue(newValue, true); - } - protected void setChanged() { - super.setChanged(); - } - private synchronized void setValue(T newValue, boolean notify) { - if ((newValue == null) && (value == null)) - return; - - if ((newValue != null) && newValue.equals(value)) return; - - T oldValue = value; - value = newValue; - setChanged(); - if (notify) notifyObservers(oldValue); - } - public T getValue() { - return value; - } - } -} \ No newline at end of file diff --git a/app/src/main/res/drawable-anydpi-v26/app_icon.xml b/app/src/main/res/drawable-anydpi-v26/app_icon.xml index b82ac92d..cd0b7c71 100644 --- a/app/src/main/res/drawable-anydpi-v26/app_icon.xml +++ b/app/src/main/res/drawable-anydpi-v26/app_icon.xml @@ -19,4 +19,5 @@ + \ No newline at end of file diff --git a/app/src/main/res/drawable/launcher_foreground.xml b/app/src/main/res/drawable/launcher_foreground.xml index af159e2b..b78a67ed 100644 --- a/app/src/main/res/drawable/launcher_foreground.xml +++ b/app/src/main/res/drawable/launcher_foreground.xml @@ -1,5 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml index 4ba6fe38..ccb47516 100644 --- a/app/src/main/res/values-bg/strings.xml +++ b/app/src/main/res/values-bg/strings.xml @@ -1,6 +1,6 @@ - - - - diff --git a/app/src/main/res/xml/network_security_config.xml b/app/src/main/res/xml/network_security_config.xml index 223e12e8..c1da3227 100644 --- a/app/src/main/res/xml/network_security_config.xml +++ b/app/src/main/res/xml/network_security_config.xml @@ -1,5 +1,5 @@ - + + + + + + \ No newline at end of file diff --git a/art/app-icon-adaptive.svg b/art/app-icon-adaptive.svg new file mode 100644 index 00000000..b0f4b05e --- /dev/null +++ b/art/app-icon-adaptive.svg @@ -0,0 +1,191 @@ + + + MoLe app icon + + + + + + + + + + + image/svg+xml + + MoLe app icon + + + + Damyan Ivanov <dam+mole@ktnx.net> + + + + + Copyright © 2019 Damyan Ivanov <dam+mole@ktnx.net>. All rights reserved. + + + + + + + + + + + + + + + + + + + + + diff --git a/build.gradle b/build.gradle index acc8190a..e9d71576 100644 --- a/build.gradle +++ b/build.gradle @@ -24,7 +24,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:7.1.2' + classpath 'com.android.tools.build:gradle:8.0.2' // NOTE: Do not place your application dependencies here; they belong diff --git a/gradle.properties b/gradle.properties index 048e816e..db96c772 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ # -# Copyright © 2019 Damyan Ivanov. +# Copyright © 2024 Damyan Ivanov. # This file is part of MoLe. # MoLe is free software: you can distribute it and/or modify it # under the term of the GNU General Public License as published by @@ -15,19 +15,25 @@ # along with MoLe. If not, see . # -# Project-wide Gradle settings. -# IDE (e.g. Android Studio) users: -# Gradle settings configured through the IDE *will override* -# any settings specified in this file. -# For more details on how to configure your build environment visit +## For more details on how to configure your build environment visit # http://www.gradle.org/docs/current/userguide/build_environment.html +# # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. -org.gradle.jvmargs=-Xmx1536m +# Default value: -Xmx1024m -XX:MaxPermSize=256m +# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 +# # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true +#Sun Mar 17 11:29:00 EET 2024 android.debug.obsoleteApi=true +android.defaults.buildfeatures.buildconfig=true +android.enableJetifier=false +android.enableR8.fullMode=false +android.nonFinalResIds=false +android.nonTransitiveRClass=true android.useAndroidX=true -android.enableJetifier=true \ No newline at end of file +org.gradle.jvmargs=-Xmx1024M -Dkotlin.daemon.jvm.options\="-Xmx1536M" +org.gradle.unsafe.configuration-cache=true diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 08b1a0c0..855f89cc 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,21 @@ -#Sat Nov 28 08:24:27 EET 2020 +# +# Copyright © 2024 Damyan Ivanov. +# This file is part of MoLe. +# MoLe is free software: you can distribute it and/or modify it +# under the term of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your opinion), any later version. +# +# MoLe is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License terms for details. +# +# You should have received a copy of the GNU General Public License +# along with MoLe. If not, see . +# distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip diff --git a/metadata/bg-BG/changelogs/52.txt b/metadata/bg-BG/changelogs/52.txt new file mode 100644 index 00000000..9c8c8dbc --- /dev/null +++ b/metadata/bg-BG/changelogs/52.txt @@ -0,0 +1,5 @@ +* ПОПРАВКИ + + коригирани версии на gradle +* ДРУГИ + + обновени версии на множество библиотеки + + прицелване във версия 31 на платформата diff --git a/metadata/bg-BG/changelogs/53.txt b/metadata/bg-BG/changelogs/53.txt new file mode 100644 index 00000000..427dbb36 --- /dev/null +++ b/metadata/bg-BG/changelogs/53.txt @@ -0,0 +1,4 @@ +* ПОПРАВКИ + + отстранен проблем със съвместимостта с hledger-web 1.23+ при изпращане на нови транзакции. Благодарности на Faye Duxovni за поправката! + + поправен срив при изтриване на шаблони + + поправен рядък срив при изпращане на транзакции, съдържащи повече от една сметка без сума и нулев остатъчен баланс diff --git a/metadata/bg-BG/changelogs/54.txt b/metadata/bg-BG/changelogs/54.txt new file mode 100644 index 00000000..2e1dace0 --- /dev/null +++ b/metadata/bg-BG/changelogs/54.txt @@ -0,0 +1,2 @@ +* ПОПРАВКИ + + отстранен проблем с централизираното резервно копие на настройките diff --git a/metadata/bg-BG/changelogs/55.txt b/metadata/bg-BG/changelogs/55.txt new file mode 100644 index 00000000..208db922 --- /dev/null +++ b/metadata/bg-BG/changelogs/55.txt @@ -0,0 +1,2 @@ +* ПОПРАВКИ + + отстранен проблем при изпращане на транзакции към hledger-web 1.23+ diff --git a/metadata/bg-BG/changelogs/56.txt b/metadata/bg-BG/changelogs/56.txt new file mode 100644 index 00000000..218ba4a3 --- /dev/null +++ b/metadata/bg-BG/changelogs/56.txt @@ -0,0 +1,4 @@ +* ПОПРАВКИ + + Позволяване на потребителски сертификати в настройките за сигурността на мрежовите връзки +* ДРУГИ + + Обновена версия на gradle diff --git a/metadata/en-US/changelogs/52.txt b/metadata/en-US/changelogs/52.txt new file mode 100644 index 00000000..13f6c20c --- /dev/null +++ b/metadata/en-US/changelogs/52.txt @@ -0,0 +1,6 @@ +* FIXES + + sync gradle version requirements +* OTHERS + + bump version of several dependent libraries + + bump SDK version to 31 + + adjust deprecated constructor usage diff --git a/metadata/en-US/changelogs/53.txt b/metadata/en-US/changelogs/53.txt new file mode 100644 index 00000000..8e8601b5 --- /dev/null +++ b/metadata/en-US/changelogs/53.txt @@ -0,0 +1,4 @@ +* FIXES + + fix compatibility wuth hledger-web 1.23+ when submitting new transactions. Thanks to Faye Duxovni for the patch! + + fix a crash when deleting templates + + fix a rare crash when submitting transactions with multiple accounts with no amounts with zero remaining balance diff --git a/metadata/en-US/changelogs/54.txt b/metadata/en-US/changelogs/54.txt new file mode 100644 index 00000000..a2e3a20c --- /dev/null +++ b/metadata/en-US/changelogs/54.txt @@ -0,0 +1,2 @@ +* FIXES + + fix cloud backup diff --git a/metadata/en-US/changelogs/55.txt b/metadata/en-US/changelogs/55.txt new file mode 100644 index 00000000..3697ab50 --- /dev/null +++ b/metadata/en-US/changelogs/55.txt @@ -0,0 +1,2 @@ +* FIXES: + + fixed sending of transactions to hledger-web 1.23+ diff --git a/metadata/en-US/changelogs/56.txt b/metadata/en-US/changelogs/56.txt new file mode 100644 index 00000000..9b7d3ac4 --- /dev/null +++ b/metadata/en-US/changelogs/56.txt @@ -0,0 +1,4 @@ +* FIXES: + + allow user certificates in network security config +* OTHERS: + + bump gradle version