]> git.ktnx.net Git - mobile-ledger.git/commitdiff
add functional currency selector when entering new transactions
authorDamyan Ivanov <dam+mobileledger@ktnx.net>
Thu, 20 Feb 2020 17:58:23 +0000 (19:58 +0200)
committerDamyan Ivanov <dam+mobileledger@ktnx.net>
Thu, 20 Feb 2020 18:01:14 +0000 (20:01 +0200)
18 files changed:
app/src/main/java/net/ktnx/mobileledger/json/v1_14/ParsedPosting.java
app/src/main/java/net/ktnx/mobileledger/json/v1_15/ParsedPosting.java
app/src/main/java/net/ktnx/mobileledger/model/Currency.java [new file with mode: 0644]
app/src/main/java/net/ktnx/mobileledger/model/LedgerTransactionAccount.java
app/src/main/java/net/ktnx/mobileledger/model/MobileLedgerProfile.java
app/src/main/java/net/ktnx/mobileledger/ui/CurrencySelectorFragment.java [new file with mode: 0644]
app/src/main/java/net/ktnx/mobileledger/ui/CurrencySelectorModel.java [new file with mode: 0644]
app/src/main/java/net/ktnx/mobileledger/ui/CurrencySelectorRecyclerViewAdapter.java [new file with mode: 0644]
app/src/main/java/net/ktnx/mobileledger/ui/OnCurrencyLongClickListener.java [new file with mode: 0644]
app/src/main/java/net/ktnx/mobileledger/ui/OnCurrencySelectedListener.java [new file with mode: 0644]
app/src/main/java/net/ktnx/mobileledger/ui/activity/NewTransactionItemHolder.java
app/src/main/java/net/ktnx/mobileledger/ui/activity/NewTransactionModel.java
app/src/main/java/net/ktnx/mobileledger/utils/DimensionUtils.java
app/src/main/res/layout/fragment_currency_selector.xml [new file with mode: 0644]
app/src/main/res/layout/fragment_currency_selector_list.xml [new file with mode: 0644]
app/src/main/res/layout/new_transaction_row.xml
app/src/main/res/values-bg/strings.xml
app/src/main/res/values/strings.xml

