# Changes
+## [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
minSdkVersion 22
targetSdkVersion 31
vectorDrawables.useSupportLibrary true
- versionCode 55
- versionName '0.21.6'
+ versionCode 56
+ versionName '0.21.7'
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
javaCompileOptions {
annotationProcessorOptions {
/*
- * 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
@Query("DELETE FROM accounts")
public abstract void deleteAllSync();
- @Query("SELECT * FROM accounts WHERE profile_id=:profileId ORDER BY name")
- public abstract LiveData<List<Account>> 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<List<Account>> getAll(long profileId, boolean includeZeroBalances);
@Transaction
- @Query("SELECT * FROM accounts WHERE profile_id = :profileId ORDER BY name")
- public abstract LiveData<List<AccountWithAmounts>> 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<List<AccountWithAmounts>> getAllWithAmounts(long profileId,
+ boolean includeZeroBalances);
@Query("SELECT * FROM accounts WHERE id=:id")
public abstract Account getByIdSync(long id);
/*
- * 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
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}
((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 {
/*
- * 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
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<LedgerAmount> getAmounts() {
return amounts;
}
/*
- * 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
public class MainModel extends ViewModel {
public final MutableLiveData<Integer> foundTransactionItemIndex = new MutableLiveData<>(null);
private final MutableLiveData<Boolean> updatingFlag = new MutableLiveData<>(false);
+ private final MutableLiveData<Boolean> showZeroBalanceAccounts = new MutableLiveData<>(true);
private final MutableLiveData<String> accountFilter = new MutableLiveData<>(null);
private final MutableLiveData<List<TransactionListItem>> displayedTransactions =
new MutableLiveData<>(new ArrayList<>());
private SimpleDate firstTransactionDate;
private SimpleDate lastTransactionDate;
transient private RetrieveTransactionsTask retrieveTransactionsTask;
- transient private Thread displayedAccountsUpdater;
private TransactionsDisplayedFilter displayedTransactionsUpdater;
public LiveData<Boolean> getUpdatingFlag() {
return updatingFlag;
public void setFirstTransactionDate(SimpleDate earliestDate) {
this.firstTransactionDate = earliestDate;
}
+ public MutableLiveData<Boolean> getShowZeroBalanceAccounts() {return showZeroBalanceAccounts;}
public MutableLiveData<String> getAccountFilter() {
return accountFilter;
}
return;
}
Profile profile = Data.getProfile();
+ assert profile != null;
retrieveTransactionsTask = new RetrieveTransactionsTask(profile);
Logger.debug("db", "Created a background transaction retrieval task");
/*
- * 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
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;
import java.util.List;
import java.util.Locale;
-import static net.ktnx.mobileledger.utils.Logger.debug;
-
public class AccountSummaryAdapter extends RecyclerView.Adapter<AccountSummaryAdapter.RowHolder> {
public static final int AMOUNT_LIMIT = 3;
private static final int ITEM_TYPE_HEADER = 1;
@NonNull AccountListItem newItem) {
Change changes = new Change();
- final LedgerAccount oldAcc = oldItem.getAccount();
- final LedgerAccount newAcc = newItem.getAccount();
+ final LedgerAccount oldAcc = oldItem.toAccount()
+ .getAccount();
+ final LedgerAccount newAcc = newItem.toAccount()
+ .getAccount();
if (!Misc.equalStrings(oldAcc.getName(), newAcc.getName()))
changes.add(Change.NAME);
if (oldType == AccountListItem.Type.HEADER)
return true;
- return oldItem.getAccount()
- .getId() == newItem.getAccount()
+ return oldItem.toAccount()
+ .getAccount()
+ .getId() == newItem.toAccount()
+ .getAccount()
.getId();
}
@Override
return 0;
return listDiffer.getCurrentList()
.get(position)
+ .toAccount()
.getAccount()
.getId();
}
private LedgerAccount getAccount() {
return listDiffer.getCurrentList()
.get(getBindingAdapterPosition())
+ .toAccount()
.getAccount();
}
private void toggleAmountsExpanded() {
}
@Override
public void bind(AccountListItem item, @Nullable List<Object> payloads) {
- LedgerAccount acc = item.getAccount();
+ LedgerAccount acc = item.toAccount()
+ .getAccount();
Change changes = new Change();
if (payloads != null) {
/*
- * 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
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;
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;
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);
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);
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<AccountListItem> adapterList = new ArrayList<>();
adapterList.add(new AccountListItem.Header(Data.lastAccountsUpdateText));
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<AccountListItem> list) {
+ boolean removed = true;
+
+ while (removed) {
+ AccountListItem last = null;
+ removed = false;
+ List<AccountListItem> 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);
+ }
+ }
}
/*
- * 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
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;
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);
}
}
/*
- * 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
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;
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<Integer> themeWatch = new MutableLiveData<>(0);
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) {
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);
}
}
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));
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ 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 <https://www.gnu.org/licenses/>.
+ -->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ >
+
+ <item
+ android:id="@+id/menu_account_list_show_zero_balances"
+ android:checkable="true"
+ android:enabled="true"
+ android:menuCategory="container"
+ android:title="@string/accounts_menu_show_zero"
+ android:titleCondensed="@string/accounts_menu_show_zero_condensed"
+ android:visible="true"
+ app:showAsAction="withText"
+ />
+</menu>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<!--
- ~ 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
<string name="no_profile_restore_hint">… а може и да възстановите настройките от резервно копие</string>
<string name="profile_not_available">Недостъпен профил</string>
<string name="api_1_23">Версия 1.23</string>
+ <string name="accounts_menu_show_zero">Сметки с нулев баланс</string>
+ <string name="accounts_menu_show_zero_condensed">Нулеви сметки</string>
</resources>
<!--
- ~ 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
<string name="no_profile_restore_hint">… or, you may restore from backup</string>
<string name="profile_not_available">Profile not available</string>
<string name="api_1_23">Version 1.23</string>
+ <string name="accounts_menu_show_zero">Show zero balances</string>
+ <string name="accounts_menu_show_zero_condensed">Zero balances</string>
</resources>
android.enableJetifier=false
android.enableR8.fullMode=false
android.nonFinalResIds=false
-android.nonTransitiveRClass=false
+android.nonTransitiveRClass=true
android.useAndroidX=true
org.gradle.jvmargs=-Xmx1024M -Dkotlin.daemon.jvm.options\="-Xmx1536M"
org.gradle.unsafe.configuration-cache=true
--- /dev/null
+* ПОПРАВКИ
+ + Позволяване на потребителски сертификати в настройките за сигурността на мрежовите връзки
+* ДРУГИ
+ + Обновена версия на gradle
--- /dev/null
+* FIXES:
+ + allow user certificates in network security config
+* OTHERS:
+ + bump gradle version