replace dates in transaction list items with delimiters between items in different...
authorDamyan Ivanov <dam+mobileledger@ktnx.net>
Sat, 12 Jan 2019 12:43:46 +0000 (12:43 +0000)
committerDamyan Ivanov <dam+mobileledger@ktnx.net>
Sat, 12 Jan 2019 12:43:46 +0000 (12:43 +0000)
16 files changed:
app/src/main/java/net/ktnx/mobileledger/MobileLedgerApplication.java
app/src/main/java/net/ktnx/mobileledger/async/SaveTransactionTask.java
app/src/main/java/net/ktnx/mobileledger/async/UpdateTransactionsTask.java
app/src/main/java/net/ktnx/mobileledger/model/LedgerTransaction.java
app/src/main/java/net/ktnx/mobileledger/model/MobileLedgerProfile.java
app/src/main/java/net/ktnx/mobileledger/model/TransactionList.java
app/src/main/java/net/ktnx/mobileledger/model/TransactionListItem.java [new file with mode: 0644]
app/src/main/java/net/ktnx/mobileledger/ui/activity/NewTransactionActivity.java
app/src/main/java/net/ktnx/mobileledger/ui/transaction_list/TransactionListAdapter.java
app/src/main/java/net/ktnx/mobileledger/ui/transaction_list/TransactionListViewModel.java
app/src/main/java/net/ktnx/mobileledger/ui/transaction_list/TransactionLoaderStep.java
app/src/main/java/net/ktnx/mobileledger/ui/transaction_list/TransactionRowHolder.java
app/src/main/java/net/ktnx/mobileledger/utils/Globals.java
app/src/main/res/layout/transaction_list_row.xml
app/src/main/res/values-bg/strings.xml
app/src/main/res/values/strings.xml