index e099f703c36019593417b7ccccc8d5f183777e0b..472fee7c91864aabbcd58b947a4a12383e63eb33 100644 (file)
@@ -57,11 +57,13 @@ public class ParsedPosting extends net.ktnx.mobileledger.json.ParsedPosting {
         qty.setDecimalMantissa(Math.round(acc.getAmount() * 100));
         amt.setAquantity(qty);
         ParsedStyle style = new ParsedStyle();
-        style.setAscommodityside('L');
-        style.setAscommodityspaced(false);
+        style.setAscommodityside(getCommoditySide());
+        style.setAscommodityspaced(getCommoditySpaced());
         style.setAsprecision(2);
         style.setAsdecimalpoint('.');
         amt.setAstyle(style);
+        if (acc.getCurrency() != null)
+            amt.setAcommodity(acc.getCurrency());
         amounts.add(amt);
         result.setPamount(amounts);
         return result;
index 7e2a05732c070ad86032f4724e901fac1dfc5298..38777684bbd8e3aec545f407bb836d9945fce809 100644 (file)
@@ -57,11 +57,13 @@ public class ParsedPosting extends net.ktnx.mobileledger.json.ParsedPosting {
         qty.setDecimalMantissa(Math.round(acc.getAmount() * 100));
         amt.setAquantity(qty);
         ParsedStyle style = new ParsedStyle();
-        style.setAscommodityside('L');
-        style.setAscommodityspaced(false);
+        style.setAscommodityside(getCommoditySide());
+        style.setAscommodityspaced(getCommoditySpaced());
         style.setAsprecision(2);
         style.setAsdecimalpoint('.');
         amt.setAstyle(style);
+        if (acc.getCurrency() != null)
+            amt.setAcommodity(acc.getCurrency());
         amounts.add(amt);
         result.setPamount(amounts);
         return result;
diff --git a/app/src/main/java/net/ktnx/mobileledger/model/Currency.java b/app/src/main/java/net/ktnx/mobileledger/model/Currency.java
new file mode 100644 (file)
index 0000000..a4854c0
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * Copyright © 2019 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/>.
+ */
+
+package net.ktnx.mobileledger.model;
+
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.DiffUtil;
+
+import net.ktnx.mobileledger.App;
+
+public class Currency {
+    public static final DiffUtil.ItemCallback<Currency> DIFF_CALLBACK =
+            new DiffUtil.ItemCallback<Currency>() {
+                @Override
+                public boolean areItemsTheSame(@NonNull Currency oldItem,
+                                               @NonNull Currency newItem) {
+                    return oldItem.id == newItem.id;
+                }
+                @Override
+                public boolean areContentsTheSame(@NonNull Currency oldItem,
+                                                  @NonNull Currency newItem) {
+                    return oldItem.name.equals(newItem.name) &&
+                           oldItem.position.equals(newItem.position) &&
+                           (oldItem.hasGap == newItem.hasGap);
+                }
+            };
+    private int id;
+    private String name;
+    private Position position;
+    private boolean hasGap;
+    public Currency(int id, String name) {
+        this.id = id;
+        this.name = name;
+        position = Position.after;
+        hasGap = true;
+    }
+    public Currency(int id, String name, Position position, boolean hasGap) {
+        this.id = id;
+        this.name = name;
+        this.position = position;
+        this.hasGap = hasGap;
+    }
+    public Currency(MobileLedgerProfile profile, String name, Position position, boolean hasGap) {
+        SQLiteDatabase db = App.getDatabase();
+        int attempts = 0;
+        while (true) {
+            if (++attempts > 10)
+                throw new RuntimeException("Giving up getting next ID after 10 attempts");
+
+            try (Cursor c = db.rawQuery("select max(rowid) from currencies", null)) {
+                c.moveToNext();
+                int nextId = c.getInt(0) + 1;
+                db.execSQL("insert into currencies(id, name, position, has_gap) values(?, ?, ?, ?)",
+                        new Object[]{nextId, name, position.toString(), hasGap});
+
+                this.id = nextId;
+                break;
+            }
+        }
+
+        this.name = name;
+        this.position = position;
+        this.hasGap = hasGap;
+    }
+    public int getId() {
+        return id;
+    }
+    public String getName() {
+        return name;
+    }
+    public void setName(String name) {
+        this.name = name;
+    }
+    public Position getPosition() {
+        return position;
+    }
+    public void setPosition(Position position) {
+        this.position = position;
+    }
+    public boolean hasGap() {
+        return hasGap;
+    }
+    public void setHasGap(boolean hasGap) {
+        this.hasGap = hasGap;
+    }
+    public enum Position {
+        before(-1), after(1), unknown(0), none(-2);
+        private int value;
+        Position(int value) {
+            this.value = value;
+        }
+        static Position valueOf(int value) {
+            switch (value) {
+                case -1:
+                    return before;
+                case +1:
+                    return after;
+                case 0:
+                    return unknown;
+                case -2:
+                    return none;
+                default:
+                    throw new IllegalStateException(String.format("Unexpected value (%d)", value));
+            }
+        }
+    }
+}
index 59e1dab00fb90d69b8d28ec45f465d9574db74a4..ba80ac615f4991bdd8d237b0f07779510d76840d 100644 (file)
@@ -67,22 +67,22 @@ public class LedgerTransactionAccount {
 
         return amount;
     }
-
     public void setAmount(float account_amount) {
         this.amount = account_amount;
         this.amountSet = true;
     }
-
     public void resetAmount() {
         this.amountSet = false;
     }
-
     public boolean isAmountSet() {
         return amountSet;
     }
     public String getCurrency() {
         return currency;
     }
+    public void setCurrency(String currency) {
+        this.currency = currency;
+    }
     @NonNull
     public String toString() {
         if (!amountSet)
index 7dc4bbfcecc79fabf6e86a6883f9e1041993e442..831dc63f0a04a558b12a15be71bc79e9cac44b9c 100644 (file)
@@ -488,6 +488,23 @@ public final class MobileLedgerProfile {
             db.endTransaction();
         }
     }
+    public List<Currency> getCurrencies() {
+        SQLiteDatabase db = App.getDatabase();
+
+        ArrayList<Currency> result = new ArrayList<>();
+
+        try (Cursor c = db.rawQuery("SELECT c.id, c.name, c.position, c.has_gap FROM currencies c",
+                new String[]{}))
+        {
+            while (c.moveToNext()) {
+                Currency currency = new Currency(c.getInt(0), c.getString(1),
+                        Currency.Position.valueOf(c.getInt(2)), c.getInt(3) == 1);
+                result.add(currency);
+            }
+        }
+
+        return result;
+    }
     public enum FutureDates {
         None(0), OneWeek(7), TwoWeeks(14), OneMonth(30), TwoMonths(60), ThreeMonths(90),
         SixMonths(180), OneYear(365), All(-1);
diff --git a/app/src/main/java/net/ktnx/mobileledger/ui/CurrencySelectorFragment.java b/app/src/main/java/net/ktnx/mobileledger/ui/CurrencySelectorFragment.java
new file mode 100644 (file)
index 0000000..76a7d34
--- /dev/null
@@ -0,0 +1,208 @@
+/*
+ * Copyright © 2019 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/>.
+ */
+
+package net.ktnx.mobileledger.ui;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.RadioButton;
+import android.widget.RadioGroup;
+import android.widget.Switch;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatDialogFragment;
+import androidx.lifecycle.ViewModelProvider;
+import androidx.recyclerview.widget.GridLayoutManager;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import net.ktnx.mobileledger.App;
+import net.ktnx.mobileledger.R;
+import net.ktnx.mobileledger.model.Currency;
+import net.ktnx.mobileledger.model.Data;
+import net.ktnx.mobileledger.model.MobileLedgerProfile;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * A fragment representing a list of Items.
+ * <p/>
+ * Activities containing this fragment MUST implement the {@link OnCurrencySelectedListener}
+ * interface.
+ */
+public class CurrencySelectorFragment extends AppCompatDialogFragment
+        implements OnCurrencySelectedListener, OnCurrencyLongClickListener {
+
+    public static final int DEFAULT_COLUMN_COUNT = 2;
+    private static final String ARG_COLUMN_COUNT = "column-count";
+    private int mColumnCount = DEFAULT_COLUMN_COUNT;
+    private OnCurrencySelectedListener mListener;
+    private CurrencySelectorModel model;
+
+    /**
+     * Mandatory empty constructor for the fragment manager to instantiate the
+     * fragment (e.g. upon screen orientation changes).
+     */
+    public CurrencySelectorFragment() {
+    }
+    @SuppressWarnings("unused")
+    public static CurrencySelectorFragment newInstance() {
+        return newInstance(DEFAULT_COLUMN_COUNT);
+    }
+    public static CurrencySelectorFragment newInstance(int columnCount) {
+        CurrencySelectorFragment fragment = new CurrencySelectorFragment();
+        Bundle args = new Bundle();
+        args.putInt(ARG_COLUMN_COUNT, columnCount);
+        fragment.setArguments(args);
+        return fragment;
+    }
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        if (getArguments() != null) {
+            mColumnCount = getArguments().getInt(ARG_COLUMN_COUNT, DEFAULT_COLUMN_COUNT);
+        }
+    }
+    @NonNull
+    @Override
+    public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
+        Context context = Objects.requireNonNull(getContext());
+        Dialog csd = new Dialog(context);
+        csd.setContentView(R.layout.fragment_currency_selector_list);
+        csd.setTitle(R.string.choose_currency_label);
+
+        RecyclerView recyclerView = csd.findViewById(R.id.list);
+
+        if (mColumnCount <= 1) {
+            recyclerView.setLayoutManager(new LinearLayoutManager(context));
+        }
+        else {
+            recyclerView.setLayoutManager(new GridLayoutManager(context, mColumnCount));
+        }
+        model = new ViewModelProvider(this).get(CurrencySelectorModel.class);
+        MobileLedgerProfile profile = Objects.requireNonNull(Data.profile.getValue());
+
+        model.currencies.setValue(new CopyOnWriteArrayList<>(profile.getCurrencies()));
+        CurrencySelectorRecyclerViewAdapter adapter = new CurrencySelectorRecyclerViewAdapter();
+        model.currencies.observe(this, list -> adapter.submitList(list));
+
+        recyclerView.setAdapter(adapter);
+        adapter.setCurrencySelectedListener(this);
+        adapter.setCurrencyLongClickListener(this);
+
+        final TextView tvNewCurrName = csd.findViewById(R.id.new_currency_name);
+        final TextView tvNoCurrBtn = csd.findViewById(R.id.btn_no_currency);
+        final TextView tvAddCurrOkBtn = csd.findViewById(R.id.btn_add_currency);
+        final TextView tvAddCurrBtn = csd.findViewById(R.id.btn_add_new);
+
+        tvNewCurrName.setVisibility(View.GONE);
+        tvAddCurrOkBtn.setVisibility(View.GONE);
+        tvNoCurrBtn.setVisibility(View.VISIBLE);
+        tvAddCurrBtn.setVisibility(View.VISIBLE);
+
+        tvAddCurrBtn.setOnClickListener(v -> {
+            tvNewCurrName.setVisibility(View.VISIBLE);
+            tvAddCurrOkBtn.setVisibility(View.VISIBLE);
+
+            tvNoCurrBtn.setVisibility(View.GONE);
+            tvAddCurrBtn.setVisibility(View.GONE);
+
+            tvNewCurrName.setText(null);
+            tvNewCurrName.requestFocus();
+            net.ktnx.mobileledger.utils.Misc.showSoftKeyboard(this);
+        });
+
+        tvAddCurrOkBtn.setOnClickListener(v -> {
+
+
+            String currName = String.valueOf(tvNewCurrName.getText());
+            if (!currName.isEmpty()) {
+                List<Currency> list = new ArrayList<>( model.currencies.getValue());
+                // FIXME hardcoded position and gap setting
+                list.add(new Currency(profile, String.valueOf(tvNewCurrName.getText()),
+                        Currency.Position.after, false));
+                model.currencies.setValue(list);
+            }
+
+            tvNewCurrName.setVisibility(View.GONE);
+            tvAddCurrOkBtn.setVisibility(View.GONE);
+
+            tvNoCurrBtn.setVisibility(View.VISIBLE);
+            tvAddCurrBtn.setVisibility(View.VISIBLE);
+        });
+
+        tvNoCurrBtn.setOnClickListener(v -> {
+            adapter.notifyCurrencySelected(null);
+            dismiss();
+        });
+
+        RadioButton rbPositionLeft = csd.findViewById(R.id.currency_position_left);
+        RadioButton rbPositionRight = csd.findViewById(R.id.currency_position_right);
+
+        if (Data.currencySymbolPosition.getValue() == Currency.Position.before)
+            rbPositionLeft.toggle();
+        else
+            rbPositionRight.toggle();
+
+        RadioGroup rgPosition = csd.findViewById(R.id.position_radio_group);
+        rgPosition.setOnCheckedChangeListener((group, checkedId) -> {
+            if (checkedId == R.id.currency_position_left)
+                Data.currencySymbolPosition.setValue(Currency.Position.before);
+            else
+                Data.currencySymbolPosition.setValue(Currency.Position.after);
+        });
+
+        Switch gap = csd.findViewById(R.id.currency_gap);
+
+        gap.setChecked(Data.currencyGap.getValue());
+
+        gap.setOnCheckedChangeListener((v, checked) -> {
+            Data.currencyGap.setValue(checked);
+        });
+
+        return csd;
+    }
+    public void setOnCurrencySelectedListener(OnCurrencySelectedListener listener) {
+        mListener = listener;
+    }
+    public void resetOnCurrencySelectedListener() {
+        mListener = null;
+    }
+    @Override
+    public void onCurrencySelected(Currency item) {
+        if (mListener != null)
+            mListener.onCurrencySelected(item);
+
+        dismiss();
+    }
+
+    @Override
+    public void onCurrencyLongClick(Currency item) {
+        ArrayList<Currency> list = new ArrayList<>(model.currencies.getValue());
+        App.getDatabase().execSQL("delete from currencies where id=?", new Object[]{item.getId()});
+        list.remove(item);
+        model.currencies.setValue(list);
+    }
+}
diff --git a/app/src/main/java/net/ktnx/mobileledger/ui/CurrencySelectorModel.java b/app/src/main/java/net/ktnx/mobileledger/ui/CurrencySelectorModel.java
new file mode 100644 (file)
index 0000000..10fea51
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * 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 <https://www.gnu.org/licenses/>.
+ */
+
+package net.ktnx.mobileledger.ui;
+
+import androidx.lifecycle.MutableLiveData;
+import androidx.lifecycle.ViewModel;
+
+import net.ktnx.mobileledger.model.Currency;
+
+import java.util.List;
+
+public class CurrencySelectorModel extends ViewModel {
+    public final MutableLiveData<List<Currency>> currencies;
+    public CurrencySelectorModel() {
+        this.currencies = new MutableLiveData<>();
+    }
+}
diff --git a/app/src/main/java/net/ktnx/mobileledger/ui/CurrencySelectorRecyclerViewAdapter.java b/app/src/main/java/net/ktnx/mobileledger/ui/CurrencySelectorRecyclerViewAdapter.java
new file mode 100644 (file)
index 0000000..b7d90a3
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * Copyright © 2019 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/>.
+ */
+
+package net.ktnx.mobileledger.ui;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import androidx.recyclerview.widget.ListAdapter;
+import androidx.recyclerview.widget.RecyclerView;
+
+import net.ktnx.mobileledger.R;
+import net.ktnx.mobileledger.model.Currency;
+
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * {@link RecyclerView.Adapter} that can display a {@link Currency} and makes a call to the
+ * specified {@link OnCurrencySelectedListener}.
+ */
+public class CurrencySelectorRecyclerViewAdapter
+        extends ListAdapter<Currency, CurrencySelectorRecyclerViewAdapter.ViewHolder> {
+
+    private OnCurrencySelectedListener currencySelectedListener;
+    private OnCurrencyLongClickListener currencyLongClickListener;
+    public CurrencySelectorRecyclerViewAdapter() {
+        super(Currency.DIFF_CALLBACK);
+    }
+    @NotNull
+    @Override
+    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+        View view = LayoutInflater.from(parent.getContext())
+                                  .inflate(R.layout.fragment_currency_selector, parent, false);
+        return new ViewHolder(view);
+    }
+
+    @Override
+    public void onBindViewHolder(final ViewHolder holder, int position) {
+        holder.bindTo(getItem(position));
+    }
+    public void setCurrencySelectedListener(OnCurrencySelectedListener listener) {
+        this.currencySelectedListener = listener;
+    }
+    public void resetCurrencySelectedListener() {
+        currencySelectedListener = null;
+    }
+    public void notifyCurrencySelected(Currency currency) {
+        if (null != currencySelectedListener)
+            currencySelectedListener.onCurrencySelected(currency);
+    }
+    public void setCurrencyLongClickListener(OnCurrencyLongClickListener listener) {
+        this.currencyLongClickListener = listener;
+    }
+    public void resetCurrencyLockClickListener() { currencyLongClickListener = null; }
+    private void notifyCurrencyLongClicked(Currency mItem) {
+        if (null != currencyLongClickListener)
+            currencyLongClickListener.onCurrencyLongClick(mItem);
+    }
+
+    public class ViewHolder extends RecyclerView.ViewHolder {
+        private final TextView mNameView;
+        private Currency mItem;
+
+        ViewHolder(View view) {
+            super(view);
+            mNameView = view.findViewById(R.id.content);
+
+            view.setOnClickListener(v -> notifyCurrencySelected(mItem));
+            view.setOnLongClickListener(v -> {
+                notifyCurrencyLongClicked(mItem);
+                return false;
+            });
+        }
+
+        @NotNull
+        @Override
+        public String toString() {
+            return super.toString() + " '" + mNameView.getText() + "'";
+        }
+        void bindTo(Currency item) {
+            mItem = item;
+            mNameView.setText(item.getName());
+        }
+    }
+}
diff --git a/app/src/main/java/net/ktnx/mobileledger/ui/OnCurrencyLongClickListener.java b/app/src/main/java/net/ktnx/mobileledger/ui/OnCurrencyLongClickListener.java
new file mode 100644 (file)
index 0000000..f681f99
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * 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 <https://www.gnu.org/licenses/>.
+ */
+
+package net.ktnx.mobileledger.ui;
+
+import net.ktnx.mobileledger.model.Currency;
+
+/**
+ * This interface must be implemented by activities that contain this
+ * fragment to allow an interaction in this fragment to be communicated
+ * to the activity and potentially other fragments contained in that
+ * activity.
+ * <p/>
+ * See the Android Training lesson <a href=
+ * "http://developer.android.com/training/basics/fragments/communicating.html"
+ * >Communicating with Other Fragments</a> for more information.
+ */
+public interface OnCurrencyLongClickListener {
+    void onCurrencyLongClick(Currency item);
+}
diff --git a/app/src/main/java/net/ktnx/mobileledger/ui/OnCurrencySelectedListener.java b/app/src/main/java/net/ktnx/mobileledger/ui/OnCurrencySelectedListener.java
new file mode 100644 (file)
index 0000000..c12f32a
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * 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 <https://www.gnu.org/licenses/>.
+ */
+
+package net.ktnx.mobileledger.ui;
+
+import net.ktnx.mobileledger.model.Currency;
+
+/**
+ * This interface must be implemented by activities that contain this
+ * fragment to allow an interaction in this fragment to be communicated
+ * to the activity and potentially other fragments contained in that
+ * activity.
+ * <p/>
+ * See the Android Training lesson <a href=
+ * "http://developer.android.com/training/basics/fragments/communicating.html"
+ * >Communicating with Other Fragments</a> for more information.
+ */
+public interface OnCurrencySelectedListener {
+    void onCurrencySelected(Currency item);
+}
index 0ee99136eaa0d51579677b296e4a55e42a397b85..6f410924081ef85e41f21e7073f51382d795f6da 100644 (file)
@@ -22,6 +22,7 @@ import android.os.Build;
 import android.text.Editable;
 import android.text.TextWatcher;
 import android.text.method.DigitsKeyListener;
