From 2d2b1b42f0c9551e838bfa1ea2794c0d37913452 Mon Sep 17 00:00:00 2001
From: sononio
Date: Mon, 8 Jan 2018 23:10:40 +0300
Subject: [PATCH 01/43] CSV Export button added
---
app/src/main/res/layout/fragment_export_form.xml | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/app/src/main/res/layout/fragment_export_form.xml b/app/src/main/res/layout/fragment_export_form.xml
index 162b3f58f..45cb91273 100644
--- a/app/src/main/res/layout/fragment_export_form.xml
+++ b/app/src/main/res/layout/fragment_export_form.xml
@@ -91,7 +91,13 @@
+
Date: Tue, 9 Jan 2018 00:28:40 +0300
Subject: [PATCH 02/43] CSV Export completed
---
.../android/export/ExportAsyncTask.java | 6 +-
.../gnucash/android/export/ExportFormat.java | 5 +-
.../android/export/csv/CsvExporter.java | 206 ++++++++++++++++++
.../android/ui/export/ExportFormFragment.java | 9 +
4 files changed, 223 insertions(+), 3 deletions(-)
create mode 100644 app/src/main/java/org/gnucash/android/export/csv/CsvExporter.java
diff --git a/app/src/main/java/org/gnucash/android/export/ExportAsyncTask.java b/app/src/main/java/org/gnucash/android/export/ExportAsyncTask.java
index 9b7b9fb3f..d267bdb5f 100644
--- a/app/src/main/java/org/gnucash/android/export/ExportAsyncTask.java
+++ b/app/src/main/java/org/gnucash/android/export/ExportAsyncTask.java
@@ -57,6 +57,7 @@
import org.gnucash.android.db.adapter.DatabaseAdapter;
import org.gnucash.android.db.adapter.SplitsDbAdapter;
import org.gnucash.android.db.adapter.TransactionsDbAdapter;
+import org.gnucash.android.export.csv.CsvExporter;
import org.gnucash.android.export.ofx.OfxExporter;
import org.gnucash.android.export.qif.QifExporter;
import org.gnucash.android.export.xml.GncXmlExporter;
@@ -217,7 +218,7 @@ private void dismissProgressDialog() {
/**
* Returns an exporter corresponding to the user settings.
- * @return Object of one of {@link QifExporter}, {@link OfxExporter} or {@link GncXmlExporter}
+ * @return Object of one of {@link QifExporter}, {@link OfxExporter} or {@link GncXmlExporter} or {@Link CsvExporter}
*/
private Exporter getExporter() {
switch (mExportParams.getExportFormat()) {
@@ -228,8 +229,9 @@ private Exporter getExporter() {
return new OfxExporter(mExportParams, mDb);
case XML:
- default:
return new GncXmlExporter(mExportParams, mDb);
+ default:
+ return new CsvExporter(mExportParams, mDb);
}
}
diff --git a/app/src/main/java/org/gnucash/android/export/ExportFormat.java b/app/src/main/java/org/gnucash/android/export/ExportFormat.java
index 99ca1a347..d86bd94be 100644
--- a/app/src/main/java/org/gnucash/android/export/ExportFormat.java
+++ b/app/src/main/java/org/gnucash/android/export/ExportFormat.java
@@ -22,7 +22,8 @@
public enum ExportFormat {
QIF("Quicken Interchange Format"),
OFX("Open Financial eXchange"),
- XML("GnuCash XML");
+ XML("GnuCash XML"),
+ CSV("GnuCash CSV");
/**
* Full name of the export format acronym
@@ -45,6 +46,8 @@ public String getExtension(){
return ".ofx";
case XML:
return ".gnca";
+ case CSV:
+ return ".csv";
default:
return ".txt";
}
diff --git a/app/src/main/java/org/gnucash/android/export/csv/CsvExporter.java b/app/src/main/java/org/gnucash/android/export/csv/CsvExporter.java
new file mode 100644
index 000000000..afe00b5e8
--- /dev/null
+++ b/app/src/main/java/org/gnucash/android/export/csv/CsvExporter.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 2014 - 2015 Ngewi Fet
+ * Copyright (c) 2014 Yongxin Wang
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gnucash.android.export.csv;
+
+import org.gnucash.android.export.xml.*;
+
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.net.Uri;
+import android.util.Log;
+
+import com.crashlytics.android.Crashlytics;
+
+import org.gnucash.android.app.GnuCashApplication;
+import org.gnucash.android.db.DatabaseSchema;
+import org.gnucash.android.db.adapter.BooksDbAdapter;
+import org.gnucash.android.db.adapter.CommoditiesDbAdapter;
+import org.gnucash.android.db.adapter.RecurrenceDbAdapter;
+import org.gnucash.android.db.adapter.TransactionsDbAdapter;
+import org.gnucash.android.export.ExportFormat;
+import org.gnucash.android.export.ExportParams;
+import org.gnucash.android.export.Exporter;
+import org.gnucash.android.model.Account;
+import org.gnucash.android.model.AccountType;
+import org.gnucash.android.model.BaseModel;
+import org.gnucash.android.model.Book;
+import org.gnucash.android.model.Budget;
+import org.gnucash.android.model.BudgetAmount;
+import org.gnucash.android.model.Commodity;
+import org.gnucash.android.model.Money;
+import org.gnucash.android.model.PeriodType;
+import org.gnucash.android.model.Price;
+import org.gnucash.android.model.Recurrence;
+import org.gnucash.android.model.ScheduledAction;
+import org.gnucash.android.model.Split;
+import org.gnucash.android.model.Transaction;
+import org.gnucash.android.model.TransactionType;
+import org.gnucash.android.util.BookUtils;
+import org.gnucash.android.util.TimestampHelper;
+import org.xmlpull.v1.XmlPullParserFactory;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.BufferedOutputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.math.BigDecimal;
+import java.sql.Timestamp;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.zip.GZIPOutputStream;
+
+import static org.gnucash.android.db.DatabaseSchema.ScheduledActionEntry;
+import static org.gnucash.android.db.DatabaseSchema.SplitEntry;
+import static org.gnucash.android.db.DatabaseSchema.TransactionEntry;
+
+/**
+ * Creates a GnuCash XML representation of the accounts and transactions
+ *
+ * @author Ngewi Fet
+ * @author Yongxin Wang
+ */
+public class CsvExporter extends Exporter{
+
+ /**
+ * Root account for template accounts
+ */
+ private Account mRootTemplateAccount;
+ private Map mTransactionToTemplateAccountMap = new TreeMap<>();
+
+ /**
+ * Construct a new exporter with export parameters
+ * @param params Parameters for the export
+ */
+ public CsvExporter(ExportParams params) {
+ super(params, null);
+ LOG_TAG = "GncXmlExporter";
+ }
+
+ /**
+ * Overloaded constructor.
+ * Creates an exporter with an already open database instance.
+ * @param params Parameters for the export
+ * @param db SQLite database
+ */
+ public CsvExporter(ExportParams params, SQLiteDatabase db) {
+ super(params, db);
+ LOG_TAG = "GncXmlExporter";
+ }
+
+ @Override
+ public List generateExport() throws ExporterException {
+ OutputStreamWriter writer = null;
+ String outputFile = getExportCacheFilePath();
+ try {
+ FileOutputStream fileOutputStream = new FileOutputStream(outputFile);
+ BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
+ writer = new OutputStreamWriter(bufferedOutputStream);
+
+ generateExport(writer);
+ } catch (IOException ex){
+ Crashlytics.log("Error exporting XML");
+ Crashlytics.logException(ex);
+ } finally {
+ if (writer != null) {
+ try {
+ writer.close();
+ } catch (IOException e) {
+ throw new ExporterException(mExportParams, e);
+ }
+ }
+ }
+
+ List exportedFiles = new ArrayList<>();
+ exportedFiles.add(outputFile);
+
+ return exportedFiles;
+ }
+
+ public void generateExport(final Writer writer) throws ExporterException {
+ try {
+ String separator = ",";
+ List names = new ArrayList();
+ names.add("type");
+ names.add("full_name");
+ names.add("name");
+ names.add("code");
+ names.add("description");
+ names.add("color");
+ names.add("notes");
+ names.add("commoditym");
+ names.add("commodityn");
+ names.add("hidden");
+ names.add("tax");
+ names.add("place_holder");
+
+
+ List transactions = mTransactionsDbAdapter.getAllTransactions();
+ List budgets = mBudgetsDbAdapter.getAllRecords();
+ List accounts = mAccountsDbAdapter.getAllRecords();
+ List commodities = mCommoditiesDbAdapter.getAllRecords();
+ List prices = mPricesDbAdapter.getAllRecords();
+ List scheduledActions = mScheduledActionDbAdapter.getAllRecords();
+ List splits = mSplitsDbAdapter.getAllRecords();
+
+
+ for(int i = 0; i < names.size(); i++) {
+ writer.write(names.get(i) + separator);
+ }
+ writer.write('\n');
+ for(int i = 0; i < accounts.size(); i++) {
+ Account account = accounts.get(i);
+
+ writer.write(account.getAccountType().toString() + separator);
+ writer.write(account.getFullName() + separator);
+ writer.write(account.getName() + separator);
+
+ //Code
+ writer.write(separator);
+
+ writer.write(account.getDescription() + separator);
+ writer.write(account.getColor() + separator);
+
+ //Notes
+ writer.write(separator);
+
+ writer.write(account.getCommodity().getCurrencyCode() + separator);
+ writer.write("CURRENCY" + separator);
+ writer.write(account.isHidden()?"T":"F" + separator);
+
+ //Not exactly
+ writer.write("F" + separator);
+
+ writer.write(account.isPlaceholderAccount()?"T":"F" + separator);
+
+
+ //writer.write();
+
+ writer.write('\n');
+ }
+ } catch (Exception e) {
+ Crashlytics.logException(e);
+ throw new ExporterException(mExportParams, e);
+ }
+ }
+}
diff --git a/app/src/main/java/org/gnucash/android/ui/export/ExportFormFragment.java b/app/src/main/java/org/gnucash/android/ui/export/ExportFormFragment.java
index 90503473f..2449792b4 100644
--- a/app/src/main/java/org/gnucash/android/ui/export/ExportFormFragment.java
+++ b/app/src/main/java/org/gnucash/android/ui/export/ExportFormFragment.java
@@ -137,6 +137,7 @@ public class ExportFormFragment extends Fragment implements
@BindView(R.id.radio_ofx_format) RadioButton mOfxRadioButton;
@BindView(R.id.radio_qif_format) RadioButton mQifRadioButton;
@BindView(R.id.radio_xml_format) RadioButton mXmlRadioButton;
+ @BindView(R.id.radio_csv_format) RadioButton mCsVRadioButton;
@BindView(R.id.recurrence_options) View mRecurrenceOptionsView;
/**
@@ -204,6 +205,12 @@ private void onRadioButtonClicked(View view){
mExportWarningTextView.setText(R.string.export_warning_xml);
mExportDateLayout.setVisibility(View.GONE);
break;
+
+ case R.id.radio_csv_format:
+ mExportFormat = ExportFormat.CSV;
+ mExportWarningTextView.setText("");
+ mExportDateLayout.setVisibility(View.INVISIBLE);
+ break;
}
}
@@ -464,12 +471,14 @@ public void onClick(View view) {
mOfxRadioButton.setOnClickListener(radioClickListener);
mQifRadioButton.setOnClickListener(radioClickListener);
mXmlRadioButton.setOnClickListener(radioClickListener);
+ mCsVRadioButton.setOnClickListener(radioClickListener);
ExportFormat defaultFormat = ExportFormat.valueOf(defaultExportFormat.toUpperCase());
switch (defaultFormat){
case QIF: mQifRadioButton.performClick(); break;
case OFX: mOfxRadioButton.performClick(); break;
case XML: mXmlRadioButton.performClick(); break;
+ case CSV: mCsVRadioButton.performClick(); break;
}
if (GnuCashApplication.isDoubleEntryEnabled()){
From 58a014e78fdcf6dd290bae872a24423c9ec54825 Mon Sep 17 00:00:00 2001
From: AngelicosPhosphoros
Date: Tue, 9 Jan 2018 11:58:11 +0300
Subject: [PATCH 03/43] Added CSV-formats to export_formats string resource
---
app/src/main/res/values-af-rZA/strings.xml | 2 ++
app/src/main/res/values-ar-rSA/strings.xml | 2 ++
app/src/main/res/values-ca-rES/strings.xml | 2 ++
app/src/main/res/values-cs-rCZ/strings.xml | 2 ++
app/src/main/res/values-de/strings.xml | 2 ++
app/src/main/res/values-el-rGR/strings.xml | 2 ++
app/src/main/res/values-en-rGB/strings.xml | 2 ++
app/src/main/res/values-es-rMX/strings.xml | 2 ++
app/src/main/res/values-es/strings.xml | 2 ++
app/src/main/res/values-fi-rFI/strings.xml | 2 ++
app/src/main/res/values-fr/strings.xml | 2 ++
app/src/main/res/values-hu-rHU/strings.xml | 2 ++
app/src/main/res/values-in-rID/strings.xml | 2 ++
app/src/main/res/values-it-rIT/strings.xml | 2 ++
app/src/main/res/values-iw-rIL/strings.xml | 2 ++
app/src/main/res/values-ja-rJP/strings.xml | 2 ++
app/src/main/res/values-ko-rKR/strings.xml | 2 ++
app/src/main/res/values-lv-rLV/strings.xml | 2 ++
app/src/main/res/values-nb/strings.xml | 2 ++
app/src/main/res/values-nl-rNL/strings.xml | 2 ++
app/src/main/res/values-no-rNO/strings.xml | 2 ++
app/src/main/res/values-pl-rPL/strings.xml | 2 ++
app/src/main/res/values-pt-rBR/strings.xml | 2 ++
app/src/main/res/values-pt-rPT/strings.xml | 2 ++
app/src/main/res/values-ro-rRO/strings.xml | 2 ++
app/src/main/res/values-ru/strings.xml | 2 ++
app/src/main/res/values-sr-rSP/strings.xml | 2 ++
app/src/main/res/values-sv-rSE/strings.xml | 2 ++
app/src/main/res/values-tr-rTR/strings.xml | 2 ++
app/src/main/res/values-uk-rUA/strings.xml | 2 ++
app/src/main/res/values-vi-rVN/strings.xml | 2 ++
app/src/main/res/values-zh-rCN/strings.xml | 2 ++
app/src/main/res/values-zh-rTW/strings.xml | 2 ++
app/src/main/res/values/strings.xml | 2 ++
34 files changed, 68 insertions(+)
diff --git a/app/src/main/res/values-af-rZA/strings.xml b/app/src/main/res/values-af-rZA/strings.xml
index 8bc5253ca..6ebabab18 100644
--- a/app/src/main/res/values-af-rZA/strings.xml
+++ b/app/src/main/res/values-af-rZA/strings.xml
@@ -179,6 +179,8 @@
- QIF
- OFX
- XML
+ - CSV-accounts
+ - CSV-transactions
Select a Color
diff --git a/app/src/main/res/values-ar-rSA/strings.xml b/app/src/main/res/values-ar-rSA/strings.xml
index 7ccf20003..2a1162b2b 100644
--- a/app/src/main/res/values-ar-rSA/strings.xml
+++ b/app/src/main/res/values-ar-rSA/strings.xml
@@ -183,6 +183,8 @@
- QIF
- OFX
- XML
+ - CSV-accounts
+ - CSV-transactions
Select a Color
diff --git a/app/src/main/res/values-ca-rES/strings.xml b/app/src/main/res/values-ca-rES/strings.xml
index c5bc4eb4a..e0759fc47 100644
--- a/app/src/main/res/values-ca-rES/strings.xml
+++ b/app/src/main/res/values-ca-rES/strings.xml
@@ -177,6 +177,8 @@
- QIF
- OFX
- XML
+ - CSV-comptes
+ - CSV-transaccions
Seleccioneu un color
diff --git a/app/src/main/res/values-cs-rCZ/strings.xml b/app/src/main/res/values-cs-rCZ/strings.xml
index dc5b11a03..474479365 100644
--- a/app/src/main/res/values-cs-rCZ/strings.xml
+++ b/app/src/main/res/values-cs-rCZ/strings.xml
@@ -176,6 +176,8 @@
- QIF
- OFX
- XML
+ - CSV-účty
+ - CSV-transakcí
Select a Color
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index a9529b883..0a5c06f49 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -176,6 +176,8 @@
- QIF
- OFX
- XML
+ - CSV-Konten
+ - CSV-Transaktionen
Farbe auswählen
diff --git a/app/src/main/res/values-el-rGR/strings.xml b/app/src/main/res/values-el-rGR/strings.xml
index 49f40617d..db79ae430 100644
--- a/app/src/main/res/values-el-rGR/strings.xml
+++ b/app/src/main/res/values-el-rGR/strings.xml
@@ -188,6 +188,8 @@
- QIF
- OFX
- XML
+ - CSV-λογαριασμούς
+ - CSV-συναλλαγών
Επιλογή χρώματος
diff --git a/app/src/main/res/values-en-rGB/strings.xml b/app/src/main/res/values-en-rGB/strings.xml
index 1715b6f83..dafd6ff32 100644
--- a/app/src/main/res/values-en-rGB/strings.xml
+++ b/app/src/main/res/values-en-rGB/strings.xml
@@ -179,6 +179,8 @@
- QIF
- OFX
- XML
+ - CSV-accounts
+ - CSV-transactions
Select a Color
diff --git a/app/src/main/res/values-es-rMX/strings.xml b/app/src/main/res/values-es-rMX/strings.xml
index 1d8d70660..08c0591b6 100644
--- a/app/src/main/res/values-es-rMX/strings.xml
+++ b/app/src/main/res/values-es-rMX/strings.xml
@@ -172,6 +172,8 @@
- QIF
- OFX
- XML
+ - CSV-cuentas
+ - CSV-actas
Seleccionar un color
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
index 994bd4661..9f12b30d3 100644
--- a/app/src/main/res/values-es/strings.xml
+++ b/app/src/main/res/values-es/strings.xml
@@ -172,6 +172,8 @@
- QIF
- OFX
- XML
+ - CSV-cuentas
+ - CSV-actas
Seleccione un color
diff --git a/app/src/main/res/values-fi-rFI/strings.xml b/app/src/main/res/values-fi-rFI/strings.xml
index 202c16fd2..6a033bf2b 100644
--- a/app/src/main/res/values-fi-rFI/strings.xml
+++ b/app/src/main/res/values-fi-rFI/strings.xml
@@ -179,6 +179,8 @@
- QIF
- OFX
- XML
+ - CSV-tilit
+ - CSV-liiketoimet
Select a Color
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
index 9ab498501..ac18b6f6a 100644
--- a/app/src/main/res/values-fr/strings.xml
+++ b/app/src/main/res/values-fr/strings.xml
@@ -171,6 +171,8 @@
- QIF
- OFX
- XML
+ - CSV-comptes
+ - CSV-transactions
Sélectionnez une couleur
diff --git a/app/src/main/res/values-hu-rHU/strings.xml b/app/src/main/res/values-hu-rHU/strings.xml
index 6fb7cee73..f52f03d47 100644
--- a/app/src/main/res/values-hu-rHU/strings.xml
+++ b/app/src/main/res/values-hu-rHU/strings.xml
@@ -179,6 +179,8 @@
- QIF
- OFX
- XML
+ - CSV-fiókok
+ - CSV-ügyletek
Select a Color
diff --git a/app/src/main/res/values-in-rID/strings.xml b/app/src/main/res/values-in-rID/strings.xml
index 32e24d46f..395f803a9 100644
--- a/app/src/main/res/values-in-rID/strings.xml
+++ b/app/src/main/res/values-in-rID/strings.xml
@@ -171,6 +171,8 @@
- QIF
- OFX
- XML
+ - CSV-accounts
+ - CSV-transactions
Pilih Warna
diff --git a/app/src/main/res/values-it-rIT/strings.xml b/app/src/main/res/values-it-rIT/strings.xml
index 421590559..be47b1120 100644
--- a/app/src/main/res/values-it-rIT/strings.xml
+++ b/app/src/main/res/values-it-rIT/strings.xml
@@ -176,6 +176,8 @@
- QIF
- OFX
- XML
+ - CSV-conti
+ - CSV-transazioni
Seleziona un colore
diff --git a/app/src/main/res/values-iw-rIL/strings.xml b/app/src/main/res/values-iw-rIL/strings.xml
index 2dd55c1a7..222a5b9ac 100644
--- a/app/src/main/res/values-iw-rIL/strings.xml
+++ b/app/src/main/res/values-iw-rIL/strings.xml
@@ -181,6 +181,8 @@
- QIF
- OFX
- XML
+ - CSV-accounts
+ - CSV-transactions
Select a Color
diff --git a/app/src/main/res/values-ja-rJP/strings.xml b/app/src/main/res/values-ja-rJP/strings.xml
index b5d7e1c9e..2518fb141 100644
--- a/app/src/main/res/values-ja-rJP/strings.xml
+++ b/app/src/main/res/values-ja-rJP/strings.xml
@@ -173,6 +173,8 @@
- QIF
- OFX
- XML
+ - CSV-アカウント
+ - CSV-取引
色を選択
diff --git a/app/src/main/res/values-ko-rKR/strings.xml b/app/src/main/res/values-ko-rKR/strings.xml
index f20d6f229..78d95474e 100644
--- a/app/src/main/res/values-ko-rKR/strings.xml
+++ b/app/src/main/res/values-ko-rKR/strings.xml
@@ -175,6 +175,8 @@
- QIF
- OFX
- XML
+ - CSV-계정
+ - CSV-업무
색상 선택
diff --git a/app/src/main/res/values-lv-rLV/strings.xml b/app/src/main/res/values-lv-rLV/strings.xml
index 3deabdcda..405e89843 100644
--- a/app/src/main/res/values-lv-rLV/strings.xml
+++ b/app/src/main/res/values-lv-rLV/strings.xml
@@ -180,6 +180,8 @@
- QIF
- OFX
- XML
+ - CSV-accounts
+ - CSV-transactions
Select a Color
diff --git a/app/src/main/res/values-nb/strings.xml b/app/src/main/res/values-nb/strings.xml
index 84f2daf11..c8b3c83c2 100644
--- a/app/src/main/res/values-nb/strings.xml
+++ b/app/src/main/res/values-nb/strings.xml
@@ -170,6 +170,8 @@
- QIF
- OFX
- XML
+ - CSV-kontoer
+ - CSV-transaksjoner
Velg en farge
diff --git a/app/src/main/res/values-nl-rNL/strings.xml b/app/src/main/res/values-nl-rNL/strings.xml
index 3da93b9d5..a8e998ae1 100644
--- a/app/src/main/res/values-nl-rNL/strings.xml
+++ b/app/src/main/res/values-nl-rNL/strings.xml
@@ -179,6 +179,8 @@
- QIF
- OFX
- XML
+ - CSV-rekeningen
+ - CSV-transacties
Kies een kleur
diff --git a/app/src/main/res/values-no-rNO/strings.xml b/app/src/main/res/values-no-rNO/strings.xml
index f4b098eec..abc690a6e 100644
--- a/app/src/main/res/values-no-rNO/strings.xml
+++ b/app/src/main/res/values-no-rNO/strings.xml
@@ -175,6 +175,8 @@
- QIF
- OFX
- XML
+ - CSV-kontoer
+ - CSV-transaksjoner
Velg en farge
diff --git a/app/src/main/res/values-pl-rPL/strings.xml b/app/src/main/res/values-pl-rPL/strings.xml
index 079d66f5c..8788b4e6d 100644
--- a/app/src/main/res/values-pl-rPL/strings.xml
+++ b/app/src/main/res/values-pl-rPL/strings.xml
@@ -177,6 +177,8 @@ Konto docelowe używa innej waluty niż konto wyjściowe
- QIF
- OFX
- XML
+ - CSV-konta
+ - CSV-transakcje
Wybierz kolor
diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml
index 2f3bfca4f..839ae4abf 100644
--- a/app/src/main/res/values-pt-rBR/strings.xml
+++ b/app/src/main/res/values-pt-rBR/strings.xml
@@ -174,6 +174,8 @@
- QIF
- OFX
- XML
+ - CSV-contas
+ - CSV-transações
Selecione uma Cor
diff --git a/app/src/main/res/values-pt-rPT/strings.xml b/app/src/main/res/values-pt-rPT/strings.xml
index fed5df41b..a0f5cda62 100644
--- a/app/src/main/res/values-pt-rPT/strings.xml
+++ b/app/src/main/res/values-pt-rPT/strings.xml
@@ -174,6 +174,8 @@
- QIF
- OFX
- XML
+ - CSV-contas
+ - CSV-transações
Escolha uma côr
diff --git a/app/src/main/res/values-ro-rRO/strings.xml b/app/src/main/res/values-ro-rRO/strings.xml
index ff9007834..3e86e8fd9 100644
--- a/app/src/main/res/values-ro-rRO/strings.xml
+++ b/app/src/main/res/values-ro-rRO/strings.xml
@@ -180,6 +180,8 @@
- QIF
- OFX
- XML
+ - CSV-conturi
+ - CSV-tranzacții
Select a Color
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index 9ba4502df..148176f2d 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -176,6 +176,8 @@
- QIF
- OFX
- XML
+ - CSV-счета
+ - CSV-транзакции
Выберите цвет
diff --git a/app/src/main/res/values-sr-rSP/strings.xml b/app/src/main/res/values-sr-rSP/strings.xml
index d7eb355dc..0c264592e 100644
--- a/app/src/main/res/values-sr-rSP/strings.xml
+++ b/app/src/main/res/values-sr-rSP/strings.xml
@@ -180,6 +180,8 @@
- QIF
- OFX
- XML
+ - CSV-рачуне
+ - CSV-трансакције
Select a Color
diff --git a/app/src/main/res/values-sv-rSE/strings.xml b/app/src/main/res/values-sv-rSE/strings.xml
index be8308fd5..d6067e6c3 100644
--- a/app/src/main/res/values-sv-rSE/strings.xml
+++ b/app/src/main/res/values-sv-rSE/strings.xml
@@ -170,6 +170,8 @@
- QIF
- OFX
- XML
+ - CSV-konton
+ - CSV-transaktioner
Välj en färg
diff --git a/app/src/main/res/values-tr-rTR/strings.xml b/app/src/main/res/values-tr-rTR/strings.xml
index 2c93676fd..e3b8f243a 100644
--- a/app/src/main/res/values-tr-rTR/strings.xml
+++ b/app/src/main/res/values-tr-rTR/strings.xml
@@ -179,6 +179,8 @@
- QIF
- OFX
- XML
+ - CSV-hesapları
+ - CSV-işlemler
Renk Seçin
diff --git a/app/src/main/res/values-uk-rUA/strings.xml b/app/src/main/res/values-uk-rUA/strings.xml
index b4aa3b352..0d919a5d2 100644
--- a/app/src/main/res/values-uk-rUA/strings.xml
+++ b/app/src/main/res/values-uk-rUA/strings.xml
@@ -176,6 +176,8 @@
- QIF
- OFX
- XML
+ - CSV-счета
+ - CSV-транзакции
Оберіть колір
diff --git a/app/src/main/res/values-vi-rVN/strings.xml b/app/src/main/res/values-vi-rVN/strings.xml
index 082e3fac1..d8a21f5bd 100644
--- a/app/src/main/res/values-vi-rVN/strings.xml
+++ b/app/src/main/res/values-vi-rVN/strings.xml
@@ -178,6 +178,8 @@
- QIF
- OFX
- XML
+ - CSV-tài khoản
+ - CSV-giao dịch
Select a Color
diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml
index 60a54c00c..9b7eaebe4 100644
--- a/app/src/main/res/values-zh-rCN/strings.xml
+++ b/app/src/main/res/values-zh-rCN/strings.xml
@@ -175,6 +175,8 @@
- QIF
- OFX
- XML
+ - CSV-账户
+ - CSV-交易
选择一种颜色
diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml
index 70dd0e16b..4c2bbce04 100644
--- a/app/src/main/res/values-zh-rTW/strings.xml
+++ b/app/src/main/res/values-zh-rTW/strings.xml
@@ -175,6 +175,8 @@
- QIF
- OFX
- XML
+ - CSV-账户
+ - CSV-交易
选择一种颜色
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 624257895..4c0901836 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -179,6 +179,8 @@
- QIF
- OFX
- XML
+ - CSV-accounts
+ - CSV-transactions
Select a Color
From 2a439c59a768757bfc1faa8b053142502d3883fc Mon Sep 17 00:00:00 2001
From: AngelicosPhosphoros
Date: Tue, 9 Jan 2018 12:12:50 +0300
Subject: [PATCH 04/43] Make change of CSV export name
---
.../java/org/gnucash/android/ui/export/ExportFormFragment.java | 2 ++
1 file changed, 2 insertions(+)
diff --git a/app/src/main/java/org/gnucash/android/ui/export/ExportFormFragment.java b/app/src/main/java/org/gnucash/android/ui/export/ExportFormFragment.java
index 2449792b4..e8f49d268 100644
--- a/app/src/main/java/org/gnucash/android/ui/export/ExportFormFragment.java
+++ b/app/src/main/java/org/gnucash/android/ui/export/ExportFormFragment.java
@@ -223,6 +223,8 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container,
bindViewListeners();
+ mCsVRadioButton.setText(getResources().getStringArray(R.array.export_formats)[3]);
+
return view;
}
@Override
From 4fca1c5487a74d05f283047685b3c99e662838e4 Mon Sep 17 00:00:00 2001
From: AngelicosPhosphoros
Date: Tue, 9 Jan 2018 13:01:28 +0300
Subject: [PATCH 05/43] Added buttons for CSV. Added texts import from
resources.
---
.../android/ui/export/ExportFormFragment.java | 13 ++++++++-----
app/src/main/res/layout/fragment_export_form.xml | 12 +++++++++---
2 files changed, 17 insertions(+), 8 deletions(-)
diff --git a/app/src/main/java/org/gnucash/android/ui/export/ExportFormFragment.java b/app/src/main/java/org/gnucash/android/ui/export/ExportFormFragment.java
index e8f49d268..c2c403633 100644
--- a/app/src/main/java/org/gnucash/android/ui/export/ExportFormFragment.java
+++ b/app/src/main/java/org/gnucash/android/ui/export/ExportFormFragment.java
@@ -137,7 +137,8 @@ public class ExportFormFragment extends Fragment implements
@BindView(R.id.radio_ofx_format) RadioButton mOfxRadioButton;
@BindView(R.id.radio_qif_format) RadioButton mQifRadioButton;
@BindView(R.id.radio_xml_format) RadioButton mXmlRadioButton;
- @BindView(R.id.radio_csv_format) RadioButton mCsVRadioButton;
+ @BindView(R.id.radio_csv_accounts_format) RadioButton mCsvAccountsRadioButton;
+ @BindView(R.id.radio_csv_transactions_format) RadioButton mCsvTransactionsRadioButton;
@BindView(R.id.recurrence_options) View mRecurrenceOptionsView;
/**
@@ -206,7 +207,7 @@ private void onRadioButtonClicked(View view){
mExportDateLayout.setVisibility(View.GONE);
break;
- case R.id.radio_csv_format:
+ case R.id.radio_csv_accounts_format:
mExportFormat = ExportFormat.CSV;
mExportWarningTextView.setText("");
mExportDateLayout.setVisibility(View.INVISIBLE);
@@ -223,7 +224,9 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container,
bindViewListeners();
- mCsVRadioButton.setText(getResources().getStringArray(R.array.export_formats)[3]);
+ String[] export_format_strings = getResources().getStringArray(R.array.export_formats);
+ mCsvAccountsRadioButton.setText(export_format_strings[3]);
+ mCsvTransactionsRadioButton.setText(export_format_strings[4]);
return view;
}
@@ -473,14 +476,14 @@ public void onClick(View view) {
mOfxRadioButton.setOnClickListener(radioClickListener);
mQifRadioButton.setOnClickListener(radioClickListener);
mXmlRadioButton.setOnClickListener(radioClickListener);
- mCsVRadioButton.setOnClickListener(radioClickListener);
+ mCsvAccountsRadioButton.setOnClickListener(radioClickListener);
ExportFormat defaultFormat = ExportFormat.valueOf(defaultExportFormat.toUpperCase());
switch (defaultFormat){
case QIF: mQifRadioButton.performClick(); break;
case OFX: mOfxRadioButton.performClick(); break;
case XML: mXmlRadioButton.performClick(); break;
- case CSV: mCsVRadioButton.performClick(); break;
+ case CSV: mCsvAccountsRadioButton.performClick(); break;
}
if (GnuCashApplication.isDoubleEntryEnabled()){
diff --git a/app/src/main/res/layout/fragment_export_form.xml b/app/src/main/res/layout/fragment_export_form.xml
index 45cb91273..49e6a6897 100644
--- a/app/src/main/res/layout/fragment_export_form.xml
+++ b/app/src/main/res/layout/fragment_export_form.xml
@@ -73,7 +73,7 @@
android:layout_height="wrap_content"
android:layout_marginLeft="35dp"
android:gravity="center_vertical"
- android:orientation="horizontal">
+ android:orientation="vertical">
-
+ android:text="CSV-acc"/>
+
From 7198d7df77898999794f3903fa6a3dfe33a82f1d Mon Sep 17 00:00:00 2001
From: sononio
Date: Tue, 9 Jan 2018 21:15:27 +0300
Subject: [PATCH 06/43] CSV Export transactions added
---
.../android/export/ExportAsyncTask.java | 9 +-
.../gnucash/android/export/ExportFormat.java | 7 +-
.../gnucash/android/export/ExportParams.java | 21 ++
.../org/gnucash/android/export/Exporter.java | 5 +-
...vExporter.java => CsvAccountExporter.java} | 38 +--
.../export/csv/CsvTransactionsExporter.java | 237 ++++++++++++++++++
.../gnucash/android/export/csv/CsvWriter.java | 48 ++++
.../android/ui/export/ExportFormFragment.java | 40 ++-
.../main/res/layout/fragment_export_form.xml | 42 ++++
9 files changed, 420 insertions(+), 27 deletions(-)
rename app/src/main/java/org/gnucash/android/export/csv/{CsvExporter.java => CsvAccountExporter.java} (86%)
create mode 100644 app/src/main/java/org/gnucash/android/export/csv/CsvTransactionsExporter.java
create mode 100644 app/src/main/java/org/gnucash/android/export/csv/CsvWriter.java
diff --git a/app/src/main/java/org/gnucash/android/export/ExportAsyncTask.java b/app/src/main/java/org/gnucash/android/export/ExportAsyncTask.java
index d267bdb5f..aec60b471 100644
--- a/app/src/main/java/org/gnucash/android/export/ExportAsyncTask.java
+++ b/app/src/main/java/org/gnucash/android/export/ExportAsyncTask.java
@@ -57,7 +57,8 @@
import org.gnucash.android.db.adapter.DatabaseAdapter;
import org.gnucash.android.db.adapter.SplitsDbAdapter;
import org.gnucash.android.db.adapter.TransactionsDbAdapter;
-import org.gnucash.android.export.csv.CsvExporter;
+import org.gnucash.android.export.csv.CsvAccountExporter;
+import org.gnucash.android.export.csv.CsvTransactionsExporter;
import org.gnucash.android.export.ofx.OfxExporter;
import org.gnucash.android.export.qif.QifExporter;
import org.gnucash.android.export.xml.GncXmlExporter;
@@ -218,7 +219,7 @@ private void dismissProgressDialog() {
/**
* Returns an exporter corresponding to the user settings.
- * @return Object of one of {@link QifExporter}, {@link OfxExporter} or {@link GncXmlExporter} or {@Link CsvExporter}
+ * @return Object of one of {@link QifExporter}, {@link OfxExporter} or {@link GncXmlExporter} or {@Link CsvAccountExporter}
*/
private Exporter getExporter() {
switch (mExportParams.getExportFormat()) {
@@ -230,8 +231,10 @@ private Exporter getExporter() {
case XML:
return new GncXmlExporter(mExportParams, mDb);
+ case CSVA:
+ return new CsvAccountExporter(mExportParams, mDb);
default:
- return new CsvExporter(mExportParams, mDb);
+ return new CsvTransactionsExporter(mExportParams, mDb);
}
}
diff --git a/app/src/main/java/org/gnucash/android/export/ExportFormat.java b/app/src/main/java/org/gnucash/android/export/ExportFormat.java
index d86bd94be..4e30654fa 100644
--- a/app/src/main/java/org/gnucash/android/export/ExportFormat.java
+++ b/app/src/main/java/org/gnucash/android/export/ExportFormat.java
@@ -23,7 +23,8 @@ public enum ExportFormat {
QIF("Quicken Interchange Format"),
OFX("Open Financial eXchange"),
XML("GnuCash XML"),
- CSV("GnuCash CSV");
+ CSVA("GnuCash accounts CSV"),
+ CSVT("GnuCash transactions CSV");
/**
* Full name of the export format acronym
@@ -46,7 +47,9 @@ public String getExtension(){
return ".ofx";
case XML:
return ".gnca";
- case CSV:
+ case CSVA:
+ return ".csv";
+ case CSVT:
return ".csv";
default:
return ".txt";
diff --git a/app/src/main/java/org/gnucash/android/export/ExportParams.java b/app/src/main/java/org/gnucash/android/export/ExportParams.java
index 90c4e4221..e85ad3e8f 100644
--- a/app/src/main/java/org/gnucash/android/export/ExportParams.java
+++ b/app/src/main/java/org/gnucash/android/export/ExportParams.java
@@ -78,6 +78,11 @@ public String getDescription(){
*/
private String mExportLocation;
+ /**
+ * CSV-separator char
+ */
+ private char mCsvSeparator = ',';
+
/**
* Creates a new set of paramters and specifies the export format
* @param format Format to use when exporting the transactions
@@ -169,6 +174,22 @@ public void setExportLocation(String exportLocation){
mExportLocation = exportLocation;
}
+ /**
+ * Get the CSV-separator char
+ * @return CSV-separator char
+ */
+ public char getCsvSeparator(){
+ return mCsvSeparator;
+ }
+
+ /**
+ * Set the CSV-separator char
+ * @param separator CSV-separator char
+ */
+ public void setCsvSeparator(char separator) {
+ mCsvSeparator = separator;
+ }
+
@Override
public String toString() {
return "Export all transactions created since " + TimestampHelper.getUtcStringFromTimestamp(mExportStartTime) + " UTC"
diff --git a/app/src/main/java/org/gnucash/android/export/Exporter.java b/app/src/main/java/org/gnucash/android/export/Exporter.java
index 87c60d569..51e2fc9e3 100644
--- a/app/src/main/java/org/gnucash/android/export/Exporter.java
+++ b/app/src/main/java/org/gnucash/android/export/Exporter.java
@@ -160,7 +160,10 @@ public static String sanitizeFilename(String inputName) {
*/
public static String buildExportFilename(ExportFormat format, String bookName) {
return EXPORT_FILENAME_DATE_FORMAT.format(new Date(System.currentTimeMillis()))
- + "_gnucash_export_" + sanitizeFilename(bookName) + format.getExtension();
+ + "_gnucash_export_" + sanitizeFilename(bookName) +
+ (format==ExportFormat.CSVA?"_accounts_":"") +
+ (format==ExportFormat.CSVT?"_transactions_":"") +
+ format.getExtension();
}
/**
diff --git a/app/src/main/java/org/gnucash/android/export/csv/CsvExporter.java b/app/src/main/java/org/gnucash/android/export/csv/CsvAccountExporter.java
similarity index 86%
rename from app/src/main/java/org/gnucash/android/export/csv/CsvExporter.java
rename to app/src/main/java/org/gnucash/android/export/csv/CsvAccountExporter.java
index afe00b5e8..13582ed1a 100644
--- a/app/src/main/java/org/gnucash/android/export/csv/CsvExporter.java
+++ b/app/src/main/java/org/gnucash/android/export/csv/CsvAccountExporter.java
@@ -1,6 +1,5 @@
/*
- * Copyright (c) 2014 - 2015 Ngewi Fet
- * Copyright (c) 2014 Yongxin Wang
+ * Copyright (c) 2018 Semyannikov Gleb
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -75,25 +74,26 @@
import static org.gnucash.android.db.DatabaseSchema.TransactionEntry;
/**
- * Creates a GnuCash XML representation of the accounts and transactions
+ * Creates a GnuCash CSV account representation of the accounts and transactions
*
- * @author Ngewi Fet
- * @author Yongxin Wang
+ * @author Semyannikov Gleb
*/
-public class CsvExporter extends Exporter{
+public class CsvAccountExporter extends Exporter{
/**
* Root account for template accounts
*/
private Account mRootTemplateAccount;
private Map mTransactionToTemplateAccountMap = new TreeMap<>();
+ private char mCsvSeparator;
/**
* Construct a new exporter with export parameters
* @param params Parameters for the export
*/
- public CsvExporter(ExportParams params) {
+ public CsvAccountExporter(ExportParams params) {
super(params, null);
+ mCsvSeparator = params.getCsvSeparator();
LOG_TAG = "GncXmlExporter";
}
@@ -103,28 +103,30 @@ public CsvExporter(ExportParams params) {
* @param params Parameters for the export
* @param db SQLite database
*/
- public CsvExporter(ExportParams params, SQLiteDatabase db) {
+ public CsvAccountExporter(ExportParams params, SQLiteDatabase db) {
super(params, db);
+ mCsvSeparator = params.getCsvSeparator();
LOG_TAG = "GncXmlExporter";
}
@Override
public List generateExport() throws ExporterException {
- OutputStreamWriter writer = null;
+ OutputStreamWriter writerStream = null;
+ CsvWriter writer = null;
String outputFile = getExportCacheFilePath();
try {
FileOutputStream fileOutputStream = new FileOutputStream(outputFile);
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
- writer = new OutputStreamWriter(bufferedOutputStream);
-
+ writerStream = new OutputStreamWriter(bufferedOutputStream);
+ writer = new CsvWriter(writerStream);
generateExport(writer);
} catch (IOException ex){
- Crashlytics.log("Error exporting XML");
+ Crashlytics.log("Error exporting CSV");
Crashlytics.logException(ex);
} finally {
- if (writer != null) {
+ if (writerStream != null) {
try {
- writer.close();
+ writerStream.close();
} catch (IOException e) {
throw new ExporterException(mExportParams, e);
}
@@ -137,9 +139,9 @@ public List generateExport() throws ExporterException {
return exportedFiles;
}
- public void generateExport(final Writer writer) throws ExporterException {
+ public void generateExport(final CsvWriter writer) throws ExporterException {
try {
- String separator = ",";
+ String separator = mCsvSeparator + "";
List names = new ArrayList();
names.add("type");
names.add("full_name");
@@ -167,7 +169,7 @@ public void generateExport(final Writer writer) throws ExporterException {
for(int i = 0; i < names.size(); i++) {
writer.write(names.get(i) + separator);
}
- writer.write('\n');
+ writer.write("\n");
for(int i = 0; i < accounts.size(); i++) {
Account account = accounts.get(i);
@@ -196,7 +198,7 @@ public void generateExport(final Writer writer) throws ExporterException {
//writer.write();
- writer.write('\n');
+ writer.write("\n");
}
} catch (Exception e) {
Crashlytics.logException(e);
diff --git a/app/src/main/java/org/gnucash/android/export/csv/CsvTransactionsExporter.java b/app/src/main/java/org/gnucash/android/export/csv/CsvTransactionsExporter.java
new file mode 100644
index 000000000..6e8c7ae63
--- /dev/null
+++ b/app/src/main/java/org/gnucash/android/export/csv/CsvTransactionsExporter.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright (c) 2018 Semyannikov Gleb
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gnucash.android.export.csv;
+
+import org.gnucash.android.export.xml.*;
+
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.net.Uri;
+import android.util.Log;
+
+import com.crashlytics.android.Crashlytics;
+
+import org.gnucash.android.app.GnuCashApplication;
+import org.gnucash.android.db.DatabaseSchema;
+import org.gnucash.android.db.adapter.BooksDbAdapter;
+import org.gnucash.android.db.adapter.CommoditiesDbAdapter;
+import org.gnucash.android.db.adapter.RecurrenceDbAdapter;
+import org.gnucash.android.db.adapter.TransactionsDbAdapter;
+import org.gnucash.android.export.ExportFormat;
+import org.gnucash.android.export.ExportParams;
+import org.gnucash.android.export.Exporter;
+import org.gnucash.android.model.Account;
+import org.gnucash.android.model.AccountType;
+import org.gnucash.android.model.BaseModel;
+import org.gnucash.android.model.Book;
+import org.gnucash.android.model.Budget;
+import org.gnucash.android.model.BudgetAmount;
+import org.gnucash.android.model.Commodity;
+import org.gnucash.android.model.Money;
+import org.gnucash.android.model.PeriodType;
+import org.gnucash.android.model.Price;
+import org.gnucash.android.model.Recurrence;
+import org.gnucash.android.model.ScheduledAction;
+import org.gnucash.android.model.Split;
+import org.gnucash.android.model.Transaction;
+import org.gnucash.android.model.TransactionType;
+import org.gnucash.android.util.BookUtils;
+import org.gnucash.android.util.TimestampHelper;
+import org.xmlpull.v1.XmlPullParserFactory;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.BufferedOutputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.math.BigDecimal;
+import java.sql.Timestamp;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.zip.GZIPOutputStream;
+
+import static org.gnucash.android.db.DatabaseSchema.ScheduledActionEntry;
+import static org.gnucash.android.db.DatabaseSchema.SplitEntry;
+import static org.gnucash.android.db.DatabaseSchema.TransactionEntry;
+
+/**
+ * Creates a GnuCash CSV transactions representation of the accounts and transactions
+ *
+ * @author Semyannikov Gleb
+ */
+public class CsvTransactionsExporter extends Exporter{
+
+ /**
+ * Root account for template accounts
+ */
+ private Account mRootTemplateAccount;
+ private Map mTransactionToTemplateAccountMap = new TreeMap<>();
+ private char mCsvSeparator;
+
+ /**
+ * Construct a new exporter with export parameters
+ * @param params Parameters for the export
+ */
+ public CsvTransactionsExporter(ExportParams params) {
+ super(params, null);
+ mCsvSeparator = params.getCsvSeparator();
+ LOG_TAG = "GncXmlExporter";
+ }
+
+ /**
+ * Overloaded constructor.
+ * Creates an exporter with an already open database instance.
+ * @param params Parameters for the export
+ * @param db SQLite database
+ */
+ public CsvTransactionsExporter(ExportParams params, SQLiteDatabase db) {
+ super(params, db);
+ mCsvSeparator = params.getCsvSeparator();
+ LOG_TAG = "GncXmlExporter";
+ }
+
+ @Override
+ public List generateExport() throws ExporterException {
+ OutputStreamWriter writerStream = null;
+ CsvWriter writer = null;
+ String outputFile = getExportCacheFilePath();
+ try {
+ FileOutputStream fileOutputStream = new FileOutputStream(outputFile);
+ BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
+ writerStream = new OutputStreamWriter(bufferedOutputStream);
+ writer = new CsvWriter(writerStream);
+ generateExport(writer);
+ } catch (IOException ex){
+ Crashlytics.log("Error exporting CSV");
+ Crashlytics.logException(ex);
+ } finally {
+ if (writerStream != null) {
+ try {
+ writerStream.close();
+ } catch (IOException e) {
+ throw new ExporterException(mExportParams, e);
+ }
+ }
+ }
+
+ List exportedFiles = new ArrayList<>();
+ exportedFiles.add(outputFile);
+
+ return exportedFiles;
+ }
+
+ public void generateExport(final CsvWriter writer) throws ExporterException {
+ try {
+ String separator = mCsvSeparator + "";
+ List names = new ArrayList();
+ names.add("Date");
+ names.add("Account name");
+ names.add("Number");
+ names.add("Description");
+ names.add("Notes");
+ names.add("Memo");
+ names.add("Category");
+ names.add("Type");
+ names.add("Action");
+ names.add("Reconcile");
+ names.add("To With Sym");
+ names.add("From With Sym");
+ names.add("To Num.");
+ names.add("From Num.");
+ names.add("To Rate/Price");
+ names.add("From Rate/Price");
+
+
+ List transactions = mTransactionsDbAdapter.getAllTransactions();
+ //List budgets = mBudgetsDbAdapter.getAllRecords();
+ //List accounts = mAccountsDbAdapter.getAllRecords();
+ //List commodities = mCommoditiesDbAdapter.getAllRecords();
+ //List prices = mPricesDbAdapter.getAllRecords();
+ //List scheduledActions = mScheduledActionDbAdapter.getAllRecords();
+ //List splits = mSplitsDbAdapter.getAllRecords();
+
+
+ for(int i = 0; i < names.size(); i++) {
+ writer.write(names.get(i) + separator);
+ }
+ writer.write("\n");
+ for(int i = 0; i < transactions.size(); i++) {
+ Transaction transaction = transactions.get(i);
+ List splits = transaction.getSplits();
+ for (int j = 0; j < splits.size()/2; j++) {
+ Split split = splits.get(j);
+ Split pair = null;
+ for (int k = 0; k < splits.size(); k++) {
+ if (split.isPairOf(splits.get(k))) {
+ pair = splits.get(k);
+ }
+ }
+
+ Account account = mAccountsDbAdapter.getRecord(split.getAccountUID());
+ Account account_pair = null;
+ if (pair != null) {
+ account_pair = mAccountsDbAdapter.getRecord(pair.getAccountUID());
+ }
+
+ Date date = new Date(transaction.getTimeMillis());
+ DateFormat df = new SimpleDateFormat("dd/MM/yyyy");
+ writer.write(df.format(date) + separator);
+
+ writer.write(account.getName() + separator);
+
+ //Number
+ writer.write(separator);
+
+ writer.write(transaction.getDescription() + separator);
+
+ writer.write(transaction.getNote() + separator);
+
+ writer.write((split.getMemo()==null?"":split.getMemo()) + separator);
+
+ writer.write((account_pair.getName()==null?"":account_pair.getName()) + separator);
+
+ writer.write((split.getType().name()) + separator);
+
+ //Action
+ writer.write(separator);
+
+ writer.write(split.getReconcileState() + separator);
+
+ writer.write(split.getFormattedQuantity().toPlainString() + separator);
+ writer.write(separator);
+ writer.write(split.getFormattedQuantity().toPlainString() + separator);
+ writer.write(separator);
+ writer.write(separator);
+ writer.write("\n");
+ }
+ }
+
+ } catch (Exception e) {
+ Crashlytics.logException(e);
+ throw new ExporterException(mExportParams, e);
+ }
+ }
+}
diff --git a/app/src/main/java/org/gnucash/android/export/csv/CsvWriter.java b/app/src/main/java/org/gnucash/android/export/csv/CsvWriter.java
new file mode 100644
index 000000000..5e6570d37
--- /dev/null
+++ b/app/src/main/java/org/gnucash/android/export/csv/CsvWriter.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2018 Semyannikov Gleb
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gnucash.android.export.csv;
+
+
+import java.io.IOException;
+import java.io.Writer;
+
+/**
+ * Format data to be CSV-compatible
+ *
+ * @author Semyannikov Gleb
+ */
+public class CsvWriter {
+ private Writer writer;
+
+ public CsvWriter(Writer writer){
+ this.writer = writer;
+ }
+
+ public void write(String str) throws IOException {
+ if (str == null || str.length() < 1) {
+ return;
+ }
+
+ String head = str.substring(0, str.length() - 1);
+ char separator = str.charAt(str.length() - 1);
+ if (head.indexOf(separator) > -1) {
+ head = '"' + head + '"';
+ }
+
+ writer.write(head + separator);
+ }
+}
diff --git a/app/src/main/java/org/gnucash/android/ui/export/ExportFormFragment.java b/app/src/main/java/org/gnucash/android/ui/export/ExportFormFragment.java
index c2c403633..43ca6c6bc 100644
--- a/app/src/main/java/org/gnucash/android/ui/export/ExportFormFragment.java
+++ b/app/src/main/java/org/gnucash/android/ui/export/ExportFormFragment.java
@@ -133,6 +133,7 @@ public class ExportFormFragment extends Fragment implements
@BindView(R.id.switch_export_all) SwitchCompat mExportAllSwitch;
@BindView(R.id.export_date_layout) LinearLayout mExportDateLayout;
+ @BindView(R.id.export_separator_layout) LinearLayout mExportSeparatorLayout;
@BindView(R.id.radio_ofx_format) RadioButton mOfxRadioButton;
@BindView(R.id.radio_qif_format) RadioButton mQifRadioButton;
@@ -140,6 +141,10 @@ public class ExportFormFragment extends Fragment implements
@BindView(R.id.radio_csv_accounts_format) RadioButton mCsvAccountsRadioButton;
@BindView(R.id.radio_csv_transactions_format) RadioButton mCsvTransactionsRadioButton;
+ @BindView(R.id.radio_separator_comma_format) RadioButton mSeparatorCommaButton;
+ @BindView(R.id.radio_separator_colon_format) RadioButton mSeparatorColonButton;
+ @BindView(R.id.radio_separator_semicolon_format) RadioButton mSeparatorSemicolonButton;
+
@BindView(R.id.recurrence_options) View mRecurrenceOptionsView;
/**
* Event recurrence options
@@ -170,6 +175,8 @@ public class ExportFormFragment extends Fragment implements
*/
private Uri mExportUri;
+ private char mExportCsvSeparator = ',';
+
/**
* Flag to determine if export has been started.
* Used to continue export after user has picked a destination file
@@ -187,6 +194,7 @@ private void onRadioButtonClicked(View view){
mExportWarningTextView.setVisibility(View.GONE);
}
mExportDateLayout.setVisibility(View.VISIBLE);
+ mExportSeparatorLayout.setVisibility(View.GONE);
break;
case R.id.radio_qif_format:
@@ -199,18 +207,37 @@ private void onRadioButtonClicked(View view){
mExportWarningTextView.setVisibility(View.GONE);
}
mExportDateLayout.setVisibility(View.VISIBLE);
+ mExportSeparatorLayout.setVisibility(View.GONE);
break;
case R.id.radio_xml_format:
mExportFormat = ExportFormat.XML;
mExportWarningTextView.setText(R.string.export_warning_xml);
mExportDateLayout.setVisibility(View.GONE);
+ mExportSeparatorLayout.setVisibility(View.GONE);
break;
case R.id.radio_csv_accounts_format:
- mExportFormat = ExportFormat.CSV;
+ mExportFormat = ExportFormat.CSVA;
+ mExportWarningTextView.setText("");
+ mExportDateLayout.setVisibility(View.GONE);
+ mExportSeparatorLayout.setVisibility(View.VISIBLE);
+ break;
+ case R.id.radio_csv_transactions_format:
+ mExportFormat = ExportFormat.CSVT;
mExportWarningTextView.setText("");
- mExportDateLayout.setVisibility(View.INVISIBLE);
+ mExportDateLayout.setVisibility(View.GONE);
+ mExportSeparatorLayout.setVisibility(View.VISIBLE);
+ break;
+
+ case R.id.radio_separator_comma_format:
+ mExportCsvSeparator = ',';
+ break;
+ case R.id.radio_separator_colon_format:
+ mExportCsvSeparator = ':';
+ break;
+ case R.id.radio_separator_semicolon_format:
+ mExportCsvSeparator = ';';
break;
}
}
@@ -300,6 +327,7 @@ private void startExport(){
exportParameters.setExportTarget(mExportTarget);
exportParameters.setExportLocation(mExportUri != null ? mExportUri.toString() : null);
exportParameters.setDeleteTransactionsAfterExport(mDeleteAllCheckBox.isChecked());
+ exportParameters.setCsvSeparator(mExportCsvSeparator);
Log.i(TAG, "Commencing async export of transactions");
new ExportAsyncTask(getActivity(), GnuCashApplication.getActiveDb()).execute(exportParameters);
@@ -477,13 +505,19 @@ public void onClick(View view) {
mQifRadioButton.setOnClickListener(radioClickListener);
mXmlRadioButton.setOnClickListener(radioClickListener);
mCsvAccountsRadioButton.setOnClickListener(radioClickListener);
+ mCsvTransactionsRadioButton.setOnClickListener(radioClickListener);
+
+ mSeparatorCommaButton.setOnClickListener(radioClickListener);
+ mSeparatorColonButton.setOnClickListener(radioClickListener);
+ mSeparatorSemicolonButton.setOnClickListener(radioClickListener);
ExportFormat defaultFormat = ExportFormat.valueOf(defaultExportFormat.toUpperCase());
switch (defaultFormat){
case QIF: mQifRadioButton.performClick(); break;
case OFX: mOfxRadioButton.performClick(); break;
case XML: mXmlRadioButton.performClick(); break;
- case CSV: mCsvAccountsRadioButton.performClick(); break;
+ case CSVA: mCsvAccountsRadioButton.performClick(); break;
+ case CSVT: mCsvTransactionsRadioButton.performClick(); break;
}
if (GnuCashApplication.isDoubleEntryEnabled()){
diff --git a/app/src/main/res/layout/fragment_export_form.xml b/app/src/main/res/layout/fragment_export_form.xml
index 49e6a6897..65632118f 100644
--- a/app/src/main/res/layout/fragment_export_form.xml
+++ b/app/src/main/res/layout/fragment_export_form.xml
@@ -105,6 +105,48 @@
android:text="CSV-trans"/>
+
+
+
+
+
+
+
+
+
+
Date: Tue, 9 Jan 2018 21:34:13 +0300
Subject: [PATCH 07/43] CSV export contributors added
---
CONTRIBUTORS.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index fe12546fb..4bdb9a6c0 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -27,6 +27,7 @@ The following people (in alphabetical order) contributed (commits on GitHub) to
* Falk Brockmann
* Felipe Morato
* Geert Janssens
+* Gleb Semyannikov
* Jörg Möller
* Israel Buitron
* Jesse Shieh
@@ -54,6 +55,7 @@ The following people (in alphabetical order) contributed (commits on GitHub) to
* Stephan Windmüller
* Terry Chung
* thesebas thesebas@thesebas.net
+* Timur Khuzin
* Vladimir Rutsky
* Weslly Oliveira
* windwarrior lennartbuit@gmail.com
From 76c74cbfa77535538012d2ba70dfdb6076c9448a Mon Sep 17 00:00:00 2001
From: sononio
Date: Wed, 10 Jan 2018 23:09:44 +0300
Subject: [PATCH 08/43] CSV export refactor
---
.../android/export/ExportAsyncTask.java | 2 +-
.../export/csv/CsvAccountExporter.java | 62 -------------------
.../export/csv/CsvTransactionsExporter.java | 60 ------------------
3 files changed, 1 insertion(+), 123 deletions(-)
diff --git a/app/src/main/java/org/gnucash/android/export/ExportAsyncTask.java b/app/src/main/java/org/gnucash/android/export/ExportAsyncTask.java
index aec60b471..a72465c55 100644
--- a/app/src/main/java/org/gnucash/android/export/ExportAsyncTask.java
+++ b/app/src/main/java/org/gnucash/android/export/ExportAsyncTask.java
@@ -219,7 +219,7 @@ private void dismissProgressDialog() {
/**
* Returns an exporter corresponding to the user settings.
- * @return Object of one of {@link QifExporter}, {@link OfxExporter} or {@link GncXmlExporter} or {@Link CsvAccountExporter}
+ * @return Object of one of {@link QifExporter}, {@link OfxExporter} or {@link GncXmlExporter}, {@Link CsvAccountExporter} or {@Link CsvTransactionsExporter}
*/
private Exporter getExporter() {
switch (mExportParams.getExportFormat()) {
diff --git a/app/src/main/java/org/gnucash/android/export/csv/CsvAccountExporter.java b/app/src/main/java/org/gnucash/android/export/csv/CsvAccountExporter.java
index 13582ed1a..a5c4842e6 100644
--- a/app/src/main/java/org/gnucash/android/export/csv/CsvAccountExporter.java
+++ b/app/src/main/java/org/gnucash/android/export/csv/CsvAccountExporter.java
@@ -16,62 +16,18 @@
package org.gnucash.android.export.csv;
-import org.gnucash.android.export.xml.*;
-
-import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
-import android.net.Uri;
-import android.util.Log;
-
import com.crashlytics.android.Crashlytics;
-
-import org.gnucash.android.app.GnuCashApplication;
-import org.gnucash.android.db.DatabaseSchema;
-import org.gnucash.android.db.adapter.BooksDbAdapter;
-import org.gnucash.android.db.adapter.CommoditiesDbAdapter;
-import org.gnucash.android.db.adapter.RecurrenceDbAdapter;
-import org.gnucash.android.db.adapter.TransactionsDbAdapter;
-import org.gnucash.android.export.ExportFormat;
import org.gnucash.android.export.ExportParams;
import org.gnucash.android.export.Exporter;
import org.gnucash.android.model.Account;
-import org.gnucash.android.model.AccountType;
-import org.gnucash.android.model.BaseModel;
-import org.gnucash.android.model.Book;
-import org.gnucash.android.model.Budget;
-import org.gnucash.android.model.BudgetAmount;
-import org.gnucash.android.model.Commodity;
-import org.gnucash.android.model.Money;
-import org.gnucash.android.model.PeriodType;
-import org.gnucash.android.model.Price;
-import org.gnucash.android.model.Recurrence;
-import org.gnucash.android.model.ScheduledAction;
-import org.gnucash.android.model.Split;
-import org.gnucash.android.model.Transaction;
-import org.gnucash.android.model.TransactionType;
-import org.gnucash.android.util.BookUtils;
-import org.gnucash.android.util.TimestampHelper;
-import org.xmlpull.v1.XmlPullParserFactory;
-import org.xmlpull.v1.XmlSerializer;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
-import java.io.OutputStream;
import java.io.OutputStreamWriter;
-import java.io.Writer;
-import java.math.BigDecimal;
-import java.sql.Timestamp;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.List;
-import java.util.Map;
-import java.util.TreeMap;
-import java.util.zip.GZIPOutputStream;
-
-import static org.gnucash.android.db.DatabaseSchema.ScheduledActionEntry;
-import static org.gnucash.android.db.DatabaseSchema.SplitEntry;
-import static org.gnucash.android.db.DatabaseSchema.TransactionEntry;
/**
* Creates a GnuCash CSV account representation of the accounts and transactions
@@ -79,12 +35,6 @@
* @author Semyannikov Gleb
*/
public class CsvAccountExporter extends Exporter{
-
- /**
- * Root account for template accounts
- */
- private Account mRootTemplateAccount;
- private Map mTransactionToTemplateAccountMap = new TreeMap<>();
private char mCsvSeparator;
/**
@@ -156,15 +106,7 @@ public void generateExport(final CsvWriter writer) throws ExporterException {
names.add("tax");
names.add("place_holder");
-
- List transactions = mTransactionsDbAdapter.getAllTransactions();
- List budgets = mBudgetsDbAdapter.getAllRecords();
List accounts = mAccountsDbAdapter.getAllRecords();
- List commodities = mCommoditiesDbAdapter.getAllRecords();
- List prices = mPricesDbAdapter.getAllRecords();
- List scheduledActions = mScheduledActionDbAdapter.getAllRecords();
- List splits = mSplitsDbAdapter.getAllRecords();
-
for(int i = 0; i < names.size(); i++) {
writer.write(names.get(i) + separator);
@@ -190,14 +132,10 @@ public void generateExport(final CsvWriter writer) throws ExporterException {
writer.write("CURRENCY" + separator);
writer.write(account.isHidden()?"T":"F" + separator);
- //Not exactly
writer.write("F" + separator);
writer.write(account.isPlaceholderAccount()?"T":"F" + separator);
-
- //writer.write();
-
writer.write("\n");
}
} catch (Exception e) {
diff --git a/app/src/main/java/org/gnucash/android/export/csv/CsvTransactionsExporter.java b/app/src/main/java/org/gnucash/android/export/csv/CsvTransactionsExporter.java
index 6e8c7ae63..e306cf1bf 100644
--- a/app/src/main/java/org/gnucash/android/export/csv/CsvTransactionsExporter.java
+++ b/app/src/main/java/org/gnucash/android/export/csv/CsvTransactionsExporter.java
@@ -16,66 +16,23 @@
package org.gnucash.android.export.csv;
-import org.gnucash.android.export.xml.*;
-
-import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
-import android.net.Uri;
-import android.util.Log;
-
import com.crashlytics.android.Crashlytics;
-import org.gnucash.android.app.GnuCashApplication;
-import org.gnucash.android.db.DatabaseSchema;
-import org.gnucash.android.db.adapter.BooksDbAdapter;
-import org.gnucash.android.db.adapter.CommoditiesDbAdapter;
-import org.gnucash.android.db.adapter.RecurrenceDbAdapter;
-import org.gnucash.android.db.adapter.TransactionsDbAdapter;
-import org.gnucash.android.export.ExportFormat;
import org.gnucash.android.export.ExportParams;
import org.gnucash.android.export.Exporter;
import org.gnucash.android.model.Account;
-import org.gnucash.android.model.AccountType;
-import org.gnucash.android.model.BaseModel;
-import org.gnucash.android.model.Book;
-import org.gnucash.android.model.Budget;
-import org.gnucash.android.model.BudgetAmount;
-import org.gnucash.android.model.Commodity;
-import org.gnucash.android.model.Money;
-import org.gnucash.android.model.PeriodType;
-import org.gnucash.android.model.Price;
-import org.gnucash.android.model.Recurrence;
-import org.gnucash.android.model.ScheduledAction;
import org.gnucash.android.model.Split;
import org.gnucash.android.model.Transaction;
-import org.gnucash.android.model.TransactionType;
-import org.gnucash.android.util.BookUtils;
-import org.gnucash.android.util.TimestampHelper;
-import org.xmlpull.v1.XmlPullParserFactory;
-import org.xmlpull.v1.XmlSerializer;
-
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
-import java.io.OutputStream;
import java.io.OutputStreamWriter;
-import java.io.Writer;
-import java.math.BigDecimal;
-import java.sql.Timestamp;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.Collection;
import java.util.Date;
import java.util.List;
-import java.util.Map;
-import java.util.TreeMap;
-import java.util.zip.GZIPOutputStream;
-
-import static org.gnucash.android.db.DatabaseSchema.ScheduledActionEntry;
-import static org.gnucash.android.db.DatabaseSchema.SplitEntry;
-import static org.gnucash.android.db.DatabaseSchema.TransactionEntry;
/**
* Creates a GnuCash CSV transactions representation of the accounts and transactions
@@ -84,11 +41,6 @@
*/
public class CsvTransactionsExporter extends Exporter{
- /**
- * Root account for template accounts
- */
- private Account mRootTemplateAccount;
- private Map mTransactionToTemplateAccountMap = new TreeMap<>();
private char mCsvSeparator;
/**
@@ -164,15 +116,7 @@ public void generateExport(final CsvWriter writer) throws ExporterException {
names.add("To Rate/Price");
names.add("From Rate/Price");
-
List transactions = mTransactionsDbAdapter.getAllTransactions();
- //List budgets = mBudgetsDbAdapter.getAllRecords();
- //List accounts = mAccountsDbAdapter.getAllRecords();
- //List commodities = mCommoditiesDbAdapter.getAllRecords();
- //List prices = mPricesDbAdapter.getAllRecords();
- //List scheduledActions = mScheduledActionDbAdapter.getAllRecords();
- //List splits = mSplitsDbAdapter.getAllRecords();
-
for(int i = 0; i < names.size(); i++) {
writer.write(names.get(i) + separator);
@@ -206,13 +150,9 @@ public void generateExport(final CsvWriter writer) throws ExporterException {
writer.write(separator);
writer.write(transaction.getDescription() + separator);
-
writer.write(transaction.getNote() + separator);
-
writer.write((split.getMemo()==null?"":split.getMemo()) + separator);
-
writer.write((account_pair.getName()==null?"":account_pair.getName()) + separator);
-
writer.write((split.getType().name()) + separator);
//Action
From b657141f2bc61a1fefa7e9725babe3e344fb4ee9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=80lex=20Magaz=20Gra=C3=A7a?=
Date: Tue, 16 Jan 2018 19:51:10 +0100
Subject: [PATCH 09/43] Fix ScheduledActionService.autoBackup() not doing
backups of all books
It was doing a backup of just the active book.
It solves the problem only for the case in which the backup is created
in the default diretory. Pending to solve the case in which the user
specifies a target file.
---
.../org/gnucash/android/service/ScheduledActionService.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/src/main/java/org/gnucash/android/service/ScheduledActionService.java b/app/src/main/java/org/gnucash/android/service/ScheduledActionService.java
index 5c2470371..c8fcc6815 100644
--- a/app/src/main/java/org/gnucash/android/service/ScheduledActionService.java
+++ b/app/src/main/java/org/gnucash/android/service/ScheduledActionService.java
@@ -274,7 +274,7 @@ private static void autoBackup(){
for (String bookUID : bookUIDs) {
String backupFile = BookUtils.getBookBackupFileUri(bookUID);
if (backupFile == null){
- GncXmlExporter.createBackup();
+ GncXmlExporter.createBackup(bookUID);
continue;
}
From c02445208ecedce9d5be54f128d3d2fedd0b9424 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=80lex=20Magaz=20Gra=C3=A7a?=
Date: Sun, 14 Jan 2018 17:25:27 +0100
Subject: [PATCH 10/43] Move code to back up books into a new class
---
.../service/ScheduledActionService.java | 42 +-----------
.../gnucash/android/util/BackupManager.java | 54 +++++++++++++++
.../android/util/BackupManagerTest.java | 65 +++++++++++++++++++
3 files changed, 121 insertions(+), 40 deletions(-)
create mode 100644 app/src/main/java/org/gnucash/android/util/BackupManager.java
create mode 100644 app/src/test/java/org/gnucash/android/util/BackupManagerTest.java
diff --git a/app/src/main/java/org/gnucash/android/service/ScheduledActionService.java b/app/src/main/java/org/gnucash/android/service/ScheduledActionService.java
index c8fcc6815..3852212e5 100644
--- a/app/src/main/java/org/gnucash/android/service/ScheduledActionService.java
+++ b/app/src/main/java/org/gnucash/android/service/ScheduledActionService.java
@@ -18,10 +18,8 @@
import android.app.IntentService;
import android.content.ContentValues;
-import android.content.Context;
import android.content.Intent;
import android.database.sqlite.SQLiteDatabase;
-import android.net.Uri;
import android.os.PowerManager;
import android.support.annotation.VisibleForTesting;
import android.util.Log;
@@ -38,23 +36,17 @@
import org.gnucash.android.db.adapter.SplitsDbAdapter;
import org.gnucash.android.db.adapter.TransactionsDbAdapter;
import org.gnucash.android.export.ExportAsyncTask;
-import org.gnucash.android.export.ExportFormat;
import org.gnucash.android.export.ExportParams;
-import org.gnucash.android.export.xml.GncXmlExporter;
import org.gnucash.android.model.Book;
import org.gnucash.android.model.ScheduledAction;
import org.gnucash.android.model.Transaction;
-import org.gnucash.android.util.BookUtils;
+import org.gnucash.android.util.BackupManager;
-import java.io.BufferedOutputStream;
-import java.io.IOException;
-import java.io.OutputStreamWriter;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ExecutionException;
-import java.util.zip.GZIPOutputStream;
/**
* Service for running scheduled events.
@@ -79,7 +71,7 @@ protected void onHandleIntent(Intent intent) {
PowerManager.WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, LOG_TAG);
wakeLock.acquire();
- autoBackup(); //First run automatic backup of all books before doing anything else
+ BackupManager.backupAllBooks(); //First run automatic backup of all books before doing anything else
try {
BooksDbAdapter booksDbAdapter = BooksDbAdapter.getInstance();
List books = booksDbAdapter.getAllRecords();
@@ -261,34 +253,4 @@ private static int executeTransactions(ScheduledAction scheduledAction, SQLiteDa
scheduledAction.setExecutionCount(previousExecutionCount);
return executionCount;
}
-
- /**
- * Perform an automatic backup of all books in the database.
- * This method is run everytime the service is executed
- */
- private static void autoBackup(){
- BooksDbAdapter booksDbAdapter = BooksDbAdapter.getInstance();
- List bookUIDs = booksDbAdapter.getAllBookUIDs();
- Context context = GnuCashApplication.getAppContext();
-
- for (String bookUID : bookUIDs) {
- String backupFile = BookUtils.getBookBackupFileUri(bookUID);
- if (backupFile == null){
- GncXmlExporter.createBackup(bookUID);
- continue;
- }
-
- try (BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(context.getContentResolver().openOutputStream(Uri.parse(backupFile)))){
- GZIPOutputStream gzipOutputStream = new GZIPOutputStream(bufferedOutputStream);
- OutputStreamWriter writer = new OutputStreamWriter(gzipOutputStream);
- ExportParams params = new ExportParams(ExportFormat.XML);
- new GncXmlExporter(params).generateExport(writer);
- writer.close();
- } catch (IOException ex) {
- Log.e(LOG_TAG, "Auto backup failed for book " + bookUID);
- ex.printStackTrace();
- Crashlytics.logException(ex);
- }
- }
- }
}
diff --git a/app/src/main/java/org/gnucash/android/util/BackupManager.java b/app/src/main/java/org/gnucash/android/util/BackupManager.java
new file mode 100644
index 000000000..5e4473ecd
--- /dev/null
+++ b/app/src/main/java/org/gnucash/android/util/BackupManager.java
@@ -0,0 +1,54 @@
+package org.gnucash.android.util;
+
+import android.content.Context;
+import android.net.Uri;
+import android.util.Log;
+
+import com.crashlytics.android.Crashlytics;
+
+import org.gnucash.android.app.GnuCashApplication;
+import org.gnucash.android.db.adapter.BooksDbAdapter;
+import org.gnucash.android.export.ExportFormat;
+import org.gnucash.android.export.ExportParams;
+import org.gnucash.android.export.xml.GncXmlExporter;
+
+import java.io.BufferedOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.util.List;
+import java.util.zip.GZIPOutputStream;
+
+public class BackupManager {
+ private static final String LOG_TAG = "BackupManager";
+
+ /**
+ * Perform an automatic backup of all books in the database.
+ * This method is run everytime the service is executed
+ */
+ public static void backupAllBooks() {
+ BooksDbAdapter booksDbAdapter = BooksDbAdapter.getInstance();
+ List bookUIDs = booksDbAdapter.getAllBookUIDs();
+ Context context = GnuCashApplication.getAppContext();
+
+ for (String bookUID : bookUIDs) {
+ String backupFile = BookUtils.getBookBackupFileUri(bookUID);
+ if (backupFile == null){
+ GncXmlExporter.createBackup(bookUID);
+ continue;
+ }
+
+ try (BufferedOutputStream bufferedOutputStream =
+ new BufferedOutputStream(context.getContentResolver().openOutputStream(Uri.parse(backupFile)))){
+ GZIPOutputStream gzipOutputStream = new GZIPOutputStream(bufferedOutputStream);
+ OutputStreamWriter writer = new OutputStreamWriter(gzipOutputStream);
+ ExportParams params = new ExportParams(ExportFormat.XML);
+ new GncXmlExporter(params).generateExport(writer);
+ writer.close();
+ } catch (IOException ex) {
+ Log.e(LOG_TAG, "Auto backup failed for book " + bookUID);
+ ex.printStackTrace();
+ Crashlytics.logException(ex);
+ }
+ }
+ }
+}
diff --git a/app/src/test/java/org/gnucash/android/util/BackupManagerTest.java b/app/src/test/java/org/gnucash/android/util/BackupManagerTest.java
new file mode 100644
index 000000000..eef2caa23
--- /dev/null
+++ b/app/src/test/java/org/gnucash/android/util/BackupManagerTest.java
@@ -0,0 +1,65 @@
+package org.gnucash.android.util;
+
+import org.gnucash.android.R;
+import org.gnucash.android.app.GnuCashApplication;
+import org.gnucash.android.db.adapter.BooksDbAdapter;
+import org.gnucash.android.export.Exporter;
+import org.gnucash.android.importer.GncXmlImporter;
+import org.gnucash.android.test.unit.testutil.ShadowCrashlytics;
+import org.gnucash.android.test.unit.testutil.ShadowUserVoice;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+import org.xml.sax.SAXException;
+
+import java.io.IOException;
+
+import javax.xml.parsers.ParserConfigurationException;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+
+@RunWith(RobolectricTestRunner.class) //package is required so that resources can be found in dev mode
+@Config(sdk = 21, packageName = "org.gnucash.android",
+ shadows = {ShadowCrashlytics.class, ShadowUserVoice.class})
+public class BackupManagerTest {
+ private BooksDbAdapter mBooksDbAdapter;
+
+ @Before
+ public void setUp() throws Exception {
+ mBooksDbAdapter = BooksDbAdapter.getInstance();
+ mBooksDbAdapter.deleteAllRecords();
+ assertThat(mBooksDbAdapter.getRecordsCount()).isEqualTo(0);
+ }
+
+ @Test
+ public void backupAllBooks() throws Exception {
+ String activeBookUID = createNewBookWithDefaultAccounts();
+ BookUtils.activateBook(activeBookUID);
+ createNewBookWithDefaultAccounts();
+ assertThat(mBooksDbAdapter.getRecordsCount()).isEqualTo(2);
+
+ BackupManager.backupAllBooks();
+
+ for (String bookUID : mBooksDbAdapter.getAllBookUIDs()) {
+ File backupFolder = new File(Exporter.getBackupFolderPath(bookUID));
+ assertThat(backupFolder.list().length).isEqualTo(1);
+ }
+ }
+
+ /**
+ * Creates a new database with default accounts
+ * @return The book UID for the new database
+ * @throws RuntimeException if the new books could not be created
+ */
+ private String createNewBookWithDefaultAccounts(){
+ try {
+ return GncXmlImporter.parse(GnuCashApplication.getAppContext().getResources().openRawResource(R.raw.default_accounts));
+ } catch (ParserConfigurationException | SAXException | IOException e) {
+ e.printStackTrace();
+ throw new RuntimeException("Could not create default accounts");
+ }
+ }
+}
\ No newline at end of file
From 52045c4839d99870533512baf00bc88ac03d6d87 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=80lex=20Magaz=20Gra=C3=A7a?=
Date: Sat, 20 Jan 2018 12:36:51 +0100
Subject: [PATCH 11/43] Move backup code from GncXmlExporter to BackupManager
---
.../android/export/ExportAsyncTask.java | 3 +-
.../android/export/xml/GncXmlExporter.java | 51 ----------------
.../android/ui/account/AccountsActivity.java | 11 +---
.../ui/settings/BackupPreferenceFragment.java | 4 +-
.../DeleteAllAccountsConfirmationDialog.java | 4 +-
...leteAllTransactionsConfirmationDialog.java | 4 +-
...tionsDeleteConfirmationDialogFragment.java | 4 +-
.../gnucash/android/util/BackupManager.java | 58 ++++++++++++++++++-
.../android/test/unit/export/BackupTest.java | 7 +--
9 files changed, 71 insertions(+), 75 deletions(-)
diff --git a/app/src/main/java/org/gnucash/android/export/ExportAsyncTask.java b/app/src/main/java/org/gnucash/android/export/ExportAsyncTask.java
index d6279aefe..fa66aefd9 100644
--- a/app/src/main/java/org/gnucash/android/export/ExportAsyncTask.java
+++ b/app/src/main/java/org/gnucash/android/export/ExportAsyncTask.java
@@ -64,6 +64,7 @@
import org.gnucash.android.ui.account.AccountsListFragment;
import org.gnucash.android.ui.settings.BackupPreferenceFragment;
import org.gnucash.android.ui.transaction.TransactionsActivity;
+import org.gnucash.android.util.BackupManager;
import java.io.File;
import java.io.FileInputStream;
@@ -453,7 +454,7 @@ private String stripPathPart(String fullPathName) {
*/
private void backupAndDeleteTransactions(){
Log.i(TAG, "Backup and deleting transactions after export");
- GncXmlExporter.createBackup(); //create backup before deleting everything
+ BackupManager.createBackup(); //create backup before deleting everything
List openingBalances = new ArrayList<>();
boolean preserveOpeningBalances = GnuCashApplication.shouldSaveOpeningBalances(false);
diff --git a/app/src/main/java/org/gnucash/android/export/xml/GncXmlExporter.java b/app/src/main/java/org/gnucash/android/export/xml/GncXmlExporter.java
index 8782cc47a..d10268e30 100644
--- a/app/src/main/java/org/gnucash/android/export/xml/GncXmlExporter.java
+++ b/app/src/main/java/org/gnucash/android/export/xml/GncXmlExporter.java
@@ -908,55 +908,4 @@ public String getExportMimeType(){
return "text/xml";
}
- /**
- * Creates a backup of current database contents to the directory {@link Exporter#getBackupFolderPath(String)}
- * @return {@code true} if backup was successful, {@code false} otherwise
- */
- public static boolean createBackup(){
- return createBackup(BooksDbAdapter.getInstance().getActiveBookUID());
- }
-
- /**
- * Create a backup of the book in the default backup location
- * @param bookUID Unique ID of the book
- * @return {@code true} if backup was successful, {@code false} otherwise
- */
- public static boolean createBackup(String bookUID){
- OutputStream outputStream;
- try {
- String backupFile = BookUtils.getBookBackupFileUri(bookUID);
- if (backupFile != null){
- outputStream = GnuCashApplication.getAppContext().getContentResolver().openOutputStream(Uri.parse(backupFile));
- } else { //no Uri set by user, use default location on SD card
- backupFile = getBackupFilePath(bookUID);
- outputStream = new FileOutputStream(backupFile);
- }
-
- BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream);
- GZIPOutputStream gzipOutputStream = new GZIPOutputStream(bufferedOutputStream);
- OutputStreamWriter writer = new OutputStreamWriter(gzipOutputStream);
-
- ExportParams params = new ExportParams(ExportFormat.XML);
- new GncXmlExporter(params).generateExport(writer);
- writer.close();
- return true;
- } catch (IOException | ExporterException e) {
- Crashlytics.logException(e);
- Log.e("GncXmlExporter", "Error creating XML backup", e);
- return false;
- }
- }
-
- /**
- * Returns the full path of a file to make database backup of the specified book.
- * Backups are done in XML format and are Gzipped (with ".gnca" extension).
- * @param bookUID GUID of the book
- * @return the file path for backups of the database.
- * @see #getBackupFolderPath(String)
- */
- private static String getBackupFilePath(String bookUID){
- Book book = BooksDbAdapter.getInstance().getRecord(bookUID);
- return Exporter.getBackupFolderPath(book.getUID())
- + buildExportFilename(ExportFormat.XML, book.getDisplayName());
- }
}
diff --git a/app/src/main/java/org/gnucash/android/ui/account/AccountsActivity.java b/app/src/main/java/org/gnucash/android/ui/account/AccountsActivity.java
index 0dd960656..5c20d3934 100644
--- a/app/src/main/java/org/gnucash/android/ui/account/AccountsActivity.java
+++ b/app/src/main/java/org/gnucash/android/ui/account/AccountsActivity.java
@@ -17,8 +17,6 @@
package org.gnucash.android.ui.account;
-import android.Manifest;
-import android.annotation.TargetApi;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.ActivityNotFoundException;
@@ -28,15 +26,12 @@
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
import android.net.Uri;
-import android.os.Build;
import android.os.Bundle;
import android.support.design.widget.CoordinatorLayout;
import android.support.design.widget.FloatingActionButton;
-import android.support.design.widget.Snackbar;
import android.support.design.widget.TabLayout;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
@@ -62,7 +57,6 @@
import org.gnucash.android.db.DatabaseSchema;
import org.gnucash.android.db.adapter.AccountsDbAdapter;
import org.gnucash.android.db.adapter.BooksDbAdapter;
-import org.gnucash.android.export.xml.GncXmlExporter;
import org.gnucash.android.importer.ImportAsyncTask;
import org.gnucash.android.ui.common.BaseDrawerActivity;
import org.gnucash.android.ui.common.FormActivity;
@@ -71,6 +65,7 @@
import org.gnucash.android.ui.transaction.TransactionsActivity;
import org.gnucash.android.ui.util.TaskDelegate;
import org.gnucash.android.ui.wizard.FirstRunWizardActivity;
+import org.gnucash.android.util.BackupManager;
import butterknife.BindView;
@@ -292,7 +287,7 @@ private void handleOpenFileIntent(Intent intent) {
//when someone launches the app to view a (.gnucash or .gnca) file
Uri data = intent.getData();
if (data != null){
- GncXmlExporter.createBackup();
+ BackupManager.createBackup();
intent.setData(null);
new ImportAsyncTask(this).execute(data);
removeFirstRunFlag();
@@ -504,7 +499,7 @@ public static void startXmlFileChooser(Fragment fragment) {
* @param onFinishTask Task to be executed when import is complete
*/
public static void importXmlFileFromIntent(Activity context, Intent data, TaskDelegate onFinishTask) {
- GncXmlExporter.createBackup();
+ BackupManager.createBackup();
new ImportAsyncTask(context, onFinishTask).execute(data.getData());
}
diff --git a/app/src/main/java/org/gnucash/android/ui/settings/BackupPreferenceFragment.java b/app/src/main/java/org/gnucash/android/ui/settings/BackupPreferenceFragment.java
index 9bbd4081c..54ff5e501 100644
--- a/app/src/main/java/org/gnucash/android/ui/settings/BackupPreferenceFragment.java
+++ b/app/src/main/java/org/gnucash/android/ui/settings/BackupPreferenceFragment.java
@@ -48,9 +48,9 @@
import org.gnucash.android.app.GnuCashApplication;
import org.gnucash.android.db.adapter.BooksDbAdapter;
import org.gnucash.android.export.Exporter;
-import org.gnucash.android.export.xml.GncXmlExporter;
import org.gnucash.android.importer.ImportAsyncTask;
import org.gnucash.android.ui.settings.dialog.OwnCloudDialogFragment;
+import org.gnucash.android.util.BackupManager;
import org.gnucash.android.util.BookUtils;
import java.io.File;
@@ -193,7 +193,7 @@ public boolean onPreferenceClick(Preference preference) {
}
if (key.equals(getString(R.string.key_create_backup))){
- boolean result = GncXmlExporter.createBackup();
+ boolean result = BackupManager.createBackup();
int msg = result ? R.string.toast_backup_successful : R.string.toast_backup_failed;
Toast.makeText(getActivity(), msg, Toast.LENGTH_SHORT).show();
}
diff --git a/app/src/main/java/org/gnucash/android/ui/settings/dialog/DeleteAllAccountsConfirmationDialog.java b/app/src/main/java/org/gnucash/android/ui/settings/dialog/DeleteAllAccountsConfirmationDialog.java
index d45dfffcf..c65deb127 100644
--- a/app/src/main/java/org/gnucash/android/ui/settings/dialog/DeleteAllAccountsConfirmationDialog.java
+++ b/app/src/main/java/org/gnucash/android/ui/settings/dialog/DeleteAllAccountsConfirmationDialog.java
@@ -24,8 +24,8 @@
import org.gnucash.android.R;
import org.gnucash.android.db.adapter.AccountsDbAdapter;
-import org.gnucash.android.export.xml.GncXmlExporter;
import org.gnucash.android.ui.homescreen.WidgetConfigurationActivity;
+import org.gnucash.android.util.BackupManager;
/**
* Confirmation dialog for deleting all accounts from the system.
@@ -49,7 +49,7 @@ public Dialog onCreateDialog(Bundle savedInstanceState) {
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
Context context = getDialog().getContext();
- GncXmlExporter.createBackup();
+ BackupManager.createBackup();
AccountsDbAdapter.getInstance().deleteAllRecords();
Toast.makeText(context, R.string.toast_all_accounts_deleted, Toast.LENGTH_SHORT).show();
WidgetConfigurationActivity.updateAllWidgets(context);
diff --git a/app/src/main/java/org/gnucash/android/ui/settings/dialog/DeleteAllTransactionsConfirmationDialog.java b/app/src/main/java/org/gnucash/android/ui/settings/dialog/DeleteAllTransactionsConfirmationDialog.java
index 0b37f0bd6..b8a46873a 100644
--- a/app/src/main/java/org/gnucash/android/ui/settings/dialog/DeleteAllTransactionsConfirmationDialog.java
+++ b/app/src/main/java/org/gnucash/android/ui/settings/dialog/DeleteAllTransactionsConfirmationDialog.java
@@ -29,9 +29,9 @@
import org.gnucash.android.db.adapter.AccountsDbAdapter;
import org.gnucash.android.db.adapter.DatabaseAdapter;
import org.gnucash.android.db.adapter.TransactionsDbAdapter;
-import org.gnucash.android.export.xml.GncXmlExporter;
import org.gnucash.android.model.Transaction;
import org.gnucash.android.ui.homescreen.WidgetConfigurationActivity;
+import org.gnucash.android.util.BackupManager;
import java.util.ArrayList;
import java.util.List;
@@ -57,7 +57,7 @@ public static DeleteAllTransactionsConfirmationDialog newInstance() {
.setPositiveButton(R.string.alert_dialog_ok_delete,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
- GncXmlExporter.createBackup();
+ BackupManager.createBackup();
Context context = getActivity();
AccountsDbAdapter accountsDbAdapter = AccountsDbAdapter.getInstance();
diff --git a/app/src/main/java/org/gnucash/android/ui/transaction/dialog/TransactionsDeleteConfirmationDialogFragment.java b/app/src/main/java/org/gnucash/android/ui/transaction/dialog/TransactionsDeleteConfirmationDialogFragment.java
index 7c6f04cd0..884d93afd 100644
--- a/app/src/main/java/org/gnucash/android/ui/transaction/dialog/TransactionsDeleteConfirmationDialogFragment.java
+++ b/app/src/main/java/org/gnucash/android/ui/transaction/dialog/TransactionsDeleteConfirmationDialogFragment.java
@@ -27,11 +27,11 @@
import org.gnucash.android.db.adapter.AccountsDbAdapter;
import org.gnucash.android.db.adapter.DatabaseAdapter;
import org.gnucash.android.db.adapter.TransactionsDbAdapter;
-import org.gnucash.android.export.xml.GncXmlExporter;
import org.gnucash.android.model.Transaction;
import org.gnucash.android.ui.common.Refreshable;
import org.gnucash.android.ui.common.UxArgument;
import org.gnucash.android.ui.homescreen.WidgetConfigurationActivity;
+import org.gnucash.android.util.BackupManager;
import java.util.ArrayList;
import java.util.List;
@@ -65,7 +65,7 @@ public static TransactionsDeleteConfirmationDialogFragment newInstance(int title
public void onClick(DialogInterface dialog, int whichButton) {
TransactionsDbAdapter transactionsDbAdapter = TransactionsDbAdapter.getInstance();
if (rowId == 0) {
- GncXmlExporter.createBackup(); //create backup before deleting everything
+ BackupManager.createBackup(); //create backup before deleting everything
List openingBalances = new ArrayList();
boolean preserveOpeningBalances = GnuCashApplication.shouldSaveOpeningBalances(false);
if (preserveOpeningBalances) {
diff --git a/app/src/main/java/org/gnucash/android/util/BackupManager.java b/app/src/main/java/org/gnucash/android/util/BackupManager.java
index 5e4473ecd..cd11f8996 100644
--- a/app/src/main/java/org/gnucash/android/util/BackupManager.java
+++ b/app/src/main/java/org/gnucash/android/util/BackupManager.java
@@ -10,10 +10,14 @@
import org.gnucash.android.db.adapter.BooksDbAdapter;
import org.gnucash.android.export.ExportFormat;
import org.gnucash.android.export.ExportParams;
+import org.gnucash.android.export.Exporter;
import org.gnucash.android.export.xml.GncXmlExporter;
+import org.gnucash.android.model.Book;
import java.io.BufferedOutputStream;
+import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.List;
import java.util.zip.GZIPOutputStream;
@@ -33,7 +37,7 @@ public static void backupAllBooks() {
for (String bookUID : bookUIDs) {
String backupFile = BookUtils.getBookBackupFileUri(bookUID);
if (backupFile == null){
- GncXmlExporter.createBackup(bookUID);
+ createBackup(bookUID);
continue;
}
@@ -51,4 +55,56 @@ public static void backupAllBooks() {
}
}
}
+
+ /**
+ * Creates a backup of current database contents to the directory {@link Exporter#getBackupFolderPath(String)}
+ * @return {@code true} if backup was successful, {@code false} otherwise
+ */
+ public static boolean createBackup(){
+ return createBackup(BooksDbAdapter.getInstance().getActiveBookUID());
+ }
+
+ /**
+ * Create a backup of the book in the default backup location
+ * @param bookUID Unique ID of the book
+ * @return {@code true} if backup was successful, {@code false} otherwise
+ */
+ public static boolean createBackup(String bookUID){
+ OutputStream outputStream;
+ try {
+ String backupFile = BookUtils.getBookBackupFileUri(bookUID);
+ if (backupFile != null){
+ outputStream = GnuCashApplication.getAppContext().getContentResolver().openOutputStream(Uri.parse(backupFile));
+ } else { //no Uri set by user, use default location on SD card
+ backupFile = getBackupFilePath(bookUID);
+ outputStream = new FileOutputStream(backupFile);
+ }
+
+ BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream);
+ GZIPOutputStream gzipOutputStream = new GZIPOutputStream(bufferedOutputStream);
+ OutputStreamWriter writer = new OutputStreamWriter(gzipOutputStream);
+
+ ExportParams params = new ExportParams(ExportFormat.XML);
+ new GncXmlExporter(params).generateExport(writer);
+ writer.close();
+ return true;
+ } catch (IOException | Exporter.ExporterException e) {
+ Crashlytics.logException(e);
+ Log.e("GncXmlExporter", "Error creating XML backup", e);
+ return false;
+ }
+ }
+
+ /**
+ * Returns the full path of a file to make database backup of the specified book.
+ * Backups are done in XML format and are Gzipped (with ".gnca" extension).
+ * @param bookUID GUID of the book
+ * @return the file path for backups of the database.
+ * @see Exporter#getBackupFolderPath(String)
+ */
+ private static String getBackupFilePath(String bookUID){
+ Book book = BooksDbAdapter.getInstance().getRecord(bookUID);
+ return Exporter.getBackupFolderPath(book.getUID())
+ + Exporter.buildExportFilename(ExportFormat.XML, book.getDisplayName());
+ }
}
diff --git a/app/src/test/java/org/gnucash/android/test/unit/export/BackupTest.java b/app/src/test/java/org/gnucash/android/test/unit/export/BackupTest.java
index 3b144dd2c..7cfbc30bd 100644
--- a/app/src/test/java/org/gnucash/android/test/unit/export/BackupTest.java
+++ b/app/src/test/java/org/gnucash/android/test/unit/export/BackupTest.java
@@ -25,6 +25,7 @@
import org.gnucash.android.importer.GncXmlImporter;
import org.gnucash.android.test.unit.testutil.ShadowCrashlytics;
import org.gnucash.android.test.unit.testutil.ShadowUserVoice;
+import org.gnucash.android.util.BackupManager;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -52,12 +53,6 @@ public void setUp(){
loadDefaultAccounts();
}
- @Test
- public void shouldCreateBackup(){
- boolean backupResult = GncXmlExporter.createBackup();
- assertThat(backupResult).isTrue();
- }
-
@Test
public void shouldCreateBackupFileName() throws Exporter.ExporterException {
Exporter exporter = new GncXmlExporter(new ExportParams(ExportFormat.XML));
From 80db9ad703be522886267b7e6130886e4336890f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=80lex=20Magaz=20Gra=C3=A7a?=
Date: Sat, 20 Jan 2018 12:48:37 +0100
Subject: [PATCH 12/43] Rename methods for name consistency
---
.../gnucash/android/export/ExportAsyncTask.java | 2 +-
.../android/ui/account/AccountsActivity.java | 4 ++--
.../ui/settings/BackupPreferenceFragment.java | 2 +-
.../DeleteAllAccountsConfirmationDialog.java | 2 +-
.../DeleteAllTransactionsConfirmationDialog.java | 2 +-
...nsactionsDeleteConfirmationDialogFragment.java | 2 +-
.../org/gnucash/android/util/BackupManager.java | 15 +++++++++------
7 files changed, 16 insertions(+), 13 deletions(-)
diff --git a/app/src/main/java/org/gnucash/android/export/ExportAsyncTask.java b/app/src/main/java/org/gnucash/android/export/ExportAsyncTask.java
index fa66aefd9..509c57aee 100644
--- a/app/src/main/java/org/gnucash/android/export/ExportAsyncTask.java
+++ b/app/src/main/java/org/gnucash/android/export/ExportAsyncTask.java
@@ -454,7 +454,7 @@ private String stripPathPart(String fullPathName) {
*/
private void backupAndDeleteTransactions(){
Log.i(TAG, "Backup and deleting transactions after export");
- BackupManager.createBackup(); //create backup before deleting everything
+ BackupManager.backupActiveBook(); //create backup before deleting everything
List openingBalances = new ArrayList<>();
boolean preserveOpeningBalances = GnuCashApplication.shouldSaveOpeningBalances(false);
diff --git a/app/src/main/java/org/gnucash/android/ui/account/AccountsActivity.java b/app/src/main/java/org/gnucash/android/ui/account/AccountsActivity.java
index 5c20d3934..8b2be04b5 100644
--- a/app/src/main/java/org/gnucash/android/ui/account/AccountsActivity.java
+++ b/app/src/main/java/org/gnucash/android/ui/account/AccountsActivity.java
@@ -287,7 +287,7 @@ private void handleOpenFileIntent(Intent intent) {
//when someone launches the app to view a (.gnucash or .gnca) file
Uri data = intent.getData();
if (data != null){
- BackupManager.createBackup();
+ BackupManager.backupActiveBook();
intent.setData(null);
new ImportAsyncTask(this).execute(data);
removeFirstRunFlag();
@@ -499,7 +499,7 @@ public static void startXmlFileChooser(Fragment fragment) {
* @param onFinishTask Task to be executed when import is complete
*/
public static void importXmlFileFromIntent(Activity context, Intent data, TaskDelegate onFinishTask) {
- BackupManager.createBackup();
+ BackupManager.backupActiveBook();
new ImportAsyncTask(context, onFinishTask).execute(data.getData());
}
diff --git a/app/src/main/java/org/gnucash/android/ui/settings/BackupPreferenceFragment.java b/app/src/main/java/org/gnucash/android/ui/settings/BackupPreferenceFragment.java
index 54ff5e501..d20f3331b 100644
--- a/app/src/main/java/org/gnucash/android/ui/settings/BackupPreferenceFragment.java
+++ b/app/src/main/java/org/gnucash/android/ui/settings/BackupPreferenceFragment.java
@@ -193,7 +193,7 @@ public boolean onPreferenceClick(Preference preference) {
}
if (key.equals(getString(R.string.key_create_backup))){
- boolean result = BackupManager.createBackup();
+ boolean result = BackupManager.backupActiveBook();
int msg = result ? R.string.toast_backup_successful : R.string.toast_backup_failed;
Toast.makeText(getActivity(), msg, Toast.LENGTH_SHORT).show();
}
diff --git a/app/src/main/java/org/gnucash/android/ui/settings/dialog/DeleteAllAccountsConfirmationDialog.java b/app/src/main/java/org/gnucash/android/ui/settings/dialog/DeleteAllAccountsConfirmationDialog.java
index c65deb127..b01c7881e 100644
--- a/app/src/main/java/org/gnucash/android/ui/settings/dialog/DeleteAllAccountsConfirmationDialog.java
+++ b/app/src/main/java/org/gnucash/android/ui/settings/dialog/DeleteAllAccountsConfirmationDialog.java
@@ -49,7 +49,7 @@ public Dialog onCreateDialog(Bundle savedInstanceState) {
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
Context context = getDialog().getContext();
- BackupManager.createBackup();
+ BackupManager.backupActiveBook();
AccountsDbAdapter.getInstance().deleteAllRecords();
Toast.makeText(context, R.string.toast_all_accounts_deleted, Toast.LENGTH_SHORT).show();
WidgetConfigurationActivity.updateAllWidgets(context);
diff --git a/app/src/main/java/org/gnucash/android/ui/settings/dialog/DeleteAllTransactionsConfirmationDialog.java b/app/src/main/java/org/gnucash/android/ui/settings/dialog/DeleteAllTransactionsConfirmationDialog.java
index b8a46873a..284038e70 100644
--- a/app/src/main/java/org/gnucash/android/ui/settings/dialog/DeleteAllTransactionsConfirmationDialog.java
+++ b/app/src/main/java/org/gnucash/android/ui/settings/dialog/DeleteAllTransactionsConfirmationDialog.java
@@ -57,7 +57,7 @@ public static DeleteAllTransactionsConfirmationDialog newInstance() {
.setPositiveButton(R.string.alert_dialog_ok_delete,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
- BackupManager.createBackup();
+ BackupManager.backupActiveBook();
Context context = getActivity();
AccountsDbAdapter accountsDbAdapter = AccountsDbAdapter.getInstance();
diff --git a/app/src/main/java/org/gnucash/android/ui/transaction/dialog/TransactionsDeleteConfirmationDialogFragment.java b/app/src/main/java/org/gnucash/android/ui/transaction/dialog/TransactionsDeleteConfirmationDialogFragment.java
index 884d93afd..0fcc48892 100644
--- a/app/src/main/java/org/gnucash/android/ui/transaction/dialog/TransactionsDeleteConfirmationDialogFragment.java
+++ b/app/src/main/java/org/gnucash/android/ui/transaction/dialog/TransactionsDeleteConfirmationDialogFragment.java
@@ -65,7 +65,7 @@ public static TransactionsDeleteConfirmationDialogFragment newInstance(int title
public void onClick(DialogInterface dialog, int whichButton) {
TransactionsDbAdapter transactionsDbAdapter = TransactionsDbAdapter.getInstance();
if (rowId == 0) {
- BackupManager.createBackup(); //create backup before deleting everything
+ BackupManager.backupActiveBook(); //create backup before deleting everything
List openingBalances = new ArrayList();
boolean preserveOpeningBalances = GnuCashApplication.shouldSaveOpeningBalances(false);
if (preserveOpeningBalances) {
diff --git a/app/src/main/java/org/gnucash/android/util/BackupManager.java b/app/src/main/java/org/gnucash/android/util/BackupManager.java
index cd11f8996..7ae530000 100644
--- a/app/src/main/java/org/gnucash/android/util/BackupManager.java
+++ b/app/src/main/java/org/gnucash/android/util/BackupManager.java
@@ -37,7 +37,7 @@ public static void backupAllBooks() {
for (String bookUID : bookUIDs) {
String backupFile = BookUtils.getBookBackupFileUri(bookUID);
if (backupFile == null){
- createBackup(bookUID);
+ backupBook(bookUID);
continue;
}
@@ -57,19 +57,22 @@ public static void backupAllBooks() {
}
/**
- * Creates a backup of current database contents to the directory {@link Exporter#getBackupFolderPath(String)}
+ * Backs up the active book to the directory {@link Exporter#getBackupFolderPath(String)}.
+ *
* @return {@code true} if backup was successful, {@code false} otherwise
*/
- public static boolean createBackup(){
- return createBackup(BooksDbAdapter.getInstance().getActiveBookUID());
+ public static boolean backupActiveBook() {
+ return backupBook(BooksDbAdapter.getInstance().getActiveBookUID());
}
/**
- * Create a backup of the book in the default backup location
+ * Backs up the book with UID {@code bookUID} to the directory
+ * {@link Exporter#getBackupFolderPath(String)}.
+ *
* @param bookUID Unique ID of the book
* @return {@code true} if backup was successful, {@code false} otherwise
*/
- public static boolean createBackup(String bookUID){
+ public static boolean backupBook(String bookUID){
OutputStream outputStream;
try {
String backupFile = BookUtils.getBookBackupFileUri(bookUID);
From 4d3b862e994cad05eed049fb17733c34c2ab371b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=80lex=20Magaz=20Gra=C3=A7a?=
Date: Sat, 20 Jan 2018 13:11:22 +0100
Subject: [PATCH 13/43] Move backup code from Exporter to BackupManager
---
.../org/gnucash/android/export/Exporter.java | 14 ----------
.../ui/settings/BackupPreferenceFragment.java | 2 +-
.../gnucash/android/util/BackupManager.java | 27 ++++++++++++++++---
.../android/util/BackupManagerTest.java | 3 +--
4 files changed, 25 insertions(+), 21 deletions(-)
diff --git a/app/src/main/java/org/gnucash/android/export/Exporter.java b/app/src/main/java/org/gnucash/android/export/Exporter.java
index 87c60d569..7b10c8aa0 100644
--- a/app/src/main/java/org/gnucash/android/export/Exporter.java
+++ b/app/src/main/java/org/gnucash/android/export/Exporter.java
@@ -237,20 +237,6 @@ public static String getExportFolderPath(String bookUID){
return path;
}
- /**
- * Returns the path to the backups folder for the book with GUID {@code bookUID}
- * Each book has its own backup path
- *
- * @return Absolute path to backup folder for the book
- */
- public static String getBackupFolderPath(String bookUID){
- String path = BASE_FOLDER_PATH + "/" + bookUID + "/backups/";
- File file = new File(path);
- if (!file.exists())
- file.mkdirs();
- return path;
- }
-
/**
* Returns the MIME type for this exporter.
diff --git a/app/src/main/java/org/gnucash/android/ui/settings/BackupPreferenceFragment.java b/app/src/main/java/org/gnucash/android/ui/settings/BackupPreferenceFragment.java
index d20f3331b..05623dca8 100644
--- a/app/src/main/java/org/gnucash/android/ui/settings/BackupPreferenceFragment.java
+++ b/app/src/main/java/org/gnucash/android/ui/settings/BackupPreferenceFragment.java
@@ -393,7 +393,7 @@ public void onClick(DialogInterface dialogInterface, int i) {
}
//If no default location was set, look in the internal SD card location
- File[] backupFiles = new File(Exporter.getBackupFolderPath(bookUID)).listFiles();
+ File[] backupFiles = new File(BackupManager.getBackupFolderPath(bookUID)).listFiles();
if (backupFiles == null || backupFiles.length == 0){
android.support.v7.app.AlertDialog.Builder builder = new android.support.v7.app.AlertDialog.Builder(getActivity())
.setTitle(R.string.title_no_backups_found)
diff --git a/app/src/main/java/org/gnucash/android/util/BackupManager.java b/app/src/main/java/org/gnucash/android/util/BackupManager.java
index 7ae530000..d2e519e90 100644
--- a/app/src/main/java/org/gnucash/android/util/BackupManager.java
+++ b/app/src/main/java/org/gnucash/android/util/BackupManager.java
@@ -15,6 +15,7 @@
import org.gnucash.android.model.Book;
import java.io.BufferedOutputStream;
+import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
@@ -57,7 +58,7 @@ public static void backupAllBooks() {
}
/**
- * Backs up the active book to the directory {@link Exporter#getBackupFolderPath(String)}.
+ * Backs up the active book to the directory {@link #getBackupFolderPath(String)}.
*
* @return {@code true} if backup was successful, {@code false} otherwise
*/
@@ -67,7 +68,7 @@ public static boolean backupActiveBook() {
/**
* Backs up the book with UID {@code bookUID} to the directory
- * {@link Exporter#getBackupFolderPath(String)}.
+ * {@link #getBackupFolderPath(String)}.
*
* @param bookUID Unique ID of the book
* @return {@code true} if backup was successful, {@code false} otherwise
@@ -103,11 +104,29 @@ public static boolean backupBook(String bookUID){
* Backups are done in XML format and are Gzipped (with ".gnca" extension).
* @param bookUID GUID of the book
* @return the file path for backups of the database.
- * @see Exporter#getBackupFolderPath(String)
+ * @see #getBackupFolderPath(String)
*/
private static String getBackupFilePath(String bookUID){
Book book = BooksDbAdapter.getInstance().getRecord(bookUID);
- return Exporter.getBackupFolderPath(book.getUID())
+ return getBackupFolderPath(book.getUID())
+ Exporter.buildExportFilename(ExportFormat.XML, book.getDisplayName());
}
+
+ /**
+ * Returns the path to the backups folder for the book with GUID {@code bookUID}.
+ *
+ * Each book has its own backup folder.
+ *
+ * @return Absolute path to backup folder for the book
+ */
+ public static String getBackupFolderPath(String bookUID){
+ String baseFolderPath = GnuCashApplication.getAppContext()
+ .getExternalFilesDir(null)
+ .getAbsolutePath();
+ String path = baseFolderPath + "/" + bookUID + "/backups/";
+ File file = new File(path);
+ if (!file.exists())
+ file.mkdirs();
+ return path;
+ }
}
diff --git a/app/src/test/java/org/gnucash/android/util/BackupManagerTest.java b/app/src/test/java/org/gnucash/android/util/BackupManagerTest.java
index eef2caa23..0cbcf2b20 100644
--- a/app/src/test/java/org/gnucash/android/util/BackupManagerTest.java
+++ b/app/src/test/java/org/gnucash/android/util/BackupManagerTest.java
@@ -3,7 +3,6 @@
import org.gnucash.android.R;
import org.gnucash.android.app.GnuCashApplication;
import org.gnucash.android.db.adapter.BooksDbAdapter;
-import org.gnucash.android.export.Exporter;
import org.gnucash.android.importer.GncXmlImporter;
import org.gnucash.android.test.unit.testutil.ShadowCrashlytics;
import org.gnucash.android.test.unit.testutil.ShadowUserVoice;
@@ -44,7 +43,7 @@ public void backupAllBooks() throws Exception {
BackupManager.backupAllBooks();
for (String bookUID : mBooksDbAdapter.getAllBookUIDs()) {
- File backupFolder = new File(Exporter.getBackupFolderPath(bookUID));
+ File backupFolder = new File(BackupManager.getBackupFolderPath(bookUID));
assertThat(backupFolder.list().length).isEqualTo(1);
}
}
From bf19595d8a1d1091829d9f56958e76e178a5ef1f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=80lex=20Magaz=20Gra=C3=A7a?=
Date: Sat, 20 Jan 2018 19:07:46 +0100
Subject: [PATCH 14/43] Move backup code from BookUtils to BackupManager
---
.../ui/settings/BackupPreferenceFragment.java | 7 +++----
.../gnucash/android/util/BackupManager.java | 19 +++++++++++++++++--
.../org/gnucash/android/util/BookUtils.java | 15 ---------------
3 files changed, 20 insertions(+), 21 deletions(-)
diff --git a/app/src/main/java/org/gnucash/android/ui/settings/BackupPreferenceFragment.java b/app/src/main/java/org/gnucash/android/ui/settings/BackupPreferenceFragment.java
index 05623dca8..a601f30ae 100644
--- a/app/src/main/java/org/gnucash/android/ui/settings/BackupPreferenceFragment.java
+++ b/app/src/main/java/org/gnucash/android/ui/settings/BackupPreferenceFragment.java
@@ -51,7 +51,6 @@
import org.gnucash.android.importer.ImportAsyncTask;
import org.gnucash.android.ui.settings.dialog.OwnCloudDialogFragment;
import org.gnucash.android.util.BackupManager;
-import org.gnucash.android.util.BookUtils;
import java.io.File;
import java.text.DateFormat;
@@ -151,7 +150,7 @@ public void onResume() {
pref = findPreference(getString(R.string.key_backup_location));
pref.setOnPreferenceClickListener(this);
- String defaultBackupLocation = BookUtils.getBookBackupFileUri(BooksDbAdapter.getInstance().getActiveBookUID());
+ String defaultBackupLocation = BackupManager.getBookBackupFileUri(BooksDbAdapter.getInstance().getActiveBookUID());
if (defaultBackupLocation != null){
pref.setSummary(Uri.parse(defaultBackupLocation).getAuthority());
}
@@ -371,7 +370,7 @@ private void restoreBackup() {
Log.i("Settings", "Opening GnuCash XML backups for restore");
String bookUID = BooksDbAdapter.getInstance().getActiveBookUID();
- final String defaultBackupFile = BookUtils.getBookBackupFileUri(bookUID);
+ final String defaultBackupFile = BackupManager.getBookBackupFileUri(bookUID);
if (defaultBackupFile != null){
android.support.v7.app.AlertDialog.Builder builder = new android.support.v7.app.AlertDialog.Builder(getActivity())
.setTitle(R.string.title_confirm_restore_backup)
@@ -477,7 +476,7 @@ public void onActivityResult(int requestCode, int resultCode, Intent data) {
PreferenceActivity.getActiveBookSharedPreferences()
.edit()
- .putString(BookUtils.KEY_BACKUP_FILE, backupFileUri.toString())
+ .putString(BackupManager.KEY_BACKUP_FILE, backupFileUri.toString())
.apply();
Preference pref = findPreference(getString(R.string.key_backup_location));
diff --git a/app/src/main/java/org/gnucash/android/util/BackupManager.java b/app/src/main/java/org/gnucash/android/util/BackupManager.java
index d2e519e90..b7a168549 100644
--- a/app/src/main/java/org/gnucash/android/util/BackupManager.java
+++ b/app/src/main/java/org/gnucash/android/util/BackupManager.java
@@ -1,7 +1,9 @@
package org.gnucash.android.util;
import android.content.Context;
+import android.content.SharedPreferences;
import android.net.Uri;
+import android.support.annotation.Nullable;
import android.util.Log;
import com.crashlytics.android.Crashlytics;
@@ -13,6 +15,7 @@
import org.gnucash.android.export.Exporter;
import org.gnucash.android.export.xml.GncXmlExporter;
import org.gnucash.android.model.Book;
+import org.gnucash.android.ui.settings.PreferenceActivity;
import java.io.BufferedOutputStream;
import java.io.File;
@@ -25,6 +28,7 @@
public class BackupManager {
private static final String LOG_TAG = "BackupManager";
+ public static final String KEY_BACKUP_FILE = "book_backup_file_key";
/**
* Perform an automatic backup of all books in the database.
@@ -36,7 +40,7 @@ public static void backupAllBooks() {
Context context = GnuCashApplication.getAppContext();
for (String bookUID : bookUIDs) {
- String backupFile = BookUtils.getBookBackupFileUri(bookUID);
+ String backupFile = getBookBackupFileUri(bookUID);
if (backupFile == null){
backupBook(bookUID);
continue;
@@ -76,7 +80,7 @@ public static boolean backupActiveBook() {
public static boolean backupBook(String bookUID){
OutputStream outputStream;
try {
- String backupFile = BookUtils.getBookBackupFileUri(bookUID);
+ String backupFile = getBookBackupFileUri(bookUID);
if (backupFile != null){
outputStream = GnuCashApplication.getAppContext().getContentResolver().openOutputStream(Uri.parse(backupFile));
} else { //no Uri set by user, use default location on SD card
@@ -129,4 +133,15 @@ public static String getBackupFolderPath(String bookUID){
file.mkdirs();
return path;
}
+
+ /**
+ * Return the user-set backup file URI for the book with UID {@code bookUID}.
+ * @param bookUID Unique ID of the book
+ * @return DocumentFile for book backups, or null if the user hasn't set any.
+ */
+ @Nullable
+ public static String getBookBackupFileUri(String bookUID){
+ SharedPreferences sharedPreferences = PreferenceActivity.getBookSharedPreferences(bookUID);
+ return sharedPreferences.getString(KEY_BACKUP_FILE, null);
+ }
}
diff --git a/app/src/main/java/org/gnucash/android/util/BookUtils.java b/app/src/main/java/org/gnucash/android/util/BookUtils.java
index 67bf5f4dd..dbd308d7e 100644
--- a/app/src/main/java/org/gnucash/android/util/BookUtils.java
+++ b/app/src/main/java/org/gnucash/android/util/BookUtils.java
@@ -1,30 +1,15 @@
package org.gnucash.android.util;
-import android.content.SharedPreferences;
import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
import org.gnucash.android.app.GnuCashApplication;
import org.gnucash.android.ui.account.AccountsActivity;
-import org.gnucash.android.ui.settings.PreferenceActivity;
/**
* Utility class for common operations involving books
*/
public class BookUtils {
- public static final String KEY_BACKUP_FILE = "book_backup_file_key";
-
- /**
- * Return the backup file for the book
- * @param bookUID Unique ID of the book
- * @return DocumentFile for book backups
- */
- @Nullable
- public static String getBookBackupFileUri(String bookUID){
- SharedPreferences sharedPreferences = PreferenceActivity.getBookSharedPreferences(bookUID);
- return sharedPreferences.getString(KEY_BACKUP_FILE, null);
- }
/**
* Activates the book with unique identifer {@code bookUID}, and refreshes the database adapters
From 366f1a4e81a8d8d775839620d38a8df7ce09687f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=80lex=20Magaz=20Gra=C3=A7a?=
Date: Sun, 21 Jan 2018 17:57:15 +0100
Subject: [PATCH 15/43] Move code to get the list of backups from
BackupPreferenceFragment to BackupManager
---
.../ui/settings/BackupPreferenceFragment.java | 13 ++++-------
.../gnucash/android/util/BackupManager.java | 13 ++++++++++-
.../android/util/BackupManagerTest.java | 23 +++++++++++++++++--
3 files changed, 37 insertions(+), 12 deletions(-)
diff --git a/app/src/main/java/org/gnucash/android/ui/settings/BackupPreferenceFragment.java b/app/src/main/java/org/gnucash/android/ui/settings/BackupPreferenceFragment.java
index a601f30ae..e92f83445 100644
--- a/app/src/main/java/org/gnucash/android/ui/settings/BackupPreferenceFragment.java
+++ b/app/src/main/java/org/gnucash/android/ui/settings/BackupPreferenceFragment.java
@@ -368,7 +368,7 @@ public void onConnectionFailed(ConnectionResult connectionResult) {
*/
private void restoreBackup() {
Log.i("Settings", "Opening GnuCash XML backups for restore");
- String bookUID = BooksDbAdapter.getInstance().getActiveBookUID();
+ final String bookUID = BooksDbAdapter.getInstance().getActiveBookUID();
final String defaultBackupFile = BackupManager.getBookBackupFileUri(bookUID);
if (defaultBackupFile != null){
@@ -392,8 +392,7 @@ public void onClick(DialogInterface dialogInterface, int i) {
}
//If no default location was set, look in the internal SD card location
- File[] backupFiles = new File(BackupManager.getBackupFolderPath(bookUID)).listFiles();
- if (backupFiles == null || backupFiles.length == 0){
+ if (BackupManager.getBackupList(bookUID).isEmpty()){
android.support.v7.app.AlertDialog.Builder builder = new android.support.v7.app.AlertDialog.Builder(getActivity())
.setTitle(R.string.title_no_backups_found)
.setMessage(R.string.msg_no_backups_to_restore_from)
@@ -407,14 +406,10 @@ public void onClick(DialogInterface dialog, int which) {
return;
}
- Arrays.sort(backupFiles);
- List backupFilesList = Arrays.asList(backupFiles);
- Collections.reverse(backupFilesList);
- final File[] sortedBackupFiles = (File[]) backupFilesList.toArray();
final ArrayAdapter arrayAdapter = new ArrayAdapter<>(getActivity(), android.R.layout.select_dialog_singlechoice);
final DateFormat dateFormatter = SimpleDateFormat.getDateTimeInstance();
- for (File backupFile : sortedBackupFiles) {
+ for (File backupFile : BackupManager.getBackupList(bookUID)) {
long time = Exporter.getExportTime(backupFile.getName());
if (time > 0)
arrayAdapter.add(dateFormatter.format(new Date(time)));
@@ -434,7 +429,7 @@ public void onClick(DialogInterface dialog, int which) {
restoreDialogBuilder.setAdapter(arrayAdapter, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
- File backupFile = sortedBackupFiles[which];
+ File backupFile = BackupManager.getBackupList(bookUID).get(which);
new ImportAsyncTask(getActivity()).execute(Uri.fromFile(backupFile));
}
});
diff --git a/app/src/main/java/org/gnucash/android/util/BackupManager.java b/app/src/main/java/org/gnucash/android/util/BackupManager.java
index b7a168549..1431272f3 100644
--- a/app/src/main/java/org/gnucash/android/util/BackupManager.java
+++ b/app/src/main/java/org/gnucash/android/util/BackupManager.java
@@ -5,6 +5,7 @@
import android.net.Uri;
import android.support.annotation.Nullable;
import android.util.Log;
+import android.widget.ArrayAdapter;
import com.crashlytics.android.Crashlytics;
@@ -23,6 +24,8 @@
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
+import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
import java.util.zip.GZIPOutputStream;
@@ -123,7 +126,7 @@ private static String getBackupFilePath(String bookUID){
*
* @return Absolute path to backup folder for the book
*/
- public static String getBackupFolderPath(String bookUID){
+ private static String getBackupFolderPath(String bookUID){
String baseFolderPath = GnuCashApplication.getAppContext()
.getExternalFilesDir(null)
.getAbsolutePath();
@@ -144,4 +147,12 @@ public static String getBookBackupFileUri(String bookUID){
SharedPreferences sharedPreferences = PreferenceActivity.getBookSharedPreferences(bookUID);
return sharedPreferences.getString(KEY_BACKUP_FILE, null);
}
+
+ public static List getBackupList(String bookUID) {
+ File[] backupFiles = new File(getBackupFolderPath(bookUID)).listFiles();
+ Arrays.sort(backupFiles);
+ List backupFilesList = Arrays.asList(backupFiles);
+ Collections.reverse(backupFilesList);
+ return backupFilesList;
+ }
}
diff --git a/app/src/test/java/org/gnucash/android/util/BackupManagerTest.java b/app/src/test/java/org/gnucash/android/util/BackupManagerTest.java
index 0cbcf2b20..d7038e409 100644
--- a/app/src/test/java/org/gnucash/android/util/BackupManagerTest.java
+++ b/app/src/test/java/org/gnucash/android/util/BackupManagerTest.java
@@ -43,11 +43,30 @@ public void backupAllBooks() throws Exception {
BackupManager.backupAllBooks();
for (String bookUID : mBooksDbAdapter.getAllBookUIDs()) {
- File backupFolder = new File(BackupManager.getBackupFolderPath(bookUID));
- assertThat(backupFolder.list().length).isEqualTo(1);
+ assertThat(BackupManager.getBackupList(bookUID).size()).isEqualTo(1);
}
}
+ @Test
+ public void getBackupList() throws Exception {
+ String bookUID = createNewBookWithDefaultAccounts();
+ BookUtils.activateBook(bookUID);
+
+ BackupManager.backupActiveBook();
+ Thread.sleep(1000); // FIXME: Use Mockito to get a different date in Exporter.buildExportFilename
+ BackupManager.backupActiveBook();
+
+ assertThat(BackupManager.getBackupList(bookUID).size()).isEqualTo(2);
+ }
+
+ @Test
+ public void whenNoBackupsHaveBeenDone_shouldReturnEmptyBackupList() {
+ String bookUID = createNewBookWithDefaultAccounts();
+ BookUtils.activateBook(bookUID);
+
+ assertThat(BackupManager.getBackupList(bookUID)).isEmpty();
+ }
+
/**
* Creates a new database with default accounts
* @return The book UID for the new database
From 13f243b3e5a0183c8657eec61bfad649d9cfb2d4 Mon Sep 17 00:00:00 2001
From: AngelicosPhosphoros
Date: Tue, 23 Jan 2018 21:12:45 +0300
Subject: [PATCH 16/43] Transaction export refactored. Cursor based iteration
over transactions
---
.../export/csv/CsvTransactionsExporter.java | 116 +++++++++++++-----
1 file changed, 84 insertions(+), 32 deletions(-)
diff --git a/app/src/main/java/org/gnucash/android/export/csv/CsvTransactionsExporter.java b/app/src/main/java/org/gnucash/android/export/csv/CsvTransactionsExporter.java
index e306cf1bf..a3207148c 100644
--- a/app/src/main/java/org/gnucash/android/export/csv/CsvTransactionsExporter.java
+++ b/app/src/main/java/org/gnucash/android/export/csv/CsvTransactionsExporter.java
@@ -17,13 +17,18 @@
package org.gnucash.android.export.csv;
import android.database.sqlite.SQLiteDatabase;
+import android.database.Cursor;
import com.crashlytics.android.Crashlytics;
import org.gnucash.android.export.ExportParams;
import org.gnucash.android.export.Exporter;
import org.gnucash.android.model.Account;
+import org.gnucash.android.model.Commodity;
+import org.gnucash.android.model.Money;
import org.gnucash.android.model.Split;
import org.gnucash.android.model.Transaction;
+import org.gnucash.android.model.TransactionType;
+
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -31,6 +36,8 @@
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
import java.util.Date;
import java.util.List;
@@ -43,6 +50,21 @@ public class CsvTransactionsExporter extends Exporter{
private char mCsvSeparator;
+ private DateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");
+
+ private Comparator splitComparator = new Comparator() {
+ @Override
+ public int compare(Split o1, Split o2) {
+ if(o1.getType() == TransactionType.DEBIT
+ && o2.getType() == TransactionType.CREDIT)
+ return -1;
+ if (o1.getType() == TransactionType.CREDIT
+ && o2.getType() == TransactionType.DEBIT)
+ return 1;
+ return 0;
+ }
+ };
+
/**
* Construct a new exporter with export parameters
* @param params Parameters for the export
@@ -95,6 +117,57 @@ public List generateExport() throws ExporterException {
return exportedFiles;
}
+ private void write_split(final Transaction transaction, final Split split, final CsvWriter writer) throws IOException
+ {
+ String separator = mCsvSeparator + "";
+ Account account = mAccountsDbAdapter.getRecord(split.getAccountUID());
+
+ // Date
+ Date date = new Date(transaction.getTimeMillis());
+ writer.write(dateFormat.format(date) + separator);
+ // Account name
+ writer.write(account.getName() + separator);
+ // TODO:Number is not defined yet?
+ writer.write( separator);
+ // Description
+ writer.write(transaction.getDescription() + separator);
+ // Notes of transaction
+ writer.write(transaction.getNote() + separator);
+ // Memo
+ writer.write(
+ (split.getMemo()==null?
+ "":split.getMemo()) + separator);
+ // TODO:Category is not defined yet?
+ writer.write(separator);
+ // Type
+ writer.write(split.getType().name() + separator);
+ // TODO:Action is not defined yet?
+ writer.write(separator);
+ // Reconcile
+ writer.write(split.getReconcileState() + separator);
+
+ // Changes
+ Money change = split.getFormattedQuantity().withCurrency(transaction.getCommodity());
+ Money zero = Money.getZeroInstance().withCurrency(transaction.getCommodity());
+ // To currency; From currency; To; From
+ if (change.isNegative()) {
+ writer.write(zero.toPlainString() + separator);
+ writer.write(change.abs().toPlainString() + separator);
+ writer.write(Money.getZeroInstance().toPlainString() + separator);
+ writer.write(split.getFormattedQuantity().abs().toPlainString() + separator);
+ }
+ else {
+ writer.write(change.abs().toPlainString() + separator);
+ writer.write(zero.toPlainString() + separator);
+ writer.write(split.getFormattedQuantity().abs().toPlainString() + separator);
+ writer.write(Money.getZeroInstance().toPlainString() + separator);
+ }
+
+ // TODO: What is price?
+ writer.write(separator);
+ writer.write(separator);
+ }
+
public void generateExport(final CsvWriter writer) throws ExporterException {
try {
String separator = mCsvSeparator + "";
@@ -122,9 +195,14 @@ public void generateExport(final CsvWriter writer) throws ExporterException {
writer.write(names.get(i) + separator);
}
writer.write("\n");
- for(int i = 0; i < transactions.size(); i++) {
- Transaction transaction = transactions.get(i);
+
+
+ Cursor cursor = mTransactionsDbAdapter.fetchAllRecords();
+ while (cursor.moveToNext())
+ {
+ Transaction transaction = mTransactionsDbAdapter.buildModelInstance(cursor);
List splits = transaction.getSplits();
+ Collections.sort(splits,splitComparator);
for (int j = 0; j < splits.size()/2; j++) {
Split split = splits.get(j);
Split pair = null;
@@ -134,38 +212,12 @@ public void generateExport(final CsvWriter writer) throws ExporterException {
}
}
- Account account = mAccountsDbAdapter.getRecord(split.getAccountUID());
- Account account_pair = null;
+ write_split(transaction, split, writer);
+ writer.write("\n");
if (pair != null) {
- account_pair = mAccountsDbAdapter.getRecord(pair.getAccountUID());
+ write_split(transaction, pair, writer);
+ writer.write("\n");
}
-
- Date date = new Date(transaction.getTimeMillis());
- DateFormat df = new SimpleDateFormat("dd/MM/yyyy");
- writer.write(df.format(date) + separator);
-
- writer.write(account.getName() + separator);
-
- //Number
- writer.write(separator);
-
- writer.write(transaction.getDescription() + separator);
- writer.write(transaction.getNote() + separator);
- writer.write((split.getMemo()==null?"":split.getMemo()) + separator);
- writer.write((account_pair.getName()==null?"":account_pair.getName()) + separator);
- writer.write((split.getType().name()) + separator);
-
- //Action
- writer.write(separator);
-
- writer.write(split.getReconcileState() + separator);
-
- writer.write(split.getFormattedQuantity().toPlainString() + separator);
- writer.write(separator);
- writer.write(split.getFormattedQuantity().toPlainString() + separator);
- writer.write(separator);
- writer.write(separator);
- writer.write("\n");
}
}
From 0aae30a1403e7446c01022d4c26985a7e3aae4cd Mon Sep 17 00:00:00 2001
From: AngelicosPhosphoros
Date: Tue, 23 Jan 2018 21:25:45 +0300
Subject: [PATCH 17/43] Contributors updated
---
CONTRIBUTORS.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index 4bdb9a6c0..d5f100d38 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -55,6 +55,7 @@ The following people (in alphabetical order) contributed (commits on GitHub) to
* Stephan Windmüller
* Terry Chung
* thesebas thesebas@thesebas.net
+* Timur Badretdinov
* Timur Khuzin
* Vladimir Rutsky
* Weslly Oliveira
From d44b3f3aef2fee1eb4e08513006873283c82ab85 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=80lex=20Magaz=20Gra=C3=A7a?=
Date: Tue, 30 Jan 2018 20:57:38 +0100
Subject: [PATCH 18/43] Backup books daily instead of every time the
ScheduledActionService runs
Fixes https://github.com/codinguser/gnucash-android/issues/757
---
app/build.gradle | 8 ++--
app/src/main/AndroidManifest.xml | 8 ++++
.../android/receivers/BootReceiver.java | 4 +-
.../receivers/PeriodicJobReceiver.java | 46 +++++++++++++++++++
.../service/ScheduledActionService.java | 1 -
.../android/ui/account/AccountsActivity.java | 1 +
.../org/gnucash/android/util/BackupJob.java | 46 +++++++++++++++++++
.../gnucash/android/util/BackupManager.java | 41 +++++++++++++++--
8 files changed, 146 insertions(+), 9 deletions(-)
create mode 100644 app/src/main/java/org/gnucash/android/receivers/PeriodicJobReceiver.java
create mode 100644 app/src/main/java/org/gnucash/android/util/BackupJob.java
diff --git a/app/build.gradle b/app/build.gradle
index 1a604260e..ea22064b2 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -20,13 +20,13 @@ static def gitSha() {
android {
- compileSdkVersion 26
- buildToolsVersion '27.0.0'
+ compileSdkVersion 27
+ buildToolsVersion '27.0.3'
defaultConfig {
applicationId "org.gnucash.android"
testApplicationId 'org.gnucash.android.test'
minSdkVersion 19
- targetSdkVersion 26
+ targetSdkVersion 27
versionCode versionMajor * 10000 + versionMinor * 1000 + versionPatch * 100 + versionBuild
versionName "${versionMajor}.${versionMinor}.${versionPatch}"
resValue "string", "app_version_name", "${versionName}"
@@ -179,7 +179,7 @@ afterEvaluate {
}
-def androidSupportVersion = "26.0.1"
+def androidSupportVersion = "27.0.2"
def androidEspressoVersion = "3.0.0"
def androidSupportTestVersion = "1.0.0"
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index c8857485a..f212cde86 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -121,6 +121,8 @@
+
@@ -154,6 +156,12 @@
+
+
+
+
+
*/
public class BootReceiver extends BroadcastReceiver {
@@ -32,5 +33,6 @@ public class BootReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
GnuCashApplication.startScheduledActionExecutionService(context);
+ BackupManager.schedulePeriodicBackups(context);
}
}
diff --git a/app/src/main/java/org/gnucash/android/receivers/PeriodicJobReceiver.java b/app/src/main/java/org/gnucash/android/receivers/PeriodicJobReceiver.java
new file mode 100644
index 000000000..e0af3aa46
--- /dev/null
+++ b/app/src/main/java/org/gnucash/android/receivers/PeriodicJobReceiver.java
@@ -0,0 +1,46 @@
+/* Copyright (c) 2018 Àlex Magaz Graça
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gnucash.android.receivers;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+import org.gnucash.android.util.BackupJob;
+
+/**
+ * Receiver to run periodic jobs.
+ *
+ * @author Àlex Magaz Graça
+ */
+public class PeriodicJobReceiver extends BroadcastReceiver {
+ private static final String LOG_TAG = "PeriodicJobReceiver";
+
+ public static final String ACTION_BACKUP = "org.gnucash.android.action_backup";
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction() == null) {
+ Log.w(LOG_TAG, "No action was set in the intent. Ignoring...");
+ return;
+ }
+
+ if (intent.getAction().equals(ACTION_BACKUP)) {
+ BackupJob.enqueueWork(context);
+ }
+ }
+}
diff --git a/app/src/main/java/org/gnucash/android/service/ScheduledActionService.java b/app/src/main/java/org/gnucash/android/service/ScheduledActionService.java
index 3852212e5..c345d52c3 100644
--- a/app/src/main/java/org/gnucash/android/service/ScheduledActionService.java
+++ b/app/src/main/java/org/gnucash/android/service/ScheduledActionService.java
@@ -71,7 +71,6 @@ protected void onHandleIntent(Intent intent) {
PowerManager.WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, LOG_TAG);
wakeLock.acquire();
- BackupManager.backupAllBooks(); //First run automatic backup of all books before doing anything else
try {
BooksDbAdapter booksDbAdapter = BooksDbAdapter.getInstance();
List books = booksDbAdapter.getAllRecords();
diff --git a/app/src/main/java/org/gnucash/android/ui/account/AccountsActivity.java b/app/src/main/java/org/gnucash/android/ui/account/AccountsActivity.java
index 8b2be04b5..846330af8 100644
--- a/app/src/main/java/org/gnucash/android/ui/account/AccountsActivity.java
+++ b/app/src/main/java/org/gnucash/android/ui/account/AccountsActivity.java
@@ -342,6 +342,7 @@ private void init() {
showWhatsNewDialog(this);
}
GnuCashApplication.startScheduledActionExecutionService(this);
+ BackupManager.schedulePeriodicBackups(this);
}
@Override
diff --git a/app/src/main/java/org/gnucash/android/util/BackupJob.java b/app/src/main/java/org/gnucash/android/util/BackupJob.java
new file mode 100644
index 000000000..9f68814e5
--- /dev/null
+++ b/app/src/main/java/org/gnucash/android/util/BackupJob.java
@@ -0,0 +1,46 @@
+/* Copyright (c) 2018 Àlex Magaz Graça
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gnucash.android.util;
+
+import android.content.Context;
+import android.content.Intent;
+import android.support.annotation.NonNull;
+import android.support.v4.app.JobIntentService;
+import android.util.Log;
+
+
+/**
+ * Job to back up books periodically.
+ *
+ * The backups are triggered by an alarm set in
+ * {@link BackupManager#schedulePeriodicBackups(Context)}
+ * (through {@link org.gnucash.android.receivers.PeriodicJobReceiver}).
+ */
+public class BackupJob extends JobIntentService {
+ private static final String LOG_TAG = "BackupJob";
+ private static final int JOB_ID = 1000;
+
+ public static void enqueueWork(Context context) {
+ Intent intent = new Intent(context, BackupJob.class);
+ enqueueWork(context, BackupJob.class, JOB_ID, intent);
+ }
+
+ @Override
+ protected void onHandleWork(@NonNull Intent intent) {
+ Log.i(LOG_TAG, "Doing backup of all books.");
+ BackupManager.backupAllBooks();
+ }
+}
diff --git a/app/src/main/java/org/gnucash/android/util/BackupManager.java b/app/src/main/java/org/gnucash/android/util/BackupManager.java
index 1431272f3..9969ab638 100644
--- a/app/src/main/java/org/gnucash/android/util/BackupManager.java
+++ b/app/src/main/java/org/gnucash/android/util/BackupManager.java
@@ -1,11 +1,29 @@
+/* Copyright (c) 2018 Àlex Magaz Graça
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package org.gnucash.android.util;
+import android.app.AlarmManager;
+import android.app.PendingIntent;
import android.content.Context;
+import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
+import android.os.SystemClock;
import android.support.annotation.Nullable;
import android.util.Log;
-import android.widget.ArrayAdapter;
import com.crashlytics.android.Crashlytics;
@@ -16,6 +34,7 @@
import org.gnucash.android.export.Exporter;
import org.gnucash.android.export.xml.GncXmlExporter;
import org.gnucash.android.model.Book;
+import org.gnucash.android.receivers.PeriodicJobReceiver;
import org.gnucash.android.ui.settings.PreferenceActivity;
import java.io.BufferedOutputStream;
@@ -29,15 +48,19 @@
import java.util.List;
import java.util.zip.GZIPOutputStream;
+
+/**
+ * Deals with all backup-related tasks.
+ */
public class BackupManager {
private static final String LOG_TAG = "BackupManager";
public static final String KEY_BACKUP_FILE = "book_backup_file_key";
/**
* Perform an automatic backup of all books in the database.
- * This method is run everytime the service is executed
+ * This method is run every time the service is executed
*/
- public static void backupAllBooks() {
+ static void backupAllBooks() {
BooksDbAdapter booksDbAdapter = BooksDbAdapter.getInstance();
List bookUIDs = booksDbAdapter.getAllBookUIDs();
Context context = GnuCashApplication.getAppContext();
@@ -155,4 +178,16 @@ public static List getBackupList(String bookUID) {
Collections.reverse(backupFilesList);
return backupFilesList;
}
+
+ public static void schedulePeriodicBackups(Context context) {
+ Log.i(LOG_TAG, "Scheduling backup job");
+ Intent intent = new Intent(context, PeriodicJobReceiver.class);
+ intent.setAction(PeriodicJobReceiver.ACTION_BACKUP);
+ PendingIntent alarmIntent = PendingIntent.getBroadcast(context,0, intent,
+ PendingIntent.FLAG_UPDATE_CURRENT);
+ AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+ alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ SystemClock.elapsedRealtime() + AlarmManager.INTERVAL_FIFTEEN_MINUTES,
+ AlarmManager.INTERVAL_DAY, alarmIntent);
+ }
}
From cd5ff90ddc2e5f2d80776f572976baf23b3a0331 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=80lex=20Magaz=20Gra=C3=A7a?=
Date: Fri, 9 Feb 2018 22:43:34 +0100
Subject: [PATCH 19/43] Fix travis builds
---
.travis.yml | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/.travis.yml b/.travis.yml
index ee3a22909..af9de01b1 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -5,17 +5,17 @@ android:
- platform-tools
- tools
- tools #not a typo. Needed for SDK update
- - build-tools-27.0.0
+ - build-tools-27.0.3
# The SDK version used to compile your project
- - android-26
+ - android-27
# Additional components
- extra-android-support
- extra-google-google_play_services
- extra-google-m2repository
- extra-android-m2repository
- - addon-google_apis-google-25
+ - addon-google_apis-google-26
# Specify at least one system image,
# if you need to run emulator(s) during your tests
From 394ef4f8b05afd6d76f76a560a7d73c5b452af32 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=80lex=20Magaz=20Gra=C3=A7a?=
Date: Fri, 9 Feb 2018 23:26:27 +0100
Subject: [PATCH 20/43] Temporary workaround for travis builds with Android SDK
27
See https://github.com/travis-ci/travis-ci/issues/8874
---
.travis.yml | 3 +++
1 file changed, 3 insertions(+)
diff --git a/.travis.yml b/.travis.yml
index af9de01b1..9dcd1ce95 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -21,6 +21,9 @@ android:
# if you need to run emulator(s) during your tests
#- sys-img-armeabi-v7a-android-23
+# XXX: Temporary workaround. Remove once fixed
+before_install:
+ - yes | sdkmanager "platforms;android-27"
# Emulator Management: Create, Start and Wait
# Re-enable this when we figure out how to reliably build on Travis
From 589acb61bcc5ebbd3d5d1df97bbe6a7a6ae9defe Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=80lex=20Magaz=20Gra=C3=A7a?=
Date: Sat, 10 Feb 2018 19:33:08 +0100
Subject: [PATCH 21/43] Backup before deleting a book
---
.../ui/settings/dialog/DeleteBookConfirmationDialog.java | 2 ++
1 file changed, 2 insertions(+)
diff --git a/app/src/main/java/org/gnucash/android/ui/settings/dialog/DeleteBookConfirmationDialog.java b/app/src/main/java/org/gnucash/android/ui/settings/dialog/DeleteBookConfirmationDialog.java
index fe5dfccf9..2f58bcaf8 100644
--- a/app/src/main/java/org/gnucash/android/ui/settings/dialog/DeleteBookConfirmationDialog.java
+++ b/app/src/main/java/org/gnucash/android/ui/settings/dialog/DeleteBookConfirmationDialog.java
@@ -24,6 +24,7 @@
import org.gnucash.android.R;
import org.gnucash.android.db.adapter.BooksDbAdapter;
import org.gnucash.android.ui.common.Refreshable;
+import org.gnucash.android.util.BackupManager;
/**
* Confirmation dialog for deleting a book.
@@ -52,6 +53,7 @@ public Dialog onCreateDialog(Bundle savedInstanceState) {
@Override
public void onClick(DialogInterface dialogInterface, int which) {
final String bookUID = getArguments().getString("bookUID");
+ BackupManager.backupBook(bookUID);
BooksDbAdapter.getInstance().deleteBook(bookUID);
((Refreshable) getTargetFragment()).refresh();
}
From 5526180d91de0711fd8a6fbe23ffe43c433bbbb4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=80lex=20Magaz=20Gra=C3=A7a?=
Date: Sat, 10 Feb 2018 19:37:27 +0100
Subject: [PATCH 22/43] Backup before deleting a transaction
---
.../android/ui/transaction/TransactionsListFragment.java | 2 ++
1 file changed, 2 insertions(+)
diff --git a/app/src/main/java/org/gnucash/android/ui/transaction/TransactionsListFragment.java b/app/src/main/java/org/gnucash/android/ui/transaction/TransactionsListFragment.java
index bb5796009..9ea0847e9 100644
--- a/app/src/main/java/org/gnucash/android/ui/transaction/TransactionsListFragment.java
+++ b/app/src/main/java/org/gnucash/android/ui/transaction/TransactionsListFragment.java
@@ -60,6 +60,7 @@
import org.gnucash.android.ui.transaction.dialog.BulkMoveDialogFragment;
import org.gnucash.android.ui.util.CursorRecyclerAdapter;
import org.gnucash.android.ui.util.widget.EmptyRecyclerView;
+import org.gnucash.android.util.BackupManager;
import java.util.List;
@@ -354,6 +355,7 @@ public void onClick(View v) {
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
case R.id.context_menu_delete:
+ BackupManager.backupActiveBook();
mTransactionsDbAdapter.deleteRecord(transactionId);
WidgetConfigurationActivity.updateAllWidgets(getActivity());
refresh();
From 831045199c975ef2cd0d18b4af9a2a7f2ca3964d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=80lex=20Magaz=20Gra=C3=A7a?=
Date: Sat, 10 Feb 2018 20:00:52 +0100
Subject: [PATCH 23/43] Backup before deleting an account
---
.../org/gnucash/android/ui/account/AccountsListFragment.java | 2 ++
.../gnucash/android/ui/account/DeleteAccountDialogFragment.java | 2 ++
2 files changed, 4 insertions(+)
diff --git a/app/src/main/java/org/gnucash/android/ui/account/AccountsListFragment.java b/app/src/main/java/org/gnucash/android/ui/account/AccountsListFragment.java
index d099027d2..a83ab10bc 100644
--- a/app/src/main/java/org/gnucash/android/ui/account/AccountsListFragment.java
+++ b/app/src/main/java/org/gnucash/android/ui/account/AccountsListFragment.java
@@ -64,6 +64,7 @@
import org.gnucash.android.ui.util.AccountBalanceTask;
import org.gnucash.android.ui.util.CursorRecyclerAdapter;
import org.gnucash.android.ui.util.widget.EmptyRecyclerView;
+import org.gnucash.android.util.BackupManager;
import java.util.List;
@@ -247,6 +248,7 @@ public void tryDeleteAccount(long rowId) {
if (acc.getTransactionCount() > 0 || mAccountsDbAdapter.getSubAccountCount(acc.getUID()) > 0) {
showConfirmationDialog(rowId);
} else {
+ BackupManager.backupActiveBook();
// Avoid calling AccountsDbAdapter.deleteRecord(long). See #654
String uid = mAccountsDbAdapter.getUID(rowId);
mAccountsDbAdapter.deleteRecord(uid);
diff --git a/app/src/main/java/org/gnucash/android/ui/account/DeleteAccountDialogFragment.java b/app/src/main/java/org/gnucash/android/ui/account/DeleteAccountDialogFragment.java
index bc84ac4e1..4d5f6c551 100644
--- a/app/src/main/java/org/gnucash/android/ui/account/DeleteAccountDialogFragment.java
+++ b/app/src/main/java/org/gnucash/android/ui/account/DeleteAccountDialogFragment.java
@@ -39,6 +39,7 @@
import org.gnucash.android.model.AccountType;
import org.gnucash.android.ui.common.Refreshable;
import org.gnucash.android.ui.homescreen.WidgetConfigurationActivity;
+import org.gnucash.android.util.BackupManager;
import org.gnucash.android.util.QualifiedAccountNameCursorAdapter;
import java.util.List;
@@ -209,6 +210,7 @@ public void onClick(View v) {
@Override
public void onClick(View v) {
+ BackupManager.backupActiveBook();
AccountsDbAdapter accountsDbAdapter = AccountsDbAdapter.getInstance();
From 068988f74c0c706bdcd986d41bfdc073366b8136 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=80lex=20Magaz=20Gra=C3=A7a?=
Date: Mon, 12 Feb 2018 20:40:56 +0100
Subject: [PATCH 24/43] Backup before deleting scheduled actions
---
.../android/ui/transaction/ScheduledActionsListFragment.java | 3 +++
1 file changed, 3 insertions(+)
diff --git a/app/src/main/java/org/gnucash/android/ui/transaction/ScheduledActionsListFragment.java b/app/src/main/java/org/gnucash/android/ui/transaction/ScheduledActionsListFragment.java
index ac382c53f..01f25340e 100644
--- a/app/src/main/java/org/gnucash/android/ui/transaction/ScheduledActionsListFragment.java
+++ b/app/src/main/java/org/gnucash/android/ui/transaction/ScheduledActionsListFragment.java
@@ -60,6 +60,7 @@
import org.gnucash.android.model.Transaction;
import org.gnucash.android.ui.common.FormActivity;
import org.gnucash.android.ui.common.UxArgument;
+import org.gnucash.android.util.BackupManager;
import java.text.DateFormat;
import java.util.Date;
@@ -116,6 +117,8 @@ public void onDestroyActionMode(ActionMode mode) {
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
switch (item.getItemId()) {
case R.id.context_menu_delete:
+ BackupManager.backupActiveBook();
+
for (long id : getListView().getCheckedItemIds()) {
if (mActionType == ScheduledAction.ActionType.TRANSACTION) {
From dea1741a65c5e9dd1ace8c9045555026878dcf6d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=80lex=20Magaz=20Gra=C3=A7a?=
Date: Sun, 18 Feb 2018 12:16:04 +0100
Subject: [PATCH 25/43] Avoid ScheduledActionService crashing due to new
Android 8 restrictions
See https://developer.android.com/about/versions/oreo/background.html#services
Fixes https://github.com/codinguser/gnucash-android/issues/768
---
app/src/main/AndroidManifest.xml | 2 +
.../android/app/GnuCashApplication.java | 12 +--
.../receivers/PeriodicJobReceiver.java | 6 ++
.../service/ScheduledActionService.java | 75 +++++++++----------
4 files changed, 52 insertions(+), 43 deletions(-)
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index f212cde86..b2f2575f8 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -120,6 +120,7 @@
@@ -160,6 +161,7 @@
android:name=".receivers.PeriodicJobReceiver" android:exported="false">
+
For now, backups and scheduled actions.
+ *
* @author Àlex Magaz Graça
*/
public class PeriodicJobReceiver extends BroadcastReceiver {
private static final String LOG_TAG = "PeriodicJobReceiver";
public static final String ACTION_BACKUP = "org.gnucash.android.action_backup";
+ public static final String ACTION_SCHEDULED_ACTIONS = "org.gnucash.android.action_scheduled_actions";
@Override
public void onReceive(Context context, Intent intent) {
@@ -41,6 +45,8 @@ public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(ACTION_BACKUP)) {
BackupJob.enqueueWork(context);
+ } else if (intent.getAction().equals(ACTION_SCHEDULED_ACTIONS)) {
+ ScheduledActionService.enqueueWork(context);
}
}
}
diff --git a/app/src/main/java/org/gnucash/android/service/ScheduledActionService.java b/app/src/main/java/org/gnucash/android/service/ScheduledActionService.java
index c345d52c3..71ab0cac3 100644
--- a/app/src/main/java/org/gnucash/android/service/ScheduledActionService.java
+++ b/app/src/main/java/org/gnucash/android/service/ScheduledActionService.java
@@ -16,12 +16,13 @@
package org.gnucash.android.service;
-import android.app.IntentService;
import android.content.ContentValues;
+import android.content.Context;
import android.content.Intent;
import android.database.sqlite.SQLiteDatabase;
-import android.os.PowerManager;
+import android.support.annotation.NonNull;
import android.support.annotation.VisibleForTesting;
+import android.support.v4.app.JobIntentService;
import android.util.Log;
import com.crashlytics.android.Crashlytics;
@@ -40,7 +41,6 @@
import org.gnucash.android.model.Book;
import org.gnucash.android.model.ScheduledAction;
import org.gnucash.android.model.Transaction;
-import org.gnucash.android.util.BackupManager;
import java.sql.Timestamp;
import java.util.ArrayList;
@@ -50,51 +50,50 @@
/**
* Service for running scheduled events.
- * The service is started and goes through all scheduled event entries in the the database and executes them.
- * Then it is stopped until the next time it is run.
- * Scheduled runs of the service should be achieved using an {@link android.app.AlarmManager}
+ *
+ * It's run every time the enqueueWork
is called. It goes
+ * through all scheduled event entries in the the database and executes them.
+ *
+ * Scheduled runs of the service should be achieved using an
+ * {@link android.app.AlarmManager}, with
+ * {@link org.gnucash.android.receivers.PeriodicJobReceiver} as an intermediary.
+ *
* @author Ngewi Fet
*/
-public class ScheduledActionService extends IntentService {
+public class ScheduledActionService extends JobIntentService {
- public static final String LOG_TAG = "ScheduledActionService";
+ private static final String LOG_TAG = "ScheduledActionService";
+ private static final int JOB_ID = 1001;
- public ScheduledActionService() {
- super(LOG_TAG);
+
+ public static void enqueueWork(Context context) {
+ Intent intent = new Intent(context, ScheduledActionService.class);
+ enqueueWork(context, ScheduledActionService.class, JOB_ID, intent);
}
@Override
- protected void onHandleIntent(Intent intent) {
+ protected void onHandleWork(@NonNull Intent intent) {
Log.i(LOG_TAG, "Starting scheduled action service");
- PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
- PowerManager.WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, LOG_TAG);
- wakeLock.acquire();
-
- try {
- BooksDbAdapter booksDbAdapter = BooksDbAdapter.getInstance();
- List books = booksDbAdapter.getAllRecords();
- for (Book book : books) { //// TODO: 20.04.2017 Retrieve only the book UIDs with new method
- DatabaseHelper dbHelper = new DatabaseHelper(GnuCashApplication.getAppContext(), book.getUID());
- SQLiteDatabase db = dbHelper.getWritableDatabase();
- RecurrenceDbAdapter recurrenceDbAdapter = new RecurrenceDbAdapter(db);
- ScheduledActionDbAdapter scheduledActionDbAdapter = new ScheduledActionDbAdapter(db, recurrenceDbAdapter);
-
- List scheduledActions = scheduledActionDbAdapter.getAllEnabledScheduledActions();
- Log.i(LOG_TAG, String.format("Processing %d total scheduled actions for Book: %s",
- scheduledActions.size(), book.getDisplayName()));
- processScheduledActions(scheduledActions, db);
-
- //close all databases except the currently active database
- if (!db.getPath().equals(GnuCashApplication.getActiveDb().getPath()))
- db.close();
- }
-
- Log.i(LOG_TAG, "Completed service @ " + java.text.DateFormat.getDateTimeInstance().format(new Date()));
-
- } finally { //release the lock either way
- wakeLock.release();
+ BooksDbAdapter booksDbAdapter = BooksDbAdapter.getInstance();
+ List books = booksDbAdapter.getAllRecords();
+ for (Book book : books) { //// TODO: 20.04.2017 Retrieve only the book UIDs with new method
+ DatabaseHelper dbHelper = new DatabaseHelper(GnuCashApplication.getAppContext(), book.getUID());
+ SQLiteDatabase db = dbHelper.getWritableDatabase();
+ RecurrenceDbAdapter recurrenceDbAdapter = new RecurrenceDbAdapter(db);
+ ScheduledActionDbAdapter scheduledActionDbAdapter = new ScheduledActionDbAdapter(db, recurrenceDbAdapter);
+
+ List scheduledActions = scheduledActionDbAdapter.getAllEnabledScheduledActions();
+ Log.i(LOG_TAG, String.format("Processing %d total scheduled actions for Book: %s",
+ scheduledActions.size(), book.getDisplayName()));
+ processScheduledActions(scheduledActions, db);
+
+ //close all databases except the currently active database
+ if (!db.getPath().equals(GnuCashApplication.getActiveDb().getPath()))
+ db.close();
}
+
+ Log.i(LOG_TAG, "Completed service @ " + java.text.DateFormat.getDateTimeInstance().format(new Date()));
}
/**
From 94bfc016ea59c582bc34a80714dd2f2518301f67 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=80lex=20Magaz=20Gra=C3=A7a?=
Date: Sun, 18 Feb 2018 13:22:06 +0100
Subject: [PATCH 26/43] Fix code inspector issues in ScheduledActionService
---
.../org/gnucash/android/service/ScheduledActionService.java | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/app/src/main/java/org/gnucash/android/service/ScheduledActionService.java b/app/src/main/java/org/gnucash/android/service/ScheduledActionService.java
index 71ab0cac3..e0e7095e3 100644
--- a/app/src/main/java/org/gnucash/android/service/ScheduledActionService.java
+++ b/app/src/main/java/org/gnucash/android/service/ScheduledActionService.java
@@ -188,6 +188,7 @@ private static int executeBackup(ScheduledAction scheduledAction, SQLiteDatabase
* @param scheduledAction Scheduled action
* @return {@code true} if execution is due, {@code false} otherwise
*/
+ @SuppressWarnings("RedundantIfStatement")
private static boolean shouldExecuteScheduledBackup(ScheduledAction scheduledAction) {
long now = System.currentTimeMillis();
long endTime = scheduledAction.getEndTime();
@@ -213,7 +214,7 @@ private static int executeTransactions(ScheduledAction scheduledAction, SQLiteDa
int executionCount = 0;
String actionUID = scheduledAction.getActionUID();
TransactionsDbAdapter transactionsDbAdapter = new TransactionsDbAdapter(db, new SplitsDbAdapter(db));
- Transaction trxnTemplate = null;
+ Transaction trxnTemplate;
try {
trxnTemplate = transactionsDbAdapter.getRecord(actionUID);
} catch (IllegalArgumentException ex){ //if the record could not be found, abort
From cfce758ea458663f3a1e19a4c1b1276d11ee780c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=80lex=20Magaz=20Gra=C3=A7a?=
Date: Sun, 25 Feb 2018 19:32:04 +0100
Subject: [PATCH 27/43] Use *Implementation configurations instead of the
deprecated *Compilation in build.gradle
---
app/build.gradle | 75 ++++++++++++++++++++++++------------------------
1 file changed, 38 insertions(+), 37 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index ea22064b2..5b0c13a71 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -190,69 +190,70 @@ repositories{
}
dependencies {
- compile fileTree(dir: 'libs', include: ['*.jar'])
- compile 'com.github.nextcloud:android-library:1.0.31'
- compile('com.android.support:support-v4:' + androidSupportVersion,
- 'com.android.support:appcompat-v7:' + androidSupportVersion,
- 'com.android.support:design:' + androidSupportVersion,
- 'com.android.support:cardview-v7:' + androidSupportVersion,
- 'com.android.support:preference-v7:' + androidSupportVersion,
- 'com.android.support:recyclerview-v7:' + androidSupportVersion,
- 'com.code-troopers.betterpickers:library:3.1.0',
- 'org.jraf:android-switch-backport:2.0.1@aar',
- 'com.github.PhilJay:MPAndroidChart:v2.1.3',
- 'joda-time:joda-time:2.9.4',
- 'com.google.android.gms:play-services-drive:9.6.1',
- 'io.github.kobakei:ratethisapp:1.1.3',
- 'com.squareup:android-times-square:1.6.5@aar',
- 'com.github.techfreak:wizardpager:1.0.3',
- 'net.objecthunter:exp4j:0.4.7',
- 'org.apache.jackrabbit:jackrabbit-webdav:2.13.3',
- 'com.dropbox.core:dropbox-core-sdk:3.0.3',
- 'com.android.support:multidex:1.0.1'
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+ implementation 'com.github.nextcloud:android-library:1.0.31'
+ implementation('com.android.support:support-v4:' + androidSupportVersion,
+ 'com.android.support:appcompat-v7:' + androidSupportVersion,
+ 'com.android.support:design:' + androidSupportVersion,
+ 'com.android.support:cardview-v7:' + androidSupportVersion,
+ 'com.android.support:preference-v7:' + androidSupportVersion,
+ 'com.android.support:recyclerview-v7:' + androidSupportVersion,
+ 'com.code-troopers.betterpickers:library:3.1.0',
+ 'org.jraf:android-switch-backport:2.0.1@aar',
+ 'com.github.PhilJay:MPAndroidChart:v2.1.3',
+ 'joda-time:joda-time:2.9.4',
+ 'com.google.android.gms:play-services-drive:9.6.1',
+ 'io.github.kobakei:ratethisapp:1.1.3',
+ 'com.squareup:android-times-square:1.6.5@aar',
+ 'com.github.techfreak:wizardpager:1.0.3',
+ 'net.objecthunter:exp4j:0.4.7',
+ 'org.apache.jackrabbit:jackrabbit-webdav:2.13.3',
+ 'com.dropbox.core:dropbox-core-sdk:3.0.3',
+ 'com.android.support:multidex:1.0.1'
)
- debugCompile 'com.facebook.stetho:stetho:1.5.0'
+ debugImplementation 'com.facebook.stetho:stetho:1.5.0'
- compile 'com.jakewharton:butterknife:8.8.1'
+ implementation 'com.jakewharton:butterknife:8.8.1'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'
- compile ('com.uservoice:uservoice-android-sdk:1.2.6') {
+ implementation ('com.uservoice:uservoice-android-sdk:1.2.6') {
exclude module: 'commons-logging'
exclude module: 'httpcore'
exclude module: 'httpclient'
}
- compile('com.crashlytics.sdk.android:crashlytics:2.6.7@aar') {
+ implementation('com.crashlytics.sdk.android:crashlytics:2.6.7@aar') {
transitive = true;
}
testImplementation 'org.robolectric:robolectric:3.5.1'
- testCompile(
+ testImplementation(
'junit:junit:4.12',
'joda-time:joda-time:2.9.4',
'org.assertj:assertj-core:1.7.1'
)
- testCompile 'org.robolectric:shadows-multidex:3.0'
-
- androidTestCompile ('com.android.support:support-annotations:' + androidSupportVersion,
- 'com.android.support.test:runner:' + androidSupportTestVersion,
- 'com.android.support.test:rules:' + androidSupportTestVersion,
- 'com.android.support.test.espresso:espresso-core:' + androidEspressoVersion,
- 'com.android.support.test.espresso:espresso-intents:' + androidEspressoVersion,
+ testImplementation 'org.robolectric:shadows-multidex:3.0'
+
+ androidTestImplementation (
+ 'com.android.support:support-annotations:' + androidSupportVersion,
+ 'com.android.support.test:runner:' + androidSupportTestVersion,
+ 'com.android.support.test:rules:' + androidSupportTestVersion,
+ 'com.android.support.test.espresso:espresso-core:' + androidEspressoVersion,
+ 'com.android.support.test.espresso:espresso-intents:' + androidEspressoVersion,
//the following are only added so that the app and test version both us the same versions
- 'com.android.support:appcompat-v7:' + androidSupportVersion,
- 'com.android.support:design:' + androidSupportVersion)
- androidTestCompile ('com.android.support.test.espresso:espresso-contrib:' + androidEspressoVersion) {
+ 'com.android.support:appcompat-v7:' + androidSupportVersion,
+ 'com.android.support:design:' + androidSupportVersion)
+ androidTestImplementation ('com.android.support.test.espresso:espresso-contrib:' + androidEspressoVersion) {
exclude group: 'com.android.support', module: 'support-v4'
exclude module: 'recyclerview-v7'
}
- androidTestCompile('com.squareup.assertj:assertj-android:1.1.1'){
+ androidTestImplementation('com.squareup.assertj:assertj-android:1.1.1'){
exclude group: 'com.android.support', module:'support-annotations'
}
- androidTestCompile 'com.squareup.spoon:spoon-client:1.6.4'
+ androidTestImplementation 'com.squareup.spoon:spoon-client:1.6.4'
}
From 131c53416df18714a932b16ecdc55ff9cf4e66d2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=80lex=20Magaz=20Gra=C3=A7a?=
Date: Sun, 25 Feb 2018 11:20:24 +0100
Subject: [PATCH 28/43] Fix ScheduledActionService.executeBackup not checking
the return value from the export task
---
.../gnucash/android/service/ScheduledActionService.java | 9 ++++++++-
.../test/unit/service/ScheduledActionServiceTest.java | 8 ++++----
2 files changed, 12 insertions(+), 5 deletions(-)
diff --git a/app/src/main/java/org/gnucash/android/service/ScheduledActionService.java b/app/src/main/java/org/gnucash/android/service/ScheduledActionService.java
index e0e7095e3..b1a3f7a11 100644
--- a/app/src/main/java/org/gnucash/android/service/ScheduledActionService.java
+++ b/app/src/main/java/org/gnucash/android/service/ScheduledActionService.java
@@ -179,7 +179,14 @@ private static int executeBackup(ScheduledAction scheduledAction, SQLiteDatabase
Crashlytics.logException(e);
Log.e(LOG_TAG, e.getMessage());
}
- Log.i(LOG_TAG, "Backup/export did not occur. There might have beeen no new transactions to export or it might have crashed");
+ if (!result) {
+ Log.i(LOG_TAG, "Backup/export did not occur. There might have been no"
+ + " new transactions to export or it might have crashed");
+ // We don't know if something failed or there weren't transactions to export,
+ // so fall on the safe side and return as if something had failed.
+ // FIXME: Change ExportAsyncTask to distinguish between the two cases
+ return 0;
+ }
return 1;
}
diff --git a/app/src/test/java/org/gnucash/android/test/unit/service/ScheduledActionServiceTest.java b/app/src/test/java/org/gnucash/android/test/unit/service/ScheduledActionServiceTest.java
index d0a974803..e4b3a6d5e 100644
--- a/app/src/test/java/org/gnucash/android/test/unit/service/ScheduledActionServiceTest.java
+++ b/app/src/test/java/org/gnucash/android/test/unit/service/ScheduledActionServiceTest.java
@@ -381,8 +381,8 @@ public void scheduledBackups_shouldNotRunBeforeNextScheduledExecution(){
}
/**
- * Tests that an scheduled backup doesn't include transactions added or modified
- * previous to the last run.
+ * Tests that a scheduled QIF backup isn't done when no transactions have
+ * been added or modified after the last run.
*/
@Test
public void scheduledBackups_shouldNotIncludeTransactionsPreviousToTheLastRun() {
@@ -422,8 +422,8 @@ public void scheduledBackups_shouldNotIncludeTransactionsPreviousToTheLastRun()
actions.add(scheduledBackup);
ScheduledActionService.processScheduledActions(actions, mDb);
- assertThat(scheduledBackup.getExecutionCount()).isEqualTo(2);
- assertThat(scheduledBackup.getLastRunTime()).isGreaterThan(previousLastRun);
+ assertThat(scheduledBackup.getExecutionCount()).isEqualTo(1);
+ assertThat(scheduledBackup.getLastRunTime()).isEqualTo(previousLastRun);
assertThat(backupFolder.listFiles()).hasSize(0);
}
From 034bf52022173d01418726baa4908de2cbddb3bc Mon Sep 17 00:00:00 2001
From: Ngewi Fet
Date: Tue, 27 Feb 2018 18:55:53 +0100
Subject: [PATCH 29/43] Hide CSV export separator label when another export
format is selected
---
.../android/export/csv/CsvTransactionsExporter.java | 7 ++++---
.../gnucash/android/ui/export/ExportFormFragment.java | 9 +++++----
app/src/main/res/layout/fragment_export_form.xml | 4 +++-
3 files changed, 12 insertions(+), 8 deletions(-)
diff --git a/app/src/main/java/org/gnucash/android/export/csv/CsvTransactionsExporter.java b/app/src/main/java/org/gnucash/android/export/csv/CsvTransactionsExporter.java
index a3207148c..46ef62ab2 100644
--- a/app/src/main/java/org/gnucash/android/export/csv/CsvTransactionsExporter.java
+++ b/app/src/main/java/org/gnucash/android/export/csv/CsvTransactionsExporter.java
@@ -16,14 +16,14 @@
package org.gnucash.android.export.csv;
-import android.database.sqlite.SQLiteDatabase;
import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+
import com.crashlytics.android.Crashlytics;
import org.gnucash.android.export.ExportParams;
import org.gnucash.android.export.Exporter;
import org.gnucash.android.model.Account;
-import org.gnucash.android.model.Commodity;
import org.gnucash.android.model.Money;
import org.gnucash.android.model.Split;
import org.gnucash.android.model.Transaction;
@@ -40,6 +40,7 @@
import java.util.Comparator;
import java.util.Date;
import java.util.List;
+import java.util.Locale;
/**
* Creates a GnuCash CSV transactions representation of the accounts and transactions
@@ -50,7 +51,7 @@ public class CsvTransactionsExporter extends Exporter{
private char mCsvSeparator;
- private DateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");
+ private DateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy", Locale.US);
private Comparator splitComparator = new Comparator() {
@Override
diff --git a/app/src/main/java/org/gnucash/android/ui/export/ExportFormFragment.java b/app/src/main/java/org/gnucash/android/ui/export/ExportFormFragment.java
index 43ca6c6bc..285c7a84f 100644
--- a/app/src/main/java/org/gnucash/android/ui/export/ExportFormFragment.java
+++ b/app/src/main/java/org/gnucash/android/ui/export/ExportFormFragment.java
@@ -144,6 +144,7 @@ public class ExportFormFragment extends Fragment implements
@BindView(R.id.radio_separator_comma_format) RadioButton mSeparatorCommaButton;
@BindView(R.id.radio_separator_colon_format) RadioButton mSeparatorColonButton;
@BindView(R.id.radio_separator_semicolon_format) RadioButton mSeparatorSemicolonButton;
+ @BindView(R.id.layout_csv_options) LinearLayout mCsvOptionsLayout;
@BindView(R.id.recurrence_options) View mRecurrenceOptionsView;
/**
@@ -207,27 +208,27 @@ private void onRadioButtonClicked(View view){
mExportWarningTextView.setVisibility(View.GONE);
}
mExportDateLayout.setVisibility(View.VISIBLE);
- mExportSeparatorLayout.setVisibility(View.GONE);
+ mCsvOptionsLayout.setVisibility(View.GONE);
break;
case R.id.radio_xml_format:
mExportFormat = ExportFormat.XML;
mExportWarningTextView.setText(R.string.export_warning_xml);
mExportDateLayout.setVisibility(View.GONE);
- mExportSeparatorLayout.setVisibility(View.GONE);
+ mCsvOptionsLayout.setVisibility(View.GONE);
break;
case R.id.radio_csv_accounts_format:
mExportFormat = ExportFormat.CSVA;
mExportWarningTextView.setText("");
mExportDateLayout.setVisibility(View.GONE);
- mExportSeparatorLayout.setVisibility(View.VISIBLE);
+ mCsvOptionsLayout.setVisibility(View.VISIBLE);
break;
case R.id.radio_csv_transactions_format:
mExportFormat = ExportFormat.CSVT;
mExportWarningTextView.setText("");
mExportDateLayout.setVisibility(View.GONE);
- mExportSeparatorLayout.setVisibility(View.VISIBLE);
+ mCsvOptionsLayout.setVisibility(View.VISIBLE);
break;
case R.id.radio_separator_comma_format:
diff --git a/app/src/main/res/layout/fragment_export_form.xml b/app/src/main/res/layout/fragment_export_form.xml
index 65632118f..5844a2300 100644
--- a/app/src/main/res/layout/fragment_export_form.xml
+++ b/app/src/main/res/layout/fragment_export_form.xml
@@ -105,7 +105,9 @@
android:text="CSV-trans"/>
-
Date: Mon, 26 Feb 2018 19:21:35 +0100
Subject: [PATCH 30/43] Don't zip QIF exports when it results in only one
exported file
Fixes https://github.com/codinguser/gnucash-android/issues/759
---
.../android/export/qif/QifExporter.java | 5 +-
.../test/unit/export/QifExporterTest.java | 55 +++++++++++--------
.../service/ScheduledActionServiceTest.java | 1 +
3 files changed, 36 insertions(+), 25 deletions(-)
diff --git a/app/src/main/java/org/gnucash/android/export/qif/QifExporter.java b/app/src/main/java/org/gnucash/android/export/qif/QifExporter.java
index b42bb8dbd..eb2b38fb4 100644
--- a/app/src/main/java/org/gnucash/android/export/qif/QifExporter.java
+++ b/app/src/main/java/org/gnucash/android/export/qif/QifExporter.java
@@ -242,11 +242,14 @@ public List generateExport() throws ExporterException {
/// export successful
PreferencesHelper.setLastExportTime(TimestampHelper.getTimestampFromNow());
+
List exportedFiles = splitQIF(file);
if (exportedFiles.isEmpty())
return Collections.emptyList();
- else
+ else if (exportedFiles.size() > 1)
return zipQifs(exportedFiles);
+ else
+ return exportedFiles;
} catch (IOException e) {
throw new ExporterException(mExportParams, e);
}
diff --git a/app/src/test/java/org/gnucash/android/test/unit/export/QifExporterTest.java b/app/src/test/java/org/gnucash/android/test/unit/export/QifExporterTest.java
index 9f399d362..689e69b33 100644
--- a/app/src/test/java/org/gnucash/android/test/unit/export/QifExporterTest.java
+++ b/app/src/test/java/org/gnucash/android/test/unit/export/QifExporterTest.java
@@ -16,6 +16,7 @@
package org.gnucash.android.test.unit.export;
import android.database.sqlite.SQLiteDatabase;
+import android.support.annotation.NonNull;
import org.gnucash.android.app.GnuCashApplication;
import org.gnucash.android.db.BookDbHelper;
@@ -45,6 +46,7 @@
import java.io.FileReader;
import java.io.IOException;
import java.util.List;
+import java.util.zip.ZipFile;
import static org.assertj.core.api.Assertions.assertThat;
@@ -83,7 +85,7 @@ public void testWithNoTransactionsToExport_shouldNotCreateAnyFile(){
/**
* Test that QIF files are generated
*/
- //// FIXME: 20.04.2017 Test failing with NPE
+ @Test
public void testGenerateQIFExport(){
AccountsDbAdapter accountsDbAdapter = new AccountsDbAdapter(mDb);
@@ -109,10 +111,11 @@ public void testGenerateQIFExport(){
}
/**
- * Test that when more than one currency is in use, multiple QIF files will be generated
+ * Test that when more than one currency is in use, a zip with multiple QIF files
+ * will be generated
*/
- //// FIXME: 20.04.2017 test failing with NPE
- public void multiCurrencyTransactions_shouldResultInMultipleQifFiles(){
+ // @Test Fails randomly. Sometimes it doesn't split the QIF.
+ public void multiCurrencyTransactions_shouldResultInMultipleZippedQifFiles() throws IOException {
AccountsDbAdapter accountsDbAdapter = new AccountsDbAdapter(mDb);
Account account = new Account("Basic Account", Commodity.getInstance("EUR"));
@@ -139,25 +142,29 @@ public void multiCurrencyTransactions_shouldResultInMultipleQifFiles(){
QifExporter qifExporter = new QifExporter(exportParameters, mDb);
List exportedFiles = qifExporter.generateExport();
- assertThat(exportedFiles).hasSize(2);
+ assertThat(exportedFiles).hasSize(1);
File file = new File(exportedFiles.get(0));
- assertThat(file).exists().hasExtension("qif");
- assertThat(file.length()).isGreaterThan(0L);
+ assertThat(file).exists().hasExtension("zip");
+ assertThat(new ZipFile(file).size()).isEqualTo(2);
}
- //@Test
- public void description_and_memo_field_test() {
- // arrange
-
+ /**
+ * Test that the memo and description fields of transactions are exported.
+ */
+ @Test
+ public void memoAndDescription_shouldBeExported() throws IOException {
String expectedDescription = "my description";
String expectedMemo = "my memo";
AccountsDbAdapter accountsDbAdapter = new AccountsDbAdapter(mDb);
+
Account account = new Account("Basic Account");
Transaction transaction = new Transaction("One transaction");
+ transaction.addSplit(new Split(Money.createZeroInstance("EUR"), account.getUID()));
transaction.setDescription(expectedDescription);
transaction.setNote(expectedMemo);
account.addTransaction(transaction);
+
accountsDbAdapter.addRecord(account);
ExportParams exportParameters = new ExportParams(ExportFormat.QIF);
@@ -165,26 +172,26 @@ public void description_and_memo_field_test() {
exportParameters.setExportTarget(ExportParams.ExportTarget.SD_CARD);
exportParameters.setDeleteTransactionsAfterExport(false);
- // act
-
QifExporter qifExporter = new QifExporter(exportParameters, mDb);
List exportedFiles = qifExporter.generateExport();
- // assert
-
assertThat(exportedFiles).hasSize(1);
File file = new File(exportedFiles.get(0));
+ String fileContent = readFileContent(file);
assertThat(file).exists().hasExtension("qif");
- StringBuilder fileContentsBuilder = new StringBuilder();
- try {
- BufferedReader reader = new BufferedReader(new FileReader(file));
- fileContentsBuilder.append(reader.readLine());
- } catch (IOException e) {
- e.printStackTrace();
- }
- // todo: check the description & memo fields.
- String fileContent = fileContentsBuilder.toString();
assertThat(fileContent.contains(expectedDescription));
assertThat(fileContent.contains(expectedMemo));
}
+
+ @NonNull
+ public String readFileContent(File file) throws IOException {
+ StringBuilder fileContentsBuilder = new StringBuilder();
+ BufferedReader reader = new BufferedReader(new FileReader(file));
+ String line;
+ while ((line = reader.readLine()) != null) {
+ fileContentsBuilder.append(line).append('\n');
+ }
+
+ return fileContentsBuilder.toString();
+ }
}
\ No newline at end of file
diff --git a/app/src/test/java/org/gnucash/android/test/unit/service/ScheduledActionServiceTest.java b/app/src/test/java/org/gnucash/android/test/unit/service/ScheduledActionServiceTest.java
index e4b3a6d5e..fd555a7f1 100644
--- a/app/src/test/java/org/gnucash/android/test/unit/service/ScheduledActionServiceTest.java
+++ b/app/src/test/java/org/gnucash/android/test/unit/service/ScheduledActionServiceTest.java
@@ -481,6 +481,7 @@ public void scheduledBackups_shouldIncludeTransactionsAfterTheLastRun() {
assertThat(scheduledBackup.getExecutionCount()).isEqualTo(2);
assertThat(scheduledBackup.getLastRunTime()).isGreaterThan(previousLastRun);
assertThat(backupFolder.listFiles()).hasSize(1);
+ assertThat(backupFolder.listFiles()[0].getName()).endsWith(".qif");
}
@After
From 261a10938cd95d3ebd1bf039e86692cfdf701a83 Mon Sep 17 00:00:00 2001
From: Ngewi Fet
Date: Fri, 2 Mar 2018 09:53:02 +0100
Subject: [PATCH 31/43] Fixes #731 - Persian currency symbol displayed doubled
This fixes the issue for new installs, but unfortunately not for existing users.
---
app/src/main/res/raw/iso_4217_currencies.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/src/main/res/raw/iso_4217_currencies.xml b/app/src/main/res/raw/iso_4217_currencies.xml
index 1312e8db2..27a93a61b 100644
--- a/app/src/main/res/raw/iso_4217_currencies.xml
+++ b/app/src/main/res/raw/iso_4217_currencies.xml
@@ -1313,7 +1313,7 @@
exchange-code="364"
parts-per-unit="1"
smallest-fraction="1"
- local-symbol="﷼﷼"
+ local-symbol="﷼"
/>
From bec24f6f5eedfe8a35db1130978d7ebbd6569a84 Mon Sep 17 00:00:00 2001
From: Ngewi Fet
Date: Fri, 2 Mar 2018 13:56:27 +0100
Subject: [PATCH 32/43] Include future transactions when computing account
balance in account list - fixes #465
- This would make it consistent with GnuCash desktop accounts display
---
.../android/test/ui/AccountsActivityTest.java | 49 ++++++++++++++++++-
.../android/ui/util/AccountBalanceTask.java | 2 +-
2 files changed, 48 insertions(+), 3 deletions(-)
diff --git a/app/src/androidTest/java/org/gnucash/android/test/ui/AccountsActivityTest.java b/app/src/androidTest/java/org/gnucash/android/test/ui/AccountsActivityTest.java
index d57cfd240..eb7f43c13 100644
--- a/app/src/androidTest/java/org/gnucash/android/test/ui/AccountsActivityTest.java
+++ b/app/src/androidTest/java/org/gnucash/android/test/ui/AccountsActivityTest.java
@@ -17,13 +17,11 @@
package org.gnucash.android.test.ui;
import android.Manifest;
-import android.annotation.TargetApi;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences.Editor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
-import android.os.Build;
import android.preference.PreferenceManager;
import android.support.test.espresso.Espresso;
import android.support.test.espresso.matcher.ViewMatchers;
@@ -32,6 +30,7 @@
import android.support.test.runner.AndroidJUnit4;
import android.support.v4.app.Fragment;
import android.util.Log;
+import android.view.View;
import com.kobakei.ratethisapp.RateThisApp;
@@ -54,6 +53,9 @@
import org.gnucash.android.test.ui.util.DisableAnimationsRule;
import org.gnucash.android.ui.account.AccountsActivity;
import org.gnucash.android.ui.account.AccountsListFragment;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.hamcrest.TypeSafeMatcher;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
@@ -87,6 +89,7 @@
import static android.support.test.espresso.matcher.ViewMatchers.withText;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.Matchers.allOf;
+import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
@@ -236,6 +239,21 @@ public void testCreateAccount(){
assertThat(newestAccount.isPlaceholderAccount()).isTrue();
}
+ @Test
+ public void should_IncludeFutureTransactionsInAccountBalance(){
+ Transaction transaction = new Transaction("Future transaction");
+ Split split1 = new Split(new Money("4.15", ACCOUNTS_CURRENCY_CODE), SIMPLE_ACCOUNT_UID);
+ transaction.addSplit(split1);
+ transaction.setTime(System.currentTimeMillis() + 4815162342L);
+ mTransactionsDbAdapter.addRecord(transaction);
+
+ refreshAccountsList();
+
+ List trxns = mTransactionsDbAdapter.getAllTransactions();
+
+ onView(first(withText(containsString("4.15")))).check(matches(isDisplayed()));
+ }
+
@Test
public void testChangeParentAccount() {
final String accountName = "Euro Account";
@@ -511,4 +529,31 @@ public void run() {
System.err.println("Failed to refresh fragment");
}
}
+
+ /**
+ * Matcher to select the first of multiple views which are matched in the UI
+ * @param expected Matcher which fits multiple views
+ * @return Single match
+ */
+ public static Matcher first(final Matcher expected){
+
+ return new TypeSafeMatcher() {
+ private boolean first = false;
+
+ @Override
+ protected boolean matchesSafely(View item) {
+
+ if( expected.matches(item) && !first ){
+ return first = true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("Matcher.first( " + expected.toString() + " )" );
+ }
+ };
+ }
}
diff --git a/app/src/main/java/org/gnucash/android/ui/util/AccountBalanceTask.java b/app/src/main/java/org/gnucash/android/ui/util/AccountBalanceTask.java
index e16b21da3..8bef261af 100644
--- a/app/src/main/java/org/gnucash/android/ui/util/AccountBalanceTask.java
+++ b/app/src/main/java/org/gnucash/android/ui/util/AccountBalanceTask.java
@@ -55,7 +55,7 @@ protected Money doInBackground(String... params) {
Money balance = Money.getZeroInstance();
try {
- balance = accountsDbAdapter.getAccountBalance(params[0], -1, System.currentTimeMillis());
+ balance = accountsDbAdapter.getAccountBalance(params[0], -1, -1);
} catch (Exception ex) {
Log.e(LOG_TAG, "Error computing account balance ", ex);
Crashlytics.logException(ex);
From b5496aaa7c076b98eecbfd5124b82fe426b66564 Mon Sep 17 00:00:00 2001
From: Greg Walters
Date: Wed, 25 Apr 2018 13:10:38 -0500
Subject: [PATCH 33/43] Fixing error in
https://github.com/codinguser/gnucash-android/issues/771
---
.../java/org/gnucash/android/export/qif/QifExporter.java | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/app/src/main/java/org/gnucash/android/export/qif/QifExporter.java b/app/src/main/java/org/gnucash/android/export/qif/QifExporter.java
index eb2b38fb4..9f1478df8 100644
--- a/app/src/main/java/org/gnucash/android/export/qif/QifExporter.java
+++ b/app/src/main/java/org/gnucash/android/export/qif/QifExporter.java
@@ -213,6 +213,15 @@ public List generateExport() throws ExporterException {
case 1000:
precision = 3;
break;
+ case 10000:
+ precision = 4;
+ break;
+ case 100000:
+ precision = 5;
+ break;
+ case 1000000:
+ precision = 6;
+ break;
default:
throw new ExporterException(mExportParams, "split quantity has illegal denominator: "+ quantity_denom);
}
From d85fb0d36ca7ef7c5a343a13f04272ff5ab04199 Mon Sep 17 00:00:00 2001
From: xzfantom
Date: Wed, 9 May 2018 00:09:46 +0300
Subject: [PATCH 34/43] Added ability to load GNUCash v. 3.0 xml
---
.../main/java/org/gnucash/android/importer/GncXmlHandler.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/src/main/java/org/gnucash/android/importer/GncXmlHandler.java b/app/src/main/java/org/gnucash/android/importer/GncXmlHandler.java
index e2518b911..3c9e046af 100644
--- a/app/src/main/java/org/gnucash/android/importer/GncXmlHandler.java
+++ b/app/src/main/java/org/gnucash/android/importer/GncXmlHandler.java
@@ -432,7 +432,7 @@ public void endElement(String uri, String localName, String qualifiedName) throw
mAccount.setHidden(accountType == AccountType.ROOT); //flag root account as hidden
break;
case GncXmlHelper.TAG_COMMODITY_SPACE:
- if (characterString.equals("ISO4217")) {
+ if (characterString.equals("ISO4217") || characterString.equals("CURRENCY") ) {
mISO4217Currency = true;
} else {
// price of non-ISO4217 commodities cannot be handled
From 68a8ef1ad28fe0f71cd1ccefc0488bd9de880009 Mon Sep 17 00:00:00 2001
From: Ngewi Fet
Date: Thu, 31 May 2018 23:10:48 +0200
Subject: [PATCH 35/43] Update version numbers and CHANGELOG in prep for v2.4.0
release
---
CHANGELOG.md | 14 ++++++++++++++
app/build.gradle | 4 ++--
2 files changed, 16 insertions(+), 2 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0dda3af41..230e565fb 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,19 @@
Change Log
===============================================================================
+Version 2.4.0 *(2018-06-06)*
+----------------------------
+* Feature #665: Adds CSV export format
+* Feature #544: Add extra checkbox confirmation for irreversible actions
+* Feature #767: Backup before destructive actions
+* Feature #465: Account balances now include future transactions
+* Fixed #764: Crash when importing XML files from Gnucash desktop v2.7 and up
+* Fixed #768: ScheduledActionService crashes on Android 8 (Oreo)
+* Fixed #731: Double display of Persian currency symbol
+* Fixed #771: QIF export crashes due to illegal denominator
+* Fixed #757: Backups are created every hour
+* Fixed #766: Backups are kept forever
+
+
Version 2.3.0 *(2018-01-10)*
----------------------------
* Feature #544: Use double confirmation dialog boxes before irreversible actions
diff --git a/app/build.gradle b/app/build.gradle
index 5b0c13a71..9fa3e96da 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -4,9 +4,9 @@ apply plugin: 'com.android.application'
apply plugin: 'io.fabric'
def versionMajor = 2
-def versionMinor = 3
+def versionMinor = 4
def versionPatch = 0
-def versionBuild = 4
+def versionBuild = 1
static def buildTime() {
def df = new SimpleDateFormat("yyyyMMdd HH:mm 'UTC'")
From 5764f868ded906695e8b6e868c5b03d78713f4bc Mon Sep 17 00:00:00 2001
From: Ngewi Fet
Date: Thu, 31 May 2018 23:58:57 +0200
Subject: [PATCH 36/43] Update What's New string for version 2.4.0
---
app/src/main/res/values/strings.xml | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 53e93eb45..ab6d9d75e 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -117,11 +117,10 @@
Enable this option when exporting to third-party application other than GnuCash for desktop
What\'s New
- - Added ability to export to any service which supports the Storage Access Framework \n
- - Added option to set the location for regular automatic backups (See backup settings)\n
- - Added Bitcoin currency support\n
- - Added support for renaming books\n
- - Multiple bug fixes and improvements\n
+ - Adds CSV export format \n
+ - Improve compatibility with GnuCash desktop files\n
+ - Limit space usage by backups \n
+ - Multiple bug fixes and enhancements\n
Dismiss
Enter an amount to save the transaction
From 6788412848c424b6175613dbbf1db3aa0f24aea2 Mon Sep 17 00:00:00 2001
From: Ngewi Fet
Date: Mon, 4 Jun 2018 12:27:11 +0200
Subject: [PATCH 37/43] Remove CSV accounts exports from the UI - only
transactions will be exported
---
.../android/ui/export/ExportFormFragment.java | 14 ++------------
app/src/main/res/layout/fragment_export_form.xml | 9 ++-------
app/src/main/res/values/strings.xml | 3 +--
3 files changed, 5 insertions(+), 21 deletions(-)
diff --git a/app/src/main/java/org/gnucash/android/ui/export/ExportFormFragment.java b/app/src/main/java/org/gnucash/android/ui/export/ExportFormFragment.java
index 3d6a2d6ab..e883305e2 100644
--- a/app/src/main/java/org/gnucash/android/ui/export/ExportFormFragment.java
+++ b/app/src/main/java/org/gnucash/android/ui/export/ExportFormFragment.java
@@ -139,7 +139,6 @@ public class ExportFormFragment extends Fragment implements
@BindView(R.id.radio_ofx_format) RadioButton mOfxRadioButton;
@BindView(R.id.radio_qif_format) RadioButton mQifRadioButton;
@BindView(R.id.radio_xml_format) RadioButton mXmlRadioButton;
- @BindView(R.id.radio_csv_accounts_format) RadioButton mCsvAccountsRadioButton;
@BindView(R.id.radio_csv_transactions_format) RadioButton mCsvTransactionsRadioButton;
@BindView(R.id.radio_separator_comma_format) RadioButton mSeparatorCommaButton;
@@ -219,15 +218,9 @@ private void onRadioButtonClicked(View view){
mCsvOptionsLayout.setVisibility(View.GONE);
break;
- case R.id.radio_csv_accounts_format:
- mExportFormat = ExportFormat.CSVA;
- mExportWarningTextView.setText("");
- mExportDateLayout.setVisibility(View.GONE);
- mCsvOptionsLayout.setVisibility(View.VISIBLE);
- break;
case R.id.radio_csv_transactions_format:
mExportFormat = ExportFormat.CSVT;
- mExportWarningTextView.setText("");
+ mExportWarningTextView.setText("Exports registered transactions as CSV");
mExportDateLayout.setVisibility(View.GONE);
mCsvOptionsLayout.setVisibility(View.VISIBLE);
break;
@@ -254,8 +247,7 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container,
bindViewListeners();
String[] export_format_strings = getResources().getStringArray(R.array.export_formats);
- mCsvAccountsRadioButton.setText(export_format_strings[3]);
- mCsvTransactionsRadioButton.setText(export_format_strings[4]);
+ mCsvTransactionsRadioButton.setText(export_format_strings[3]);
return view;
}
@@ -506,7 +498,6 @@ public void onClick(View view) {
mOfxRadioButton.setOnClickListener(radioClickListener);
mQifRadioButton.setOnClickListener(radioClickListener);
mXmlRadioButton.setOnClickListener(radioClickListener);
- mCsvAccountsRadioButton.setOnClickListener(radioClickListener);
mCsvTransactionsRadioButton.setOnClickListener(radioClickListener);
mSeparatorCommaButton.setOnClickListener(radioClickListener);
@@ -518,7 +509,6 @@ public void onClick(View view) {
case QIF: mQifRadioButton.performClick(); break;
case OFX: mOfxRadioButton.performClick(); break;
case XML: mXmlRadioButton.performClick(); break;
- case CSVA: mCsvAccountsRadioButton.performClick(); break;
case CSVT: mCsvTransactionsRadioButton.performClick(); break;
}
diff --git a/app/src/main/res/layout/fragment_export_form.xml b/app/src/main/res/layout/fragment_export_form.xml
index 5844a2300..aa3c8d3bd 100644
--- a/app/src/main/res/layout/fragment_export_form.xml
+++ b/app/src/main/res/layout/fragment_export_form.xml
@@ -73,7 +73,7 @@
android:layout_height="wrap_content"
android:layout_marginLeft="35dp"
android:gravity="center_vertical"
- android:orientation="vertical">
+ android:orientation="horizontal">
-
+ android:text="CSV"/>
QIF
- OFX
- XML
- - CSV-accounts
- - CSV-transactions
+ - CSV
Select a Color
From ad478f69aa2eed37f8f98c66fa6fd5523f7c1d32 Mon Sep 17 00:00:00 2001
From: Ngewi Fet
Date: Mon, 4 Jun 2018 18:58:03 +0200
Subject: [PATCH 38/43] Enable support for exporting transaction splits
separately in CSV exports
Modified transactions CSV exporter to produce similar CSV like GnuCash desktop - #756
Localize transaction CSV headers
---
.../export/csv/CsvTransactionsExporter.java | 182 +++++-------------
.../gnucash/android/export/csv/CsvWriter.java | 68 +++++--
.../java/org/gnucash/android/model/Money.java | 14 +-
app/src/main/res/values/strings.xml | 18 ++
4 files changed, 140 insertions(+), 142 deletions(-)
diff --git a/app/src/main/java/org/gnucash/android/export/csv/CsvTransactionsExporter.java b/app/src/main/java/org/gnucash/android/export/csv/CsvTransactionsExporter.java
index 46ef62ab2..c0419f7c7 100644
--- a/app/src/main/java/org/gnucash/android/export/csv/CsvTransactionsExporter.java
+++ b/app/src/main/java/org/gnucash/android/export/csv/CsvTransactionsExporter.java
@@ -18,26 +18,24 @@
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
+import android.support.annotation.NonNull;
import com.crashlytics.android.Crashlytics;
+import org.gnucash.android.R;
import org.gnucash.android.export.ExportParams;
import org.gnucash.android.export.Exporter;
import org.gnucash.android.model.Account;
-import org.gnucash.android.model.Money;
import org.gnucash.android.model.Split;
import org.gnucash.android.model.Transaction;
import org.gnucash.android.model.TransactionType;
-import java.io.BufferedOutputStream;
-import java.io.FileOutputStream;
+import java.io.FileWriter;
import java.io.IOException;
-import java.io.OutputStreamWriter;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
+import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Locale;
@@ -51,20 +49,7 @@ public class CsvTransactionsExporter extends Exporter{
private char mCsvSeparator;
- private DateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy", Locale.US);
-
- private Comparator splitComparator = new Comparator() {
- @Override
- public int compare(Split o1, Split o2) {
- if(o1.getType() == TransactionType.DEBIT
- && o2.getType() == TransactionType.CREDIT)
- return -1;
- if (o1.getType() == TransactionType.CREDIT
- && o2.getType() == TransactionType.DEBIT)
- return 1;
- return 0;
- }
- };
+ private DateFormat dateFormat = new SimpleDateFormat("YYYY-MM-dd", Locale.US);
/**
* Construct a new exporter with export parameters
@@ -90,26 +75,13 @@ public CsvTransactionsExporter(ExportParams params, SQLiteDatabase db) {
@Override
public List generateExport() throws ExporterException {
- OutputStreamWriter writerStream = null;
- CsvWriter writer = null;
String outputFile = getExportCacheFilePath();
- try {
- FileOutputStream fileOutputStream = new FileOutputStream(outputFile);
- BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
- writerStream = new OutputStreamWriter(bufferedOutputStream);
- writer = new CsvWriter(writerStream);
- generateExport(writer);
+
+ try (CsvWriter csvWriter = new CsvWriter(new FileWriter(outputFile), "" + mCsvSeparator)){
+ generateExport(csvWriter);
} catch (IOException ex){
Crashlytics.log("Error exporting CSV");
Crashlytics.logException(ex);
- } finally {
- if (writerStream != null) {
- try {
- writerStream.close();
- } catch (IOException e) {
- throw new ExporterException(mExportParams, e);
- }
- }
}
List exportedFiles = new ArrayList<>();
@@ -118,111 +90,63 @@ public List generateExport() throws ExporterException {
return exportedFiles;
}
- private void write_split(final Transaction transaction, final Split split, final CsvWriter writer) throws IOException
- {
- String separator = mCsvSeparator + "";
- Account account = mAccountsDbAdapter.getRecord(split.getAccountUID());
-
- // Date
- Date date = new Date(transaction.getTimeMillis());
- writer.write(dateFormat.format(date) + separator);
- // Account name
- writer.write(account.getName() + separator);
- // TODO:Number is not defined yet?
- writer.write( separator);
- // Description
- writer.write(transaction.getDescription() + separator);
- // Notes of transaction
- writer.write(transaction.getNote() + separator);
- // Memo
- writer.write(
- (split.getMemo()==null?
- "":split.getMemo()) + separator);
- // TODO:Category is not defined yet?
- writer.write(separator);
- // Type
- writer.write(split.getType().name() + separator);
- // TODO:Action is not defined yet?
- writer.write(separator);
- // Reconcile
- writer.write(split.getReconcileState() + separator);
-
- // Changes
- Money change = split.getFormattedQuantity().withCurrency(transaction.getCommodity());
- Money zero = Money.getZeroInstance().withCurrency(transaction.getCommodity());
- // To currency; From currency; To; From
- if (change.isNegative()) {
- writer.write(zero.toPlainString() + separator);
- writer.write(change.abs().toPlainString() + separator);
- writer.write(Money.getZeroInstance().toPlainString() + separator);
- writer.write(split.getFormattedQuantity().abs().toPlainString() + separator);
- }
- else {
- writer.write(change.abs().toPlainString() + separator);
- writer.write(zero.toPlainString() + separator);
- writer.write(split.getFormattedQuantity().abs().toPlainString() + separator);
- writer.write(Money.getZeroInstance().toPlainString() + separator);
+ /**
+ * Write splits to CSV format
+ * @param splits Splits to be written
+ */
+ private void writeSplitsToCsv(@NonNull List splits, @NonNull CsvWriter writer) throws IOException {
+ int index = 0;
+ for (Split split : splits) {
+ if (index++ > 0){ // the first split is on the same line as the transactions. But after that, we
+ writer.write("" + mCsvSeparator + mCsvSeparator + mCsvSeparator + mCsvSeparator
+ + mCsvSeparator + mCsvSeparator + mCsvSeparator + mCsvSeparator);
+ }
+ writer.writeToken(split.getMemo());
+ Account account = mAccountsDbAdapter.getRecord(split.getAccountUID());
+ writer.writeToken(account.getFullName());
+ writer.writeToken(account.getName());
+
+ String sign = split.getType() == TransactionType.CREDIT ? "-" : "";
+ writer.writeToken(sign + split.getQuantity().formattedString());
+ writer.writeToken(sign + split.getQuantity().toLocaleString());
+ writer.writeToken("" + split.getReconcileState());
+ if (split.getReconcileState() == Split.FLAG_RECONCILED) {
+ String recDateString = dateFormat.format(new Date(split.getReconcileDate().getTime()));
+ writer.writeToken(recDateString);
+ } else {
+ writer.writeToken(null);
+ }
+ writer.writeEndToken(split.getQuantity().divide(split.getValue()).toLocaleString());
}
-
- // TODO: What is price?
- writer.write(separator);
- writer.write(separator);
}
- public void generateExport(final CsvWriter writer) throws ExporterException {
+ private void generateExport(final CsvWriter csvWriter) throws ExporterException {
try {
- String separator = mCsvSeparator + "";
- List names = new ArrayList();
- names.add("Date");
- names.add("Account name");
- names.add("Number");
- names.add("Description");
- names.add("Notes");
- names.add("Memo");
- names.add("Category");
- names.add("Type");
- names.add("Action");
- names.add("Reconcile");
- names.add("To With Sym");
- names.add("From With Sym");
- names.add("To Num.");
- names.add("From Num.");
- names.add("To Rate/Price");
- names.add("From Rate/Price");
-
- List transactions = mTransactionsDbAdapter.getAllTransactions();
-
+ List names = Arrays.asList(mContext.getResources().getStringArray(R.array.csv_transaction_headers));
for(int i = 0; i < names.size(); i++) {
- writer.write(names.get(i) + separator);
+ csvWriter.writeToken(names.get(i));
}
- writer.write("\n");
+ csvWriter.newLine();
Cursor cursor = mTransactionsDbAdapter.fetchAllRecords();
- while (cursor.moveToNext())
- {
+ while (cursor.moveToNext()){
Transaction transaction = mTransactionsDbAdapter.buildModelInstance(cursor);
- List splits = transaction.getSplits();
- Collections.sort(splits,splitComparator);
- for (int j = 0; j < splits.size()/2; j++) {
- Split split = splits.get(j);
- Split pair = null;
- for (int k = 0; k < splits.size(); k++) {
- if (split.isPairOf(splits.get(k))) {
- pair = splits.get(k);
- }
- }
-
- write_split(transaction, split, writer);
- writer.write("\n");
- if (pair != null) {
- write_split(transaction, pair, writer);
- writer.write("\n");
- }
- }
+ Date date = new Date(transaction.getTimeMillis());
+ csvWriter.writeToken(dateFormat.format(date));
+ csvWriter.writeToken(transaction.getUID());
+ csvWriter.writeToken(null); //Transaction number
+
+ csvWriter.writeToken(transaction.getDescription());
+ csvWriter.writeToken(transaction.getNote());
+
+ csvWriter.writeToken("CURRENCY::" + transaction.getCurrencyCode());
+ csvWriter.writeToken(null); // Void Reason
+ csvWriter.writeToken(null); // Action
+ writeSplitsToCsv(transaction.getSplits(), csvWriter);
}
- } catch (Exception e) {
+ } catch (IOException e) {
Crashlytics.logException(e);
throw new ExporterException(mExportParams, e);
}
diff --git a/app/src/main/java/org/gnucash/android/export/csv/CsvWriter.java b/app/src/main/java/org/gnucash/android/export/csv/CsvWriter.java
index 5e6570d37..4c885aa53 100644
--- a/app/src/main/java/org/gnucash/android/export/csv/CsvWriter.java
+++ b/app/src/main/java/org/gnucash/android/export/csv/CsvWriter.java
@@ -17,6 +17,9 @@
package org.gnucash.android.export.csv;
+import android.support.annotation.NonNull;
+
+import java.io.BufferedWriter;
import java.io.IOException;
import java.io.Writer;
@@ -24,25 +27,68 @@
* Format data to be CSV-compatible
*
* @author Semyannikov Gleb
+ * @author Ngewi Fet
*/
-public class CsvWriter {
- private Writer writer;
+public class CsvWriter extends BufferedWriter {
+ private String separator = ",";
public CsvWriter(Writer writer){
- this.writer = writer;
+ super(writer);
+ }
+
+ public CsvWriter(Writer writer, String separator){
+ super(writer);
+ this.separator = separator;
+ }
+
+ @Override
+ public void write(@NonNull String str) throws IOException {
+ this.write(str, 0, str.length());
}
- public void write(String str) throws IOException {
- if (str == null || str.length() < 1) {
- return;
+ /**
+ * Writes a CSV token and the separator to the underlying output stream.
+ *
+ * The token **MUST NOT** not contain the CSV separator. If the separator is found in the token, then
+ * the token will be escaped as specified by RFC 4180
+ * @param token Token to be written to file
+ * @throws IOException if the token could not be written to the underlying stream
+ */
+ public void writeToken(String token) throws IOException {
+ if (token == null || token.isEmpty()){
+ write(separator);
+ } else {
+ token = escape(token);
+ write(token + separator);
}
+ }
- String head = str.substring(0, str.length() - 1);
- char separator = str.charAt(str.length() - 1);
- if (head.indexOf(separator) > -1) {
- head = '"' + head + '"';
+ /**
+ * Escape any CSV separators by surrounding the token in double quotes
+ * @param token String token to be written to CSV
+ * @return Escaped CSV token
+ */
+ @NonNull
+ private String escape(@NonNull String token) {
+ if (token.contains(separator)){
+ return "\"" + token + "\"";
}
+ return token;
+ }
- writer.write(head + separator);
+ /**
+ * Writes a token to the CSV file and appends end of line to it.
+ *
+ * The token **MUST NOT** not contain the CSV separator. If the separator is found in the token, then
+ * the token will be escaped as specified by RFC 4180
+ * @param token The token to be written to the file
+ * @throws IOException if token could not be written to underlying writer
+ */
+ public void writeEndToken(String token) throws IOException {
+ if (token != null && !token.isEmpty()) {
+ write(escape(token));
+ }
+ this.newLine();
}
+
}
diff --git a/app/src/main/java/org/gnucash/android/model/Money.java b/app/src/main/java/org/gnucash/android/model/Money.java
index 1f7fbfaf4..eedc67ac1 100644
--- a/app/src/main/java/org/gnucash/android/model/Money.java
+++ b/app/src/main/java/org/gnucash/android/model/Money.java
@@ -28,7 +28,6 @@
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
-import java.util.Currency;
import java.util.Locale;
/**
@@ -427,13 +426,24 @@ public boolean isNegative(){
/**
* Returns the string representation of the amount (without currency) of the Money object.
- * This string is not locale-formatted. The decimal operator is a period (.)
+ *
+ * This string is not locale-formatted. The decimal operator is a period (.)
+ * For a locale-formatted version, see the method overload {@link #toLocaleString(Locale)}
* @return String representation of the amount (without currency) of the Money object
*/
public String toPlainString(){
return mAmount.setScale(mCommodity.getSmallestFractionDigits(), ROUNDING_MODE).toPlainString();
}
+ /**
+ * Returns a locale-specific representation of the amount of the Money object (excluding the currency)
+ *
+ * @return String representation of the amount (without currency) of the Money object
+ */
+ public String toLocaleString(){
+ return String.format(Locale.getDefault(), "%.2f", asDouble());
+ }
+
/**
* Returns the string representation of the Money object (value + currency) formatted according
* to the default locale
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 5c1fe98ee..4c159f49c 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -463,4 +463,22 @@
Export to \'/Apps/GnuCash Android/\' folder on Dropbox
Preferences
Yes, I\'m sure
+
+ - Date
+ - Transaction ID
+ - Number
+ - Description
+ - Notes
+ - Commodity/Currency
+ - Void Reason
+ - Action
+ - Memo
+ - Full Account Name
+ - Account Name
+ - Amount With Sym.
+ - Amount Num
+ - Reconcile
+ - Reconcile Date
+ - Rate/Price
+
From c83084a45459dd7852135f87f2abff4dc9eaa365 Mon Sep 17 00:00:00 2001
From: Ngewi Fet
Date: Wed, 6 Jun 2018 09:48:00 +0200
Subject: [PATCH 39/43] Moves account CSV export to the Settings->Accounts
section (was previously in the transactions export dialog)
Export account color as hex instead of integer
Localize account CSV headers
---
.../android/db/adapter/AccountsDbAdapter.java | 6 +-
.../gnucash/android/export/ExportFormat.java | 1 -
.../export/csv/CsvAccountExporter.java | 94 ++++++-------------
.../export/csv/CsvTransactionsExporter.java | 7 +-
.../org/gnucash/android/model/Account.java | 8 ++
.../settings/AccountPreferencesFragment.java | 50 ++++++++++
app/src/main/res/values/strings.xml | 19 +++-
.../res/xml/fragment_account_preferences.xml | 3 +
8 files changed, 113 insertions(+), 75 deletions(-)
diff --git a/app/src/main/java/org/gnucash/android/db/adapter/AccountsDbAdapter.java b/app/src/main/java/org/gnucash/android/db/adapter/AccountsDbAdapter.java
index a6631ce3e..e19138306 100644
--- a/app/src/main/java/org/gnucash/android/db/adapter/AccountsDbAdapter.java
+++ b/app/src/main/java/org/gnucash/android/db/adapter/AccountsDbAdapter.java
@@ -206,7 +206,7 @@ public long bulkAddRecords(@NonNull List accountList, UpdateMethod upda
stmt.bindString(3, account.getAccountType().name());
stmt.bindString(4, account.getCommodity().getCurrencyCode());
if (account.getColor() != Account.DEFAULT_COLOR) {
- stmt.bindString(5, convertToRGBHexString(account.getColor()));
+ stmt.bindString(5, account.getColorHexString());
}
stmt.bindLong(6, account.isFavorite() ? 1 : 0);
stmt.bindString(7, account.getFullName());
@@ -230,10 +230,6 @@ public long bulkAddRecords(@NonNull List accountList, UpdateMethod upda
return stmt;
}
- private String convertToRGBHexString(int color) {
- return String.format("#%06X", (0xFFFFFF & color));
- }
-
/**
* Marks all transactions for a given account as exported
* @param accountUID Unique ID of the record to be marked as exported
diff --git a/app/src/main/java/org/gnucash/android/export/ExportFormat.java b/app/src/main/java/org/gnucash/android/export/ExportFormat.java
index 4e30654fa..7b6fc99c1 100644
--- a/app/src/main/java/org/gnucash/android/export/ExportFormat.java
+++ b/app/src/main/java/org/gnucash/android/export/ExportFormat.java
@@ -48,7 +48,6 @@ public String getExtension(){
case XML:
return ".gnca";
case CSVA:
- return ".csv";
case CSVT:
return ".csv";
default:
diff --git a/app/src/main/java/org/gnucash/android/export/csv/CsvAccountExporter.java b/app/src/main/java/org/gnucash/android/export/csv/CsvAccountExporter.java
index a5c4842e6..47907c163 100644
--- a/app/src/main/java/org/gnucash/android/export/csv/CsvAccountExporter.java
+++ b/app/src/main/java/org/gnucash/android/export/csv/CsvAccountExporter.java
@@ -17,16 +17,17 @@
package org.gnucash.android.export.csv;
import android.database.sqlite.SQLiteDatabase;
+
import com.crashlytics.android.Crashlytics;
+
+import org.gnucash.android.R;
import org.gnucash.android.export.ExportParams;
import org.gnucash.android.export.Exporter;
import org.gnucash.android.model.Account;
-import java.io.BufferedOutputStream;
-import java.io.FileOutputStream;
+import java.io.FileWriter;
import java.io.IOException;
-import java.io.OutputStreamWriter;
-import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
/**
@@ -61,84 +62,51 @@ public CsvAccountExporter(ExportParams params, SQLiteDatabase db) {
@Override
public List generateExport() throws ExporterException {
- OutputStreamWriter writerStream = null;
- CsvWriter writer = null;
String outputFile = getExportCacheFilePath();
- try {
- FileOutputStream fileOutputStream = new FileOutputStream(outputFile);
- BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
- writerStream = new OutputStreamWriter(bufferedOutputStream);
- writer = new CsvWriter(writerStream);
+ try (CsvWriter writer = new CsvWriter(new FileWriter(outputFile), mCsvSeparator + "")) {
generateExport(writer);
} catch (IOException ex){
Crashlytics.log("Error exporting CSV");
Crashlytics.logException(ex);
- } finally {
- if (writerStream != null) {
- try {
- writerStream.close();
- } catch (IOException e) {
- throw new ExporterException(mExportParams, e);
- }
- }
+ throw new ExporterException(mExportParams, ex);
}
- List exportedFiles = new ArrayList<>();
- exportedFiles.add(outputFile);
-
- return exportedFiles;
+ return Arrays.asList(outputFile);
}
- public void generateExport(final CsvWriter writer) throws ExporterException {
+ /**
+ * Writes out all the accounts in the system as CSV to the provided writer
+ * @param csvWriter Destination for the CSV export
+ * @throws ExporterException if an error occurred while writing to the stream
+ */
+ public void generateExport(final CsvWriter csvWriter) throws ExporterException {
try {
- String separator = mCsvSeparator + "";
- List names = new ArrayList();
- names.add("type");
- names.add("full_name");
- names.add("name");
- names.add("code");
- names.add("description");
- names.add("color");
- names.add("notes");
- names.add("commoditym");
- names.add("commodityn");
- names.add("hidden");
- names.add("tax");
- names.add("place_holder");
-
+ List names = Arrays.asList(mContext.getResources().getStringArray(R.array.csv_account_headers));
List accounts = mAccountsDbAdapter.getAllRecords();
for(int i = 0; i < names.size(); i++) {
- writer.write(names.get(i) + separator);
+ csvWriter.writeToken(names.get(i));
}
- writer.write("\n");
- for(int i = 0; i < accounts.size(); i++) {
- Account account = accounts.get(i);
-
- writer.write(account.getAccountType().toString() + separator);
- writer.write(account.getFullName() + separator);
- writer.write(account.getName() + separator);
-
- //Code
- writer.write(separator);
-
- writer.write(account.getDescription() + separator);
- writer.write(account.getColor() + separator);
-
- //Notes
- writer.write(separator);
- writer.write(account.getCommodity().getCurrencyCode() + separator);
- writer.write("CURRENCY" + separator);
- writer.write(account.isHidden()?"T":"F" + separator);
+ csvWriter.newLine();
+ for (Account account : accounts) {
+ csvWriter.writeToken(account.getAccountType().toString());
+ csvWriter.writeToken(account.getFullName());
+ csvWriter.writeToken(account.getName());
- writer.write("F" + separator);
+ csvWriter.writeToken(null); //Account code
+ csvWriter.writeToken(account.getDescription());
+ csvWriter.writeToken(account.getColorHexString());
+ csvWriter.writeToken(null); //Account notes
- writer.write(account.isPlaceholderAccount()?"T":"F" + separator);
+ csvWriter.writeToken(account.getCommodity().getCurrencyCode());
+ csvWriter.writeToken("CURRENCY");
+ csvWriter.writeToken(account.isHidden() ? "T" : "F");
- writer.write("\n");
+ csvWriter.writeToken("F"); //Tax
+ csvWriter.writeEndToken(account.isPlaceholderAccount() ? "T": "F");
}
- } catch (Exception e) {
+ } catch (IOException e) {
Crashlytics.logException(e);
throw new ExporterException(mExportParams, e);
}
diff --git a/app/src/main/java/org/gnucash/android/export/csv/CsvTransactionsExporter.java b/app/src/main/java/org/gnucash/android/export/csv/CsvTransactionsExporter.java
index c0419f7c7..56f9a0766 100644
--- a/app/src/main/java/org/gnucash/android/export/csv/CsvTransactionsExporter.java
+++ b/app/src/main/java/org/gnucash/android/export/csv/CsvTransactionsExporter.java
@@ -34,7 +34,6 @@
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
@@ -82,12 +81,10 @@ public List generateExport() throws ExporterException {
} catch (IOException ex){
Crashlytics.log("Error exporting CSV");
Crashlytics.logException(ex);
+ throw new ExporterException(mExportParams, ex);
}
- List exportedFiles = new ArrayList<>();
- exportedFiles.add(outputFile);
-
- return exportedFiles;
+ return Arrays.asList(outputFile);
}
/**
diff --git a/app/src/main/java/org/gnucash/android/model/Account.java b/app/src/main/java/org/gnucash/android/model/Account.java
index 4fa09fecb..f6234d304 100644
--- a/app/src/main/java/org/gnucash/android/model/Account.java
+++ b/app/src/main/java/org/gnucash/android/model/Account.java
@@ -282,6 +282,14 @@ public int getColor() {
return mColor;
}
+ /**
+ * Returns the account color as an RGB hex string
+ * @return Hex color of the account
+ */
+ public String getColorHexString(){
+ return String.format("#%06X", (0xFFFFFF & mColor));
+ }
+
/**
* Sets the color of the account.
* @param color Color as an int as returned by {@link Color}.
diff --git a/app/src/main/java/org/gnucash/android/ui/settings/AccountPreferencesFragment.java b/app/src/main/java/org/gnucash/android/ui/settings/AccountPreferencesFragment.java
index 7a35d542c..977b06b57 100644
--- a/app/src/main/java/org/gnucash/android/ui/settings/AccountPreferencesFragment.java
+++ b/app/src/main/java/org/gnucash/android/ui/settings/AccountPreferencesFragment.java
@@ -27,17 +27,26 @@
import android.support.v7.preference.ListPreference;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceFragmentCompat;
+import android.widget.Toast;
+
+import com.crashlytics.android.Crashlytics;
import org.gnucash.android.R;
import org.gnucash.android.app.GnuCashApplication;
import org.gnucash.android.db.DatabaseSchema;
+import org.gnucash.android.db.adapter.BooksDbAdapter;
import org.gnucash.android.db.adapter.CommoditiesDbAdapter;
+import org.gnucash.android.export.ExportAsyncTask;
+import org.gnucash.android.export.ExportFormat;
+import org.gnucash.android.export.ExportParams;
+import org.gnucash.android.export.Exporter;
import org.gnucash.android.model.Money;
import org.gnucash.android.ui.account.AccountsActivity;
import org.gnucash.android.ui.settings.dialog.DeleteAllAccountsConfirmationDialog;
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.ExecutionException;
/**
* Account settings fragment inside the Settings activity
@@ -48,6 +57,8 @@
public class AccountPreferencesFragment extends PreferenceFragmentCompat implements
Preference.OnPreferenceChangeListener, Preference.OnPreferenceClickListener{
+ private static final int REQUEST_EXPORT_FILE = 0xC5;
+
List mCurrencyEntries = new ArrayList<>();
List mCurrencyEntryValues = new ArrayList<>();
@@ -91,6 +102,9 @@ public void onResume() {
Preference preference = findPreference(getString(R.string.key_import_accounts));
preference.setOnPreferenceClickListener(this);
+ preference = findPreference(getString(R.string.key_export_accounts_csv));
+ preference.setOnPreferenceClickListener(this);
+
preference = findPreference(getString(R.string.key_delete_all_accounts));
preference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
@@ -137,9 +151,29 @@ public boolean onPreferenceClick(Preference preference) {
return true;
}
+ if (key.equals(getString(R.string.key_export_accounts_csv))){
+ selectExportFile();
+ return true;
+ }
+
return false;
}
+ /**
+ * Open a chooser for user to pick a file to export to
+ */
+ private void selectExportFile() {
+ Intent createIntent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
+ createIntent.setType("*/*").addCategory(Intent.CATEGORY_OPENABLE);
+ String bookName = BooksDbAdapter.getInstance().getActiveBookDisplayName();
+
+ String filename = Exporter.buildExportFilename(ExportFormat.CSVA, bookName);
+ createIntent.setType("application/text");
+
+ createIntent.putExtra(Intent.EXTRA_TITLE, filename);
+ startActivityForResult(createIntent, REQUEST_EXPORT_FILE);
+ }
+
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
if (preference.getKey().equals(getString(R.string.key_default_currency))){
@@ -167,6 +201,22 @@ public void onActivityResult(int requestCode, int resultCode, Intent data) {
AccountsActivity.importXmlFileFromIntent(getActivity(), data, null);
}
break;
+
+ case REQUEST_EXPORT_FILE:
+ if (resultCode == Activity.RESULT_OK && data != null){
+ ExportParams exportParams = new ExportParams(ExportFormat.CSVA);
+ exportParams.setExportTarget(ExportParams.ExportTarget.URI);
+ exportParams.setExportLocation(data.getData().toString());
+ ExportAsyncTask exportTask = new ExportAsyncTask(getActivity(), GnuCashApplication.getActiveDb());
+
+ try {
+ exportTask.execute(exportParams).get();
+ } catch (InterruptedException | ExecutionException e) {
+ Crashlytics.logException(e);
+ Toast.makeText(getActivity(), "An error occurred during the Accounts CSV export",
+ Toast.LENGTH_LONG).show();
+ }
+ }
}
}
}
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 4c159f49c..2644d560e 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -126,7 +126,7 @@
Enter an amount to save the transaction
An error occurred while importing the GnuCash accounts
GnuCash Accounts successfully imported
- Import account structure exported from GnuCash desktop
+ Import account structure from GnuCash XML
Import GnuCash XML
Delete all accounts in the database. All transactions will be deleted as
well.
@@ -463,6 +463,9 @@
Export to \'/Apps/GnuCash Android/\' folder on Dropbox
Preferences
Yes, I\'m sure
+ export_accounts_csv_key
+ Export all accounts (without transactions) to CSV
+ Export as CSV
- Date
- Transaction ID
@@ -481,4 +484,18 @@
- Reconcile Date
- Rate/Price
+
+ - Type
+ - Full Name
+ - Name
+ - Code
+ - Description
+ - Color
+ - Notes
+ - Commoditym
+ - Commodityn
+ - Hidden
+ - Tax
+ - Placeholder
+
diff --git a/app/src/main/res/xml/fragment_account_preferences.xml b/app/src/main/res/xml/fragment_account_preferences.xml
index ace62559e..d8299897d 100644
--- a/app/src/main/res/xml/fragment_account_preferences.xml
+++ b/app/src/main/res/xml/fragment_account_preferences.xml
@@ -28,6 +28,9 @@
+
From 41626c5e03ff65091abd7694c95955c31b8b7487 Mon Sep 17 00:00:00 2001
From: Ngewi Fet
Date: Thu, 7 Jun 2018 09:42:21 +0200
Subject: [PATCH 40/43] Respect export time range setting when creating CSV
transaction exports
- Make CSV default export format, and re-order export options to put CSV first
- Performance optimizations for account lookup during CSV export
- Use animations when hiding/displaying export options
- File selection dialog is only opened AFTER export is started (not immediately when export fragment is launched)
---
.../db/adapter/TransactionsDbAdapter.java | 14 ++-
.../android/export/ExportAsyncTask.java | 14 +--
.../org/gnucash/android/export/Exporter.java | 4 +-
.../export/csv/CsvTransactionsExporter.java | 24 ++++-
.../android/ui/export/ExportFormFragment.java | 96 ++++++++++++++-----
.../main/res/layout/fragment_export_form.xml | 45 ++++-----
app/src/main/res/values/donottranslate.xml | 1 +
app/src/main/res/values/strings.xml | 4 +-
8 files changed, 143 insertions(+), 59 deletions(-)
diff --git a/app/src/main/java/org/gnucash/android/db/adapter/TransactionsDbAdapter.java b/app/src/main/java/org/gnucash/android/db/adapter/TransactionsDbAdapter.java
index f86c6fa5b..cc491075e 100644
--- a/app/src/main/java/org/gnucash/android/db/adapter/TransactionsDbAdapter.java
+++ b/app/src/main/java/org/gnucash/android/db/adapter/TransactionsDbAdapter.java
@@ -32,7 +32,6 @@
import org.gnucash.android.app.GnuCashApplication;
import org.gnucash.android.model.AccountType;
-import org.gnucash.android.model.Commodity;
import org.gnucash.android.model.Money;
import org.gnucash.android.model.Split;
import org.gnucash.android.model.Transaction;
@@ -332,6 +331,19 @@ public Cursor fetchTransactionsWithSplits(String [] columns, @Nullable String wh
orderBy);
}
+ /**
+ * Fetch all transactions modified since a given timestamp
+ * @param timestamp Timestamp in milliseconds (since Epoch)
+ * @return Cursor to the results
+ */
+ public Cursor fetchTransactionsModifiedSince(Timestamp timestamp){
+ SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
+ queryBuilder.setTables(TransactionEntry.TABLE_NAME);
+ String startTimeString = TimestampHelper.getUtcStringFromTimestamp(timestamp);
+ return queryBuilder.query(mDb, null, TransactionEntry.COLUMN_MODIFIED_AT + " >= \"" + startTimeString + "\"",
+ null, null, null, TransactionEntry.COLUMN_TIMESTAMP + " ASC", null);
+ }
+
public Cursor fetchTransactionsWithSplitsWithTransactionAccount(String [] columns, String where, String[] whereArgs, String orderBy) {
// table is :
// trans_split_acct , trans_extra_info ON trans_extra_info.trans_acct_t_uid = transactions_uid ,
diff --git a/app/src/main/java/org/gnucash/android/export/ExportAsyncTask.java b/app/src/main/java/org/gnucash/android/export/ExportAsyncTask.java
index c9e190ea4..0336de1ff 100644
--- a/app/src/main/java/org/gnucash/android/export/ExportAsyncTask.java
+++ b/app/src/main/java/org/gnucash/android/export/ExportAsyncTask.java
@@ -74,6 +74,7 @@
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
@@ -105,7 +106,7 @@ public class ExportAsyncTask extends AsyncTask {
private ExportParams mExportParams;
// File paths generated by the exporter
- private List mExportedFiles;
+ private List mExportedFiles = Collections.emptyList();
private Exporter mExporter;
@@ -221,16 +222,15 @@ private Exporter getExporter() {
switch (mExportParams.getExportFormat()) {
case QIF:
return new QifExporter(mExportParams, mDb);
-
case OFX:
return new OfxExporter(mExportParams, mDb);
-
- case XML:
- return new GncXmlExporter(mExportParams, mDb);
case CSVA:
return new CsvAccountExporter(mExportParams, mDb);
- default:
+ case CSVT:
return new CsvTransactionsExporter(mExportParams, mDb);
+ case XML:
+ default:
+ return new GncXmlExporter(mExportParams, mDb);
}
}
@@ -284,7 +284,7 @@ private void moveExportToUri() throws Exporter.ExporterException {
if (mExportedFiles.size() > 0){
try {
OutputStream outputStream = mContext.getContentResolver().openOutputStream(exportUri);
- // Now we always get just one file exported (QIFs are zipped)
+ // Now we always get just one file exported (multi-currency QIFs are zipped)
org.gnucash.android.util.FileUtils.moveFile(mExportedFiles.get(0), outputStream);
} catch (IOException ex) {
throw new Exporter.ExporterException(mExportParams, "Error when moving file to URI");
diff --git a/app/src/main/java/org/gnucash/android/export/Exporter.java b/app/src/main/java/org/gnucash/android/export/Exporter.java
index a720facde..5fd56f3b5 100644
--- a/app/src/main/java/org/gnucash/android/export/Exporter.java
+++ b/app/src/main/java/org/gnucash/android/export/Exporter.java
@@ -161,8 +161,8 @@ public static String sanitizeFilename(String inputName) {
public static String buildExportFilename(ExportFormat format, String bookName) {
return EXPORT_FILENAME_DATE_FORMAT.format(new Date(System.currentTimeMillis()))
+ "_gnucash_export_" + sanitizeFilename(bookName) +
- (format==ExportFormat.CSVA?"_accounts_":"") +
- (format==ExportFormat.CSVT?"_transactions_":"") +
+ (format == ExportFormat.CSVA ? "_accounts" : "") +
+ (format == ExportFormat.CSVT ? "_transactions" : "") +
format.getExtension();
}
diff --git a/app/src/main/java/org/gnucash/android/export/csv/CsvTransactionsExporter.java b/app/src/main/java/org/gnucash/android/export/csv/CsvTransactionsExporter.java
index 56f9a0766..f0d082e6e 100644
--- a/app/src/main/java/org/gnucash/android/export/csv/CsvTransactionsExporter.java
+++ b/app/src/main/java/org/gnucash/android/export/csv/CsvTransactionsExporter.java
@@ -19,6 +19,7 @@
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.support.annotation.NonNull;
+import android.util.Log;
import com.crashlytics.android.Crashlytics;
@@ -29,6 +30,8 @@
import org.gnucash.android.model.Split;
import org.gnucash.android.model.Transaction;
import org.gnucash.android.model.TransactionType;
+import org.gnucash.android.util.PreferencesHelper;
+import org.gnucash.android.util.TimestampHelper;
import java.io.FileWriter;
import java.io.IOException;
@@ -36,8 +39,10 @@
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
+import java.util.HashMap;
import java.util.List;
import java.util.Locale;
+import java.util.Map;
/**
* Creates a GnuCash CSV transactions representation of the accounts and transactions
@@ -93,13 +98,26 @@ public List generateExport() throws ExporterException {
*/
private void writeSplitsToCsv(@NonNull List splits, @NonNull CsvWriter writer) throws IOException {
int index = 0;
+
+ Map uidAccountMap = new HashMap<>();
+
for (Split split : splits) {
if (index++ > 0){ // the first split is on the same line as the transactions. But after that, we
writer.write("" + mCsvSeparator + mCsvSeparator + mCsvSeparator + mCsvSeparator
+ mCsvSeparator + mCsvSeparator + mCsvSeparator + mCsvSeparator);
}
writer.writeToken(split.getMemo());
- Account account = mAccountsDbAdapter.getRecord(split.getAccountUID());
+
+ //cache accounts so that we do not have to go to the DB each time
+ String accountUID = split.getAccountUID();
+ Account account;
+ if (uidAccountMap.containsKey(accountUID)) {
+ account = uidAccountMap.get(accountUID);
+ } else {
+ account = mAccountsDbAdapter.getRecord(accountUID);
+ uidAccountMap.put(accountUID, account);
+ }
+
writer.writeToken(account.getFullName());
writer.writeToken(account.getName());
@@ -126,7 +144,8 @@ private void generateExport(final CsvWriter csvWriter) throws ExporterException
csvWriter.newLine();
- Cursor cursor = mTransactionsDbAdapter.fetchAllRecords();
+ Cursor cursor = mTransactionsDbAdapter.fetchTransactionsModifiedSince(mExportParams.getExportStartTime());
+ Log.d(LOG_TAG, String.format("Exporting %d transactions to CSV", cursor.getCount()));
while (cursor.moveToNext()){
Transaction transaction = mTransactionsDbAdapter.buildModelInstance(cursor);
Date date = new Date(transaction.getTimeMillis());
@@ -143,6 +162,7 @@ private void generateExport(final CsvWriter csvWriter) throws ExporterException
writeSplitsToCsv(transaction.getSplits(), csvWriter);
}
+ PreferencesHelper.setLastExportTime(TimestampHelper.getTimestampFromNow());
} catch (IOException e) {
Crashlytics.logException(e);
throw new ExporterException(mExportParams, e);
diff --git a/app/src/main/java/org/gnucash/android/ui/export/ExportFormFragment.java b/app/src/main/java/org/gnucash/android/ui/export/ExportFormFragment.java
index e883305e2..500c37128 100644
--- a/app/src/main/java/org/gnucash/android/ui/export/ExportFormFragment.java
+++ b/app/src/main/java/org/gnucash/android/ui/export/ExportFormFragment.java
@@ -34,6 +34,8 @@
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
+import android.view.animation.Animation;
+import android.view.animation.Transformation;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.CheckBox;
@@ -134,7 +136,6 @@ public class ExportFormFragment extends Fragment implements
@BindView(R.id.switch_export_all) SwitchCompat mExportAllSwitch;
@BindView(R.id.export_date_layout) LinearLayout mExportDateLayout;
- @BindView(R.id.export_separator_layout) LinearLayout mExportSeparatorLayout;
@BindView(R.id.radio_ofx_format) RadioButton mOfxRadioButton;
@BindView(R.id.radio_qif_format) RadioButton mQifRadioButton;
@@ -194,8 +195,9 @@ private void onRadioButtonClicked(View view){
} else {
mExportWarningTextView.setVisibility(View.GONE);
}
- mExportDateLayout.setVisibility(View.VISIBLE);
- mExportSeparatorLayout.setVisibility(View.GONE);
+
+ OptionsViewAnimationUtils.expand(mExportDateLayout);
+ OptionsViewAnimationUtils.collapse(mCsvOptionsLayout);
break;
case R.id.radio_qif_format:
@@ -207,22 +209,23 @@ private void onRadioButtonClicked(View view){
} else {
mExportWarningTextView.setVisibility(View.GONE);
}
- mExportDateLayout.setVisibility(View.VISIBLE);
- mCsvOptionsLayout.setVisibility(View.GONE);
+
+ OptionsViewAnimationUtils.expand(mExportDateLayout);
+ OptionsViewAnimationUtils.collapse(mCsvOptionsLayout);
break;
case R.id.radio_xml_format:
mExportFormat = ExportFormat.XML;
mExportWarningTextView.setText(R.string.export_warning_xml);
- mExportDateLayout.setVisibility(View.GONE);
- mCsvOptionsLayout.setVisibility(View.GONE);
+ OptionsViewAnimationUtils.collapse(mExportDateLayout);
+ OptionsViewAnimationUtils.collapse(mCsvOptionsLayout);
break;
case R.id.radio_csv_transactions_format:
mExportFormat = ExportFormat.CSVT;
- mExportWarningTextView.setText("Exports registered transactions as CSV");
- mExportDateLayout.setVisibility(View.GONE);
- mCsvOptionsLayout.setVisibility(View.VISIBLE);
+ mExportWarningTextView.setText(R.string.export_notice_csv);
+ OptionsViewAnimationUtils.expand(mExportDateLayout);
+ OptionsViewAnimationUtils.expand(mCsvOptionsLayout);
break;
case R.id.radio_separator_comma_format:
@@ -246,9 +249,6 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container,
bindViewListeners();
- String[] export_format_strings = getResources().getStringArray(R.array.export_formats);
- mCsvTransactionsRadioButton.setText(export_format_strings[3]);
-
return view;
}
@Override
@@ -359,12 +359,11 @@ public void onItemSelected(AdapterView> parent, View view, int position, long
if (view == null) //the item selection is fired twice by the Android framework. Ignore the first one
return;
switch (position) {
- case 0:
+ case 0: //Save As..
mExportTarget = ExportParams.ExportTarget.URI;
mRecurrenceOptionsView.setVisibility(View.VISIBLE);
if (mExportUri != null)
setExportUriText(mExportUri.toString());
- selectExportFile();
break;
case 1: //DROPBOX
setExportUriText(getString(R.string.label_dropbox_export_destination));
@@ -377,7 +376,7 @@ public void onItemSelected(AdapterView> parent, View view, int position, long
Auth.startOAuth2Authentication(getActivity(), dropboxAppKey);
}
break;
- case 2:
+ case 2: //OwnCloud
setExportUriText(null);
mRecurrenceOptionsView.setVisibility(View.VISIBLE);
mExportTarget = ExportParams.ExportTarget.OWNCLOUD;
@@ -387,7 +386,7 @@ public void onItemSelected(AdapterView> parent, View view, int position, long
ocDialog.show(getActivity().getSupportFragmentManager(), "ownCloud dialog");
}
break;
- case 3:
+ case 3: //Share File
setExportUriText(getString(R.string.label_select_destination_after_export));
mExportTarget = ExportParams.ExportTarget.SHARING;
mRecurrenceOptionsView.setVisibility(View.GONE);
@@ -401,7 +400,7 @@ public void onItemSelected(AdapterView> parent, View view, int position, long
@Override
public void onNothingSelected(AdapterView> parent) {
-
+ //nothing to see here, move along
}
});
@@ -482,7 +481,7 @@ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
mRecurrenceTextView.setOnClickListener(new RecurrenceViewClickListener((AppCompatActivity) getActivity(), mRecurrenceRule, this));
//this part (setting the export format) must come after the recurrence view bindings above
- String defaultExportFormat = sharedPrefs.getString(getString(R.string.key_default_export_format), ExportFormat.QIF.name());
+ String defaultExportFormat = sharedPrefs.getString(getString(R.string.key_default_export_format), ExportFormat.CSVT.name());
mExportFormat = ExportFormat.valueOf(defaultExportFormat);
View.OnClickListener radioClickListener = new View.OnClickListener() {
@@ -543,11 +542,6 @@ private void selectExportFile() {
String bookName = BooksDbAdapter.getInstance().getActiveBookDisplayName();
String filename = Exporter.buildExportFilename(mExportFormat, bookName);
- if (mExportFormat == ExportFormat.QIF) {
- createIntent.setType("application/zip");
- filename += ".zip";
- }
-
createIntent.putExtra(Intent.EXTRA_TITLE, filename);
startActivityForResult(createIntent, REQUEST_EXPORT_FILE);
}
@@ -615,3 +609,57 @@ public void onTimeSet(RadialTimePickerDialogFragment dialog, int hourOfDay, int
}
}
+// Gotten from: https://stackoverflow.com/a/31720191
+class OptionsViewAnimationUtils {
+
+ public static void expand(final View v) {
+ v.measure(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+ final int targetHeight = v.getMeasuredHeight();
+
+ v.getLayoutParams().height = 0;
+ v.setVisibility(View.VISIBLE);
+ Animation a = new Animation()
+ {
+ @Override
+ protected void applyTransformation(float interpolatedTime, Transformation t) {
+ v.getLayoutParams().height = interpolatedTime == 1
+ ? ViewGroup.LayoutParams.WRAP_CONTENT
+ : (int)(targetHeight * interpolatedTime);
+ v.requestLayout();
+ }
+
+ @Override
+ public boolean willChangeBounds() {
+ return true;
+ }
+ };
+
+ a.setDuration((int)(3 * targetHeight / v.getContext().getResources().getDisplayMetrics().density));
+ v.startAnimation(a);
+ }
+
+ public static void collapse(final View v) {
+ final int initialHeight = v.getMeasuredHeight();
+
+ Animation a = new Animation()
+ {
+ @Override
+ protected void applyTransformation(float interpolatedTime, Transformation t) {
+ if(interpolatedTime == 1){
+ v.setVisibility(View.GONE);
+ }else{
+ v.getLayoutParams().height = initialHeight - (int)(initialHeight * interpolatedTime);
+ v.requestLayout();
+ }
+ }
+
+ @Override
+ public boolean willChangeBounds() {
+ return true;
+ }
+ };
+
+ a.setDuration((int)(3 * initialHeight / v.getContext().getResources().getDisplayMetrics().density));
+ v.startAnimation(a);
+ }
+}
diff --git a/app/src/main/res/layout/fragment_export_form.xml b/app/src/main/res/layout/fragment_export_form.xml
index aa3c8d3bd..a4cc5ee71 100644
--- a/app/src/main/res/layout/fragment_export_form.xml
+++ b/app/src/main/res/layout/fragment_export_form.xml
@@ -75,41 +75,50 @@
android:gravity="center_vertical"
android:orientation="horizontal">
-
+ android:text="CSV"/>
-
+ android:text="QIF" />
-
+ android:text="OFX"/>
+
+
+
-
-
+
+ android:text=":" />
-
-
diff --git a/app/src/main/res/values/donottranslate.xml b/app/src/main/res/values/donottranslate.xml
index aa1a56cb0..39cae7266 100644
--- a/app/src/main/res/values/donottranslate.xml
+++ b/app/src/main/res/values/donottranslate.xml
@@ -57,6 +57,7 @@
- TRADING
+ - CSVT
- QIF
- OFX
- XML
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 2644d560e..4ef7ae17e 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -175,10 +175,10 @@
- TRADING
+ - CSV
- QIF
- OFX
- XML
- - CSV
Select a Color
@@ -466,6 +466,8 @@
export_accounts_csv_key
Export all accounts (without transactions) to CSV
Export as CSV
+ Separator
+ Exports transactions as CSV
- Date
- Transaction ID
From 359e242ec8f8ac5ccc8ffb9ec2f792291bc133db Mon Sep 17 00:00:00 2001
From: Ngewi Fet
Date: Thu, 7 Jun 2018 09:59:50 +0200
Subject: [PATCH 41/43] Update version number for v2.4.0-beta2 release
Update CrowdIn configuration - (strings were not being properly uploaded anymore)
Update translations from Crowdin
---
CHANGELOG.md | 2 +-
README.md | 7 +-
app/build.gradle | 2 +-
app/src/main/res/values-af-rZA/strings.xml | 2 -
app/src/main/res/values-ar-rSA/strings.xml | 2 -
app/src/main/res/values-ca-rES/strings.xml | 2 -
app/src/main/res/values-cs-rCZ/strings.xml | 2 -
app/src/main/res/values-de/strings.xml | 2 -
app/src/main/res/values-el-rGR/strings.xml | 20 +-
app/src/main/res/values-en-rGB/strings.xml | 2 -
app/src/main/res/values-es-rMX/strings.xml | 2 -
app/src/main/res/values-es/strings.xml | 4 +-
app/src/main/res/values-fa-rIR/strings.xml | 457 ++++++++++++++++++
app/src/main/res/values-fi-rFI/strings.xml | 2 -
app/src/main/res/values-fr/strings.xml | 2 -
app/src/main/res/values-hu-rHU/strings.xml | 2 -
app/src/main/res/values-in-rID/strings.xml | 14 +-
app/src/main/res/values-it-rIT/strings.xml | 2 -
app/src/main/res/values-iw-rIL/strings.xml | 2 -
app/src/main/res/values-ja-rJP/strings.xml | 2 -
app/src/main/res/values-ko-rKR/strings.xml | 2 -
app/src/main/res/values-lv-rLV/strings.xml | 2 -
app/src/main/res/values-nb/strings.xml | 2 -
app/src/main/res/values-nl-rNL/strings.xml | 2 -
app/src/main/res/values-no-rNO/strings.xml | 2 -
app/src/main/res/values-pl-rPL/strings.xml | 2 -
app/src/main/res/values-pt-rBR/strings.xml | 18 +-
app/src/main/res/values-pt-rPT/strings.xml | 4 +-
app/src/main/res/values-ro-rRO/strings.xml | 2 -
app/src/main/res/values-ru/strings.xml | 12 +-
app/src/main/res/values-sr-rSP/strings.xml | 2 -
app/src/main/res/values-sv-rSE/strings.xml | 2 -
app/src/main/res/values-tr-rTR/strings.xml | 2 -
app/src/main/res/values-uk-rUA/strings.xml | 2 -
app/src/main/res/values-vi-rVN/strings.xml | 2 -
app/src/main/res/values-zh-rCN/strings.xml | 4 +-
app/src/main/res/values-zh-rTW/strings.xml | 48 +-
crowdin.yml | 76 +--
.../playstore/play_store_description.txt | 14 +-
.../play_store_description_i18n/.keep | 0
40 files changed, 533 insertions(+), 199 deletions(-)
create mode 100644 app/src/main/res/values-fa-rIR/strings.xml
rename play_store_description.txt => resources/playstore/play_store_description.txt (69%)
create mode 100644 resources/playstore/play_store_description_i18n/.keep
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 230e565fb..0e7fb0f76 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,6 @@
Change Log
===============================================================================
-Version 2.4.0 *(2018-06-06)*
+Version 2.4.0 *(2018-06-xx)*
----------------------------
* Feature #665: Adds CSV export format
* Feature #544: Add extra checkbox confirmation for irreversible actions
diff --git a/README.md b/README.md
index 245c2fb20..03dee803e 100644
--- a/README.md
+++ b/README.md
@@ -11,7 +11,7 @@ Accounts | Transactions | Reports
:-------------------------:|:-------------------------:|:-------------------------:
 |  | 
-The application supports Android 4.4 KitKat (API level 19) and above.
+The application supports Android 4.4 KitKat (API level 19) and above.
Features include:
@@ -90,9 +90,10 @@ There are several ways you could contribute to the development.
* Pull requests are always welcome! You could contribute code by fixing bugs, adding new features or automated tests.
Take a look at the [bug tracker](https://github.com/codinguser/gnucash-android/issues?state=open)
-for ideas where to start. Also make sure to read our [contribution guidlines](https://github.com/codinguser/gnucash-android/blob/master/.github/CONTRIBUTING.md)
+for ideas where to start. It is also preferable to target issues in the current [milestone](https://github.com/codinguser/gnucash-android/milestones).
+* Make sure to read our [contribution guidelines](https://github.com/codinguser/gnucash-android/blob/master/.github/CONTRIBUTING.md) before starting to code.
-* One way is providing translations for locales which are not yet available, or improving translations.
+* Another way to contribute is by providing translations for languages, or improving translations.
Please visit [CrowdIn](https://crowdin.com/project/gnucash-android) in order to update and create new translations
For development, it is recommended to use the Android Studio for development which is available for free.
diff --git a/app/build.gradle b/app/build.gradle
index 9fa3e96da..3d03548c3 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -6,7 +6,7 @@ apply plugin: 'io.fabric'
def versionMajor = 2
def versionMinor = 4
def versionPatch = 0
-def versionBuild = 1
+def versionBuild = 2
static def buildTime() {
def df = new SimpleDateFormat("yyyyMMdd HH:mm 'UTC'")
diff --git a/app/src/main/res/values-af-rZA/strings.xml b/app/src/main/res/values-af-rZA/strings.xml
index 6ebabab18..8bc5253ca 100644
--- a/app/src/main/res/values-af-rZA/strings.xml
+++ b/app/src/main/res/values-af-rZA/strings.xml
@@ -179,8 +179,6 @@
- QIF
- OFX
- XML
- - CSV-accounts
- - CSV-transactions
Select a Color
diff --git a/app/src/main/res/values-ar-rSA/strings.xml b/app/src/main/res/values-ar-rSA/strings.xml
index 9b5220b13..f593b1675 100644
--- a/app/src/main/res/values-ar-rSA/strings.xml
+++ b/app/src/main/res/values-ar-rSA/strings.xml
@@ -183,8 +183,6 @@
- QIF
- OFX
- XML
- - CSV-accounts
- - CSV-transactions
Select a Color
diff --git a/app/src/main/res/values-ca-rES/strings.xml b/app/src/main/res/values-ca-rES/strings.xml
index e0759fc47..c5bc4eb4a 100644
--- a/app/src/main/res/values-ca-rES/strings.xml
+++ b/app/src/main/res/values-ca-rES/strings.xml
@@ -177,8 +177,6 @@
- QIF
- OFX
- XML
- - CSV-comptes
- - CSV-transaccions
Seleccioneu un color
diff --git a/app/src/main/res/values-cs-rCZ/strings.xml b/app/src/main/res/values-cs-rCZ/strings.xml
index 474479365..dc5b11a03 100644
--- a/app/src/main/res/values-cs-rCZ/strings.xml
+++ b/app/src/main/res/values-cs-rCZ/strings.xml
@@ -176,8 +176,6 @@
- QIF
- OFX
- XML
- - CSV-účty
- - CSV-transakcí
Select a Color
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index 0a5c06f49..a9529b883 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -176,8 +176,6 @@
- QIF
- OFX
- XML
- - CSV-Konten
- - CSV-Transaktionen
Farbe auswählen
diff --git a/app/src/main/res/values-el-rGR/strings.xml b/app/src/main/res/values-el-rGR/strings.xml
index db79ae430..cf674c732 100644
--- a/app/src/main/res/values-el-rGR/strings.xml
+++ b/app/src/main/res/values-el-rGR/strings.xml
@@ -188,8 +188,6 @@
- QIF
- OFX
- XML
- - CSV-λογαριασμούς
- - CSV-συναλλαγών
Επιλογή χρώματος
@@ -208,10 +206,10 @@
Memo
Spend
Receive
- Withdrawal
- Deposit
- Payment
- Charge
+ Ανάληψη
+ Κατάθεση
+ Πληρωμή
+ Χρέωση
Decrease
Increase
Income
@@ -219,8 +217,8 @@
Expense
Bill
Invoice
- Buy
- Sell
+ Αγορά
+ Πώληση
Αρχικά υπόλοιπα
Καθαρή Θέση
Enable to save the current account balance (before deleting transactions) as new opening balance after deleting transactions
@@ -230,14 +228,14 @@
Generates separate QIF files per currency
Imbalance:
Add split
- Favorite
+ Αγαπημένο
Navigation drawer opened
Navigation drawer closed
- Reports
+ Αναφορές
Pie Chart
Line Chart
Bar Chart
- Report Preferences
+ Προτιμήσεις αναφοράς
Account color in reports
Use account color in the bar/pie chart
Order by size
diff --git a/app/src/main/res/values-en-rGB/strings.xml b/app/src/main/res/values-en-rGB/strings.xml
index dafd6ff32..1715b6f83 100644
--- a/app/src/main/res/values-en-rGB/strings.xml
+++ b/app/src/main/res/values-en-rGB/strings.xml
@@ -179,8 +179,6 @@
- QIF
- OFX
- XML
- - CSV-accounts
- - CSV-transactions
Select a Color
diff --git a/app/src/main/res/values-es-rMX/strings.xml b/app/src/main/res/values-es-rMX/strings.xml
index 91042cbbb..a6dddb325 100644
--- a/app/src/main/res/values-es-rMX/strings.xml
+++ b/app/src/main/res/values-es-rMX/strings.xml
@@ -172,8 +172,6 @@
- QIF
- OFX
- XML
- - CSV-cuentas
- - CSV-actas
Seleccionar un color
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
index 11c424eb9..e83540caf 100644
--- a/app/src/main/res/values-es/strings.xml
+++ b/app/src/main/res/values-es/strings.xml
@@ -38,7 +38,7 @@
ABONO
Cuentas
Transacciones
- BORRAR
+ Borrar
Borrar
Cancelar
Cuenta borrada
@@ -172,8 +172,6 @@
- QIF
- OFX
- XML
- - CSV-cuentas
- - CSV-actas
Seleccione un color
diff --git a/app/src/main/res/values-fa-rIR/strings.xml b/app/src/main/res/values-fa-rIR/strings.xml
new file mode 100644
index 000000000..7fec943b9
--- /dev/null
+++ b/app/src/main/res/values-fa-rIR/strings.xml
@@ -0,0 +1,457 @@
+
+
+
+
+ Create Account
+ Edit Account
+ Add a new transaction to an account
+ View account details
+ No accounts to display
+ Account name
+ Cancel
+ Save
+ Test
+ Enter Passcode
+ Wrong passcode, please try again
+ Passcode set
+ Please confirm your passcode
+ Invalid passcode confirmation. Please try again
+ Description
+ Amount
+ New transaction
+ No transactions to display
+ DEBIT
+ CREDIT
+ Accounts
+ Transactions
+ Delete
+ Delete
+ Cancel
+ Account deleted
+ Confirm delete
+ Edit Transaction
+ Add note
+ %1$d selected
+ Balance:
+ Export To:
+ Export Transactions
+ By default, only new transactions since last export will be exported. Check this option to export all transactions
+ Error exporting %1$s file
+ Export
+ Delete transactions after export
+ All exported transactions will be deleted when exporting is completed
+ Settings
+
+ - Save As…
+ - Dropbox
+ - ownCloud
+ - Send to…
+
+ Move
+ Move %1$d transaction(s)
+ Destination Account
+ Cannot move transactions.\nThe destination account uses a different currency from origin account
+ General
+ About
+ Choose default currency
+ Default currency
+ Default currency to assign to new accounts
+ Enables recording transactions in GnuCash for Android
+ Enables creation of accounts in GnuCash for Android
+ Your GnuCash data
+ Read and modify GnuCash data
+ Record transactions in GnuCash
+ Create accounts in GnuCash
+ Display account
+ Hide account balance in widget
+ Create Accounts
+ No accounts exist in GnuCash.\nCreate an account before adding a widget
+ License
+ Apache License v2.0. Click for details
+ General Preferences
+ Select Account
+ There are no transactions available to export
+ Passcode Preferences
+ Enable passcode
+ Change Passcode
+ About GnuCash
+ GnuCash Android %1$s export
+ GnuCash Android Export from
+ Transactions
+ Transaction Preferences
+ Account Preferences
+ Default Transaction Type
+ The type of transaction to use by default, CREDIT or DEBIT
+
+ - CREDIT
+ - DEBIT
+
+ Are you sure you want to delete ALL transactions?
+ Are you sure you want to delete this transaction?
+ Export
+ Export all transactions
+ Delete exported transactions
+ Default export email
+ The default email address to send exports to. You can still change this when you export.
+ All transactions will be a transfer from one account to another
+ Activate Double Entry
+ Balance
+ Enter an account name to create an account
+ Currency
+ Parent account
+ Use XML OFX header
+ Enable this option when exporting to third-party application other than GnuCash for desktop
+ What\'s New
+
+ - Added ability to export to any service which supports the Storage Access Framework \n
+ - Added option to set the location for regular automatic backups (See backup settings)\n
+ - Added Bitcoin currency support\n
+ - Added support for renaming books\n
+ - Multiple bug fixes and improvements\n
+
+ Dismiss
+ Enter an amount to save the transaction
+ An error occurred while importing the GnuCash accounts
+ GnuCash Accounts successfully imported
+ Import account structure exported from GnuCash desktop
+ Import GnuCash XML
+ Delete all accounts in the database. All transactions will be deleted as
+ well.
+
+ Delete all accounts
+ Accounts
+ All accounts have been successfully deleted
+ Are you sure you want to delete all accounts and transactions?\n\nThis
+ operation cannot be undone!
+
+ All transactions in all accounts will be deleted!
+ Delete all transactions
+ All transactions successfully deleted!
+ Importing accounts
+ Transactions
+ Sub-Accounts
+ Search
+ Default Export Format
+ File format to use by default when exporting transactions
+ Recurrence
+
+ Imbalance
+ Exporting transactions
+ No recurring transactions to display.
+ Successfully deleted recurring transaction
+ Placeholder account
+ Default Transfer Account
+
+ - %d sub-account
+ - %d sub-accounts
+
+
+ - CASH
+ - BANK
+ - CREDIT CARD
+ - ASSET
+ - LIABILITY
+ - INCOME
+ - EXPENSE
+ - PAYABLE
+ - RECEIVABLE
+ - EQUITY
+ - CURRENCY
+ - STOCK
+ - MUTUAL FUND
+ - TRADING
+
+
+ - QIF
+ - OFX
+ - XML
+
+
+ Select a Color
+
+
+ Account Color & Type
+ Delete sub-accounts
+ Recent
+ Favorites
+ All
+ Creates default GnuCash commonly-used account structure
+ Create default accounts
+ A new book will be opened with the default accounts\n\nYour current accounts and transactions will not be modified!
+ Transactions
+ Select destination for export
+ Memo
+ Spend
+ Receive
+ Withdrawal
+ Deposit
+ Payment
+ Charge
+ Decrease
+ Increase
+ Income
+ Rebate
+ Expense
+ Bill
+ Invoice
+ Buy
+ Sell
+ Opening Balances
+ Equity
+ Enable to save the current account balance (before deleting transactions) as new opening balance after deleting transactions
+
+ Save account opening balances
+ OFX does not support double-entry transactions
+ Generates separate QIF files per currency
+ Imbalance:
+ Add split
+ Favorite
+ Navigation drawer opened
+ Navigation drawer closed
+ Reports
+ Pie Chart
+ Line Chart
+ Bar Chart
+ Report Preferences
+ Account color in reports
+ Use account color in the bar/pie chart
+ Order by size
+ Show legend
+ Show labels
+ Show percentage
+ Show average lines
+ Group Smaller Slices
+ No chart data available
+ Total
+ Other
+ The percentage of selected value calculated from the total amount
+ The percentage of selected value calculated from the current stacked bar amount
+ Save as template
+ This account contains transactions. \nWhat would you like to do with these transactions
+ This account contains sub-accounts. \nWhat would you like to do with these sub-accounts
+ Delete transactions
+ Create and specify a transfer account OR disable double-entry in settings to save the transaction
+ Tap to create schedule
+ Restore Backup…
+ Backup & export
+ Enable DropBox
+ Enable ownCloud
+ Backup
+ Enable exporting to DropBox
+ Enable exporting to ownCloud
+ Backup Preferences
+ Create Backup
+ Create a backup of the active book
+ Restore most recent backup of active book
+ Backup successful
+ Backup failed
+ Exports all accounts and transactions
+ Install a file manager to select files
+ Select backup to restore
+ Favorites
+ Open…
+ Reports
+ Export…
+ Settings
+ User Name
+ Password
+ owncloud
+ https://
+ OC server not found
+ OC username/password invalid
+ Invalid chars: \\ < > : \" | * ?
+ OC server OK
+ OC username/password OK
+ Dir name OK
+
+ - Hourly
+ - Every %d hours
+
+
+ - Daily
+ - Every %d days
+
+
+ - Weekly
+ - Every %d weeks
+
+
+ - Monthly
+ - Every %d months
+
+
+ - Yearly
+ - Every %d years
+
+ Enable Crash Logging
+ Automatically send information about app malfunction to the developers.
+ Format
+ Enter your old passcode
+ Enter your new passcode
+ Exports
+ No scheduled exports to display
+ Create export schedule
+ Exported to: %1$s
+ The legend is too long
+ Account description
+ No recent accounts
+ No favorite accounts
+ Scheduled Actions
+ "Ended, last executed on %1$s"
+ Next
+ Done
+ Default Currency
+ Account Setup
+ Select Currency
+ Feedback Options
+ Create default accounts
+ Import my accounts
+ Let me handle it
+ Other…
+ Automatically send crash reports
+ Disable crash reports
+ Back
+ Setup GnuCash
+ Welcome to GnuCash
+ Before you dive in, \nlet\'s setup a few things first\n\nTo continue, press Next
+ Split Editor
+ Check that all splits have valid amounts before saving!
+ Invalid expression!
+ Scheduled recurring transaction
+ Transfer Funds
+
+ Select a slice to see details
+ Period:
+ From:
+ To:
+ Provide either the converted amount or exchange rate in order to transfer funds
+ Exchange rate
+ Fetch quote
+ Converted Amount
+ Sheet
+ Expenses for last 3 months
+ Total Assets
+ Total Liabilities
+ Net Worth
+ Assets
+ Liabilities
+ Equity
+ Move to:
+ Group By
+ Month
+ Quarter
+ Year
+ Balance Sheet
+ Total:
+ Google+ Community
+ Translate GnuCash
+ Share ideas, discuss changes or report problems
+ Translate or proof-read on CrowdIn
+ No compatible apps to receive the exported transactions!
+ Move…
+ Duplicate
+ Cash Flow
+ Budgets
+ Enable compact view
+ Enable to always use compact view for transactions list
+ Invalid exchange rate
+ e.g. 1 %1$s = x.xx %2$s
+ Invalid amount
+
+ - Current month
+ - Last 3 months
+ - Last 6 months
+ - Last 12 months
+ - All time
+ - Custom range…
+
+
+ 1
+
+
+ 2
+ ABC
+ 3
+ DEF
+ 4
+ GHI
+ 5
+ JKL
+ 6
+ MNO
+ 7
+ PQRS
+ 8
+ TUV
+ 9
+ WXYZ
+ 0
+ +
+ Manage Books
+ Manage Books…
+ Select any part of the chart to view details
+ Confirm delete Book
+ All accounts and transactions in this book will be deleted!
+ Delete Book
+ Last Exported:
+ Enable Sync
+ New Book
+ The selected transaction has no splits and cannot be opened
+ %1$d splits
+ in %1$s
+
+ - %d account
+ - %d accounts
+
+
+ - %d transaction
+ - %d transactions
+
+
+ - EXPENSE
+ - INCOME
+
+ Connected to Google Drive
+ Unable to connect to Google Drive
+ Please enter an amount to split
+ external service
+ Updated transaction recurring schedule
+ Since
+ All time
+ Recommend in Play Store
+ until %1$s
+ on %1$s
+ for %1$d times
+ Compact View
+ Book %1$d
+ never
+ Rename Book
+ Rename
+ Rename
+ Select backup file
+ Select a file for automatic backups
+ Confirm restore from backup
+ A new book will be opened with the contents of this backup. Do you wish to proceed?
+ Restore
+ No backups found
+ There are no existing backup files to restore from
+
+ gnucash_android_backup.gnca
+ Select the destination after export is complete
+ Export to \'/Apps/GnuCash Android/\' folder on Dropbox
+ Preferences
+
diff --git a/app/src/main/res/values-fi-rFI/strings.xml b/app/src/main/res/values-fi-rFI/strings.xml
index 6a033bf2b..202c16fd2 100644
--- a/app/src/main/res/values-fi-rFI/strings.xml
+++ b/app/src/main/res/values-fi-rFI/strings.xml
@@ -179,8 +179,6 @@
- QIF
- OFX
- XML
- - CSV-tilit
- - CSV-liiketoimet
Select a Color
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
index ac18b6f6a..9ab498501 100644
--- a/app/src/main/res/values-fr/strings.xml
+++ b/app/src/main/res/values-fr/strings.xml
@@ -171,8 +171,6 @@
- QIF
- OFX
- XML
- - CSV-comptes
- - CSV-transactions
Sélectionnez une couleur
diff --git a/app/src/main/res/values-hu-rHU/strings.xml b/app/src/main/res/values-hu-rHU/strings.xml
index f52f03d47..6fb7cee73 100644
--- a/app/src/main/res/values-hu-rHU/strings.xml
+++ b/app/src/main/res/values-hu-rHU/strings.xml
@@ -179,8 +179,6 @@
- QIF
- OFX
- XML
- - CSV-fiókok
- - CSV-ügyletek
Select a Color
diff --git a/app/src/main/res/values-in-rID/strings.xml b/app/src/main/res/values-in-rID/strings.xml
index 9a4b3fa39..8f9d8d940 100644
--- a/app/src/main/res/values-in-rID/strings.xml
+++ b/app/src/main/res/values-in-rID/strings.xml
@@ -20,15 +20,15 @@
Ubah Akun
Tambah transaksi baru ke akun
Lihat rincian akun
- Tidak ada akun untuk ditampilkan
+ Tak ada akun untuk ditampilkan
Nama akun
Batal
Simpan
- Tes
- Masukkan Kode akses
- Kode akses salah, silakan coba lagi
- Kode akses dibuat
- Harap konfirmasi kode akses Anda
+ Uji coba
+ Masukkan sandi
+ Sandi salah, silakan coba lagi
+ Sandi dibuat
+ Harap konfirmasi sandi Anda
Konfirmasi kode akses tidak valid. Silakan cobalagi
Deskripsi
Jumlah
@@ -171,8 +171,6 @@
- QIF
- OFX
- XML
- - CSV-accounts
- - CSV-transactions
Pilih Warna
diff --git a/app/src/main/res/values-it-rIT/strings.xml b/app/src/main/res/values-it-rIT/strings.xml
index be47b1120..421590559 100644
--- a/app/src/main/res/values-it-rIT/strings.xml
+++ b/app/src/main/res/values-it-rIT/strings.xml
@@ -176,8 +176,6 @@
- QIF
- OFX
- XML
- - CSV-conti
- - CSV-transazioni
Seleziona un colore
diff --git a/app/src/main/res/values-iw-rIL/strings.xml b/app/src/main/res/values-iw-rIL/strings.xml
index 222a5b9ac..2dd55c1a7 100644
--- a/app/src/main/res/values-iw-rIL/strings.xml
+++ b/app/src/main/res/values-iw-rIL/strings.xml
@@ -181,8 +181,6 @@
- QIF
- OFX
- XML
- - CSV-accounts
- - CSV-transactions
Select a Color
diff --git a/app/src/main/res/values-ja-rJP/strings.xml b/app/src/main/res/values-ja-rJP/strings.xml
index 2518fb141..b5d7e1c9e 100644
--- a/app/src/main/res/values-ja-rJP/strings.xml
+++ b/app/src/main/res/values-ja-rJP/strings.xml
@@ -173,8 +173,6 @@
- QIF
- OFX
- XML
- - CSV-アカウント
- - CSV-取引
色を選択
diff --git a/app/src/main/res/values-ko-rKR/strings.xml b/app/src/main/res/values-ko-rKR/strings.xml
index 78d95474e..f20d6f229 100644
--- a/app/src/main/res/values-ko-rKR/strings.xml
+++ b/app/src/main/res/values-ko-rKR/strings.xml
@@ -175,8 +175,6 @@
- QIF
- OFX
- XML
- - CSV-계정
- - CSV-업무
색상 선택
diff --git a/app/src/main/res/values-lv-rLV/strings.xml b/app/src/main/res/values-lv-rLV/strings.xml
index 405e89843..3deabdcda 100644
--- a/app/src/main/res/values-lv-rLV/strings.xml
+++ b/app/src/main/res/values-lv-rLV/strings.xml
@@ -180,8 +180,6 @@
- QIF
- OFX
- XML
- - CSV-accounts
- - CSV-transactions
Select a Color
diff --git a/app/src/main/res/values-nb/strings.xml b/app/src/main/res/values-nb/strings.xml
index c8b3c83c2..84f2daf11 100644
--- a/app/src/main/res/values-nb/strings.xml
+++ b/app/src/main/res/values-nb/strings.xml
@@ -170,8 +170,6 @@
- QIF
- OFX
- XML
- - CSV-kontoer
- - CSV-transaksjoner
Velg en farge
diff --git a/app/src/main/res/values-nl-rNL/strings.xml b/app/src/main/res/values-nl-rNL/strings.xml
index a8e998ae1..3da93b9d5 100644
--- a/app/src/main/res/values-nl-rNL/strings.xml
+++ b/app/src/main/res/values-nl-rNL/strings.xml
@@ -179,8 +179,6 @@
- QIF
- OFX
- XML
- - CSV-rekeningen
- - CSV-transacties
Kies een kleur
diff --git a/app/src/main/res/values-no-rNO/strings.xml b/app/src/main/res/values-no-rNO/strings.xml
index abc690a6e..f4b098eec 100644
--- a/app/src/main/res/values-no-rNO/strings.xml
+++ b/app/src/main/res/values-no-rNO/strings.xml
@@ -175,8 +175,6 @@
- QIF
- OFX
- XML
- - CSV-kontoer
- - CSV-transaksjoner
Velg en farge
diff --git a/app/src/main/res/values-pl-rPL/strings.xml b/app/src/main/res/values-pl-rPL/strings.xml
index 8788b4e6d..079d66f5c 100644
--- a/app/src/main/res/values-pl-rPL/strings.xml
+++ b/app/src/main/res/values-pl-rPL/strings.xml
@@ -177,8 +177,6 @@ Konto docelowe używa innej waluty niż konto wyjściowe
- QIF
- OFX
- XML
- - CSV-konta
- - CSV-transakcje
Wybierz kolor
diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml
index 839ae4abf..37681f758 100644
--- a/app/src/main/res/values-pt-rBR/strings.xml
+++ b/app/src/main/res/values-pt-rBR/strings.xml
@@ -138,7 +138,7 @@
Todas as transações apagadas com sucesso!
Importando contas
Transações
- Sub-Contas
+ Subcontas
Procurar
Formato de Exportação padrão
Formato de arquivo a ser usado por padrão ao exportar transações
@@ -151,8 +151,8 @@
Conta não editável
Conta para transferências padrão
- - %d sub-conta
- - %d sub-contas
+ - %d subconta
+ - %d subconta
- DINHEIRO
@@ -174,17 +174,15 @@
- QIF
- OFX
- XML
- - CSV-contas
- - CSV-transações
Selecione uma Cor
Cor de conta & Tipo
- Apagar sub-contas
+ Apagar subcontas
Recentes
- Favoritos
+ Favoritas
Todas
Cria uma estrutura de contas GnuCash padrão
Cria contas padrão
@@ -224,8 +222,8 @@
Gráfico de Linhas
Gráfico de Barras
Preferências de relatórios
- Côr da conta nos relatórios
- Use côr da conta no gráfico de barras/linhas
+ Cor da conta nos relatórios
+ Use cor da conta no gráfico de barras/linhas
Ordenar por tamanho
Alterna visibilidade da legenda
Alterna visibilidade das etiquetas
@@ -430,7 +428,7 @@ Neste processo não serão recolhidas informações do utilizador!
Agenda recorrente de transação atualizada
Desde
Desde o início
- Recomendado na Play Store
+ Recomendar na Play Store
até %1$s
em %1$s
por %1$d vezes
diff --git a/app/src/main/res/values-pt-rPT/strings.xml b/app/src/main/res/values-pt-rPT/strings.xml
index a0f5cda62..eedcb017a 100644
--- a/app/src/main/res/values-pt-rPT/strings.xml
+++ b/app/src/main/res/values-pt-rPT/strings.xml
@@ -174,8 +174,6 @@
- QIF
- OFX
- XML
- - CSV-contas
- - CSV-transações
Escolha uma côr
@@ -430,7 +428,7 @@ Neste processo não serão recolhidas informações do utilizador!
Transação atualizada agendamento recorrente
Desde
Todo o tempo
- Recomendado na Play Store
+ Recomendar na Play Store
desde%1$s
na %1$s
por %1$d vezes
diff --git a/app/src/main/res/values-ro-rRO/strings.xml b/app/src/main/res/values-ro-rRO/strings.xml
index 3e86e8fd9..ff9007834 100644
--- a/app/src/main/res/values-ro-rRO/strings.xml
+++ b/app/src/main/res/values-ro-rRO/strings.xml
@@ -180,8 +180,6 @@
- QIF
- OFX
- XML
- - CSV-conturi
- - CSV-tranzacții
Select a Color
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index c388ec91e..1b625cc34 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -154,6 +154,7 @@
- %d дочерний счёт
- %d шт. дочерних счетов
+ - %d sub-accounts
- %d шт. дочерних счетов
@@ -176,8 +177,6 @@
- QIF
- OFX
- XML
- - CSV-счета
- - CSV-транзакции
Выберите цвет
@@ -226,7 +225,7 @@
График
Гистограмма
Настройки отчётов
- Цвет счёта в отчётых
+ Цвет счёта в отчётах
Использовать цвет счёта в отчётах
Отсортировать по размеру
Показать легенду
@@ -279,26 +278,31 @@
- Каждый час
- Каждые %d часа
+ - Every %d hours
- Каждые %d часов
- Ежедневно
- Каждые %d дня
+ - Every %d days
- Каждые %d дней
- Еженедельно
- Каждые %d недели
+ - Every %d weeks
- Каждые %d недель
- Ежемесячно
- Каждые %d месяца
+ - Every %d months
- Каждые %d месяцев
- Ежегодно
- Каждые %d года
+ - Every %d years
- Каждые %d лет
Записывать отказы программы
@@ -420,11 +424,13 @@
- %d счета
- %d счета
+ - %d accounts
- %d счетов
- %d транзакция
- %d транзакции
+ - %d transactions
- %d транзакций
diff --git a/app/src/main/res/values-sr-rSP/strings.xml b/app/src/main/res/values-sr-rSP/strings.xml
index 0c264592e..d7eb355dc 100644
--- a/app/src/main/res/values-sr-rSP/strings.xml
+++ b/app/src/main/res/values-sr-rSP/strings.xml
@@ -180,8 +180,6 @@
- QIF
- OFX
- XML
- - CSV-рачуне
- - CSV-трансакције
Select a Color
diff --git a/app/src/main/res/values-sv-rSE/strings.xml b/app/src/main/res/values-sv-rSE/strings.xml
index b2d675059..71645393c 100644
--- a/app/src/main/res/values-sv-rSE/strings.xml
+++ b/app/src/main/res/values-sv-rSE/strings.xml
@@ -170,8 +170,6 @@
- QIF
- OFX
- XML
- - CSV-konton
- - CSV-transaktioner
Välj en färg
diff --git a/app/src/main/res/values-tr-rTR/strings.xml b/app/src/main/res/values-tr-rTR/strings.xml
index e3b8f243a..2c93676fd 100644
--- a/app/src/main/res/values-tr-rTR/strings.xml
+++ b/app/src/main/res/values-tr-rTR/strings.xml
@@ -179,8 +179,6 @@
- QIF
- OFX
- XML
- - CSV-hesapları
- - CSV-işlemler
Renk Seçin
diff --git a/app/src/main/res/values-uk-rUA/strings.xml b/app/src/main/res/values-uk-rUA/strings.xml
index 0d919a5d2..b4aa3b352 100644
--- a/app/src/main/res/values-uk-rUA/strings.xml
+++ b/app/src/main/res/values-uk-rUA/strings.xml
@@ -176,8 +176,6 @@
- QIF
- OFX
- XML
- - CSV-счета
- - CSV-транзакции
Оберіть колір
diff --git a/app/src/main/res/values-vi-rVN/strings.xml b/app/src/main/res/values-vi-rVN/strings.xml
index d8a21f5bd..082e3fac1 100644
--- a/app/src/main/res/values-vi-rVN/strings.xml
+++ b/app/src/main/res/values-vi-rVN/strings.xml
@@ -178,8 +178,6 @@
- QIF
- OFX
- XML
- - CSV-tài khoản
- - CSV-giao dịch
Select a Color
diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml
index 9b7eaebe4..40fcb6920 100644
--- a/app/src/main/res/values-zh-rCN/strings.xml
+++ b/app/src/main/res/values-zh-rCN/strings.xml
@@ -19,7 +19,7 @@
创建科目
编辑科目
为科目增加交易
- 科目的详细资料
+ 查看帐户详细信息
没有可以显示的科目
科目名称
取消
@@ -175,8 +175,6 @@
- QIF
- OFX
- XML
- - CSV-账户
- - CSV-交易
选择一种颜色
diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml
index aee1a58c3..8b502ea05 100644
--- a/app/src/main/res/values-zh-rTW/strings.xml
+++ b/app/src/main/res/values-zh-rTW/strings.xml
@@ -18,9 +18,9 @@
新增科目
編輯科目
- 给科目添加交易
+ 在科目中新增交易
檢視帳戶詳細資訊
- 没有要显示的科目
+ 沒有科目可顯示
科目名稱
取消
存檔
@@ -78,7 +78,7 @@
建立會計科目
顯示科目名字
小工具中隱藏帳戶餘額
- 创建科目
+ 建立科目
GnuCash裡還没有會計科目信息。\n使用小部件前需要添加會計科目
授權許可
Apache License v2.0,點擊查看詳细(將打開網頁)。
@@ -125,7 +125,7 @@
知道了
輸入金額才能保存交易
- 匯入GnuCash科目时發生錯誤。
+ 匯入GnuCash科目時發生錯誤。
GnuCash科目資料匯入完成。
匯入從GnuCash桌面版匯出的科目設置
匯入GnuCash科目
@@ -175,11 +175,9 @@
- QIF
- OFX
- XML
- - CSV-账户
- - CSV-交易
- 选择一种颜色
+ 選擇顏色
科目顏色和類型
@@ -189,7 +187,7 @@
所有
建立通用的科目結構
建立預設科目
- 這將用預設科目來創建新的帳簿\n\n目前擁有的科目與交易不會受影響
+ 這將用預設科目來建立新的帳簿\n\n目前擁有的科目與交易不會受影響
交易
選擇儲存的位置
描述
@@ -198,7 +196,7 @@
提款
存款
付款
- 费用
+ 費用
減少
增加
收入
@@ -222,10 +220,10 @@
報表
圓形圖
折線圖
- 橫條圖
+ 長條圖
報表設置
用不同顏色区分科目
- 在饼图中使用科目的颜色
+ 在圓餅圖中使用科目的顏色
按數量排序
顯示圖例
顯示標籤
@@ -281,7 +279,7 @@
- 每 %d 天
- - 每 %d 周
+ - 每 %d 週
- 每 %d 月
@@ -313,13 +311,13 @@ No user-identifiable information will be collected as part of this process!
選擇幣種
回饋選項
建立預設科目
- 汇入科目
+ 匯入科目
稍后处理
其他...
自動發送故障報告
禁用崩潰報告
後退
- 设置GnuCash
+ 設定GnuCash
歡迎來到GnuCash
在使用之前,需 \n要设置几个参数\n\n请点击“下一步”繼續
拆分交易
@@ -345,7 +343,7 @@ No user-identifiable information will be collected as part of this process!
負債
財產淨值
移動至
- 分组方式
+ 分組方式
月
季度
年
@@ -354,13 +352,13 @@ No user-identifiable information will be collected as part of this process!
Google+ 社群
翻譯GnuCash
在Google+上提交问题和建议
- 帮忙翻譯或校對( CrowdIn)
+ 在CrowdIn上協助翻譯或校對GnuCash
没有合适的应用接收汇出的文档
移動...
複製
現金流
預算
- 啟用紧凑視圖
+ 啟用緊湊視圖
交易清單總是啟用緊湊視圖
匯率不正確
例如 1 %1$s = x.xx %2$s
@@ -395,18 +393,18 @@ No user-identifiable information will be collected as part of this process!
WXYZ
0
+
- 管理帐簿
- 管理帐簿
+ 管理帳簿
+ 管理帳簿…
選擇該圖表以查看詳細資訊的任何部分
確認删除
- 帐簿中所有科目和交易都將被刪除 !
- 删除帐簿
+ 帳簿中所有科目和交易都將被刪除 !
+ 删除帳簿
最後匯出︰
啟用同步
新建帐簿
选择的交易没有拆分
%1$d 项分割
- 于 %1$s
+ 於 %1$s
- %d 個科目
@@ -421,15 +419,15 @@ No user-identifiable information will be collected as part of this process!
無法連線到伺服器
請輸入要拆分的金額
外部服務
- 排程交易已经更新
+ 排程交易已經更新
自從
全部時間
在 Play Store 推薦
直到%1$s
在%1$s
%1$d 次
- 紧凑视图
- 账簿 %1$d
+ 緊湊視圖
+ 帳簿 %1$d
從未
重新命名帳簿
重新命名
diff --git a/crowdin.yml b/crowdin.yml
index be4fb23ca..57f6ea49d 100644
--- a/crowdin.yml
+++ b/crowdin.yml
@@ -5,7 +5,7 @@
# Choose file structure in crowdin
# e.g. true or false
#
-"preserve_hierarchy": true
+"preserve_hierarchy": false
#
# Files configuration
@@ -13,19 +13,16 @@
files: [
{
#
- # Source files filter
- # e.g. "/resources/en/*.json"
- #
+ # Source files
"source" : "/app/src/main/res/values/strings.xml",
#
# where translations live
- # e.g. "/resources/%two_letters_code%/%original_file_name%"
- #
"translation" : "/app/src/main/res/values-%android_code%/%original_file_name%",
#
- # Often software projects have custom names for locale directories. crowdin-cli allows you to map your own languages to be understandable by Crowdin.
+ # Often software projects have custom names for locale directories.
+ # crowdin-cli allows you to map your own languages to be understandable by Crowdin.
#
"languages_mapping" : {
"android_code": {
@@ -61,66 +58,9 @@ files: [
#
#"update_option" : "",
- #
- # Start block only for XML
- #
-
- #
- # Defines whether to translate tags attributes.
- # e.g. 0 or 1 (Default is 1)
- #
- # "translate_attributes" : 1,
-
- #
- # Defines whether to translate texts placed inside the tags.
- # e.g. 0 or 1 (Default is 1)
- #
- # "translate_content" : 1,
-
- #
- # This is an array of strings, where each item is the XPaths to DOM element that should be imported
- # e.g. ["/content/text", "/content/text[@value]"]
- #
- # "translatable_elements" : [],
-
- #
- # Defines whether to split long texts into smaller text segments.
- # e.g. 0 or 1 (Default is 1)
- #
- # "content_segmentation" : 1,
-
- #
- # End block only for XML
- #
-
- #
- # Start .properties block
- #
-
- #
- # Defines whether single quote should be escaped by another single quote or backslash in exported translations.
- # e.g. 0 or 1 or 2 or 3 (Default is 3)
- # 0 - do not escape single quote;
- # 1 - escape single quote by another single quote;
- # 2 - escape single quote by backslash;
- # 3 - escape single quote by another single quote only in strings containing variables ( {0} ).
- #
- # "escape_quotes" : 3,
-
- #
- # End .properties block
- #
-
- #
- # Is first line contains header?
- # e.g. true or false
- #
- #"first_line_contains_header" : true,
-
- #
- # for spreadsheets
- # e.g. "identifier,source_phrase,context,uk,ru,fr"
- #
- # "scheme" : "",
+ },
+ {
+ "source" : "/resources/playstore/play_store_description.txt",
+ "translation": "/resources/playstore/play_store_description_i18n/play_store_description_%android_code%.txt"
}
]
\ No newline at end of file
diff --git a/play_store_description.txt b/resources/playstore/play_store_description.txt
similarity index 69%
rename from play_store_description.txt
rename to resources/playstore/play_store_description.txt
index 45e63bf94..b40f0cde8 100644
--- a/play_store_description.txt
+++ b/resources/playstore/play_store_description.txt
@@ -1,8 +1,9 @@
-GnuCash is a mobile finance expense tracker application for Android.
+GnuCash is a mobile finance manager for Android.
-It is a companion application for GnuCash for the desktop and enables flexible tracking of expenses on-the-go which can be exported to QIF or OFX formats.
+It is a companion application for GnuCash on the desktop, and enables flexible tracking of expenses on-the-go.
+Recorded transactions can be exported back to the desktop GnuCash via several formats (CSV / QIF / OFX).
-Some of feature highlights include:
+Some of feature highlights include:
• An easy-to-use interface.
@@ -26,10 +27,3 @@ You can also import an existing account hierarchy from GnuCash desktop.
NOTE: that the app does not offer full compatibility with GnuCash for the desktop. You cannot synchronize between the desktop app and this one. But you can import your accounts and transactions from GnuCash XML files.
-
-What does the app use the requested permissions for?
-• VIBRATE: Used to provide haptic feedback when entering some inputs
-• WAKE_LOCK: Used for keeping device active when exporting scheduled transactions in the background service
-• RECEIVE_BOOT_COMPLETED: Used to restart service for scheduled transactions or exports after device is rebooted
-• INTERNET/ACCESS_NETWORK_STATE: Used when exporting accounts/transactions to 3rd-party service like DropBox or ownCloud
-
diff --git a/resources/playstore/play_store_description_i18n/.keep b/resources/playstore/play_store_description_i18n/.keep
new file mode 100644
index 000000000..e69de29bb
From 78fa394ea64427e46910bdfa24d60b75a7b05cc5 Mon Sep 17 00:00:00 2001
From: Ngewi Fet
Date: Fri, 15 Jun 2018 15:21:34 +0200
Subject: [PATCH 42/43] Update version number for v2.4.0 final release
---
CHANGELOG.md | 2 +-
app/build.gradle | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0e7fb0f76..17a7f8258 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,6 @@
Change Log
===============================================================================
-Version 2.4.0 *(2018-06-xx)*
+Version 2.4.0 *(2018-06-15)*
----------------------------
* Feature #665: Adds CSV export format
* Feature #544: Add extra checkbox confirmation for irreversible actions
diff --git a/app/build.gradle b/app/build.gradle
index 3d03548c3..674ca8088 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -6,7 +6,7 @@ apply plugin: 'io.fabric'
def versionMajor = 2
def versionMinor = 4
def versionPatch = 0
-def versionBuild = 2
+def versionBuild = 3
static def buildTime() {
def df = new SimpleDateFormat("yyyyMMdd HH:mm 'UTC'")
From e65eceb981c91f48800d947f25583c094999aa26 Mon Sep 17 00:00:00 2001
From: Jeff Widman
Date: Sat, 4 Jan 2020 21:42:43 -0800
Subject: [PATCH 43/43] Use "License" everywhere for consistency
While "Licence" is an allowed spelling, the rest of the paragraph uses "License" so switch to that for consistency.
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 03dee803e..b0dc7dfb6 100644
--- a/README.md
+++ b/README.md
@@ -99,7 +99,7 @@ Please visit [CrowdIn](https://crowdin.com/project/gnucash-android) in order to
For development, it is recommended to use the Android Studio for development which is available for free.
Import the project into the IDE using the build.gradle file. The IDE will resolve dependencies automatically.
-# Licence
+# License
GnuCash Android is free software; you can redistribute it and/or
modify it under the terms of the Apache license, version 2.0.
You may obtain a copy of the License at