index 7f0dfeb..804575b 100644 (file)
@@ -36,15 +36,19 @@ public class MobileLedgerApplication extends Application {
     public void onCreate() {
         super.onCreate();
         updateColorValues();
+        updateMonthNames();
         MLDB.init(this);
         SharedPreferences p = PreferenceManager.getDefaultSharedPreferences(this);
         Data.optShowOnlyStarred.set(p.getBoolean(PREF_KEY_SHOW_ONLY_STARRED_ACCOUNTS, false));
-        SharedPreferences.OnSharedPreferenceChangeListener handler = (preference, value) -> {
-            Data.optShowOnlyStarred
-                    .set(preference.getBoolean(PREF_KEY_SHOW_ONLY_STARRED_ACCOUNTS, false));
-        };
+        SharedPreferences.OnSharedPreferenceChangeListener handler =
+                (preference, value) -> Data.optShowOnlyStarred
+                        .set(preference.getBoolean(PREF_KEY_SHOW_ONLY_STARRED_ACCOUNTS, false));
         p.registerOnSharedPreferenceChangeListener(handler);
     }
+    private void updateMonthNames() {
+        Resources rm = getResources();
+        Globals.monthNames = rm.getStringArray(R.array.month_names);
+    }
     @Override
     public void onTerminate() {
         MLDB.done();
@@ -54,6 +58,7 @@ public class MobileLedgerApplication extends Application {
     public void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
         updateColorValues();
+        updateMonthNames();
     }
     private void updateColorValues() {
         Resources rm = getResources();
index fb520d4..fae87bc 100644 (file)
@@ -23,6 +23,7 @@ import android.util.Log;
 import net.ktnx.mobileledger.model.Data;
 import net.ktnx.mobileledger.model.LedgerTransaction;
 import net.ktnx.mobileledger.model.LedgerTransactionAccount;
+import net.ktnx.mobileledger.utils.Globals;
 import net.ktnx.mobileledger.utils.NetworkUtil;
 import net.ktnx.mobileledger.utils.UrlEncodedFormData;
 
@@ -42,11 +43,11 @@ import static java.lang.Thread.sleep;
 
 public class SaveTransactionTask extends AsyncTask<LedgerTransaction, Void, Void> {
     private final TaskCallback taskCallback;
+    protected String error;
     private String token;
     private String session;
     private String backendUrl;
     private LedgerTransaction ltr;
-    protected String error;
 
     public SaveTransactionTask(TaskCallback callback) {
         taskCallback = callback;
@@ -65,7 +66,7 @@ public class SaveTransactionTask extends AsyncTask<LedgerTransaction, Void, Void
         UrlEncodedFormData params = new UrlEncodedFormData();
         params.addPair("_formid", "identify-add");
         if (token != null) params.addPair("_token", token);
-        params.addPair("date", ltr.getDate());
+        params.addPair("date", Globals.formatLedgerDate(ltr.getDate()));
         params.addPair("description", ltr.getDescription());
         for (LedgerTransactionAccount acc : ltr.getAccounts()) {
             params.addPair("account", acc.getAccountName());
index 178db99..b039b11 100644 (file)
@@ -24,28 +24,33 @@ import android.util.Log;
 
 import net.ktnx.mobileledger.model.Data;
 import net.ktnx.mobileledger.model.LedgerTransaction;
+import net.ktnx.mobileledger.model.TransactionListItem;
+import net.ktnx.mobileledger.utils.Globals;
 import net.ktnx.mobileledger.utils.MLDB;
 
 import java.util.ArrayList;
+import java.util.Date;
 import java.util.List;
 
-public class UpdateTransactionsTask extends AsyncTask<String, Void, List<LedgerTransaction>> {
-    protected List<LedgerTransaction> doInBackground(String[] filterAccName) {
+public class UpdateTransactionsTask extends AsyncTask<String, Void, List<TransactionListItem>> {
+    protected List<TransactionListItem> doInBackground(String[] filterAccName) {
         Data.backgroundTaskCount.incrementAndGet();
         String profile_uuid = Data.profile.get().getUuid();
         try {
-            ArrayList<LedgerTransaction> newList = new ArrayList<>();
+            ArrayList<TransactionListItem> newList = new ArrayList<>();
 
             String sql;
             String[] params;
 
             if (filterAccName[0] == null) {
-                sql = "SELECT id FROM transactions WHERE profile=? ORDER BY date desc, id desc";
+                sql = "SELECT id, date FROM transactions WHERE profile=? ORDER BY date desc, id " +
+                      "desc";
                 params = new String[]{profile_uuid};
 
             }
             else {
-                sql = "SELECT distinct tr.id from transactions tr JOIN transaction_accounts ta " +
+                sql = "SELECT distinct tr.id, tr.date from transactions tr JOIN " +
+                      "transaction_accounts ta " +
                       "ON ta.transaction_id=tr.id AND ta.profile=tr.profile WHERE tr.profile=? " +
                       "and ta.account_name LIKE ?||'%' AND ta" +
                       ".amount <> 0 ORDER BY tr.date desc, tr.id desc";
@@ -54,13 +59,27 @@ public class UpdateTransactionsTask extends AsyncTask<String, Void, List<LedgerT
 
             Log.d("UTT", sql);
             SQLiteDatabase db = MLDB.getReadableDatabase();
+            Date lastDate = null;
             try (Cursor cursor = db.rawQuery(sql, params)) {
                 while (cursor.moveToNext()) {
                     if (isCancelled()) return null;
 
                     int transaction_id = cursor.getInt(0);
-                    newList.add(new LedgerTransaction(transaction_id));
+                    String dateString = cursor.getString(1);
+                    Date date = Globals.parseLedgerDate(dateString);
+
+                    if ((lastDate == null) || !lastDate.equals(date)) {
+                        boolean showMonth = (lastDate == null) || (date != null) &&
+                                                                  (date.getMonth() !=
+                                                                   lastDate.getMonth() ||
+                                                                   date.getYear() !=
+                                                                   lastDate.getYear());
+                        newList.add(new TransactionListItem(date, showMonth));
+                    }
+                    newList.add(new TransactionListItem(new LedgerTransaction(transaction_id)));
 //                    Log.d("UTT", String.format("got transaction %d", transaction_id));
+
+                    lastDate = date;
                 }
                 Data.transactions.set(newList);
                 Log.d("UTT", "transaction list value updated");
index 3e5a45f..8e6f2bf 100644 (file)
@@ -22,11 +22,13 @@ import android.database.sqlite.SQLiteDatabase;
 import android.util.Log;
 
 import net.ktnx.mobileledger.utils.Digest;
+import net.ktnx.mobileledger.utils.Globals;
 
 import java.nio.charset.Charset;
 import java.security.NoSuchAlgorithmException;
 import java.util.ArrayList;
 import java.util.Comparator;
+import java.util.Date;
 
 public class LedgerTransaction {
     private static final String DIGEST_TYPE = "SHA-256";
@@ -43,12 +45,15 @@ public class LedgerTransaction {
             };
     private String profile;
     private Integer id;
-    private String date;
+    private Date date;
     private String description;
     private ArrayList<LedgerTransactionAccount> accounts;
     private String dataHash;
     private boolean dataLoaded;
-    public LedgerTransaction(Integer id, String date, String description) {
+    public LedgerTransaction(Integer id, String dateString, String description) {
+        this(id, Globals.parseLedgerDate(dateString), description);
+    }
+    public LedgerTransaction(Integer id, Date date, String description) {
         this.profile = Data.profile.get().getUuid();
         this.id = id;
         this.date = date;
@@ -57,11 +62,11 @@ public class LedgerTransaction {
         this.dataHash = null;
         dataLoaded = false;
     }
-    public LedgerTransaction(String date, String description) {
+    public LedgerTransaction(Date date, String description) {
         this(null, date, description);
     }
     public LedgerTransaction(int id) {
-        this(id, null, null);
+        this(id, (Date) null, null);
     }
     public ArrayList<LedgerTransactionAccount> getAccounts() {
         return accounts;
@@ -70,10 +75,10 @@ public class LedgerTransaction {
         accounts.add(item);
         dataHash = null;
     }
-    public String getDate() {
+    public Date getDate() {
         return date;
     }
-    public void setDate(String date) {
+    public void setDate(Date date) {
         this.date = date;
         dataHash = null;
     }
@@ -131,7 +136,8 @@ public class LedgerTransaction {
                         new String[]{profile, String.valueOf(id)}))
         {
             if (cTr.moveToFirst()) {
-                date = cTr.getString(0);
+                String dateString = cTr.getString(0);
+                date = Globals.parseLedgerDate(dateString);
                 description = cTr.getString(1);
 
                 try (Cursor cAcc = db.rawQuery("SELECT account_name, amount, currency FROM " +
index 8514e71..70c12ab 100644 (file)
@@ -21,6 +21,7 @@ import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
 import android.util.Log;
 
+import net.ktnx.mobileledger.utils.Globals;
 import net.ktnx.mobileledger.utils.MLDB;
 
 import java.util.ArrayList;
@@ -209,7 +210,8 @@ public final class MobileLedgerProfile {
 
         db.execSQL("INSERT INTO transactions(profile, id, date, description, data_hash, keep) " +
                    "values(?,?,?,?,?,1)",
-                new Object[]{uuid, tr.getId(), tr.getDate(), tr.getDescription(), tr.getDataHash()
+                new Object[]{uuid, tr.getId(), Globals.formatLedgerDate(tr.getDate()),
+                             tr.getDescription(), tr.getDataHash()
                 });
 
         for (LedgerTransactionAccount item : tr.getAccounts()) {
index c01c2aa..548303a 100644 (file)
@@ -21,5 +21,5 @@ import net.ktnx.mobileledger.utils.ObservableValue;
 
 import java.util.List;
 
-public class TransactionList extends ObservableValue<List<LedgerTransaction>> {
+public class TransactionList extends ObservableValue<List<TransactionListItem>> {
 }
diff --git a/app/src/main/java/net/ktnx/mobileledger/model/TransactionListItem.java b/app/src/main/java/net/ktnx/mobileledger/model/TransactionListItem.java
new file mode 100644 (file)
index 0000000..ee1810a
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright © 2019 Damyan Ivanov.
+ * This file is part of Mobile-Ledger.
+ * Mobile-Ledger 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.
+ *
+ * Mobile-Ledger 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 Mobile-Ledger. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package net.ktnx.mobileledger.model;
+
+import java.util.Date;
+
+public class TransactionListItem {
+    private Type type;
+    private Date date;
+    private boolean monthShown;
+    private LedgerTransaction transaction;
+    public TransactionListItem(Date date, boolean monthShown) {
+        this.type = Type.DELIMITER;
+        this.date = date;
+        this.monthShown = monthShown;
+    }
+    public TransactionListItem(LedgerTransaction transaction) {
+        this.type = Type.TRANSACTION;
+        this.transaction = transaction;
+    }
+    public Type getType() {
+        return type;
+    }
+    public Date getDate() {
+        return date;
+    }
+    public boolean isMonthShown() {
+        return monthShown;
+    }
+    public LedgerTransaction getTransaction() {
+        return transaction;
+    }
+    public enum Type {TRANSACTION, DELIMITER}
+}
index 0856bb5..a08f88c 100644 (file)
@@ -49,6 +49,7 @@ import net.ktnx.mobileledger.model.LedgerTransaction;
 import net.ktnx.mobileledger.model.LedgerTransactionAccount;
 import net.ktnx.mobileledger.ui.DatePickerFragment;
 import net.ktnx.mobileledger.ui.OnSwipeTouchListener;
+import net.ktnx.mobileledger.utils.Globals;
 import net.ktnx.mobileledger.utils.MLDB;
 
 import java.util.Date;
@@ -135,8 +136,10 @@ public class NewTransactionActivity extends AppCompatActivity implements TaskCal
 
         saver = new SaveTransactionTask(this);
 
-        String date = tvDate.getText().toString();
-        if (date.isEmpty()) date = String.valueOf(new Date().getDate());
+        String dateString = tvDate.getText().toString();
+        Date date;
+        if (dateString.isEmpty()) date = new Date();
+        else date = Globals.parseLedgerDate(dateString);
         LedgerTransaction tr = new LedgerTransaction(date, tvDescription.getText().toString());
 
         TableLayout table = findViewById(R.id.new_transaction_accounts_table);
index 7004dd7..febca01 100644 (file)
@@ -34,26 +34,57 @@ import android.widget.TextView;
 import net.ktnx.mobileledger.R;
 import net.ktnx.mobileledger.model.LedgerTransaction;
 import net.ktnx.mobileledger.model.LedgerTransactionAccount;
+import net.ktnx.mobileledger.model.TransactionListItem;
 import net.ktnx.mobileledger.utils.Globals;
 import net.ktnx.mobileledger.utils.MLDB;
 
+import java.text.DateFormat;
+import java.util.Date;
+
 import static net.ktnx.mobileledger.utils.DimensionUtils.dp2px;
 
 public class TransactionListAdapter extends RecyclerView.Adapter<TransactionRowHolder> {
     private String boldAccountName;
     public void onBindViewHolder(@NonNull TransactionRowHolder holder, int position) {
-        LedgerTransaction tr = TransactionListViewModel.getTransaction(position);
-        // in a race when transaction value is reduced, but the model hasn't been notified yet
-        // the view will disappear when the notifications reaches the model, so by simply omitting
-        // the out-of-range get() call nothing bad happens - just a to-be-deleted view remains
-        // a bit longer
-        if (tr == null) return;
+        TransactionListItem item = TransactionListViewModel.getTransactionListItem(position);
+
+        if (item.getType() == TransactionListItem.Type.TRANSACTION) {
+            holder.vTransaction.setVisibility(View.VISIBLE);
+            holder.vDelimiter.setVisibility(View.GONE);
+            LedgerTransaction tr = item.getTransaction();
+            // in a race when transaction value is reduced, but the model hasn't been notified yet
+            // the view will disappear when the notifications reaches the model, so by simply omitting
+            // the out-of-range get() call nothing bad happens - just a to-be-deleted view remains
+            // a bit longer
+            if (tr == null) return;
+
+            LedgerTransaction previous = null;
+            TransactionListItem previousItem = null;
+            if (position > 0)
+                previousItem = TransactionListViewModel.getTransactionListItem(position - 1);
 
 //        Log.d("transactions", String.format("Filling position %d with %d accounts", position,
 //                tr.getAccounts().size()));
 
-        TransactionLoader loader = new TransactionLoader();
-        loader.execute(new TransactionLoaderParams(tr, holder, position, boldAccountName));
+            TransactionLoader loader = new TransactionLoader();
+            loader.execute(
+                    new TransactionLoaderParams(tr, previous, holder, position, boldAccountName));
+
+            // WORKAROUND what seems to be a bug in CardHolder somewhere
+            // when a view that was previously holding a delimiter is re-purposed
+            // occasionally it stays too short (not high enough)
+            holder.vTransaction.measure(View.MeasureSpec
+                            .makeMeasureSpec(holder.itemView.getWidth(), View.MeasureSpec.EXACTLY),
+                    View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
+        }
+        else {
+            Date date = item.getDate();
+            holder.vTransaction.setVisibility(View.GONE);
+            holder.vDelimiter.setVisibility(View.VISIBLE);
+            holder.tvDelimiterDate.setText(DateFormat.getDateInstance().format(date));
+            holder.tvDelimiterMonth
+                    .setText(item.isMonthShown() ? Globals.monthNames[date.getMonth()] : "");
+        }
     }
 
     @NonNull
@@ -83,11 +114,18 @@ public class TransactionListAdapter extends RecyclerView.Adapter<TransactionRowH
         @Override
         protected Void doInBackground(TransactionLoaderParams... p) {
             LedgerTransaction tr = p[0].transaction;
+            LedgerTransaction previous = p[0].previousTransaction;
 
             SQLiteDatabase db = MLDB.getReadableDatabase();
             tr.loadData(db);
 
-            publishProgress(new TransactionLoaderStep(p[0].holder, p[0].position, tr));
+            boolean showDate;
+            if (previous == null) showDate = true;
+            else {
+                previous.loadData(db);
+                showDate = !previous.getDate().equals(tr.getDate());
+            }
+            publishProgress(new TransactionLoaderStep(p[0].holder, p[0].position, tr, showDate));
 
             int rowIndex = 0;
             for (LedgerTransactionAccount acc : tr.getAccounts()) {
@@ -109,7 +147,6 @@ public class TransactionListAdapter extends RecyclerView.Adapter<TransactionRowH
             switch (step.getStep()) {
                 case HEAD:
                     holder.tvDescription.setText(step.getTransaction().getDescription());
-                    holder.tvDate.setText(step.getTransaction().getDate());
 
                     if (step.getPosition() % 2 == 0) {
                         holder.row.setBackgroundColor(Globals.tableRowEvenBG);
@@ -189,13 +226,18 @@ public class TransactionListAdapter extends RecyclerView.Adapter<TransactionRowH
     }
 
     private class TransactionLoaderParams {
-        LedgerTransaction transaction;
+        LedgerTransaction transaction, previousTransaction;
         TransactionRowHolder holder;
         int position;
         String boldAccountName;
-        TransactionLoaderParams(LedgerTransaction transaction, TransactionRowHolder holder,
+        TransactionLoaderParams(LedgerTransaction transaction, TransactionRowHolder holder, int position, String boldAccountName) {
+            this(transaction, null, holder, position, boldAccountName);
+        }
+        TransactionLoaderParams(LedgerTransaction transaction,
+                                LedgerTransaction previousTransaction, TransactionRowHolder holder,
                                 int position, String boldAccountName) {
             this.transaction = transaction;
+            this.previousTransaction = previousTransaction;
             this.holder = holder;
             this.position = position;
             this.boldAccountName = boldAccountName;
index 89b5602..53f48d3 100644 (file)
@@ -22,7 +22,7 @@ import android.os.AsyncTask;
 
 import net.ktnx.mobileledger.async.UpdateTransactionsTask;
 import net.ktnx.mobileledger.model.Data;
-import net.ktnx.mobileledger.model.LedgerTransaction;
+import net.ktnx.mobileledger.model.TransactionListItem;
 import net.ktnx.mobileledger.utils.ObservableValue;
 
 import java.util.List;
@@ -32,22 +32,22 @@ public class TransactionListViewModel extends ViewModel {
 
     public static void scheduleTransactionListReload() {
         String filter = TransactionListFragment.accountFilter.get();
-        AsyncTask<String, Void, List<LedgerTransaction>> task = new UTT();
+        AsyncTask<String, Void, List<TransactionListItem>> task = new UTT();
         task.execute(filter);
     }
-    public static LedgerTransaction getTransaction(int position) {
-        List<LedgerTransaction> transactions = Data.transactions.get();
+    public static TransactionListItem getTransactionListItem(int position) {
+        List<TransactionListItem> transactions = Data.transactions.get();
         if (position >= transactions.size()) return null;
         return transactions.get(position);
     }
     public static int getTransactionCount() {
-        List<LedgerTransaction> transactions = Data.transactions.get();
+        List<TransactionListItem> transactions = Data.transactions.get();
         if (transactions == null) return 0;
         return transactions.size();
     }
     private static class UTT extends UpdateTransactionsTask {
         @Override
-        protected void onPostExecute(List<LedgerTransaction> list) {
+        protected void onPostExecute(List<TransactionListItem> list) {
             super.onPostExecute(list);
             if (list != null) Data.transactions.set(list);
         }
index e524358..6b9bd40 100644 (file)
@@ -29,12 +29,14 @@ class TransactionLoaderStep {
     private LedgerTransactionAccount account;
     private int accountPosition;
     private String boldAccountName;
+    private boolean showDate;
     public TransactionLoaderStep(TransactionRowHolder holder, int position,
-                                 LedgerTransaction transaction) {
+                                 LedgerTransaction transaction, boolean showDate) {
         this.step = TransactionListAdapter.LoaderStep.HEAD;
         this.holder = holder;
         this.transaction = transaction;
         this.position = position;
+        this.showDate = showDate;
     }
     public TransactionLoaderStep(TransactionRowHolder holder, LedgerTransactionAccount account,
                                  int accountPosition, String boldAccountName) {
@@ -74,4 +76,7 @@ class TransactionLoaderStep {
     public LedgerTransactionAccount getAccount() {
         return account;
     }
+    public boolean isDateShown() {
+        return showDate;
+    }
 }
index 5e97596..f23311a 100644 (file)
@@ -19,6 +19,7 @@ package net.ktnx.mobileledger.ui.transaction_list;
 
 import android.support.annotation.NonNull;
 import android.support.constraint.ConstraintLayout;
+import android.support.v7.widget.CardView;
 import android.support.v7.widget.RecyclerView;
 import android.view.View;
 import android.widget.LinearLayout;
@@ -27,14 +28,20 @@ import android.widget.TextView;
 import net.ktnx.mobileledger.R;
 
 class TransactionRowHolder extends RecyclerView.ViewHolder {
-    TextView tvDescription, tvDate;
+    TextView tvDescription;
     LinearLayout tableAccounts;
     ConstraintLayout row;
+    ConstraintLayout vDelimiter;
+    CardView vTransaction;
+    TextView tvDelimiterMonth, tvDelimiterDate;
     public TransactionRowHolder(@NonNull View itemView) {
         super(itemView);
         this.row = itemView.findViewById(R.id.transaction_row);
         this.tvDescription = itemView.findViewById(R.id.transaction_row_description);
-        this.tvDate = itemView.findViewById(R.id.transaction_row_date);
         this.tableAccounts = itemView.findViewById(R.id.transaction_row_acc_amounts);
+        this.vDelimiter = itemView.findViewById(R.id.transaction_delimiter);
+        this.vTransaction = itemView.findViewById(R.id.transaction_card_view);
+        this.tvDelimiterDate = itemView.findViewById(R.id.transaction_delimiter_date);
+        this.tvDelimiterMonth = itemView.findViewById(R.id.transaction_delimiter_month);
     }
 }
index 4cf2b75..30dec94 100644 (file)
@@ -23,6 +23,11 @@ import android.support.annotation.ColorInt;
 import android.view.View;
 import android.view.inputmethod.InputMethodManager;
 
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+
 public final class Globals {
     @ColorInt
     public static int tableRowEvenBG;
@@ -30,6 +35,7 @@ public final class Globals {
     public static int tableRowOddBG;
     @ColorInt
     public static int primaryDark, defaultTextColor;
+    public static String[] monthNames;
     public static void hideSoftKeyboard(Activity act) {
         // hide the keyboard
         View v = act.getCurrentFocus();
@@ -39,5 +45,16 @@ public final class Globals {
             imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
         }
     }
-
+    private static SimpleDateFormat ledgerDateFormatter = new SimpleDateFormat("y/M/d", Locale.US);
+    public static Date parseLedgerDate(String dateString) {
+        try {
+            return ledgerDateFormatter.parse(dateString);
+        }
+        catch (ParseException e) {
+            throw new RuntimeException(String.format("Error parsing date '%s'", dateString), e);
+        }
+    }
+    public static String formatLedgerDate(Date date) {
+        return ledgerDateFormatter.format(date);
+    }
 }
\ No newline at end of file
index f2ed757..775dc79 100644 (file)
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 
 <!--
-  ~ Copyright © 2018 Damyan Ivanov.
+  ~ Copyright © 2019 Damyan Ivanov.
   ~ This file is part of Mobile-Ledger.
   ~ Mobile-Ledger is free software: you can distribute it and/or modify it
   ~ under the term of the GNU General Public License as published by
   ~ along with Mobile-Ledger. If not, see <https://www.gnu.org/licenses/>.
   -->
 
-<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
+<android.support.v7.widget.ContentFrameLayout 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="wrap_content"
-    android:layout_margin="8dp"
-    app:cardCornerRadius="16dp"
-    app:cardElevation="4dp"
-    app:cardUseCompatPadding="false"
-    app:layout_constraintEnd_toEndOf="parent"
-    app:layout_constraintStart_toStartOf="parent"
-    app:layout_goneMarginBottom="8dp">
+    android:layout_height="wrap_content">
 
-    <android.support.constraint.ConstraintLayout
-        android:id="@+id/transaction_row"
+    <android.support.v7.widget.CardView
+        android:id="@+id/transaction_card_view"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:gravity="center_vertical"
-        android:minHeight="36dp"
-        android:orientation="horizontal"
-        android:padding="8dp">
-
-        <LinearLayout
-            android:id="@+id/transaction_row_head"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:orientation="horizontal"
-            app:layout_constraintEnd_toEndOf="parent"
-            app:layout_constraintStart_toStartOf="parent">
+        android:layout_margin="8dp"
+        android:visibility="gone"
+        app:cardCornerRadius="16dp"
+        app:cardElevation="4dp"
+        app:cardUseCompatPadding="false"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_goneMarginBottom="8dp">
 
-            <TextView
-                android:id="@+id/transaction_row_description"
-                style="@style/account_summary_account_name"
-                android:layout_width="0dp"
-                android:layout_height="wrap_content"
-                android:layout_weight="5"
-                android:text="---."
-                android:textStyle="bold"
-                tools:ignore="HardcodedText" />
-
-            <TextView
-                android:id="@+id/transaction_row_date"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_marginEnd="0dp"
-                app:layout_constraintEnd_toEndOf="parent" />
-        </LinearLayout>
-
-        <LinearLayout
-            android:id="@+id/transaction_row_header_border"
+        <android.support.constraint.ConstraintLayout
+            android:id="@+id/transaction_row"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:background="@drawable/dashed_border_1dp"
-            android:minHeight="2dp"
+            android:gravity="center_vertical"
+            android:minHeight="36dp"
             android:orientation="horizontal"
-            app:layout_constraintEnd_toEndOf="parent"
-            app:layout_constraintStart_toStartOf="parent"
-            app:layout_constraintTop_toBottomOf="@+id/transaction_row_head" />
-
-        <LinearLayout
-            android:id="@+id/transaction_row_acc_amounts"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_weight="5"
-            android:orientation="vertical"
-            app:layout_constraintEnd_toEndOf="parent"
-            app:layout_constraintStart_toStartOf="parent"
-            app:layout_constraintTop_toBottomOf="@+id/transaction_row_header_border">
+            android:padding="8dp">
 
             <LinearLayout
+                android:id="@+id/transaction_row_head"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                android:gravity="center_vertical"
                 android:orientation="horizontal"
-                android:paddingStart="8dp"
-                android:paddingEnd="0dp">
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintStart_toStartOf="parent">
 
                 <TextView
+                    android:id="@+id/transaction_row_description"
+                    style="@style/account_summary_account_name"
                     android:layout_width="0dp"
                     android:layout_height="wrap_content"
                     android:layout_weight="5"
-                    android:text="---"
-                    android:textAlignment="viewStart"
+                    android:text="---."
+                    android:textStyle="bold"
                     tools:ignore="HardcodedText" />
 
-                <TextView
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:layout_marginEnd="0dp"
-                    android:minWidth="60dp"
-                    android:text="€ --,--"
-                    android:textAlignment="viewEnd"
-                    tools:ignore="HardcodedText" />
             </LinearLayout>
 
             <LinearLayout
+                android:id="@+id/transaction_row_header_border"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                android:gravity="center_vertical"
+                android:background="@drawable/dashed_border_1dp"
+                android:minHeight="2dp"
                 android:orientation="horizontal"
-                android:paddingStart="8dp"
-                android:paddingEnd="0dp">
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toBottomOf="@+id/transaction_row_head" />
 
-                <TextView
-                    android:layout_width="0dp"
+            <LinearLayout
+                android:id="@+id/transaction_row_acc_amounts"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_weight="5"
+                android:orientation="vertical"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toBottomOf="@+id/transaction_row_header_border">
+
+                <LinearLayout
+                    android:layout_width="match_parent"
                     android:layout_height="wrap_content"
-                    android:layout_weight="5"
-                    android:text="---"
-                    android:textAlignment="viewStart"
-                    tools:ignore="HardcodedText" />
+                    android:gravity="center_vertical"
+                    android:orientation="horizontal"
+                    android:paddingStart="8dp"
+                    android:paddingEnd="0dp">
 
-                <TextView
-                    android:layout_width="wrap_content"
+                    <TextView
+                        android:layout_width="0dp"
+                        android:layout_height="wrap_content"
+                        android:layout_weight="5"
+                        android:text="---"
+                        android:textAlignment="viewStart"
+                        tools:ignore="HardcodedText" />
+
+                    <TextView
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:layout_marginEnd="0dp"
+                        android:minWidth="60dp"
+                        android:text="€ --,--"
+                        android:textAlignment="viewEnd"
+                        tools:ignore="HardcodedText" />
+                </LinearLayout>
+
+                <LinearLayout
+                    android:layout_width="match_parent"
                     android:layout_height="wrap_content"
-                    android:layout_marginEnd="0dp"
-                    android:minWidth="60dp"
-                    android:text="---,--"
-                    android:textAlignment="viewEnd"
-                    tools:ignore="HardcodedText" />
+                    android:gravity="center_vertical"
+                    android:orientation="horizontal"
+                    android:paddingStart="8dp"
+                    android:paddingEnd="0dp">
+
+                    <TextView
+                        android:layout_width="0dp"
+                        android:layout_height="wrap_content"
+                        android:layout_weight="5"
+                        android:text="---"
+                        android:textAlignment="viewStart"
+                        tools:ignore="HardcodedText" />
+
+                    <TextView
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:layout_marginEnd="0dp"
+                        android:minWidth="60dp"
+                        android:text="---,--"
+                        android:textAlignment="viewEnd"
+                        tools:ignore="HardcodedText" />
+                </LinearLayout>
             </LinearLayout>
-        </LinearLayout>
+
+        </android.support.constraint.ConstraintLayout>
+    </android.support.v7.widget.CardView>
+
+    <android.support.constraint.ConstraintLayout
+        android:id="@+id/transaction_delimiter"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="8dp"
+        android:layout_marginTop="16dp"
+        android:layout_marginEnd="8dp"
+        android:foregroundGravity="center_vertical"
+        android:orientation="horizontal">
+
+        <TextView
+            android:id="@+id/transaction_delimiter_month"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:text="---------"
+            android:textStyle="bold"
+            app:layout_constraintStart_toStartOf="parent"
+            tools:ignore="HardcodedText" />
+
+        <TextView
+            android:id="@+id/transaction_delimiter_date"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:text="--.--.----"
+            android:textStyle="bold"
+            app:layout_constraintEnd_toEndOf="parent"
+            tools:ignore="HardcodedText" />
+
+        <View
+            android:id="@+id/view"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:layout_marginStart="8dp"
+            android:layout_marginEnd="8dp"
+            android:background="@drawable/dashed_border_1dp"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toStartOf="@id/transaction_delimiter_date"
+            app:layout_constraintStart_toEndOf="@id/transaction_delimiter_month"
+            app:layout_constraintTop_toTopOf="parent" />
 
     </android.support.constraint.ConstraintLayout>
-</android.support.v7.widget.CardView>
+</android.support.v7.widget.ContentFrameLayout>
\ No newline at end of file
index b9ad9e7..805c12b 100644 (file)
     <string name="new_profile_title">Нов профил</string>
     <string name="delete_profile">Изтриване на профила</string>
     <string name="delete">Изтриване</string>
+    <string-array name="month_names">
+        <item>Януари</item>
+        <item>Февруари</item>
+        <item>Март</item>
+        <item>Април</item>
+        <item>Май</item>
+        <item>Юни</item>
+        <item>Юли</item>
+        <item>Август</item>
+        <item>Септември</item>
+        <item>Октомври</item>
+        <item>Ноември</item>
+        <item>Декември</item>
+    </string-array>
+
 </resources>
\ No newline at end of file
index c054669..2390373 100644 (file)
     <string name="new_profile_title" type="id">New profile</string>
     <string name="delete_profile">Delete profile</string>
     <string name="delete">Delete</string>
+    <string-array name="month_names">
+        <item>January</item>
+        <item>February</item>
+        <item>Match</item>
+        <item>April</item>
+        <item>May</item>
+        <item>June</item>
+        <item>July</item>
+        <item>August</item>
+        <item>September</item>
+        <item>October</item>
+        <item>November</item>
+        <item>December</item>
+    </string-array>
 </resources>