+import android.view.Gravity;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.inputmethod.EditorInfo;
@@ -39,13 +40,16 @@ import androidx.recyclerview.widget.RecyclerView;
 
 import net.ktnx.mobileledger.R;
 import net.ktnx.mobileledger.async.DescriptionSelectedCallback;
+import net.ktnx.mobileledger.model.Currency;
 import net.ktnx.mobileledger.model.Data;
 import net.ktnx.mobileledger.model.LedgerTransactionAccount;
 import net.ktnx.mobileledger.model.MobileLedgerProfile;
 import net.ktnx.mobileledger.ui.CurrencySelectorFragment;
 import net.ktnx.mobileledger.ui.DatePickerFragment;
+import net.ktnx.mobileledger.ui.OnCurrencySelectedListener;
 import net.ktnx.mobileledger.ui.TextViewClearHelper;
 import net.ktnx.mobileledger.utils.Colors;
+import net.ktnx.mobileledger.utils.DimensionUtils;
 import net.ktnx.mobileledger.utils.Logger;
 import net.ktnx.mobileledger.utils.MLDB;
 import net.ktnx.mobileledger.utils.Misc;
@@ -59,9 +63,11 @@ import java.util.Locale;
 import static net.ktnx.mobileledger.ui.activity.NewTransactionModel.ItemType;
 
 class NewTransactionItemHolder extends RecyclerView.ViewHolder
