From a3ad8c2c1bcefcc2ad04ae7613756af07f79249e Mon Sep 17 00:00:00 2001 From: Ngewi Fet Date: Sat, 21 Nov 2015 13:35:32 +0100 Subject: [PATCH] Update version strings for v2.0.3 release Fix: unable to enter decimals in split editor Fix: split type toggle not working in split editor Remove transaction exported flag from XML Add app/system version to UserVoice feedback --- CHANGELOG.md | 6 +++ app/build.gradle | 4 +- .../test/ui/TransactionsActivityTest.java | 8 --- .../android/app/GnuCashApplication.java | 5 ++ .../android/export/xml/GncXmlExporter.java | 9 +--- .../android/importer/ImportAsyncTask.java | 1 + .../android/ui/common/BaseDrawerActivity.java | 24 ++++++--- .../ui/transaction/SplitEditorFragment.java | 54 +++++++++++++++---- .../transaction/TransactionFormFragment.java | 2 +- .../ui/util/widget/TransactionTypeSwitch.java | 18 ++++++- 10 files changed, 93 insertions(+), 38 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c6ad17d7..10f24f1b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ Change Log =============================================================================== +Version 2.0.3 *(2015-11-21)* +---------------------------- +* Fixed: Unable to enter decimal amounts in split editor +* Fixed: Split editor shows wrong imbalance when editing transaction +* Fixed: Auto-backups not correctly generated + Version 2.0.2 *(2015-11-20)* ---------------------------- * Fixed: Exporting to external service does not work in some devices diff --git a/app/build.gradle b/app/build.gradle index 3e0644c23..820cf023d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -5,8 +5,8 @@ apply plugin: 'io.fabric' def versionMajor = 2 def versionMinor = 0 -def versionPatch = 2 -def versionBuild = 2 +def versionPatch = 3 +def versionBuild = 0 def buildTime() { def df = new SimpleDateFormat("yyyyMMdd HH:mm 'UTC'") diff --git a/app/src/androidTest/java/org/gnucash/android/test/ui/TransactionsActivityTest.java b/app/src/androidTest/java/org/gnucash/android/test/ui/TransactionsActivityTest.java index 6ec47912b..4298926c4 100644 --- a/app/src/androidTest/java/org/gnucash/android/test/ui/TransactionsActivityTest.java +++ b/app/src/androidTest/java/org/gnucash/android/test/ui/TransactionsActivityTest.java @@ -380,14 +380,6 @@ public void testDefaultTransactionType(){ onView(withId(R.id.fab_create_transaction)).perform(click()); onView(withId(R.id.input_transaction_type)).check(matches(allOf(isChecked(), withText(R.string.label_spend)))); - Espresso.pressBack(); - //now validate the other case - - setDefaultTransactionType(TransactionType.DEBIT); - - onView(withId(R.id.fab_create_transaction)).perform(click()); - onView(withId(R.id.input_transaction_type)).check(matches(allOf(not(isChecked()), withText(R.string.label_receive)))); - Espresso.pressBack(); } private void setDefaultTransactionType(TransactionType type) { diff --git a/app/src/main/java/org/gnucash/android/app/GnuCashApplication.java b/app/src/main/java/org/gnucash/android/app/GnuCashApplication.java index 8e9985637..7a17e7aba 100644 --- a/app/src/main/java/org/gnucash/android/app/GnuCashApplication.java +++ b/app/src/main/java/org/gnucash/android/app/GnuCashApplication.java @@ -24,6 +24,7 @@ import android.database.SQLException; import android.database.sqlite.SQLiteDatabase; import android.graphics.Color; +import android.os.Build; import android.preference.PreferenceManager; import android.support.annotation.NonNull; import android.util.Log; @@ -33,6 +34,7 @@ import com.uservoice.uservoicesdk.Config; import com.uservoice.uservoicesdk.UserVoice; +import org.gnucash.android.BuildConfig; import org.gnucash.android.R; import org.gnucash.android.db.AccountsDbAdapter; import org.gnucash.android.db.CommoditiesDbAdapter; @@ -108,6 +110,9 @@ public void onCreate(){ Config config = new Config("gnucash.uservoice.com"); config.setTopicId(107400); config.setForumId(320493); + config.putUserTrait("app_version_name", BuildConfig.VERSION_NAME); + config.putUserTrait("app_version_code", BuildConfig.VERSION_CODE); + config.putUserTrait("android_version", Build.VERSION.RELEASE); // config.identifyUser("USER_ID", "User Name", "email@example.com"); UserVoice.init(config, this); 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 407d65ad8..eecaeafb2 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 @@ -360,17 +360,11 @@ private void exportTransactions(XmlSerializer xmlSerializer, boolean exportTempl ArrayList slotValue = new ArrayList<>(); String notes = cursor.getString(cursor.getColumnIndexOrThrow("trans_notes")); - boolean exported = cursor.getInt(cursor.getColumnIndexOrThrow("trans_exported")) == 1; if (notes != null && notes.length() > 0) { slotKey.add(GncXmlHelper.KEY_NOTES); slotType.add(GncXmlHelper.ATTR_VALUE_STRING); slotValue.add(notes); } - if (!exported) { - slotKey.add(GncXmlHelper.KEY_EXPORTED); - slotType.add(GncXmlHelper.ATTR_VALUE_STRING); - slotValue.add("false"); - } String scheduledActionUID = cursor.getString(cursor.getColumnIndexOrThrow("trans_from_sched_action")); if (scheduledActionUID != null && !scheduledActionUID.isEmpty()){ @@ -826,7 +820,6 @@ public String getExportMimeType(){ */ public static boolean createBackup(){ try { - new File(BACKUP_FOLDER_PATH).mkdirs(); FileOutputStream fileOutputStream = new FileOutputStream(getBackupFilePath()); BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream); GZIPOutputStream gzipOutputStream = new GZIPOutputStream(bufferedOutputStream); @@ -834,6 +827,7 @@ public static boolean createBackup(){ ExportParams params = new ExportParams(ExportFormat.XML); new GncXmlExporter(params).generateExport(writer); + writer.close(); return true; } catch (IOException | ExporterException e) { Crashlytics.logException(e); @@ -849,6 +843,7 @@ public static boolean createBackup(){ * @see #BACKUP_FOLDER_PATH */ private static String getBackupFilePath(){ + new File(BACKUP_FOLDER_PATH).mkdirs(); return BACKUP_FOLDER_PATH + buildExportFilename(ExportFormat.XML) + ".zip"; } } diff --git a/app/src/main/java/org/gnucash/android/importer/ImportAsyncTask.java b/app/src/main/java/org/gnucash/android/importer/ImportAsyncTask.java index 8619362db..7f01f7d9e 100644 --- a/app/src/main/java/org/gnucash/android/importer/ImportAsyncTask.java +++ b/app/src/main/java/org/gnucash/android/importer/ImportAsyncTask.java @@ -74,6 +74,7 @@ protected Boolean doInBackground(Uri... uris) { GncXmlImporter.parse(accountInputStream); } catch (Exception exception){ Log.e(ImportAsyncTask.class.getName(), "" + exception.getMessage()); + Crashlytics.log("Could not open: " + uris[0].toString()); Crashlytics.logException(exception); exception.printStackTrace(); diff --git a/app/src/main/java/org/gnucash/android/ui/common/BaseDrawerActivity.java b/app/src/main/java/org/gnucash/android/ui/common/BaseDrawerActivity.java index 49ffe5adf..6d12529f2 100644 --- a/app/src/main/java/org/gnucash/android/ui/common/BaseDrawerActivity.java +++ b/app/src/main/java/org/gnucash/android/ui/common/BaseDrawerActivity.java @@ -121,10 +121,13 @@ public void onConfigurationChanged(Configuration newConfig) { @Override public boolean onOptionsItemSelected(MenuItem item) { - if (!mDrawerLayout.isDrawerOpen(mNavigationView)) - mDrawerLayout.openDrawer(mNavigationView); - else - mDrawerLayout.closeDrawer(mNavigationView); + if (item.getItemId() == android.R.id.home){ + if (!mDrawerLayout.isDrawerOpen(mNavigationView)) + mDrawerLayout.openDrawer(mNavigationView); + else + mDrawerLayout.closeDrawer(mNavigationView); + return true; + } return super.onOptionsItemSelected(item); } @@ -133,6 +136,7 @@ public boolean onOptionsItemSelected(MenuItem item) { * Handler for the navigation drawer items * */ protected void onDrawerMenuItemClicked(int itemId) { + switch (itemId){ case R.id.nav_item_open: { //Open... files AccountsActivity.startXmlFileChooser(this); @@ -148,8 +152,13 @@ protected void onDrawerMenuItemClicked(int itemId) { } break; - case R.id.nav_item_reports: - startActivity(new Intent(this, ReportsActivity.class)); + case R.id.nav_item_reports: { + if (!(this instanceof AccountsActivity) || !(this instanceof ReportsActivity)) + this.finish(); + Intent intent = new Intent(this, ReportsActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP); + startActivity(intent); + } break; case R.id.nav_item_scheduled_actions: { //show scheduled transactions @@ -159,9 +168,8 @@ protected void onDrawerMenuItemClicked(int itemId) { } break; - case R.id.nav_item_export:{ + case R.id.nav_item_export: AccountsActivity.openExportFragment(this); - } break; case R.id.nav_item_settings: //Settings activity diff --git a/app/src/main/java/org/gnucash/android/ui/transaction/SplitEditorFragment.java b/app/src/main/java/org/gnucash/android/ui/transaction/SplitEditorFragment.java index 0b6416069..a45fcb6f1 100644 --- a/app/src/main/java/org/gnucash/android/ui/transaction/SplitEditorFragment.java +++ b/app/src/main/java/org/gnucash/android/ui/transaction/SplitEditorFragment.java @@ -27,6 +27,7 @@ import android.support.v7.app.AppCompatActivity; import android.text.Editable; import android.text.TextWatcher; +import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; @@ -42,6 +43,11 @@ import android.widget.TextView; import android.widget.Toast; +import com.crashlytics.android.Crashlytics; + +import net.objecthunter.exp4j.Expression; +import net.objecthunter.exp4j.ExpressionBuilder; + import org.gnucash.android.R; import org.gnucash.android.db.AccountsDbAdapter; import org.gnucash.android.db.CommoditiesDbAdapter; @@ -140,6 +146,7 @@ public void onActivityCreated(Bundle savedInstanceState) { if (!splitList.isEmpty()) { //aha! there are some splits. Let's load those instead loadSplitViews(splitList); + mImbalanceWatcher.afterTextChanged(null); } else { final String currencyCode = mAccountsDbAdapter.getAccountCurrencyCode(mAccountUID); Split split = new Split(new Money(mBaseAmount.abs(), Commodity.getInstance(currencyCode)), mAccountUID); @@ -149,9 +156,9 @@ public void onActivityCreated(Bundle savedInstanceState) { View view = addSplitView(split); view.findViewById(R.id.input_accounts_spinner).setEnabled(false); view.findViewById(R.id.btn_remove_split).setVisibility(View.GONE); + TransactionsActivity.displayBalance(mImbalanceTextView, new Money(mBaseAmount.negate(), mCommodity)); } - TransactionsActivity.displayBalance(mImbalanceTextView, new Money(mBaseAmount.negate(), mCommodity)); } @Override @@ -287,7 +294,7 @@ public void onClick(View view) { } accountsSpinner.setOnItemSelectedListener(new SplitAccountListener(splitTypeSwitch, this)); - splitTypeSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + splitTypeSwitch.addOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { mImbalanceWatcher.afterTextChanged(null); @@ -295,6 +302,35 @@ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { }); splitAmountEditText.addTextChangedListener(mImbalanceWatcher); } + + /** + * Returns the value of the amount in the splitAmountEditText field without setting the value to the view + *

If the expression in the view is currently incomplete or invalid, null is returned. + * This method is used primarily for computing the imbalance

+ * @return Value in the split item amount field, or {@link BigDecimal#ZERO} if the expression is empty or invalid + */ + public BigDecimal getAmountValue(){ + String amountString = splitAmountEditText.getCleanString(); + if (amountString.isEmpty()) + return BigDecimal.ZERO; + + ExpressionBuilder expressionBuilder = new ExpressionBuilder(amountString); + Expression expression; + + try { + expression = expressionBuilder.build(); + } catch (RuntimeException e) { + return BigDecimal.ZERO; + } + + if (expression != null && expression.validate().isValid()) { + return new BigDecimal(expression.evaluate()); + } else { + Log.v(SplitEditorFragment.this.getClass().getSimpleName(), + "Incomplete expression for updating imbalance: " + expression); + return BigDecimal.ZERO; + } + } } /** @@ -406,16 +442,12 @@ public void afterTextChanged(Editable editable) { for (View splitItem : mSplitItemViewList) { SplitViewHolder viewHolder = (SplitViewHolder) splitItem.getTag(); - viewHolder.splitAmountEditText.removeTextChangedListener(this); - BigDecimal amount = viewHolder.splitAmountEditText.getValue(); - if (amount != null) { - if (viewHolder.splitTypeSwitch.isChecked()) { - imbalance = imbalance.subtract(amount); - } else { - imbalance = imbalance.add(amount); - } + BigDecimal amount = viewHolder.getAmountValue().abs(); + if (viewHolder.splitTypeSwitch.isChecked()) { + imbalance = imbalance.subtract(amount); + } else { + imbalance = imbalance.add(amount); } - viewHolder.splitAmountEditText.addTextChangedListener(this); } TransactionsActivity.displayBalance(mImbalanceTextView, new Money(imbalance.negate(), mCommodity)); diff --git a/app/src/main/java/org/gnucash/android/ui/transaction/TransactionFormFragment.java b/app/src/main/java/org/gnucash/android/ui/transaction/TransactionFormFragment.java index 6cd8d00b3..49042df76 100644 --- a/app/src/main/java/org/gnucash/android/ui/transaction/TransactionFormFragment.java +++ b/app/src/main/java/org/gnucash/android/ui/transaction/TransactionFormFragment.java @@ -456,7 +456,7 @@ private void initializeViewsWithTransaction(){ //when autocompleting, only change the amount if the user has not manually changed it already mAmountEditText.setValue(mTransaction.getBalance(mAccountUID).asBigDecimal()); } - mCurrencyTextView.setText(mTransaction.getCurrency().getSymbol(Locale.getDefault())); + mCurrencyTextView.setText(mTransaction.getCurrency().getSymbol()); mNotesEditText.setText(mTransaction.getNote()); mDateTextView.setText(DATE_FORMATTER.format(mTransaction.getTimeMillis())); mTimeTextView.setText(TIME_FORMATTER.format(mTransaction.getTimeMillis())); diff --git a/app/src/main/java/org/gnucash/android/ui/util/widget/TransactionTypeSwitch.java b/app/src/main/java/org/gnucash/android/ui/util/widget/TransactionTypeSwitch.java index bb6537090..c8fc18840 100644 --- a/app/src/main/java/org/gnucash/android/ui/util/widget/TransactionTypeSwitch.java +++ b/app/src/main/java/org/gnucash/android/ui/util/widget/TransactionTypeSwitch.java @@ -29,6 +29,8 @@ import org.gnucash.android.model.TransactionType; import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; /** * A special type of {@link android.widget.ToggleButton} which displays the appropriate CREDIT/DEBIT labels for the @@ -38,6 +40,8 @@ public class TransactionTypeSwitch extends SwitchCompat { private AccountType mAccountType = AccountType.EXPENSE; + List mOnCheckedChangeListeners = new ArrayList<>(); + public TransactionTypeSwitch(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @@ -113,6 +117,14 @@ public void setAmountFormattingListener(CalculatorEditText amoutView, TextView c setOnCheckedChangeListener(new OnTypeChangedListener(amoutView, currencyTextView)); } + /** + * Add listeners to be notified when the checked status changes + * @param checkedChangeListener Checked change listener + */ + public void addOnCheckedChangeListener(OnCheckedChangeListener checkedChangeListener){ + mOnCheckedChangeListeners.add(checkedChangeListener); + } + /** * Toggles the button checked based on the movement caused by the transaction type for the specified account * @param transactionType {@link org.gnucash.android.model.TransactionType} of the split @@ -123,7 +135,7 @@ public void setChecked(TransactionType transactionType){ /** * Returns the account type associated with this button - * @return + * @return Type of account */ public AccountType getAccountType(){ return mAccountType; @@ -173,6 +185,10 @@ public void onCheckedChanged(CompoundButton compoundButton, boolean isChecked) { } } + + for (OnCheckedChangeListener listener : mOnCheckedChangeListeners) { + listener.onCheckedChanged(compoundButton, isChecked); + } } } }