From: Damyan Ivanov Date: Sun, 31 Mar 2024 11:13:26 +0000 (+0300) Subject: more pronounced day/month delimiters in the transaction list X-Git-Url: https://git.ktnx.net/?p=mobile-ledger.git;a=commitdiff_plain;h=HEAD;hp=dd7e3b83b17546504605f93b3a462d1a1be3b382 more pronounced day/month delimiters in the transaction list --- 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..71673f6e 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 @@ -24,8 +24,8 @@ android { minSdkVersion 22 targetSdkVersion 31 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 { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 47f9b1d4..b17a1ecf 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,12 @@ 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" 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..807c16da 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,7 @@ import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; +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 +57,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 +88,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); @@ -109,15 +115,40 @@ public class AccountSummaryFragment extends MobileLedgerListFragment { model.scheduleTransactionListRetrieval(); }); - Data.observeProfile(this, this::onProfileChanged); + Data.observeProfile(this, profile -> onProfileChanged(profile, Boolean.TRUE.equals( + model.getShowZeroBalanceAccounts() + .getValue()))); + } + @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); + }); + + 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 +165,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/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/res/menu/account_list.xml b/app/src/main/res/menu/account_list.xml new file mode 100644 index 00000000..5a761610 --- /dev/null +++ b/app/src/main/res/menu/account_list.xml @@ -0,0 +1,32 @@ + + + + + + \ 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/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