-        implements DatePickerFragment.DatePickedListener, DescriptionSelectedCallback {
+        implements DatePickerFragment.DatePickedListener, DescriptionSelectedCallback,
+        OnCurrencySelectedListener {
     private final String decimalSeparator;
     private final String decimalDot;
+    private final TextView tvCurrency;
     private NewTransactionModel.Item item;
     private TextView tvDate;
     private AutoCompleteTextView tvDescription;
@@ -81,10 +87,14 @@ class NewTransactionItemHolder extends RecyclerView.ViewHolder
     private Observer<Boolean> editableObserver;
     private Observer<Boolean> commentVisibleObserver;
     private Observer<String> commentObserver;
+    private Observer<Currency.Position> currencyPositionObserver;
+    private Observer<Boolean> currencyGapObserver;
     private Observer<Locale> localeObserver;
+    private Observer<Currency> currencyObserver;
     private boolean inUpdate = false;
     private boolean syncingData = false;
     private View commentButton;
+    //TODO multiple amounts with different currencies per posting
     NewTransactionItemHolder(@NonNull View itemView, NewTransactionItemsAdapter adapter) {
         super(itemView);
         tvAccount = itemView.findViewById(R.id.account_row_acc_name);
@@ -92,6 +102,7 @@ class NewTransactionItemHolder extends RecyclerView.ViewHolder
         new TextViewClearHelper().attachToTextView((EditText) tvComment);
         commentButton = itemView.findViewById(R.id.comment_button);
         tvAmount = itemView.findViewById(R.id.account_row_acc_amounts);
+        tvCurrency = itemView.findViewById(R.id.currency);
         tvDate = itemView.findViewById(R.id.new_transaction_date);
         tvDescription = itemView.findViewById(R.id.new_transaction_description);
         lHead = itemView.findViewById(R.id.ntr_data);
@@ -178,10 +189,15 @@ class NewTransactionItemHolder extends RecyclerView.ViewHolder
         final TextWatcher amountWatcher = new TextWatcher() {
             @Override
             public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+                Logger.debug("num",
+                        String.format(Locale.US, "beforeTextChanged: start=%d, count=%d, after=%d",
+                                start, count, after));
             }
             @Override
             public void onTextChanged(CharSequence s, int start, int before, int count) {
-
+                Logger.debug("num",
+                        String.format(Locale.US, "onTextChanged: start=%d, before=%d, count=%d",
+                                start, before, count));
             }
             @Override
             public void afterTextChanged(Editable s) {
@@ -207,6 +223,13 @@ class NewTransactionItemHolder extends RecyclerView.ViewHolder
         tvComment.addTextChangedListener(tw);
         tvAmount.addTextChangedListener(amountWatcher);
 
+        tvCurrency.setOnClickListener(v -> {
+            CurrencySelectorFragment cpf = new CurrencySelectorFragment();
+            cpf.setOnCurrencySelectedListener(this);
+            final AppCompatActivity activity = (AppCompatActivity) v.getContext();
+            cpf.show(activity.getSupportFragmentManager(), "currency-selector");
+        });
+
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
             tvAmount.setKeyListener(DigitsKeyListener.getInstance(Locale.getDefault(), true, true));
         else
@@ -315,10 +338,78 @@ class NewTransactionItemHolder extends RecyclerView.ViewHolder
                 tvAmount.setImeOptions(EditorInfo.IME_ACTION_NEXT);
         };
 
+        currencyPositionObserver = position -> {
+            updateCurrencyPositionAndPadding(position, Data.currencyGap.getValue());
+        };
+
+        currencyGapObserver = hasGap -> {
+            updateCurrencyPositionAndPadding(Data.currencySymbolPosition.getValue(), hasGap);
+        };
+
         localeObserver = locale -> {
             if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
                 tvAmount.setKeyListener(DigitsKeyListener.getInstance(locale, true, true));
         };
+
+        currencyObserver = this::setCurrency;
+    }
+    private void updateCurrencyPositionAndPadding(Currency.Position position, boolean hasGap) {
+        ConstraintLayout.LayoutParams amountLP =
+                (ConstraintLayout.LayoutParams) tvAmount.getLayoutParams();
+        ConstraintLayout.LayoutParams currencyLP =
+                (ConstraintLayout.LayoutParams) tvCurrency.getLayoutParams();
+
+        if (position == Currency.Position.before) {
+            currencyLP.startToStart = ConstraintLayout.LayoutParams.PARENT_ID;
+            currencyLP.endToEnd = ConstraintLayout.LayoutParams.UNSET;
+
+            amountLP.endToEnd = ConstraintLayout.LayoutParams.PARENT_ID;
+            amountLP.endToStart = ConstraintLayout.LayoutParams.UNSET;
+            amountLP.startToStart = ConstraintLayout.LayoutParams.UNSET;
+            amountLP.startToEnd = tvCurrency.getId();
+
+            tvCurrency.setGravity(Gravity.END);
+        }
+        else {
+            currencyLP.startToStart = ConstraintLayout.LayoutParams.UNSET;
+            currencyLP.endToEnd = ConstraintLayout.LayoutParams.PARENT_ID;
+
+            amountLP.startToStart = ConstraintLayout.LayoutParams.PARENT_ID;
+            amountLP.startToEnd = ConstraintLayout.LayoutParams.UNSET;
+            amountLP.endToEnd = ConstraintLayout.LayoutParams.UNSET;
+            amountLP.endToStart = tvCurrency.getId();
+
+            tvCurrency.setGravity(Gravity.START);
+        }
+
+        amountLP.resolveLayoutDirection(tvAmount.getLayoutDirection());
+        currencyLP.resolveLayoutDirection(tvCurrency.getLayoutDirection());
+
+        tvAmount.setLayoutParams(amountLP);
+        tvCurrency.setLayoutParams(currencyLP);
+
+        // distance between the amount and the currency symbol
+        int gapSize = DimensionUtils.sp2px(tvCurrency.getContext(), 5);
+
+        if (position == Currency.Position.before) {
+            tvCurrency.setPaddingRelative(0, 0, hasGap ? gapSize : 0, 0);
+        }
+        else {
+            tvCurrency.setPaddingRelative(hasGap ? gapSize : 0, 0, 0, 0);
+        }
+    }
+    private void setCurrencyString(String currency) {
+        if ((currency == null) || currency.isEmpty()) {
+            tvCurrency.setText(R.string.currency_symbol);
+            tvCurrency.setTextColor(0x7f000000 + (0x00ffffff & Colors.defaultTextColor));
+        }
+        else {
+            tvCurrency.setText(currency);
+            tvCurrency.setTextColor(Colors.defaultTextColor);
+        }
+    }
+    private void setCurrency(Currency currency) {
+        setCurrencyString((currency == null) ? null : currency.getName());
     }
     private void setEditable(Boolean editable) {
         tvDate.setEnabled(editable);
@@ -397,6 +488,7 @@ class NewTransactionItemHolder extends RecyclerView.ViewHolder
 
                     if (amount.isEmpty()) {
                         account.resetAmount();
+                        account.setCurrency(null);
                     }
                     else {
                         try {
@@ -409,6 +501,14 @@ class NewTransactionItemHolder extends RecyclerView.ViewHolder
                                     "input was '%s'", amount));
                             account.resetAmount();
                         }
+                        final String curr = String.valueOf(tvCurrency.getText());
+                        if (curr.equals(tvCurrency.getContext()
+                                                  .getResources()
+                                                  .getString(R.string.currency_symbol)) ||
+                            curr.isEmpty())
+                            account.setCurrency(null);
+                        else
+                            account.setCurrency(curr);
                     }
 
                     break;
@@ -447,7 +547,10 @@ class NewTransactionItemHolder extends RecyclerView.ViewHolder
                          .stopObservingFocusedItem(focusedAccountObserver);
                 this.item.getModel()
                          .stopObservingAccountCount(accountCountObserver);
+                Data.currencySymbolPosition.removeObserver(currencyPositionObserver);
+                Data.currencyGap.removeObserver(currencyGapObserver);
                 Data.locale.removeObserver(localeObserver);
+                this.item.stopObservingCurrency(currencyObserver);
 
                 this.item = null;
             }
@@ -473,6 +576,7 @@ class NewTransactionItemHolder extends RecyclerView.ViewHolder
 //                        tvAmount.setHint(R.string.zero_amount);
                     }
                     tvAmount.setHint(item.getAmountHint());
+                    setCurrencyString(acc.getCurrency());
                     lHead.setVisibility(View.GONE);
                     lAccount.setVisibility(View.VISIBLE);
                     lPadding.setVisibility(View.GONE);
@@ -500,7 +604,10 @@ class NewTransactionItemHolder extends RecyclerView.ViewHolder
                     .observeFocusedItem(activity, focusedAccountObserver);
                 item.getModel()
                     .observeAccountCount(activity, accountCountObserver);
+                Data.currencySymbolPosition.observe(activity, currencyPositionObserver);
+                Data.currencyGap.observe(activity, currencyGapObserver);
                 Data.locale.observe(activity, localeObserver);
+                item.observeCurrency(activity, currencyObserver);
             }
         }
         finally {
@@ -518,6 +625,10 @@ class NewTransactionItemHolder extends RecyclerView.ViewHolder
 
     }
     @Override
+    public void onCurrencySelected(Currency item) {
+        this.item.setCurrency(item);
+    }
+    @Override
     public void descriptionSelected(String description) {
         tvAccount.setText(description);
         tvAmount.requestFocus(View.FOCUS_FORWARD);
index 5bc327a78329ffcb6c2ca3b0570d0de13d581b8f..62f85ba9bed06220d66cde8722f5750e05593a4e 100644 (file)
@@ -26,6 +26,7 @@ import androidx.lifecycle.Observer;
 import androidx.lifecycle.ViewModel;
 
 import net.ktnx.mobileledger.BuildConfig;
+import net.ktnx.mobileledger.model.Currency;
 import net.ktnx.mobileledger.model.LedgerTransactionAccount;
 import net.ktnx.mobileledger.utils.Logger;
 import net.ktnx.mobileledger.utils.Misc;
@@ -334,6 +335,7 @@ public class NewTransactionModel extends ViewModel {
         private FocusedElement focusedElement = FocusedElement.Account;
         private MutableLiveData<String> comment = new MutableLiveData<>(null);
         private MutableLiveData<Boolean> commentVisible = new MutableLiveData<>(false);
+        private MutableLiveData<Currency> currency = new MutableLiveData<>(null);
         public Item(NewTransactionModel model) {
             this.model = model;
             type = ItemType.bottomFiller;
@@ -532,8 +534,7 @@ public class NewTransactionModel extends ViewModel {
         public void stopObservingCommentVisible(Observer<Boolean> observer) {
             commentVisible.removeObserver(observer);
         }
-        public void observeComment(NewTransactionActivity activity,
-                                          Observer<String> observer) {
+        public void observeComment(NewTransactionActivity activity, Observer<String> observer) {
             comment.observe(activity, observer);
         }
         public void stopObservingComment(Observer<String> observer) {
@@ -543,5 +544,20 @@ public class NewTransactionModel extends ViewModel {
             getAccount().setComment(comment);
             this.comment.postValue(comment);
         }
+        public Currency getCurrency() {
+            return this.currency.getValue();
+        }
+        public void setCurrency(Currency currency) {
+            getAccount().setCurrency((currency != null && !currency.getName()
+                                                                   .isEmpty()) ? currency.getName()
+                                                                               : null);
+            this.currency.setValue(currency);
+        }
+        public void observeCurrency(NewTransactionActivity activity, Observer<Currency> observer) {
+            currency.observe(activity, observer);
+        }
+        public void stopObservingCurrency(Observer<Currency> observer) {
+            currency.removeObserver(observer);
+        }
     }
 }
index 0a51215a9c85f2797e3b39fdd5b747b36b38632b..d8571bfbf571ec7a12f32302d06fcc9565c69e51 100644 (file)
@@ -25,5 +25,9 @@ public class DimensionUtils {
         return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp,
                context.getResources().getDisplayMetrics()));
     }
+    public static int sp2px(Context context, float sp) {
+        return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp,
+               context.getResources().getDisplayMetrics()));
+    }
 
 }
diff --git a/app/src/main/res/layout/fragment_currency_selector.xml b/app/src/main/res/layout/fragment_currency_selector.xml
new file mode 100644 (file)
index 0000000..56309be
--- /dev/null
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright © 2019 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/>.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:orientation="horizontal"
+    android:longClickable="true">
+
+    <TextView
+        android:id="@+id/content"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_margin="@dimen/text_margin"
+        android:text="USD"
+        android:textAppearance="?attr/textAppearanceListItem"
+        tools:ignore="HardcodedText"
+        android:minWidth="20dp"
+        android:gravity="center_horizontal"
+        />
+</LinearLayout>
diff --git a/app/src/main/res/layout/fragment_currency_selector_list.xml b/app/src/main/res/layout/fragment_currency_selector_list.xml
new file mode 100644 (file)
index 0000000..e67e2d0
--- /dev/null
@@ -0,0 +1,166 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright © 2019 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/>.
+  -->
+
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:minWidth="60dp"
+    android:animateLayoutChanges="true"
+    app:layout_constraintWidth_min="60dp"
+    android:padding="@dimen/text_margin">
+
+    <com.google.android.material.textview.MaterialTextView
+        android:id="@+id/label"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginBottom="@dimen/text_margin"
+        android:text="@string/choose_currency_label"
+        android:textSize="18sp"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent" />
+
+    <androidx.recyclerview.widget.RecyclerView
+        android:id="@+id/list"
+        android:name="net.ktnx.mobileledger.ui.CurrencySelectorFragment"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginLeft="@dimen/activity_horizontal_margin"
+        android:layout_marginRight="@dimen/activity_horizontal_margin"
+        app:layoutManager="LinearLayoutManager"
+        app:layout_constraintBottom_toTopOf="@id/new_currency_panel"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/label"
+        tools:context="net.ktnx.mobileledger.ui.CurrencySelectorFragment"
+        tools:listitem="@layout/fragment_currency_selector">
+
+    </androidx.recyclerview.widget.RecyclerView>
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:id="@+id/new_currency_panel"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginLeft="@dimen/activity_horizontal_margin"
+        android:layout_marginRight="@dimen/activity_horizontal_margin"
+        app:layout_constraintTop_toBottomOf="@id/list"
+        app:layout_constraintBottom_toTopOf="@id/params_panel"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent">
+
+        <TextView
+            android:id="@+id/btn_add_new"
+            style="@style/TextAppearance.AppCompat.Widget.Button.Colored"
+            android:textColor="@color/colorPrimary"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/add_button"
+            android:layout_margin="@dimen/text_margin"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toEndOf="@id/btn_no_currency"
+            app:layout_constraintTop_toTopOf="parent" />
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:id="@+id/btn_no_currency"
+            android:text="@string/btn_no_currency"
+            style="@style/TextAppearance.AppCompat.Widget.Button.Colored"
+            android:textColor="@color/colorPrimary"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toStartOf="@id/btn_add_new"
+            android:layout_margin="@dimen/text_margin"/>
+
+        <EditText
+            android:id="@+id/new_currency_name"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:ems="10"
+            android:hint="@string/new_currency_name_hint"
+            android:inputType="text"
+            android:singleLine="true"
+            android:text=""
+            android:visibility="invisible"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent" />
+
+        <TextView
+            android:id="@+id/btn_add_currency"
+            style="@style/TextAppearance.AppCompat.Button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_margin="@dimen/text_margin"
+            android:text="@string/add_button"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintTop_toBottomOf="@id/new_currency_name" />
+    </androidx.constraintlayout.widget.ConstraintLayout>
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:id="@+id/params_panel"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/new_currency_panel">
+
+        <RadioGroup
+            android:id="@+id/position_radio_group"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            android:orientation="horizontal"
+            app:layout_constraintBottom_toTopOf="@id/currency_gap"
+            android:layout_marginBottom="@dimen/text_margin"
+            >
+
+            <RadioButton
+                android:id="@+id/currency_position_left"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_weight="1"
+                android:text="@string/currency_position_left" />
+
+            <RadioButton
+                android:id="@+id/currency_position_right"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_weight="1"
+                android:text="@string/currency_position_right" />
+        </RadioGroup>
+
+        <Switch
+            android:id="@+id/currency_gap"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/currency_has_gap"
+            app:layout_constraintTop_toBottomOf="@id/position_radio_group"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintBottom_toBottomOf="parent"/>
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
index 0b36bc4eec912c64e555e1f4a2c7f83a0844bf5b..11951e58dd60752fedd04a09ff96082e63b6c339 100644 (file)
             app:layout_constraintStart_toEndOf="@id/comment_button"
             app:layout_constraintTop_toBottomOf="@id/account_row_acc_name" />
 
-        <LinearLayout
+        <androidx.constraintlayout.widget.ConstraintLayout
             android:id="@+id/amount_layout"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             app:layout_constraintTop_toBottomOf="@id/comment">
 
             <EditText
+                app:layout_constraintTop_toTopOf="parent"
+                app:layout_constraintBottom_toBottomOf="parent"
+                app:layout_constraintLeft_toLeftOf="parent"
+                app:layout_constraintRight_toLeftOf="@id/currency"
                 android:id="@+id/account_row_acc_amounts"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:inputType="numberSigned|numberDecimal|number"
                 android:minWidth="80sp"
                 android:selectAllOnFocus="true"
-                android:textAlignment="viewEnd" />
-        </LinearLayout>
+                android:textAlignment="viewEnd"
+                app:layout_constraintWidth_min="@dimen/text_margin"/>
+
+            <TextView
+                app:layout_constraintTop_toTopOf="parent"
+                app:layout_constraintBottom_toBottomOf="parent"
+                app:layout_constraintRight_toRightOf="parent"
+                android:id="@+id/currency"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_vertical"
+                android:text="@string/currency_symbol"
+                style="@style/TextAppearance.AppCompat.Widget.Button"
+                android:textAllCaps="false"
+                android:visibility="visible"
+                android:minWidth="24dp"
+                android:gravity="center_horizontal"/>
+        </androidx.constraintlayout.widget.ConstraintLayout>
 
     </androidx.constraintlayout.widget.ConstraintLayout>
 
index 3f8c56cd7c2ac04431229925d16b8b2760236ad7..27f802a6943b7987f0250a9a883839715c04387f 100644 (file)
     <string name="api_post_1_14">Версия 1.15 или по-нова</string>
     <string name="api_pre_1_15">Версия 1.14.x</string>
     <string name="profile_api_version_title">Версия на сървъра</string>
+    <string name="add_button">Добавяне…</string>
+    <string name="close_button">Затваряне</string>
     <string name="transaction_account_comment_hint">бележка</string>
+    <string name="btn_no_currency">без</string>
+    <string name="choose_currency_label">Валута</string>
+    <string name="currency_has_gap">Отстояние от сумата</string>
+    <string name="currency_position_left">Вляво</string>
+    <string name="currency_position_right">Вдясно</string>
+    <string name="new_currency_name_hint">валута/ценност</string>
 
 </resources>
index 97b40641ef0e7ab6feb9708965dc2df40d22246f..cd3753587fdb038a132656ee3e081fdde8f30c41 100644 (file)
     <string name="api_post_1_14">Version 1.15 and above</string>
     <string name="api_auto">Detect automaticaly</string>
     <string name="profile_api_version_title">Backend server version</string>
+    <string name="currency_symbol" translatable="false">¤</string>
+    <string name="add_button">Add…</string>
+    <string name="close_button">Close</string>
     <string name="transaction_account_comment_hint">comment</string>
+    <string name="choose_currency_label">Currency</string>
+    <string name="new_currency_name_hint">currency/commodity</string>
+    <string name="btn_no_currency">none</string>
+    <string name="currency_position_left">Left</string>
+    <string name="currency_position_right">Right</string>
+    <string name="currency_has_gap">Offset from the value</string>
 </resources>