From ae6d8d48b4ee60eb99b9dca5ed3544bf574ae8ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=80lex=20Magaz=20Gra=C3=A7a?= Date: Mon, 27 Mar 2017 20:45:10 +0200 Subject: [PATCH 01/45] Ensure default_transfer_account_uid fields don't reference deleted accounts For some reason we don't have a foreign key constraint on this field, so we have to enforce it from code. We do so from AccountsDbAdapter.deleteRecord(String) but not from DatabaseAdapter.deleteRecord(long), which is not overriden by AccountsDbAdapter (called by AccountsListFragment). We don't override deleteRecord(long) to avoid infinite recursion (if we call deleteRecord(String) to avoid code duplication). So we just change AccountsListFragment to call deleteRecord(String). The proper solution, with a SQL constraint, will be implemented later when we restructure the database to make it compatible with the desktop. Fixes https://github.com/codinguser/gnucash-android/issues/654 --- .../gnucash/android/db/DatabaseSchema.java | 2 +- .../gnucash/android/db/MigrationHelper.java | 34 +++++++++++++++++++ .../ui/account/AccountsListFragment.java | 4 ++- 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/gnucash/android/db/DatabaseSchema.java b/app/src/main/java/org/gnucash/android/db/DatabaseSchema.java index 0f32f068d..01f07d2d9 100644 --- a/app/src/main/java/org/gnucash/android/db/DatabaseSchema.java +++ b/app/src/main/java/org/gnucash/android/db/DatabaseSchema.java @@ -39,7 +39,7 @@ public class DatabaseSchema { * Version number of database containing accounts and transactions info. * With any change to the database schema, this number must increase */ - public static final int DATABASE_VERSION = 13; + public static final int DATABASE_VERSION = 14; /** * Name of the database diff --git a/app/src/main/java/org/gnucash/android/db/MigrationHelper.java b/app/src/main/java/org/gnucash/android/db/MigrationHelper.java index f5d3805ec..b526689b2 100644 --- a/app/src/main/java/org/gnucash/android/db/MigrationHelper.java +++ b/app/src/main/java/org/gnucash/android/db/MigrationHelper.java @@ -1474,4 +1474,38 @@ static int upgradeDbToVersion13(SQLiteDatabase db){ return oldVersion; } + + /** + * Upgrades the database to version 14. + *

This migration makes the following changes to the database: + *

+ *

+ * @param db SQLite database to be upgraded + * @return New database version, 14 if migration succeeds, 13 otherwise + */ + static int upgradeDbToVersion14(SQLiteDatabase db) { + Log.i(DatabaseHelper.LOG_TAG, "Upgrading database to version 14"); + int oldVersion = 13; + + db.beginTransaction(); + try { + ContentValues contentValues = new ContentValues(); + contentValues.putNull(AccountEntry.COLUMN_DEFAULT_TRANSFER_ACCOUNT_UID); + db.update( + AccountEntry.TABLE_NAME, + contentValues, + AccountEntry.TABLE_NAME + "." + AccountEntry.COLUMN_DEFAULT_TRANSFER_ACCOUNT_UID + + " NOT IN (SELECT " + AccountEntry.COLUMN_UID + + " FROM " + AccountEntry.TABLE_NAME + ")", + null); + db.setTransactionSuccessful(); + oldVersion = 14; + } finally { + db.endTransaction(); + } + return oldVersion; + } } 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 5a449f461..f6e9382ac 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 @@ -247,7 +247,9 @@ public void tryDeleteAccount(long rowId) { if (acc.getTransactionCount() > 0 || mAccountsDbAdapter.getSubAccountCount(acc.getUID()) > 0) { showConfirmationDialog(rowId); } else { - mAccountsDbAdapter.deleteRecord(rowId); + // Avoid calling AccountsDbAdapter.deleteRecord(long). See #654 + String uid = mAccountsDbAdapter.getUID(rowId); + mAccountsDbAdapter.deleteRecord(uid); refresh(); } } From 5c2841fd3b39550fc3ddfcaa2eb812003c4404f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=80lex=20Magaz=20Gra=C3=A7a?= Date: Mon, 3 Apr 2017 21:22:32 +0200 Subject: [PATCH 02/45] Allow to rename books Fixes https://github.com/codinguser/gnucash-android/issues/634 --- .../ui/settings/BookManagerFragment.java | 28 +++++++++++++++++++ app/src/main/res/layout/cardview_book.xml | 2 +- app/src/main/res/menu/book_context_menu.xml | 10 +++++-- app/src/main/res/values/strings.xml | 3 ++ 4 files changed, 40 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/org/gnucash/android/ui/settings/BookManagerFragment.java b/app/src/main/java/org/gnucash/android/ui/settings/BookManagerFragment.java index 50df8e163..febf3ab27 100644 --- a/app/src/main/java/org/gnucash/android/ui/settings/BookManagerFragment.java +++ b/app/src/main/java/org/gnucash/android/ui/settings/BookManagerFragment.java @@ -38,6 +38,7 @@ import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; +import android.widget.EditText; import android.widget.ImageView; import android.widget.ListView; import android.widget.TextView; @@ -165,6 +166,7 @@ public void bindView(View view, final Context context, Cursor cursor) { setLastExportedText(view, bookUID); setStatisticsText(view, bookUID); + final String bookName = cursor.getString(cursor.getColumnIndexOrThrow(BookEntry.COLUMN_DISPLAY_NAME)); ImageView optionsMenu = (ImageView) view.findViewById(R.id.options_menu); optionsMenu.setOnClickListener(new View.OnClickListener() { @Override @@ -176,6 +178,32 @@ public void onClick(View v) { @Override public boolean onMenuItemClick(MenuItem item) { switch (item.getItemId()){ + case R.id.ctx_menu_rename_book: + final EditText nameEditText = new EditText(context); + nameEditText.setText(bookName); + + AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(getActivity()); + dialogBuilder.setTitle(R.string.title_rename_book) + .setView(nameEditText) + .setPositiveButton(R.string.btn_rename, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + BooksDbAdapter.getInstance() + .updateRecord(bookUID, + BookEntry.COLUMN_DISPLAY_NAME, + nameEditText.getText().toString()); + refresh(); + } + }) + .setNegativeButton(R.string.btn_cancel, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + } + }); + AlertDialog dialog = dialogBuilder.create(); + dialog.show(); + return true; case R.id.ctx_menu_sync_book: //TODO implement sync return false; diff --git a/app/src/main/res/layout/cardview_book.xml b/app/src/main/res/layout/cardview_book.xml index be292e8e1..8283bbcc6 100644 --- a/app/src/main/res/layout/cardview_book.xml +++ b/app/src/main/res/layout/cardview_book.xml @@ -43,7 +43,7 @@ android:layout_marginLeft="@dimen/dialog_padding" android:layout_marginRight="52dp"/> - + + + app:showAsAction="ifRoom" + android:orderInCategory="2" /> \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 77a0d09dc..f4706ec25 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -489,4 +489,7 @@ Compact View Book %1$d never + Rename Book + Rename + Rename From aec72f130a7d5b2a1f5e0572441ba51d3b83c792 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=80lex=20Magaz=20Gra=C3=A7a?= Date: Tue, 4 Apr 2017 19:29:56 +0200 Subject: [PATCH 03/45] Fix code inspector issues in files from previous commit --- .../ui/settings/BookManagerFragment.java | 1 - app/src/main/res/layout/cardview_book.xml | 21 ++++++++++++++----- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/org/gnucash/android/ui/settings/BookManagerFragment.java b/app/src/main/java/org/gnucash/android/ui/settings/BookManagerFragment.java index febf3ab27..c78182e8d 100644 --- a/app/src/main/java/org/gnucash/android/ui/settings/BookManagerFragment.java +++ b/app/src/main/java/org/gnucash/android/ui/settings/BookManagerFragment.java @@ -224,7 +224,6 @@ public void onClick(DialogInterface dialog, int which) { deleteBookBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - //// TODO: extract strings AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(getActivity()); dialogBuilder.setTitle(getString(R.string.title_confirm_delete_book)) .setIcon(R.drawable.ic_close_black_24dp) diff --git a/app/src/main/res/layout/cardview_book.xml b/app/src/main/res/layout/cardview_book.xml index 8283bbcc6..f4b021e0c 100644 --- a/app/src/main/res/layout/cardview_book.xml +++ b/app/src/main/res/layout/cardview_book.xml @@ -38,10 +38,13 @@ android:layout_height="wrap_content" android:layout_width="match_parent" android:layout_toRightOf="@id/account_color_strip" + android:layout_toEndOf="@id/account_color_strip" android:layout_marginTop="6dp" android:layout_marginBottom="@dimen/dialog_padding" android:layout_marginLeft="@dimen/dialog_padding" - android:layout_marginRight="52dp"/> + android:layout_marginStart="@dimen/dialog_padding" + android:layout_marginRight="52dp" + android:layout_marginEnd="52dp" /> @@ -62,10 +67,13 @@ android:layout_width="48dp" android:layout_height="48dp" android:layout_alignParentRight="true" + android:layout_alignParentEnd="true" android:background="?attr/selectableItemBackgroundBorderless" android:paddingTop="6dp" android:paddingRight="6dp" + android:paddingEnd="6dp" android:paddingLeft="22dp" + android:paddingStart="22dp" android:paddingBottom="22dp" android:src="@drawable/ic_clear_black_24dp" tools:ignore="ContentDescription" /> @@ -74,27 +82,30 @@ android:id="@+id/label_last_sync" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:singleLine="true" android:layout_below="@id/list_item_2_lines" android:layout_alignLeft="@id/list_item_2_lines" + android:layout_alignStart="@id/list_item_2_lines" android:layout_marginBottom="4dp" android:gravity="left|bottom|start" android:textSize="14sp" android:textColor="@android:color/darker_gray" - tools:text="Last export:"/> + tools:text="Last export:" + android:maxLines="1" /> + tools:text="Sat, 04 July" + android:maxLines="1" /> \ No newline at end of file From 01e98602891dbadb0a04425e940bed9a8ecd2b4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=80lex=20Magaz=20Gra=C3=A7a?= Date: Tue, 4 Apr 2017 20:06:25 +0200 Subject: [PATCH 04/45] Extract some methods from BooksCursorAdapter.bindView The method had become too large. --- .../ui/settings/BookManagerFragment.java | 111 ++++++++++-------- 1 file changed, 61 insertions(+), 50 deletions(-) diff --git a/app/src/main/java/org/gnucash/android/ui/settings/BookManagerFragment.java b/app/src/main/java/org/gnucash/android/ui/settings/BookManagerFragment.java index c78182e8d..6bbaa1922 100644 --- a/app/src/main/java/org/gnucash/android/ui/settings/BookManagerFragment.java +++ b/app/src/main/java/org/gnucash/android/ui/settings/BookManagerFragment.java @@ -165,57 +165,21 @@ public void bindView(View view, final Context context, Cursor cursor) { setLastExportedText(view, bookUID); setStatisticsText(view, bookUID); + setUpMenu(view, context, cursor, bookUID); + setUpDeleteButton(view, context, bookUID); - final String bookName = cursor.getString(cursor.getColumnIndexOrThrow(BookEntry.COLUMN_DISPLAY_NAME)); - ImageView optionsMenu = (ImageView) view.findViewById(R.id.options_menu); - optionsMenu.setOnClickListener(new View.OnClickListener() { + view.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - PopupMenu popupMenu = new PopupMenu(context, v); - MenuInflater menuInflater = popupMenu.getMenuInflater(); - menuInflater.inflate(R.menu.book_context_menu, popupMenu.getMenu()); - popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { - @Override - public boolean onMenuItemClick(MenuItem item) { - switch (item.getItemId()){ - case R.id.ctx_menu_rename_book: - final EditText nameEditText = new EditText(context); - nameEditText.setText(bookName); - - AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(getActivity()); - dialogBuilder.setTitle(R.string.title_rename_book) - .setView(nameEditText) - .setPositiveButton(R.string.btn_rename, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - BooksDbAdapter.getInstance() - .updateRecord(bookUID, - BookEntry.COLUMN_DISPLAY_NAME, - nameEditText.getText().toString()); - refresh(); - } - }) - .setNegativeButton(R.string.btn_cancel, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - dialog.dismiss(); - } - }); - AlertDialog dialog = dialogBuilder.create(); - dialog.show(); - return true; - case R.id.ctx_menu_sync_book: - //TODO implement sync - return false; - default: - return true; - } - } - }); - popupMenu.show(); + //do nothing if the active book is tapped + if (!BooksDbAdapter.getInstance().getActiveBookUID().equals(bookUID)) { + GnuCashApplication.loadBook(bookUID); + } } }); + } + private void setUpDeleteButton(View view, final Context context, final String bookUID) { ImageView deleteBookBtn = (ImageView) view.findViewById(R.id.delete_book); String activeBookUID = BooksDbAdapter.getInstance().getActiveBookUID(); if (activeBookUID.equals(bookUID)) //we cannot delete the active book @@ -248,18 +212,65 @@ public void onClick(DialogInterface dialog, int which) { } }); } + } - view.setOnClickListener(new View.OnClickListener() { + private void setUpMenu(View view, final Context context, Cursor cursor, final String bookUID) { + final String bookName = cursor.getString( + cursor.getColumnIndexOrThrow(BookEntry.COLUMN_DISPLAY_NAME)); + ImageView optionsMenu = (ImageView) view.findViewById(R.id.options_menu); + optionsMenu.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - //do nothing if the active book is tapped - if (!BooksDbAdapter.getInstance().getActiveBookUID().equals(bookUID)) { - GnuCashApplication.loadBook(bookUID); - } + PopupMenu popupMenu = new PopupMenu(context, v); + MenuInflater menuInflater = popupMenu.getMenuInflater(); + menuInflater.inflate(R.menu.book_context_menu, popupMenu.getMenu()); + popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(MenuItem item) { + switch (item.getItemId()){ + case R.id.ctx_menu_rename_book: + return handleMenuRenameBook(context, bookName, bookUID); + case R.id.ctx_menu_sync_book: + //TODO implement sync + return false; + default: + return true; + } + } + }); + popupMenu.show(); } }); } + private boolean handleMenuRenameBook(Context context, String bookName, final String bookUID) { + final EditText nameEditText = new EditText(context); + nameEditText.setText(bookName); + + AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(getActivity()); + dialogBuilder.setTitle(R.string.title_rename_book) + .setView(nameEditText) + .setPositiveButton(R.string.btn_rename, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + BooksDbAdapter.getInstance() + .updateRecord(bookUID, + BookEntry.COLUMN_DISPLAY_NAME, + nameEditText.getText().toString()); + refresh(); + } + }) + .setNegativeButton(R.string.btn_cancel, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + } + }); + AlertDialog dialog = dialogBuilder.create(); + dialog.show(); + return true; + } + private void setLastExportedText(View view, String bookUID) { TextView labelLastSync = (TextView) view.findViewById(R.id.label_last_sync); labelLastSync.setText(R.string.label_last_export_time); From b263117d221538510e9d3f026727964c7fee9894 Mon Sep 17 00:00:00 2001 From: Ngewi Fet Date: Thu, 6 Apr 2017 09:28:23 +0200 Subject: [PATCH 05/45] Raise minimum Android API level to 19 (KitKat) Update dependencies --- CHANGELOG.md | 3 +++ app/build.gradle | 25 ++++++++++++------------ build.gradle | 4 ++-- gradle/wrapper/gradle-wrapper.properties | 4 ++-- 4 files changed, 20 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 95c4b2c0d..fde06c2ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ Change Log =============================================================================== +Version 2.2.0 *(2017-05-xx)* +---------------------------- + Version 2.1.5 *(2017-04-04)* ---------------------------- * Fixed: Widget button for placeholder accounts tries to create transactions diff --git a/app/build.gradle b/app/build.gradle index 399b2c261..2bf392626 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -5,9 +5,9 @@ apply plugin: 'io.fabric' apply plugin: 'android-apt' def versionMajor = 2 -def versionMinor = 1 -def versionPatch = 5 -def versionBuild = 4 +def versionMinor = 2 +def versionPatch = 0 +def versionBuild = 0 def buildTime() { def df = new SimpleDateFormat("yyyyMMdd HH:mm 'UTC'") @@ -22,11 +22,11 @@ def gitSha() { android { compileSdkVersion 24 - buildToolsVersion '24.0.3' + buildToolsVersion '25.0.0' defaultConfig { applicationId "org.gnucash.android" testApplicationId 'org.gnucash.android.test' - minSdkVersion 10 + minSdkVersion 19 targetSdkVersion 23 versionCode versionMajor * 10000 + versionMinor * 1000 + versionPatch * 100 + versionBuild versionName "${versionMajor}.${versionMinor}.${versionPatch}" @@ -190,7 +190,7 @@ android.productFlavors.all { flavour -> } -def androidSupportVersion = "24.2.1" +def androidSupportVersion = "25.3.1" def androidEspressoVersion = "2.2.2" def androidSupportTestVersion = "0.5" @@ -209,23 +209,24 @@ dependencies { 'com.android.support:cardview-v7:' + androidSupportVersion, 'com.android.support:preference-v7:' + androidSupportVersion, 'com.android.support:recyclerview-v7:' + androidSupportVersion, - 'com.code-troopers.betterpickers:library:3.0.1', + '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.0', + '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:2.1.2', - 'com.facebook.stetho:stetho:1.4.1', 'com.android.support:multidex:1.0.1' ) - compile 'com.jakewharton:butterknife:8.4.0' - apt 'com.jakewharton:butterknife-compiler:8.4.0' + debugCompile 'com.facebook.stetho:stetho:1.4.2' + + compile 'com.jakewharton:butterknife:8.5.1' + apt 'com.jakewharton:butterknife-compiler:8.5.1' compile ('com.uservoice:uservoice-android-sdk:1.2.5') { exclude module: 'commons-logging' @@ -233,7 +234,7 @@ dependencies { exclude module: 'httpclient' } - compile('com.crashlytics.sdk.android:crashlytics:2.5.2@aar') { + compile('com.crashlytics.sdk.android:crashlytics:2.6.7@aar') { transitive = true; } diff --git a/build.gradle b/build.gradle index 8c78a1122..2073d4b2e 100644 --- a/build.gradle +++ b/build.gradle @@ -4,8 +4,8 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.2.2' - classpath 'io.fabric.tools:gradle:1.21.2' + classpath 'com.android.tools.build:gradle:2.3.1' + classpath 'io.fabric.tools:gradle:1.21.6' classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' classpath 'com.stanfy.spoon:spoon-gradle-plugin:1.2.2' } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 2621b5a1c..7e2bdc2ed 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Tue Oct 18 19:01:00 CEST 2016 +#Tue Apr 04 18:07:15 CEST 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-3.4.1-all.zip From a0351bc0e23081b63a614e453f2e7ef03db08010 Mon Sep 17 00:00:00 2001 From: Ngewi Fet Date: Thu, 6 Apr 2017 09:45:27 +0200 Subject: [PATCH 06/45] Update Travis build configuration --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 1b304c2bd..864c60a37 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,7 @@ android: - platform-tools - tools - tools #not a typo. Needed for SDK update - - build-tools-24.0.3 + - build-tools-25.0.0 # The SDK version used to compile your project - android-24 From d1979b796f5aef705419b45edb9ff281a3ceb433 Mon Sep 17 00:00:00 2001 From: Ngewi Fet Date: Thu, 6 Apr 2017 10:31:42 +0200 Subject: [PATCH 07/45] Include Stetho dependencies only for debug builds Fix broken build on Travis --- .../org/gnucash/android/app/StethoUtils.java | 39 +++++++++++++++++++ .../android/app/GnuCashApplication.java | 26 +------------ .../org/gnucash/android/app/StethoUtils.java | 15 +++++++ 3 files changed, 55 insertions(+), 25 deletions(-) create mode 100644 app/src/debug/java/org/gnucash/android/app/StethoUtils.java create mode 100644 app/src/release/java/org/gnucash/android/app/StethoUtils.java diff --git a/app/src/debug/java/org/gnucash/android/app/StethoUtils.java b/app/src/debug/java/org/gnucash/android/app/StethoUtils.java new file mode 100644 index 000000000..f3d87f5eb --- /dev/null +++ b/app/src/debug/java/org/gnucash/android/app/StethoUtils.java @@ -0,0 +1,39 @@ +package org.gnucash.android.app; + +import android.app.Application; +import android.os.Build; + +import com.facebook.stetho.Stetho; + +import org.gnucash.android.BuildConfig; + +/** + * Utility class for initializing Stetho in debug builds + */ + +public class StethoUtils { + + /** + * Sets up Stetho to enable remote debugging from Chrome developer tools. + * + *

Among other things, allows access to the database and preferences. + * See http://facebook.github.io/stetho/#features

+ */ + public static void install(Application application){ + //don't initialize stetho during tests + if (!BuildConfig.DEBUG || isRoboUnitTest()) + return; + + Stetho.initialize(Stetho.newInitializerBuilder(application) + .enableWebKitInspector(Stetho.defaultInspectorModulesProvider(application)) + .build()); + } + + /** + * Returns {@code true} if the app is being run by robolectric + * @return {@code true} if in unit testing, {@code false} otherwise + */ + private static boolean isRoboUnitTest(){ + return "robolectric".equals(Build.FINGERPRINT); + } +} 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 394926963..d67d6e145 100644 --- a/app/src/main/java/org/gnucash/android/app/GnuCashApplication.java +++ b/app/src/main/java/org/gnucash/android/app/GnuCashApplication.java @@ -33,7 +33,6 @@ import com.crashlytics.android.Crashlytics; import com.crashlytics.android.core.CrashlyticsCore; -import com.facebook.stetho.Stetho; import com.uservoice.uservoicesdk.Config; import com.uservoice.uservoicesdk.UserVoice; @@ -135,8 +134,7 @@ public void onCreate(){ initializeDatabaseAdapters(); setDefaultCurrencyCode(getDefaultCurrencyCode()); - if (BuildConfig.DEBUG && !isRoboUnitTest()) - setUpRemoteDebuggingFromChrome(); + StethoUtils.install(this); } /** @@ -252,14 +250,6 @@ public static boolean isCrashlyticsEnabled(){ return PreferenceManager.getDefaultSharedPreferences(context).getBoolean(context.getString(R.string.key_enable_crashlytics), false); } - /** - * Returns {@code true} if the app is being run by robolectric - * @return {@code true} if in unit testing, {@code false} otherwise - */ - public static boolean isRoboUnitTest(){ - return "robolectric".equals(Build.FINGERPRINT); - } - /** * Returns true if double entry is enabled in the app settings, false otherwise. * If the value is not set, the default value can be specified in the parameters. @@ -388,18 +378,4 @@ private void setUpUserVoice() { UserVoice.init(config, this); } - /** - * Sets up Stetho to enable remote debugging from Chrome developer tools. - * - *

Among other things, allows access to the database and preferences. - * See http://facebook.github.io/stetho/#features

- */ - private void setUpRemoteDebuggingFromChrome() { - Stetho.Initializer initializer = - Stetho.newInitializerBuilder(this) - .enableWebKitInspector( - Stetho.defaultInspectorModulesProvider(this)) - .build(); - Stetho.initialize(initializer); - } } \ No newline at end of file diff --git a/app/src/release/java/org/gnucash/android/app/StethoUtils.java b/app/src/release/java/org/gnucash/android/app/StethoUtils.java new file mode 100644 index 000000000..f732b8acf --- /dev/null +++ b/app/src/release/java/org/gnucash/android/app/StethoUtils.java @@ -0,0 +1,15 @@ +package org.gnucash.android.app; + +import android.app.Application; + +/** + * Dummy utility class for overriding Stetho initializing in release build variants + */ + +public class StethoUtils { + + public static void install(Application application) { + //nothing to see here, move along + //check the debug version of this class to see Stetho init code + } +} From 4a54b20dca967b83dc6095b7d56f717423fe1a4a Mon Sep 17 00:00:00 2001 From: Ngewi Fet Date: Fri, 7 Apr 2017 01:07:05 +0200 Subject: [PATCH 08/45] Move book delete button into options menu of book card Make it harder to inadvertently delete book --- .../android/test/ui/MultiBookTest.java | 26 +++++++ .../ui/settings/BookManagerFragment.java | 68 +++++++++---------- app/src/main/res/layout/cardview_account.xml | 2 +- app/src/main/res/layout/cardview_book.xml | 20 +----- app/src/main/res/menu/book_context_menu.xml | 5 ++ 5 files changed, 64 insertions(+), 57 deletions(-) diff --git a/app/src/androidTest/java/org/gnucash/android/test/ui/MultiBookTest.java b/app/src/androidTest/java/org/gnucash/android/test/ui/MultiBookTest.java index 02809a01a..b5aeef59c 100644 --- a/app/src/androidTest/java/org/gnucash/android/test/ui/MultiBookTest.java +++ b/app/src/androidTest/java/org/gnucash/android/test/ui/MultiBookTest.java @@ -21,6 +21,7 @@ import android.support.test.runner.AndroidJUnit4; import org.gnucash.android.R; +import org.gnucash.android.db.BookDbHelper; import org.gnucash.android.db.adapter.BooksDbAdapter; import org.gnucash.android.model.Book; import org.gnucash.android.test.ui.util.DisableAnimationsRule; @@ -37,10 +38,13 @@ import static android.support.test.espresso.action.ViewActions.swipeUp; import static android.support.test.espresso.assertion.ViewAssertions.matches; import static android.support.test.espresso.intent.matcher.IntentMatchers.hasComponent; +import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant; import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed; import static android.support.test.espresso.matcher.ViewMatchers.withId; +import static android.support.test.espresso.matcher.ViewMatchers.withParent; import static android.support.test.espresso.matcher.ViewMatchers.withText; import static org.assertj.core.api.Assertions.assertThat; +import static org.hamcrest.Matchers.allOf; /** * Test support for multiple books in the application @@ -119,6 +123,28 @@ public void testCreateNewBook(){ assertThat(mBooksDbAdapter.getRecordsCount()).isEqualTo(bookCount+1); } + //TODO: Finish implementation of this test + public void testDeleteBook(){ + long bookCount = mBooksDbAdapter.getRecordsCount(); + + Book book = new Book(); + String displayName = "To Be Deleted"; + book.setDisplayName(displayName); + mBooksDbAdapter.addRecord(book); + + assertThat(mBooksDbAdapter.getRecordsCount()).isEqualTo(bookCount + 1); + + shouldOpenBookManager(); + + onView(allOf(withParent(hasDescendant(withText(displayName))), + withId(R.id.options_menu))).perform(click()); + + onView(withText(R.string.menu_delete)).perform(click()); + onView(withText(R.string.btn_delete_book)).perform(click()); + + assertThat(mBooksDbAdapter.getRecordsCount()).isEqualTo(bookCount); + } + private static void sleep(long millis){ try { Thread.sleep(millis); diff --git a/app/src/main/java/org/gnucash/android/ui/settings/BookManagerFragment.java b/app/src/main/java/org/gnucash/android/ui/settings/BookManagerFragment.java index 6bbaa1922..ec5c556fa 100644 --- a/app/src/main/java/org/gnucash/android/ui/settings/BookManagerFragment.java +++ b/app/src/main/java/org/gnucash/android/ui/settings/BookManagerFragment.java @@ -166,7 +166,6 @@ public void bindView(View view, final Context context, Cursor cursor) { setLastExportedText(view, bookUID); setStatisticsText(view, bookUID); setUpMenu(view, context, cursor, bookUID); - setUpDeleteButton(view, context, bookUID); view.setOnClickListener(new View.OnClickListener() { @Override @@ -179,41 +178,6 @@ public void onClick(View v) { }); } - private void setUpDeleteButton(View view, final Context context, final String bookUID) { - ImageView deleteBookBtn = (ImageView) view.findViewById(R.id.delete_book); - String activeBookUID = BooksDbAdapter.getInstance().getActiveBookUID(); - if (activeBookUID.equals(bookUID)) //we cannot delete the active book - deleteBookBtn.setVisibility(View.GONE); - else { - deleteBookBtn.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(getActivity()); - dialogBuilder.setTitle(getString(R.string.title_confirm_delete_book)) - .setIcon(R.drawable.ic_close_black_24dp) - .setMessage(getString(R.string.msg_all_book_data_will_be_deleted)); - dialogBuilder.setPositiveButton(getString(R.string.btn_delete_book), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - BooksDbAdapter.getInstance().deleteBook(bookUID); - refresh(); - } - }); - dialogBuilder.setNegativeButton(R.string.btn_cancel, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - dialog.dismiss(); - } - }); - AlertDialog dialog = dialogBuilder.create(); - dialog.show(); //must be called before you can access buttons - dialog.getButton(AlertDialog.BUTTON_POSITIVE) - .setTextColor(ContextCompat.getColor(context, R.color.account_red)); - } - }); - } - } - private void setUpMenu(View view, final Context context, Cursor cursor, final String bookUID) { final String bookName = cursor.getString( cursor.getColumnIndexOrThrow(BookEntry.COLUMN_DISPLAY_NAME)); @@ -224,20 +188,50 @@ public void onClick(View v) { PopupMenu popupMenu = new PopupMenu(context, v); MenuInflater menuInflater = popupMenu.getMenuInflater(); menuInflater.inflate(R.menu.book_context_menu, popupMenu.getMenu()); + popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem item) { - switch (item.getItemId()){ + switch (item.getItemId()) { case R.id.ctx_menu_rename_book: return handleMenuRenameBook(context, bookName, bookUID); case R.id.ctx_menu_sync_book: //TODO implement sync return false; + case R.id.ctx_menu_delete_book: { + AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(getActivity()); + dialogBuilder.setTitle(getString(R.string.title_confirm_delete_book)) + .setIcon(R.drawable.ic_close_black_24dp) + .setMessage(getString(R.string.msg_all_book_data_will_be_deleted)); + dialogBuilder.setPositiveButton(getString(R.string.btn_delete_book), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + BooksDbAdapter.getInstance().deleteBook(bookUID); + refresh(); + } + }); + dialogBuilder.setNegativeButton(R.string.btn_cancel, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + } + }); + AlertDialog dialog = dialogBuilder.create(); + dialog.show(); //must be called before you can access buttons + dialog.getButton(AlertDialog.BUTTON_POSITIVE) + .setTextColor(ContextCompat.getColor(context, R.color.account_red)); + } + return true; default: return true; } } }); + + String activeBookUID = BooksDbAdapter.getInstance().getActiveBookUID(); + if (activeBookUID.equals(bookUID)) {//we cannot delete the active book + popupMenu.getMenu().findItem(R.id.ctx_menu_delete_book).setEnabled(false); + } popupMenu.show(); } }); diff --git a/app/src/main/res/layout/cardview_account.xml b/app/src/main/res/layout/cardview_account.xml index 7eb1ae8e6..cfae37339 100644 --- a/app/src/main/res/layout/cardview_account.xml +++ b/app/src/main/res/layout/cardview_account.xml @@ -95,7 +95,7 @@ android:id="@+id/account_balance" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:singleLine="true" + android:maxLines="1" android:layout_alignParentBottom="true" android:paddingBottom="4dp" android:textSize="18sp" diff --git a/app/src/main/res/layout/cardview_book.xml b/app/src/main/res/layout/cardview_book.xml index f4b021e0c..e431019f5 100644 --- a/app/src/main/res/layout/cardview_book.xml +++ b/app/src/main/res/layout/cardview_book.xml @@ -52,32 +52,14 @@ android:layout_height="48dp" android:layout_alignParentBottom="true" android:layout_alignParentRight="true" - android:layout_alignParentEnd="true" android:background="?attr/selectableItemBackgroundBorderless" android:paddingTop="20dp" - android:layout_marginBottom="4dp" + android:paddingBottom="4dp" android:paddingLeft="24dp" - android:paddingStart="24dp" android:scaleType="centerInside" android:src="@drawable/ic_more_vert_black_24dp" tools:ignore="ContentDescription" /> - - + + \ No newline at end of file From 3f4fba0aeaf9145065e9bed0d9af6d5ff71dc1e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=80lex=20Magaz=20Gra=C3=A7a?= Date: Wed, 12 Apr 2017 17:46:26 +0200 Subject: [PATCH 09/45] Use Commodity instead of Currency in TransferFundsDialogFragment Commodity should be used in place of Currency, as it's a superset. This is just a step in the migration process. --- .../dialog/TransferFundsDialogFragment.java | 24 ++++++++----------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/org/gnucash/android/ui/transaction/dialog/TransferFundsDialogFragment.java b/app/src/main/java/org/gnucash/android/ui/transaction/dialog/TransferFundsDialogFragment.java index 234dd892c..55bccd9c8 100644 --- a/app/src/main/java/org/gnucash/android/ui/transaction/dialog/TransferFundsDialogFragment.java +++ b/app/src/main/java/org/gnucash/android/ui/transaction/dialog/TransferFundsDialogFragment.java @@ -48,7 +48,6 @@ import java.text.DecimalFormat; import java.text.NumberFormat; import java.text.ParseException; -import java.util.Currency; import butterknife.BindView; import butterknife.ButterKnife; @@ -78,7 +77,7 @@ public class TransferFundsDialogFragment extends DialogFragment { @BindView(R.id.btn_save) Button mSaveButton; @BindView(R.id.btn_cancel) Button mCancelButton; Money mOriginAmount; - String mTargetCurrencyCode; + private Commodity mTargetCommodity; Money mConvertedAmount; OnTransferFundsListener mOnTransferFundsListener; @@ -87,7 +86,7 @@ public static TransferFundsDialogFragment getInstance(Money transactionAmount, S OnTransferFundsListener transferFundsListener){ TransferFundsDialogFragment fragment = new TransferFundsDialogFragment(); fragment.mOriginAmount = transactionAmount; - fragment.mTargetCurrencyCode = Currency.getInstance(targetCurrencyCode).getCurrencyCode(); + fragment.mTargetCommodity = CommoditiesDbAdapter.getInstance().getCommodity(targetCurrencyCode); fragment.mOnTransferFundsListener = transferFundsListener; return fragment; } @@ -101,18 +100,17 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa TransactionsActivity.displayBalance(mStartAmountLabel, mOriginAmount); String fromCurrencyCode = mOriginAmount.getCommodity().getCurrencyCode(); mFromCurrencyLabel.setText(fromCurrencyCode); - mToCurrencyLabel.setText(mTargetCurrencyCode); - mConvertedAmountCurrencyLabel.setText(mTargetCurrencyCode); + mToCurrencyLabel.setText(mTargetCommodity.getCurrencyCode()); + mConvertedAmountCurrencyLabel.setText(mTargetCommodity.getCurrencyCode()); mSampleExchangeRate.setText(String.format(getString(R.string.sample_exchange_rate), fromCurrencyCode, - mTargetCurrencyCode)); + mTargetCommodity.getCurrencyCode())); final InputLayoutErrorClearer textChangeListener = new InputLayoutErrorClearer(); CommoditiesDbAdapter commoditiesDbAdapter = CommoditiesDbAdapter.getInstance(); String commodityUID = commoditiesDbAdapter.getCommodityUID(fromCurrencyCode); - Commodity currencyCommodity = commoditiesDbAdapter.getCommodity(mTargetCurrencyCode); - String currencyUID = currencyCommodity.getUID(); + String currencyUID = mTargetCommodity.getUID(); PricesDbAdapter pricesDbAdapter = PricesDbAdapter.getInstance(); Pair pricePair = pricesDbAdapter.getPrice(commodityUID, currencyUID); @@ -127,7 +125,7 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa BigDecimal denominator = new BigDecimal(pricePair.second); // convertedAmount = mOriginAmount * numerator / denominator BigDecimal convertedAmount = mOriginAmount.asBigDecimal().multiply(numerator) - .divide(denominator, currencyCommodity.getSmallestFractionDigits(), BigDecimal.ROUND_HALF_EVEN); + .divide(denominator, mTargetCommodity.getSmallestFractionDigits(), BigDecimal.ROUND_HALF_EVEN); DecimalFormat formatter = (DecimalFormat) NumberFormat.getNumberInstance(); mConvertedAmountInput.setText(formatter.format(convertedAmount)); } @@ -197,9 +195,8 @@ public Dialog onCreateDialog(Bundle savedInstanceState) { private void transferFunds() { Price price = null; - CommoditiesDbAdapter commoditiesDbAdapter = CommoditiesDbAdapter.getInstance(); String originCommodityUID = mOriginAmount.getCommodity().getUID(); - String targetCommodityUID = commoditiesDbAdapter.getCommodityUID(mTargetCurrencyCode); + String targetCommodityUID = mTargetCommodity.getUID(); if (mExchangeRateRadioButton.isChecked()) { BigDecimal rate; @@ -211,8 +208,7 @@ private void transferFunds() { } price = new Price(originCommodityUID, targetCommodityUID, rate); - Commodity targetCommodity = Commodity.getInstance(mTargetCurrencyCode); - mConvertedAmount = mOriginAmount.multiply(rate).withCurrency(targetCommodity); + mConvertedAmount = mOriginAmount.multiply(rate).withCurrency(mTargetCommodity); } if (mConvertedAmountRadioButton.isChecked()) { @@ -223,7 +219,7 @@ private void transferFunds() { mConvertedAmountInputLayout.setError(getString(R.string.error_invalid_amount)); return; } - mConvertedAmount = new Money(amount, Commodity.getInstance(mTargetCurrencyCode)); + mConvertedAmount = new Money(amount, mTargetCommodity); price = new Price(originCommodityUID, targetCommodityUID); // fractions cannot be exactly represented by BigDecimal. From f52dbb566f7cead7e6aaac37d00dfc03e93e2518 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=80lex=20Magaz=20Gra=C3=A7a?= Date: Wed, 12 Apr 2017 20:09:11 +0200 Subject: [PATCH 10/45] Fix some code inspector issues in TransactionFormFragment --- .../android/ui/transaction/TransactionFormFragment.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) 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 14115deed..32edecb47 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 @@ -89,7 +89,6 @@ import java.text.ParseException; import java.util.ArrayList; import java.util.Calendar; -import java.util.Currency; import java.util.Date; import java.util.GregorianCalendar; import java.util.List; @@ -273,7 +272,7 @@ private void startTransferFunds() { return; BigDecimal amountBigd = mAmountEditText.getValue(); - if (amountBigd.equals(BigDecimal.ZERO)) + if ((amountBigd == null) || amountBigd.equals(BigDecimal.ZERO)) return; Money amount = new Money(amountBigd, fromCommodity).abs(); @@ -556,7 +555,7 @@ private void initalizeViews() { if (mUseDoubleEntry){ String currentAccountUID = mAccountUID; - long defaultTransferAccountID = 0; + long defaultTransferAccountID; String rootAccountUID = mAccountsDbAdapter.getOrCreateGnuCashRootAccountUID(); do { defaultTransferAccountID = mAccountsDbAdapter.getDefaultTransferAccountID(mAccountsDbAdapter.getID(currentAccountUID)); From 684b0fd36e9d30d6f081497bc461b173ad529793 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=80lex=20Magaz=20Gra=C3=A7a?= Date: Thu, 13 Apr 2017 00:47:54 +0200 Subject: [PATCH 11/45] Remove Currency usage from buildSimpleAccountInstance --- .../org/gnucash/android/db/adapter/AccountsDbAdapter.java | 4 ++-- 1 file changed, 2 insertions(+), 2 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 273dd908b..ad41f097b 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 @@ -440,8 +440,8 @@ private Account buildSimpleAccountInstance(Cursor c) { account.setDescription(description == null ? "" : description); account.setParentUID(c.getString(c.getColumnIndexOrThrow(AccountEntry.COLUMN_PARENT_ACCOUNT_UID))); account.setAccountType(AccountType.valueOf(c.getString(c.getColumnIndexOrThrow(AccountEntry.COLUMN_TYPE)))); - Currency currency = Currency.getInstance(c.getString(c.getColumnIndexOrThrow(AccountEntry.COLUMN_CURRENCY))); - account.setCommodity(mCommoditiesDbAdapter.getCommodity(currency.getCurrencyCode())); + String currencyCode = c.getString(c.getColumnIndexOrThrow(AccountEntry.COLUMN_CURRENCY)); + account.setCommodity(mCommoditiesDbAdapter.getCommodity(currencyCode)); account.setPlaceHolderFlag(c.getInt(c.getColumnIndexOrThrow(AccountEntry.COLUMN_PLACEHOLDER)) == 1); account.setDefaultTransferAccountUID(c.getString(c.getColumnIndexOrThrow(AccountEntry.COLUMN_DEFAULT_TRANSFER_ACCOUNT_UID))); String color = c.getString(c.getColumnIndexOrThrow(AccountEntry.COLUMN_COLOR_CODE)); From 1d63b6ba9c82051af802fa2e2e91d049f89373a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=80lex=20Magaz=20Gra=C3=A7a?= Date: Thu, 13 Apr 2017 01:12:35 +0200 Subject: [PATCH 12/45] Convert getCurrenciesInUse into getCommoditiesInUse --- .../android/db/adapter/AccountsDbAdapter.java | 20 ++++++++++--------- .../android/export/xml/GncXmlExporter.java | 18 ++++++++--------- .../TransactionsPreferenceFragment.java | 8 +++++--- 3 files changed, 25 insertions(+), 21 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 ad41f097b..2ffacd078 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 @@ -1263,25 +1263,27 @@ public static int getActiveAccountColorResource(@NonNull String accountUID) { } /** - * Returns the list of currencies in use in the database. - *

This is not the same as the list of all available commodities

- * @return List of currencies in use + * Returns the list of commodities in use in the database. + * + *

This is not the same as the list of all available commodities.

+ * + * @return List of commodities in use */ - public List getCurrenciesInUse(){ + public List getCommoditiesInUse() { Cursor cursor = mDb.query(true, AccountEntry.TABLE_NAME, new String[]{AccountEntry.COLUMN_CURRENCY}, null, null, null, null, null, null); - List currencyList = new ArrayList<>(); + List commodityList = new ArrayList<>(); try { while (cursor.moveToNext()) { - String currencyCode = cursor.getString(cursor.getColumnIndexOrThrow(AccountEntry.COLUMN_CURRENCY)); - currencyList.add(Currency.getInstance(currencyCode)); + String currencyCode = + cursor.getString(cursor.getColumnIndexOrThrow(AccountEntry.COLUMN_CURRENCY)); + commodityList.add(mCommoditiesDbAdapter.getCommodity(currencyCode)); } } finally { cursor.close(); } - return currencyList; + return commodityList; } - /** * Deletes all accounts, transactions (and their splits) from the database. * Basically empties all 3 tables, so use with care ;) 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 c96420275..49d37d858 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 @@ -617,15 +617,15 @@ private void serializeDate(XmlSerializer xmlSerializer, String tag, long timeMil xmlSerializer.endTag(null, tag); } - private void exportCommodities(XmlSerializer xmlSerializer, List currencies) throws IOException { - for (Currency currency : currencies) { + private void exportCommodities(XmlSerializer xmlSerializer, List commodities) throws IOException { + for (Commodity commodity : commodities) { xmlSerializer.startTag(null, GncXmlHelper.TAG_COMMODITY); xmlSerializer.attribute(null, GncXmlHelper.ATTR_KEY_VERSION, GncXmlHelper.BOOK_VERSION); xmlSerializer.startTag(null, GncXmlHelper.TAG_COMMODITY_SPACE); xmlSerializer.text("ISO4217"); xmlSerializer.endTag(null, GncXmlHelper.TAG_COMMODITY_SPACE); xmlSerializer.startTag(null, GncXmlHelper.TAG_COMMODITY_ID); - xmlSerializer.text(currency.getCurrencyCode()); + xmlSerializer.text(commodity.getCurrencyCode()); xmlSerializer.endTag(null, GncXmlHelper.TAG_COMMODITY_ID); xmlSerializer.endTag(null, GncXmlHelper.TAG_COMMODITY); } @@ -836,15 +836,15 @@ public void generateExport(Writer writer) throws ExporterException { xmlSerializer.text(BaseModel.generateUID()); xmlSerializer.endTag(null, GncXmlHelper.TAG_BOOK_ID); //commodity count - List currencies = mAccountsDbAdapter.getCurrenciesInUse(); - for (int i = 0; i < currencies.size(); i++) { - if (currencies.get(i).getCurrencyCode().equals("XXX")) { - currencies.remove(i); + List commodities = mAccountsDbAdapter.getCommoditiesInUse(); + for (int i = 0; i < commodities.size(); i++) { + if (commodities.get(i).getCurrencyCode().equals("XXX")) { + commodities.remove(i); } } xmlSerializer.startTag(null, GncXmlHelper.TAG_COUNT_DATA); xmlSerializer.attribute(null, GncXmlHelper.ATTR_KEY_CD_TYPE, "commodity"); - xmlSerializer.text(currencies.size() + ""); + xmlSerializer.text(commodities.size() + ""); xmlSerializer.endTag(null, GncXmlHelper.TAG_COUNT_DATA); //account count xmlSerializer.startTag(null, GncXmlHelper.TAG_COUNT_DATA); @@ -865,7 +865,7 @@ public void generateExport(Writer writer) throws ExporterException { xmlSerializer.endTag(null, GncXmlHelper.TAG_COUNT_DATA); } // export the commodities used in the DB - exportCommodities(xmlSerializer, currencies); + exportCommodities(xmlSerializer, commodities); // prices if (priceCount > 0) { exportPrices(xmlSerializer); diff --git a/app/src/main/java/org/gnucash/android/ui/settings/TransactionsPreferenceFragment.java b/app/src/main/java/org/gnucash/android/ui/settings/TransactionsPreferenceFragment.java index b047430cb..a388e2632 100644 --- a/app/src/main/java/org/gnucash/android/ui/settings/TransactionsPreferenceFragment.java +++ b/app/src/main/java/org/gnucash/android/ui/settings/TransactionsPreferenceFragment.java @@ -28,6 +28,8 @@ import org.gnucash.android.db.DatabaseSchema; import org.gnucash.android.db.adapter.AccountsDbAdapter; import org.gnucash.android.db.adapter.BooksDbAdapter; +import org.gnucash.android.db.adapter.CommoditiesDbAdapter; +import org.gnucash.android.model.Commodity; import org.gnucash.android.ui.settings.dialog.DeleteAllTransactionsConfirmationDialog; import java.util.Currency; @@ -122,9 +124,9 @@ public void showDeleteTransactionsDialog(){ private void setImbalanceAccountsHidden(boolean useDoubleEntry) { String isHidden = useDoubleEntry ? "0" : "1"; AccountsDbAdapter accountsDbAdapter = AccountsDbAdapter.getInstance(); - List currencies = accountsDbAdapter.getCurrenciesInUse(); - for (Currency currency : currencies) { - String uid = accountsDbAdapter.getImbalanceAccountUID(currency); + List commodities = accountsDbAdapter.getCommoditiesInUse(); + for (Commodity commodity : commodities) { + String uid = accountsDbAdapter.getImbalanceAccountUID(commodity); if (uid != null){ accountsDbAdapter.updateRecord(uid, DatabaseSchema.AccountEntry.COLUMN_HIDDEN, isHidden); } From 041ede235d404c8b27ab2e752c06890eb3923edb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=80lex=20Magaz=20Gra=C3=A7a?= Date: Thu, 13 Apr 2017 01:20:11 +0200 Subject: [PATCH 13/45] Remove the Currency version of getImbalanceAccountUID --- .../android/test/ui/TransactionsActivityTest.java | 8 ++++---- .../android/db/adapter/AccountsDbAdapter.java | 12 ------------ .../test/unit/db/TransactionsDbAdapterTest.java | 2 +- 3 files changed, 5 insertions(+), 17 deletions(-) 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 aa30d51a4..085ea5c93 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 @@ -365,7 +365,7 @@ public void testAutoBalanceTransactions(){ mTransactionsDbAdapter.deleteAllRecords(); assertThat(mTransactionsDbAdapter.getRecordsCount()).isEqualTo(0); - String imbalanceAcctUID = mAccountsDbAdapter.getImbalanceAccountUID(Currency.getInstance(CURRENCY_CODE)); + String imbalanceAcctUID = mAccountsDbAdapter.getImbalanceAccountUID(Commodity.getInstance(CURRENCY_CODE)); assertThat(imbalanceAcctUID).isNull(); validateTransactionListDisplayed(); @@ -382,7 +382,7 @@ public void testAutoBalanceTransactions(){ assertThat(mTransactionsDbAdapter.getRecordsCount()).isEqualTo(1); Transaction transaction = mTransactionsDbAdapter.getAllTransactions().get(0); assertThat(transaction.getSplits()).hasSize(2); - imbalanceAcctUID = mAccountsDbAdapter.getImbalanceAccountUID(Currency.getInstance(CURRENCY_CODE)); + imbalanceAcctUID = mAccountsDbAdapter.getImbalanceAccountUID(Commodity.getInstance(CURRENCY_CODE)); assertThat(imbalanceAcctUID).isNotNull(); assertThat(imbalanceAcctUID).isNotEmpty(); assertThat(mAccountsDbAdapter.isHiddenAccount(imbalanceAcctUID)).isTrue(); //imbalance account should be hidden in single entry mode @@ -403,7 +403,7 @@ public void testSplitEditor(){ mTransactionsDbAdapter.deleteAllRecords(); //when we start there should be no imbalance account in the system - String imbalanceAcctUID = mAccountsDbAdapter.getImbalanceAccountUID(Currency.getInstance(CURRENCY_CODE)); + String imbalanceAcctUID = mAccountsDbAdapter.getImbalanceAccountUID(Commodity.getInstance(CURRENCY_CODE)); assertThat(imbalanceAcctUID).isNull(); validateTransactionListDisplayed(); @@ -431,7 +431,7 @@ public void testSplitEditor(){ Transaction transaction = transactions.get(0); assertThat(transaction.getSplits()).hasSize(3); //auto-balanced - imbalanceAcctUID = mAccountsDbAdapter.getImbalanceAccountUID(Currency.getInstance(CURRENCY_CODE)); + imbalanceAcctUID = mAccountsDbAdapter.getImbalanceAccountUID(Commodity.getInstance(CURRENCY_CODE)); assertThat(imbalanceAcctUID).isNotNull(); assertThat(imbalanceAcctUID).isNotEmpty(); assertThat(mAccountsDbAdapter.isHiddenAccount(imbalanceAcctUID)).isFalse(); 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 2ffacd078..4e604b3e2 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 @@ -599,18 +599,6 @@ public String getOrCreateImbalanceAccountUID(Commodity commodity){ return uid; } - /** - * Returns the GUID of the imbalance account for the currency - *

This method will not create the imbalance account if it doesn't exist

- * @param currency Currency for the imbalance account - * @return GUID of the account or null if the account doesn't exist yet - * @see #getOrCreateImbalanceAccountUID(Commodity) - */ - public String getImbalanceAccountUID(Currency currency){ - String imbalanceAccountName = getImbalanceAccountName(currency); - return findAccountUidByFullName(imbalanceAccountName); - } - /** * Returns the GUID of the imbalance account for the commodity * diff --git a/app/src/test/java/org/gnucash/android/test/unit/db/TransactionsDbAdapterTest.java b/app/src/test/java/org/gnucash/android/test/unit/db/TransactionsDbAdapterTest.java index 881fc5948..fd98a2268 100644 --- a/app/src/test/java/org/gnucash/android/test/unit/db/TransactionsDbAdapterTest.java +++ b/app/src/test/java/org/gnucash/android/test/unit/db/TransactionsDbAdapterTest.java @@ -119,7 +119,7 @@ public void shouldBalanceTransactionsOnSave(){ Transaction trn = mTransactionsDbAdapter.getRecord(transaction.getUID()); assertThat(trn.getSplits()).hasSize(2); - String imbalanceAccountUID = mAccountsDbAdapter.getImbalanceAccountUID(Currency.getInstance(Money.DEFAULT_CURRENCY_CODE)); + String imbalanceAccountUID = mAccountsDbAdapter.getImbalanceAccountUID(Commodity.getInstance(Money.DEFAULT_CURRENCY_CODE)); assertThat(trn.getSplits()).extracting("mAccountUID").contains(imbalanceAccountUID); } From 59c94fc18f7d519a59881dbf44fbab467c1c7188 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=80lex=20Magaz=20Gra=C3=A7a?= Date: Thu, 13 Apr 2017 01:24:12 +0200 Subject: [PATCH 14/45] Remove the Currency version of getImbalanceAccountName --- .../gnucash/android/db/adapter/AccountsDbAdapter.java | 10 ---------- .../org/gnucash/android/export/qif/QifExporter.java | 3 ++- 2 files changed, 2 insertions(+), 11 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 4e604b3e2..f6b85bd88 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 @@ -43,7 +43,6 @@ import java.math.BigDecimal; import java.sql.Timestamp; import java.util.ArrayList; -import java.util.Currency; import java.util.HashMap; import java.util.LinkedList; import java.util.List; @@ -1184,15 +1183,6 @@ public static String getImbalanceAccountPrefix() { return GnuCashApplication.getAppContext().getString(R.string.imbalance_account_name) + "-"; } - /** - * Returns the imbalance account where to store transactions which are not double entry - * @param currency Currency of the transaction - * @return Imbalance account name - */ - public static String getImbalanceAccountName(Currency currency){ - return getImbalanceAccountPrefix() + currency.getCurrencyCode(); - } - /** * Returns the imbalance account where to store transactions which are not double entry. * 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 0e64df8db..f18445d46 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 @@ -24,6 +24,7 @@ import org.gnucash.android.db.adapter.TransactionsDbAdapter; import org.gnucash.android.export.ExportParams; import org.gnucash.android.export.Exporter; +import org.gnucash.android.model.Commodity; import org.gnucash.android.util.PreferencesHelper; import org.gnucash.android.util.TimestampHelper; @@ -159,7 +160,7 @@ public List generateExport() throws ExporterException { if (decimalImbalance.compareTo(BigDecimal.ZERO) != 0) { writer.append(QifHelper.SPLIT_CATEGORY_PREFIX) .append(AccountsDbAdapter.getImbalanceAccountName( - Currency.getInstance(cursor.getString(cursor.getColumnIndexOrThrow("acct1_currency"))) + Commodity.getInstance(cursor.getString(cursor.getColumnIndexOrThrow("acct1_currency"))) )) .append(newLine); writer.append(QifHelper.SPLIT_AMOUNT_PREFIX) From ae6f81505f63fc1e6831b8f7caeddf4a85bacd30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=80lex=20Magaz=20Gra=C3=A7a?= Date: Thu, 13 Apr 2017 02:01:11 +0200 Subject: [PATCH 15/45] Use Commodity instead of Currency in BudgetAmountEditorFragment Commodity should be used in place of Currency, as it's a superset. This are the last remaining uses of Currency. The uses in GnuCashApplication and MoneyTest are legitimate. --- .../android/ui/budget/BudgetAmountEditorFragment.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/org/gnucash/android/ui/budget/BudgetAmountEditorFragment.java b/app/src/main/java/org/gnucash/android/ui/budget/BudgetAmountEditorFragment.java index a87c40d32..fa6e6dff8 100644 --- a/app/src/main/java/org/gnucash/android/ui/budget/BudgetAmountEditorFragment.java +++ b/app/src/main/java/org/gnucash/android/ui/budget/BudgetAmountEditorFragment.java @@ -49,7 +49,6 @@ import java.math.BigDecimal; import java.util.ArrayList; -import java.util.Currency; import java.util.List; import butterknife.BindView; @@ -250,8 +249,8 @@ public BudgetAmountViewHolder(View view){ @Override public void onItemSelected(AdapterView parent, View view, int position, long id) { String currencyCode = mAccountsDbAdapter.getCurrencyCode(mAccountsDbAdapter.getUID(id)); - Currency currency = Currency.getInstance(currencyCode); - currencySymbolTextView.setText(currency.getSymbol()); + Commodity commodity = Commodity.getInstance(currencyCode); + currencySymbolTextView.setText(commodity.getSymbol()); } @Override From 2001f8c79d798b37a366cc8e0f7168ce65a26622 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=80lex=20Magaz=20Gra=C3=A7a?= Date: Thu, 13 Apr 2017 12:49:37 +0200 Subject: [PATCH 16/45] Start autocompleting transactions with one character The less the user has to type the better. Previously it used the default: two characters. --- app/src/main/res/layout/fragment_transaction_form.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/res/layout/fragment_transaction_form.xml b/app/src/main/res/layout/fragment_transaction_form.xml index 30052c9c1..74622e9e7 100644 --- a/app/src/main/res/layout/fragment_transaction_form.xml +++ b/app/src/main/res/layout/fragment_transaction_form.xml @@ -38,6 +38,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_span="2" + android:completionThreshold="1" android:hint="@string/label_transaction_name" android:inputType="textCapSentences" android:imeOptions="actionNext" From 947cd7dd84dbba610628620c6cd83e9bf3052a14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=80lex=20Magaz=20Gra=C3=A7a?= Date: Fri, 14 Apr 2017 20:26:55 +0200 Subject: [PATCH 17/45] Add Bitcoin support Patch provided by Chris Berkhout. --- app/src/main/res/raw/iso_4217_currencies.xml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/app/src/main/res/raw/iso_4217_currencies.xml b/app/src/main/res/raw/iso_4217_currencies.xml index e4e852f93..1312e8db2 100644 --- a/app/src/main/res/raw/iso_4217_currencies.xml +++ b/app/src/main/res/raw/iso_4217_currencies.xml @@ -2970,6 +2970,19 @@ smallest-fraction="1000000" local-symbol="" /> + + + gnucash_android_backup.gnca From 83c5b92ba1748953c333d51b8291a905c1cea031 Mon Sep 17 00:00:00 2001 From: Ngewi Fet Date: Thu, 20 Apr 2017 13:42:02 +0200 Subject: [PATCH 29/45] Start scheduled service every hour (instead of daily) to better support hourly schedules - related #625 Remove WRITE_EXTERNAL_STORAGE declaration. No longer needed since minimum API level is now 19 --- app/src/main/AndroidManifest.xml | 12 +++++------ .../android/app/GnuCashApplication.java | 3 +-- .../android/ui/common/BaseDrawerActivity.java | 20 +++++++------------ 3 files changed, 13 insertions(+), 22 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 22b5ccc7b..c8857485a 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -33,19 +33,17 @@ android:protectionLevel="dangerous" /> + android:name="org.gnucash.android.permission.RECORD_TRANSACTION" /> - + android:name="org.gnucash.android.permission.CREATE_ACCOUNT" /> + + android:label="Schedule repeating transactions when device is rebooted"/> + android:label="Export and backup to 3rd party hosting services" /> = Build.VERSION_CODES.KITKAT){ - //use the storage access framework - Intent openDocument = new Intent(Intent.ACTION_OPEN_DOCUMENT); - openDocument.addCategory(Intent.CATEGORY_OPENABLE); - openDocument.setType("text/*|application/*"); - String[] mimeTypes = {"text/*", "application/*"}; - openDocument.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes); - startActivityForResult(openDocument, REQUEST_OPEN_DOCUMENT); - - } else { - AccountsActivity.startXmlFileChooser(this); - } + //use the storage access framework + Intent openDocument = new Intent(Intent.ACTION_OPEN_DOCUMENT); + openDocument.addCategory(Intent.CATEGORY_OPENABLE); + openDocument.setType("text/*|application/*"); + String[] mimeTypes = {"text/*", "application/*"}; + openDocument.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes); + startActivityForResult(openDocument, REQUEST_OPEN_DOCUMENT); } break; From acbaaad1c10171eb241c16edba94e4d2c0c1cf40 Mon Sep 17 00:00:00 2001 From: Ngewi Fet Date: Thu, 20 Apr 2017 13:50:31 +0200 Subject: [PATCH 30/45] Refactored book related operations into utility class --- .../android/test/ui/PieChartReportTest.java | 3 ++- .../android/app/GnuCashApplication.java | 21 +------------------ .../android/importer/ImportAsyncTask.java | 4 ++-- .../android/ui/common/BaseDrawerActivity.java | 5 +++-- .../android/ui/common/FormActivity.java | 3 ++- .../ui/settings/BookManagerFragment.java | 3 ++- .../org/gnucash/android/util/BookUtils.java | 21 +++++++++++++++++++ .../service/ScheduledActionServiceTest.java | 3 ++- 8 files changed, 35 insertions(+), 28 deletions(-) diff --git a/app/src/androidTest/java/org/gnucash/android/test/ui/PieChartReportTest.java b/app/src/androidTest/java/org/gnucash/android/test/ui/PieChartReportTest.java index b0b1a15c4..df2e367ad 100644 --- a/app/src/androidTest/java/org/gnucash/android/test/ui/PieChartReportTest.java +++ b/app/src/androidTest/java/org/gnucash/android/test/ui/PieChartReportTest.java @@ -45,6 +45,7 @@ import org.gnucash.android.ui.report.ReportsActivity; import org.gnucash.android.ui.report.piechart.PieChartFragment; import org.gnucash.android.ui.settings.PreferenceActivity; +import org.gnucash.android.util.BookUtils; import org.joda.time.LocalDateTime; import org.junit.After; import org.junit.AfterClass; @@ -118,7 +119,7 @@ public static void prepareTestCase() throws Exception { oldActiveBookUID = BooksDbAdapter.getInstance().getActiveBookUID(); testBookUID = GncXmlImporter.parse(context.getResources().openRawResource(R.raw.default_accounts)); - GnuCashApplication.loadBook(testBookUID); + BookUtils.loadBook(testBookUID); mTransactionsDbAdapter = TransactionsDbAdapter.getInstance(); mAccountsDbAdapter = AccountsDbAdapter.getInstance(); 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 3365fa76e..58a3c0b54 100644 --- a/app/src/main/java/org/gnucash/android/app/GnuCashApplication.java +++ b/app/src/main/java/org/gnucash/android/app/GnuCashApplication.java @@ -53,7 +53,6 @@ import org.gnucash.android.model.Commodity; import org.gnucash.android.model.Money; import org.gnucash.android.service.ScheduledActionService; -import org.gnucash.android.ui.account.AccountsActivity; import org.gnucash.android.ui.settings.PreferenceActivity; import java.util.Currency; @@ -141,7 +140,7 @@ public void onCreate(){ * Initialize database adapter singletons for use in the application * This method should be called every time a new book is opened */ - private static void initializeDatabaseAdapters() { + public static void initializeDatabaseAdapters() { if (mDbHelper != null){ //close if open mDbHelper.getReadableDatabase().close(); } @@ -208,24 +207,6 @@ public static BooksDbAdapter getBooksDbAdapter(){ return mBooksDbAdapter; } - /** - * Loads the book with GUID {@code bookUID} and opens the AccountsActivity - * @param bookUID GUID of the book to be loaded - */ - public static void loadBook(@NonNull String bookUID){ - activateBook(bookUID); - AccountsActivity.start(getAppContext()); - } - - /** - * Activates the book with unique identifer {@code bookUID}, and refreshes the database adapters - * @param bookUID GUID of the book to be activated - */ - public static void activateBook(@NonNull String bookUID){ - mBooksDbAdapter.setActive(bookUID); - initializeDatabaseAdapters(); - } - /** * Returns the currently active database in the application * @return Currently active {@link SQLiteDatabase} 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 b8fdd6022..92570468f 100644 --- a/app/src/main/java/org/gnucash/android/importer/ImportAsyncTask.java +++ b/app/src/main/java/org/gnucash/android/importer/ImportAsyncTask.java @@ -31,10 +31,10 @@ 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.ui.util.TaskDelegate; +import org.gnucash.android.util.BookUtils; import java.io.InputStream; @@ -138,7 +138,7 @@ protected void onPostExecute(Boolean importSuccess) { Toast.makeText(mContext, message, Toast.LENGTH_SHORT).show(); if (mImportedBookUID != null) - GnuCashApplication.loadBook(mImportedBookUID); + BookUtils.loadBook(mImportedBookUID); if (mDelegate != null) mDelegate.onTaskComplete(); 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 5cf93cd58..83c409d95 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 @@ -49,6 +49,7 @@ import org.gnucash.android.ui.report.ReportsActivity; import org.gnucash.android.ui.settings.PreferenceActivity; import org.gnucash.android.ui.transaction.ScheduledActionsActivity; +import org.gnucash.android.util.BookUtils; import butterknife.BindView; import butterknife.ButterKnife; @@ -104,7 +105,7 @@ protected void onCreate(Bundle savedInstanceState) { //if a parameter was passed to open an account within a specific book, then switch String bookUID = getIntent().getStringExtra(UxArgument.BOOK_UID); if (bookUID != null && !bookUID.equals(BooksDbAdapter.getInstance().getActiveBookUID())){ - GnuCashApplication.activateBook(bookUID); + BookUtils.activateBook(bookUID); } ButterKnife.bind(this); @@ -324,7 +325,7 @@ public boolean onMenuItemClick(MenuItem item) { BooksDbAdapter booksDbAdapter = BooksDbAdapter.getInstance(); String bookUID = booksDbAdapter.getUID(id); if (!bookUID.equals(booksDbAdapter.getActiveBookUID())){ - GnuCashApplication.loadBook(bookUID); + BookUtils.loadBook(bookUID); finish(); } AccountsActivity.start(GnuCashApplication.getAppContext()); diff --git a/app/src/main/java/org/gnucash/android/ui/common/FormActivity.java b/app/src/main/java/org/gnucash/android/ui/common/FormActivity.java index 6d777f5f4..ba94de940 100644 --- a/app/src/main/java/org/gnucash/android/ui/common/FormActivity.java +++ b/app/src/main/java/org/gnucash/android/ui/common/FormActivity.java @@ -38,6 +38,7 @@ import org.gnucash.android.ui.transaction.SplitEditorFragment; import org.gnucash.android.ui.transaction.TransactionFormFragment; import org.gnucash.android.ui.util.widget.CalculatorKeyboard; +import org.gnucash.android.util.BookUtils; /** * Activity for displaying forms in the application. @@ -61,7 +62,7 @@ protected void onCreate(Bundle savedInstanceState) { //if a parameter was passed to open an account within a specific book, then switch String bookUID = getIntent().getStringExtra(UxArgument.BOOK_UID); if (bookUID != null && !bookUID.equals(BooksDbAdapter.getInstance().getActiveBookUID())){ - GnuCashApplication.activateBook(bookUID); + BookUtils.activateBook(bookUID); } Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); diff --git a/app/src/main/java/org/gnucash/android/ui/settings/BookManagerFragment.java b/app/src/main/java/org/gnucash/android/ui/settings/BookManagerFragment.java index ec5c556fa..08d0e3987 100644 --- a/app/src/main/java/org/gnucash/android/ui/settings/BookManagerFragment.java +++ b/app/src/main/java/org/gnucash/android/ui/settings/BookManagerFragment.java @@ -54,6 +54,7 @@ import org.gnucash.android.db.adapter.TransactionsDbAdapter; import org.gnucash.android.ui.account.AccountsActivity; import org.gnucash.android.ui.common.Refreshable; +import org.gnucash.android.util.BookUtils; import org.gnucash.android.util.PreferencesHelper; import java.sql.Timestamp; @@ -172,7 +173,7 @@ public void bindView(View view, final Context context, Cursor cursor) { public void onClick(View v) { //do nothing if the active book is tapped if (!BooksDbAdapter.getInstance().getActiveBookUID().equals(bookUID)) { - GnuCashApplication.loadBook(bookUID); + BookUtils.loadBook(bookUID); } } }); 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 92e258e9c..67bf5f4dd 100644 --- a/app/src/main/java/org/gnucash/android/util/BookUtils.java +++ b/app/src/main/java/org/gnucash/android/util/BookUtils.java @@ -1,8 +1,11 @@ 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; /** @@ -22,4 +25,22 @@ 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 + * @param bookUID GUID of the book to be activated + */ + public static void activateBook(@NonNull String bookUID){ + GnuCashApplication.getBooksDbAdapter().setActive(bookUID); + GnuCashApplication.initializeDatabaseAdapters(); + } + + /** + * Loads the book with GUID {@code bookUID} and opens the AccountsActivity + * @param bookUID GUID of the book to be loaded + */ + public static void loadBook(@NonNull String bookUID){ + activateBook(bookUID); + AccountsActivity.start(GnuCashApplication.getAppContext()); + } } 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 094c5234c..78bd9b356 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 @@ -45,6 +45,7 @@ import org.gnucash.android.test.unit.testutil.GnucashTestRunner; import org.gnucash.android.test.unit.testutil.ShadowCrashlytics; import org.gnucash.android.test.unit.testutil.ShadowUserVoice; +import org.gnucash.android.util.BookUtils; import org.gnucash.android.util.TimestampHelper; import org.joda.time.DateTime; import org.joda.time.LocalDateTime; @@ -88,7 +89,7 @@ public class ScheduledActionServiceTest { public void createAccounts(){ try { String bookUID = GncXmlImporter.parse(GnuCashApplication.getAppContext().getResources().openRawResource(R.raw.default_accounts)); - GnuCashApplication.loadBook(bookUID); + BookUtils.loadBook(bookUID); //initAdapters(bookUID); } catch (ParserConfigurationException | SAXException | IOException e) { e.printStackTrace(); From 27aa858a4abaa2af0c3ea23dc9c4333685b1836e Mon Sep 17 00:00:00 2001 From: Ngewi Fet Date: Thu, 20 Apr 2017 16:20:20 +0200 Subject: [PATCH 31/45] Remove obsolete tests (due to use of SAF for exports/backups) Display export file URI in export dialog after user chooses Highlight name of active book in book manager Re-introduced WRITE_EXTERNAL_STORAGE permission (but only requested for devices up to API level 18. Just in case someone takes the source to build and lowers the API level for whatever reason). --- app/build.gradle | 1 + .../test/ui/ExportTransactionsTest.java | 136 +----------------- .../android/test/ui/OwnCloudExportTest.java | 8 +- app/src/main/AndroidManifest.xml | 2 + .../android/ui/export/ExportFormFragment.java | 12 +- .../ui/settings/BookManagerFragment.java | 4 + .../settings/GeneralPreferenceFragment.java | 6 - .../main/res/layout/fragment_export_form.xml | 10 +- app/src/main/res/values/strings.xml | 5 +- .../res/xml/fragment_general_preferences.xml | 3 +- .../test/unit/export/OfxExporterTest.java | 42 ++++++ .../test/unit/export/QifExporterTest.java | 76 ++++++++++ 12 files changed, 155 insertions(+), 150 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 2bf392626..cc3d9d781 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -167,6 +167,7 @@ afterEvaluate { spoon { debug = true grantAllPermissions = true + codeCoverage = true } initCrashlyticsPropertiesIfNeeded() } diff --git a/app/src/androidTest/java/org/gnucash/android/test/ui/ExportTransactionsTest.java b/app/src/androidTest/java/org/gnucash/android/test/ui/ExportTransactionsTest.java index cba7a8e40..5e7117c3a 100644 --- a/app/src/androidTest/java/org/gnucash/android/test/ui/ExportTransactionsTest.java +++ b/app/src/androidTest/java/org/gnucash/android/test/ui/ExportTransactionsTest.java @@ -22,8 +22,10 @@ import android.content.pm.PackageManager; import android.database.SQLException; import android.database.sqlite.SQLiteDatabase; +import android.net.Uri; import android.os.Build; import android.support.test.InstrumentationRegistry; +import android.support.test.espresso.Espresso; import android.support.test.espresso.contrib.DrawerActions; import android.support.test.espresso.matcher.ViewMatchers; import android.support.test.runner.AndroidJUnit4; @@ -53,6 +55,7 @@ import org.gnucash.android.model.Transaction; import org.gnucash.android.ui.account.AccountsActivity; import org.gnucash.android.ui.settings.PreferenceActivity; +import org.gnucash.android.util.BookUtils; import org.junit.After; import org.junit.Before; import org.junit.FixMethodOrder; @@ -139,139 +142,6 @@ public void setUp() throws Exception { mAccountsDbAdapter.addRecord(account, DatabaseAdapter.UpdateMethod.insert); } - - /** - * Tests the export of an OFX file with the transactions from the application. - * The exported file name contains a timestamp with minute precision. - * If this test fails, it may be due to the file being created and tested in different minutes of the clock - * Just try rerunning it again. - */ - @Test - public void testOfxExport(){ - SharedPreferences.Editor prefsEditor = PreferenceActivity.getActiveBookSharedPreferences() - .edit(); - prefsEditor.putBoolean(mAcccountsActivity.getString(R.string.key_use_double_entry), false) - .commit(); - testExport(ExportFormat.OFX); - prefsEditor.putBoolean(mAcccountsActivity.getString(R.string.key_use_double_entry), true) - .commit(); - } - - @Test - public void whenInSingleEntry_shouldHideXmlExportOption(){ - SharedPreferences.Editor prefsEditor = PreferenceActivity.getActiveBookSharedPreferences() - .edit(); - prefsEditor.putBoolean(mAcccountsActivity.getString(R.string.key_use_double_entry), false) - .commit(); - - DrawerActions.openDrawer(R.id.drawer_layout); - onView(withText(R.string.nav_menu_export)).perform(click()); - onView(withId(R.id.radio_xml_format)).check(matches(withEffectiveVisibility(ViewMatchers.Visibility.GONE))); - - prefsEditor.putBoolean(mAcccountsActivity.getString(R.string.key_use_double_entry), true) - .commit(); - } - - /** - * Test the export of transactions in the QIF format - */ - @Test - public void testQifExport(){ - testExport(ExportFormat.QIF); - } - - @Test - public void testXmlExport(){ - testExport(ExportFormat.XML); - } - - /** - * Generates export for the specified format and tests that the file actually is created - * @param format Export format to use - */ - public void testExport(ExportFormat format){ - if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - if (mAcccountsActivity.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) - != PackageManager.PERMISSION_GRANTED) { - mAcccountsActivity.requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, - Manifest.permission.READ_EXTERNAL_STORAGE}, 0x23); - - onView(withId(AlertDialog.BUTTON_POSITIVE)).perform(click()); - } - } - - File folder = new File(Exporter.getExportFolderPath(BooksDbAdapter.getInstance().getActiveBookUID())); - folder.mkdirs(); - assertThat(folder).exists(); - - for (File file : folder.listFiles()) { - file.delete(); - } - - onView(withId(R.id.drawer_layout)).perform(DrawerActions.open()); - onView(withText(R.string.nav_menu_export)).perform(click()); - - onView(withId(R.id.spinner_export_destination)).perform(click()); - String[] destinations = getActivity().getResources().getStringArray(R.array.export_destinations); - - onView(withText(destinations[0])).perform(click()); - onView(withText(format.name())).perform(click()); - - onView(withId(R.id.menu_save)).perform(click()); - - assertThat(folder.listFiles().length).isEqualTo(1); - File exportFile = folder.listFiles()[0]; - assertThat(exportFile.getName()).endsWith(format.getExtension()); - } - - @Test - public void testDeleteTransactionsAfterExport(){ - assertThat(mTransactionsDbAdapter.getRecordsCount()).isGreaterThan(0); - - SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(getActivity()).edit(); //PreferenceActivity.getActiveBookSharedPreferences(getActivity()).edit(); - editor.putBoolean(mAcccountsActivity.getString(R.string.key_delete_transactions_after_export), true); - editor.commit(); - - PreferenceActivity.getActiveBookSharedPreferences() - .edit() - .putBoolean(mAcccountsActivity.getString(R.string.key_use_double_entry), true) - .apply(); - - testExport(ExportFormat.XML); - - assertThat(mTransactionsDbAdapter.getRecordsCount()).isEqualTo(0); - List transactions = mTransactionsDbAdapter.getAllTransactions(); - - editor.putBoolean(mAcccountsActivity.getString(R.string.key_delete_transactions_after_export), false).commit(); - } - - /** - * Test creating a scheduled export - * Does not work on Travis yet - */ - @Test - public void testShouldCreateExportSchedule(){ - onView(withId(R.id.drawer_layout)).perform(DrawerActions.open()); - onView(withText(R.string.nav_menu_export)).perform(click()); - - onView(withText(ExportFormat.XML.name())).perform(click()); - onView(withId(R.id.input_recurrence)).perform(click()); - - //switch on recurrence dialog - onView(allOf(isAssignableFrom(CompoundButton.class), isDisplayed(), isEnabled())).perform(click()); - onView(withText("OK")).perform(click()); - - onView(withId(R.id.menu_save)).perform(click()); - ScheduledActionDbAdapter scheduledactionDbAdapter = ScheduledActionDbAdapter.getInstance(); //new ScheduledActionDbAdapter(mDb, new RecurrenceDbAdapter(mDb)); - List scheduledActions = scheduledactionDbAdapter.getAllEnabledScheduledActions(); - assertThat(scheduledActions) - .hasSize(1) - .extracting("mActionType").contains(ScheduledAction.ActionType.BACKUP); - - ScheduledAction action = scheduledActions.get(0); - assertThat(action.getRecurrence().getPeriodType()).isEqualTo(PeriodType.WEEK); - assertThat(action.getEndTime()).isEqualTo(0); - } @Test public void testCreateBackup(){ diff --git a/app/src/androidTest/java/org/gnucash/android/test/ui/OwnCloudExportTest.java b/app/src/androidTest/java/org/gnucash/android/test/ui/OwnCloudExportTest.java index b37d61171..bda6f16e5 100644 --- a/app/src/androidTest/java/org/gnucash/android/test/ui/OwnCloudExportTest.java +++ b/app/src/androidTest/java/org/gnucash/android/test/ui/OwnCloudExportTest.java @@ -22,6 +22,7 @@ import android.database.sqlite.SQLiteDatabase; import android.net.ConnectivityManager; import android.net.NetworkInfo; +import android.support.test.espresso.Espresso; import android.support.test.espresso.contrib.DrawerActions; import android.support.test.rule.ActivityTestRule; import android.support.test.runner.AndroidJUnit4; @@ -183,20 +184,19 @@ public void OwnCloudCredentials() { assertTrue(mPrefs.getBoolean(mAccountsActivity.getString(R.string.key_owncloud_sync), false)); } - @Test + //// FIXME: 20.04.2017 This test now fails since introduction of SAF. public void OwnCloudExport() { Assume.assumeTrue(hasActiveInternetConnection()); mPrefs.edit().putBoolean(mAccountsActivity.getString(R.string.key_owncloud_sync), true).commit(); onView(withId(R.id.drawer_layout)).perform(DrawerActions.open()); onView(withText(R.string.nav_menu_export)).perform(click()); + Espresso.closeSoftKeyboard(); + Espresso.pressBack(); //close the SAF file picker window onView(withId(R.id.spinner_export_destination)).perform(click()); String[] destinations = mAccountsActivity.getResources().getStringArray(R.array.export_destinations); onView(withText(destinations[3])).perform(click()); onView(withId(R.id.menu_save)).perform(click()); -// onView(withSpinnerText( -// mAccountsActivity.getResources().getStringArray(R.array.export_destinations)[3])) -// .perform(click()); assertToastDisplayed(String.format(mAccountsActivity.getString(R.string.toast_exported_to), "ownCloud -> " + OC_DIR)); } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c8857485a..8ea28438d 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -37,6 +37,8 @@ + 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 c10b5e349..6395cdfc8 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 @@ -113,6 +113,8 @@ public class ExportFormFragment extends Fragment implements */ @BindView(R.id.export_warning) TextView mExportWarningTextView; + @BindView(R.id.target_uri) TextView mTargetUriTextView; + /** * Recurrence text view */ @@ -324,9 +326,13 @@ public void onItemSelected(AdapterView parent, View view, int position, long case 0: mExportTarget = ExportParams.ExportTarget.URI; recurrenceOptionsView.setVisibility(View.VISIBLE); - selectExportFile(); + if (mExportUri != null) + mTargetUriTextView.setText(mExportUri.toString()); + else + selectExportFile(); break; - case 1: + case 1: //DROPBOX + mTargetUriTextView.setText("Export to /Apps/GnuCash folder on Dropbox"); recurrenceOptionsView.setVisibility(View.VISIBLE); mExportTarget = ExportParams.ExportTarget.DROPBOX; String dropboxAppKey = getString(R.string.dropbox_app_key, BackupPreferenceFragment.DROPBOX_APP_KEY); @@ -337,6 +343,7 @@ public void onItemSelected(AdapterView parent, View view, int position, long } break; case 2: + mTargetUriTextView.setText(""); recurrenceOptionsView.setVisibility(View.VISIBLE); mExportTarget = ExportParams.ExportTarget.OWNCLOUD; if(!(PreferenceManager.getDefaultSharedPreferences(getActivity()) @@ -518,6 +525,7 @@ public void onActivityResult(int requestCode, int resultCode, Intent data) { & (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); getActivity().getContentResolver().takePersistableUriPermission(mExportUri, takeFlags); + mTargetUriTextView.setText(mExportUri.toString()); if (mExportStarted) startExport(); diff --git a/app/src/main/java/org/gnucash/android/ui/settings/BookManagerFragment.java b/app/src/main/java/org/gnucash/android/ui/settings/BookManagerFragment.java index 08d0e3987..ba850849e 100644 --- a/app/src/main/java/org/gnucash/android/ui/settings/BookManagerFragment.java +++ b/app/src/main/java/org/gnucash/android/ui/settings/BookManagerFragment.java @@ -291,6 +291,10 @@ private void setStatisticsText(View view, String bookUID) { String stats = accountStats + ", " + transactionStats; TextView statsText = (TextView) view.findViewById(R.id.secondary_text); statsText.setText(stats); + + if (bookUID.equals(BooksDbAdapter.getInstance().getActiveBookUID())){ + ((TextView)view.findViewById(R.id.primary_text)).setTextColor(getResources().getColor(R.color.theme_primary)); + } } } diff --git a/app/src/main/java/org/gnucash/android/ui/settings/GeneralPreferenceFragment.java b/app/src/main/java/org/gnucash/android/ui/settings/GeneralPreferenceFragment.java index 26f14fad2..db07e2753 100644 --- a/app/src/main/java/org/gnucash/android/ui/settings/GeneralPreferenceFragment.java +++ b/app/src/main/java/org/gnucash/android/ui/settings/GeneralPreferenceFragment.java @@ -77,9 +77,6 @@ public void onResume() { final Intent intent = new Intent(getActivity(), PasscodePreferenceActivity.class); mCheckBoxPreference = (CheckBoxPreference) findPreference(getString(R.string.key_enable_passcode)); - mCheckBoxPreference.setTitle(mCheckBoxPreference.isChecked() - ? getString(R.string.title_passcode_enabled) - : getString(R.string.title_passcode_disabled)); mCheckBoxPreference.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object newValue) { @@ -145,12 +142,10 @@ public void onActivityResult(int requestCode, int resultCode, Intent data) { mEditor.putString(UxArgument.PASSCODE, data.getStringExtra(UxArgument.PASSCODE)); mEditor.putBoolean(UxArgument.ENABLED_PASSCODE, true); Toast.makeText(getActivity(), R.string.toast_passcode_set, Toast.LENGTH_SHORT).show(); - mCheckBoxPreference.setTitle(getString(R.string.title_passcode_enabled)); } if (resultCode == Activity.RESULT_CANCELED) { mEditor.putBoolean(UxArgument.ENABLED_PASSCODE, false); mCheckBoxPreference.setChecked(false); - mCheckBoxPreference.setTitle(getString(R.string.title_passcode_disabled)); } break; case REQUEST_DISABLE_PASSCODE: @@ -163,7 +158,6 @@ public void onActivityResult(int requestCode, int resultCode, Intent data) { mEditor.putString(UxArgument.PASSCODE, data.getStringExtra(UxArgument.PASSCODE)); mEditor.putBoolean(UxArgument.ENABLED_PASSCODE, true); Toast.makeText(getActivity(), R.string.toast_passcode_set, Toast.LENGTH_SHORT).show(); - mCheckBoxPreference.setTitle(getString(R.string.title_passcode_enabled)); } break; } diff --git a/app/src/main/res/layout/fragment_export_form.xml b/app/src/main/res/layout/fragment_export_form.xml index 3fd084275..8621e3754 100644 --- a/app/src/main/res/layout/fragment_export_form.xml +++ b/app/src/main/res/layout/fragment_export_form.xml @@ -32,7 +32,8 @@ + android:layout_height="wrap_content" + android:layout_marginBottom="-8dp"> + + All exported transactions will be deleted when exporting is completed Settings - Select File + Save As… Dropbox ownCloud Send to… @@ -97,8 +97,7 @@ There are no transactions available to export Passcode Passcode Preferences - Passcode Turned On - Passcode Turned Off + Enable passcode Change Passcode About GnuCash A mobile finance management and expense-tracker designed for Android diff --git a/app/src/main/res/xml/fragment_general_preferences.xml b/app/src/main/res/xml/fragment_general_preferences.xml index 3e568f81d..fb170c2b9 100644 --- a/app/src/main/res/xml/fragment_general_preferences.xml +++ b/app/src/main/res/xml/fragment_general_preferences.xml @@ -1,7 +1,8 @@ - + diff --git a/app/src/test/java/org/gnucash/android/test/unit/export/OfxExporterTest.java b/app/src/test/java/org/gnucash/android/test/unit/export/OfxExporterTest.java index 58cd6e9b4..32f4cc644 100644 --- a/app/src/test/java/org/gnucash/android/test/unit/export/OfxExporterTest.java +++ b/app/src/test/java/org/gnucash/android/test/unit/export/OfxExporterTest.java @@ -15,26 +15,40 @@ */ package org.gnucash.android.test.unit.export; +import android.content.Context; import android.database.sqlite.SQLiteDatabase; import org.gnucash.android.BuildConfig; import org.gnucash.android.app.GnuCashApplication; import org.gnucash.android.db.BookDbHelper; import org.gnucash.android.db.DatabaseHelper; +import org.gnucash.android.db.adapter.AccountsDbAdapter; import org.gnucash.android.db.adapter.BooksDbAdapter; +import org.gnucash.android.db.adapter.SplitsDbAdapter; +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.export.ofx.OfxExporter; +import org.gnucash.android.model.Account; import org.gnucash.android.model.Book; +import org.gnucash.android.model.Money; +import org.gnucash.android.model.Split; +import org.gnucash.android.model.Transaction; import org.gnucash.android.test.unit.testutil.GnucashTestRunner; import org.gnucash.android.test.unit.testutil.ShadowCrashlytics; import org.gnucash.android.test.unit.testutil.ShadowUserVoice; import org.gnucash.android.util.TimestampHelper; import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; +import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; +import java.io.File; +import java.util.List; + import static org.assertj.core.api.Assertions.assertThat; @@ -70,4 +84,32 @@ public void testWithNoTransactionsToExport_shouldNotCreateAnyFile(){ OfxExporter exporter = new OfxExporter(exportParameters, mDb); assertThat(exporter.generateExport()).isEmpty(); } + + /** + * Test that OFX files are generated + */ + //FIXME: test failing with NPE + public void testGenerateOFXExport(){ + 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())); + account.addTransaction(transaction); + + accountsDbAdapter.addRecord(account); + + ExportParams exportParameters = new ExportParams(ExportFormat.OFX); + exportParameters.setExportStartTime(TimestampHelper.getTimestampFromEpochZero()); + exportParameters.setExportTarget(ExportParams.ExportTarget.SD_CARD); + exportParameters.setDeleteTransactionsAfterExport(false); + + OfxExporter ofxExporter = new OfxExporter(exportParameters, mDb); + List exportedFiles = ofxExporter.generateExport(); + + assertThat(exportedFiles).hasSize(1); + File file = new File(exportedFiles.get(0)); + assertThat(file).exists().hasExtension("ofx"); + assertThat(file.length()).isGreaterThan(0L); + } } \ No newline at end of file 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 5f17fe4a2..5ecf69544 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 @@ -21,11 +21,18 @@ import org.gnucash.android.app.GnuCashApplication; import org.gnucash.android.db.BookDbHelper; import org.gnucash.android.db.DatabaseHelper; +import org.gnucash.android.db.adapter.AccountsDbAdapter; import org.gnucash.android.db.adapter.BooksDbAdapter; import org.gnucash.android.export.ExportFormat; import org.gnucash.android.export.ExportParams; +import org.gnucash.android.export.ofx.OfxExporter; import org.gnucash.android.export.qif.QifExporter; +import org.gnucash.android.model.Account; import org.gnucash.android.model.Book; +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.test.unit.testutil.GnucashTestRunner; import org.gnucash.android.test.unit.testutil.ShadowCrashlytics; import org.gnucash.android.test.unit.testutil.ShadowUserVoice; @@ -35,6 +42,9 @@ import org.junit.runner.RunWith; import org.robolectric.annotation.Config; +import java.io.File; +import java.util.List; + import static org.assertj.core.api.Assertions.assertThat; @RunWith(GnucashTestRunner.class) //package is required so that resources can be found in dev mode @@ -69,4 +79,70 @@ public void testWithNoTransactionsToExport_shouldNotCreateAnyFile(){ QifExporter exporter = new QifExporter(exportParameters, mDb); assertThat(exporter.generateExport()).isEmpty(); } + + /** + * Test that QIF files are generated + */ + //// FIXME: 20.04.2017 Test failing with NPE + public void testGenerateQIFExport(){ + 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())); + account.addTransaction(transaction); + + accountsDbAdapter.addRecord(account); + + ExportParams exportParameters = new ExportParams(ExportFormat.QIF); + exportParameters.setExportStartTime(TimestampHelper.getTimestampFromEpochZero()); + exportParameters.setExportTarget(ExportParams.ExportTarget.SD_CARD); + exportParameters.setDeleteTransactionsAfterExport(false); + + OfxExporter ofxExporter = new OfxExporter(exportParameters, mDb); + List exportedFiles = ofxExporter.generateExport(); + + assertThat(exportedFiles).hasSize(1); + File file = new File(exportedFiles.get(0)); + assertThat(file).exists().hasExtension("qif"); + assertThat(file.length()).isGreaterThan(0L); + } + + /** + * Test that when more than one currency is in use, multiple QIF files will be generated + */ + //// FIXME: 20.04.2017 test failing with NPE + public void multiCurrencyTransactions_shouldResultInMultipleQifFiles(){ + AccountsDbAdapter accountsDbAdapter = new AccountsDbAdapter(mDb); + + Account account = new Account("Basic Account", Commodity.getInstance("EUR")); + Transaction transaction = new Transaction("One transaction"); + transaction.addSplit(new Split(Money.createZeroInstance("EUR"),account.getUID())); + account.addTransaction(transaction); + accountsDbAdapter.addRecord(account); + + Account foreignAccount = new Account("US Konto", Commodity.getInstance("USD")); + Transaction multiCulti = new Transaction("Multicurrency"); + Split split = new Split(new Money("12", "USD"), new Money("15", "EUR"), foreignAccount.getUID()); + Split split2 = split.createPair(account.getUID()); + multiCulti.addSplit(split); + multiCulti.addSplit(split2); + foreignAccount.addTransaction(multiCulti); + + accountsDbAdapter.addRecord(foreignAccount); + + ExportParams exportParameters = new ExportParams(ExportFormat.QIF); + exportParameters.setExportStartTime(TimestampHelper.getTimestampFromEpochZero()); + exportParameters.setExportTarget(ExportParams.ExportTarget.SD_CARD); + exportParameters.setDeleteTransactionsAfterExport(false); + + OfxExporter ofxExporter = new OfxExporter(exportParameters, mDb); + List exportedFiles = ofxExporter.generateExport(); + + assertThat(exportedFiles).hasSize(2); + File file = new File(exportedFiles.get(0)); + assertThat(file).exists().hasExtension("qif"); + assertThat(file.length()).isGreaterThan(0L); + } + } \ No newline at end of file From f9ea1ae6cacc9d61d78be17178bd7532a5af0304 Mon Sep 17 00:00:00 2001 From: Ngewi Fet Date: Sat, 22 Apr 2017 19:47:07 +0200 Subject: [PATCH 32/45] Allow changing the Save As.. file path during export (Save As dialog can now be opened as often as desired) Improve display of export destination - make export targets more descriptive Restart scheduled service alarms to account for new execution period --- app/build.gradle | 1 + .../gnucash/android/db/MigrationHelper.java | 24 ++++++++++--- .../gnucash/android/export/ExportParams.java | 13 ++++++- .../service/ScheduledActionService.java | 4 ++- .../android/ui/export/ExportFormFragment.java | 36 +++++++++++++------ .../ScheduledActionsListFragment.java | 7 +++- .../android/ui/util/RecurrenceParser.java | 4 ++- .../ui/util/widget/ReselectSpinner.java | 23 ++++++++---- .../main/res/layout/fragment_export_form.xml | 2 +- .../res/layout/list_item_scheduled_trxn.xml | 1 + app/src/main/res/values/strings.xml | 2 ++ 11 files changed, 92 insertions(+), 25 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index cc3d9d781..3cde90a65 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -85,6 +85,7 @@ android { } debug { debuggable true + minifyEnabled false // testCoverageEnabled true signingConfig signingConfigs.debug } diff --git a/app/src/main/java/org/gnucash/android/db/MigrationHelper.java b/app/src/main/java/org/gnucash/android/db/MigrationHelper.java index 2db4a7ad4..20fe9053e 100644 --- a/app/src/main/java/org/gnucash/android/db/MigrationHelper.java +++ b/app/src/main/java/org/gnucash/android/db/MigrationHelper.java @@ -1468,6 +1468,18 @@ static int upgradeDbToVersion13(SQLiteDatabase db){ .putBoolean(keyUseCompactView, useCompactTrnView) .apply(); + rescheduleServiceAlarm(); + + + return oldVersion; + } + + /** + * Cancel the existing alarm for the scheduled service and restarts/reschedules the service + */ + private static void rescheduleServiceAlarm() { + Context context = GnuCashApplication.getAppContext(); + //cancel the existing pending intent so that the alarm can be rescheduled Intent alarmIntent = new Intent(context, ScheduledActionService.class); PendingIntent pendingIntent = PendingIntent.getService(context, 0, alarmIntent, PendingIntent.FLAG_NO_CREATE); @@ -1477,9 +1489,7 @@ static int upgradeDbToVersion13(SQLiteDatabase db){ pendingIntent.cancel(); } - GnuCashApplication.startScheduledActionExecutionService(GnuCashApplication.getAppContext()); - - return oldVersion; + GnuCashApplication.startScheduledActionExecutionService(context); } /** @@ -1509,6 +1519,9 @@ private static void moveDirectory(File srcDir, File dstDir) throws IOException { } } + if (srcDir.listFiles() == null) //nothing to see here, move along + return; + for (File src : srcDir.listFiles()){ if (src.isDirectory()){ File dst = new File(dstDir, src.getName()); @@ -1583,7 +1596,7 @@ public void run() { * @return New database version, 14 if migration succeeds, 13 otherwise */ static int upgradeDbToVersion15(SQLiteDatabase db) { - Log.i(DatabaseHelper.LOG_TAG, "Upgrading database to version 14"); + Log.i(DatabaseHelper.LOG_TAG, "Upgrading database to version 15"); int dbVersion = 14; db.beginTransaction(); @@ -1602,6 +1615,9 @@ static int upgradeDbToVersion15(SQLiteDatabase db) { } finally { db.endTransaction(); } + + //the default interval has been changed from daily to hourly with this release. So reschedule alarm + rescheduleServiceAlarm(); return dbVersion; } } 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 836faa793..90c4e4221 100644 --- a/app/src/main/java/org/gnucash/android/export/ExportParams.java +++ b/app/src/main/java/org/gnucash/android/export/ExportParams.java @@ -37,7 +37,18 @@ public class ExportParams { * Options for the destination of the exported transctions file. * It could be stored on the {@link #SD_CARD} or exported through another program via {@link #SHARING} */ - public enum ExportTarget {SD_CARD, SHARING, DROPBOX, GOOGLE_DRIVE, OWNCLOUD, URI} + public enum ExportTarget {SD_CARD("SD Card"), SHARING("External Service"), + DROPBOX("Dropbox"), GOOGLE_DRIVE("Google Drive"), OWNCLOUD("ownCloud"), + URI("Sync Service"); + private String mDescription; + ExportTarget(String description){ + mDescription = description; + } + + public String getDescription(){ + return mDescription; + } + } /** * Format to use for the exported transactions 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 b6efb3f45..5c2470371 100644 --- a/app/src/main/java/org/gnucash/android/service/ScheduledActionService.java +++ b/app/src/main/java/org/gnucash/android/service/ScheduledActionService.java @@ -181,13 +181,15 @@ private static int executeBackup(ScheduledAction scheduledAction, SQLiteDatabase ExportParams params = ExportParams.parseCsv(scheduledAction.getTag()); // HACK: the tag isn't updated with the new date, so set the correct by hand params.setExportStartTime(new Timestamp(scheduledAction.getLastRunTime())); + Boolean result = false; try { //wait for async task to finish before we proceed (we are holding a wake lock) - new ExportAsyncTask(GnuCashApplication.getAppContext(), db).execute(params).get(); + result = new ExportAsyncTask(GnuCashApplication.getAppContext(), db).execute(params).get(); } catch (InterruptedException | ExecutionException e) { 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"); return 1; } 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 6395cdfc8..1939c865a 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 @@ -138,6 +138,7 @@ public class ExportFormFragment extends Fragment implements @BindView(R.id.radio_qif_format) RadioButton mQifRadioButton; @BindView(R.id.radio_xml_format) RadioButton mXmlRadioButton; + @BindView(R.id.recurrence_options) View mRecurrenceOptionsView; /** * Event recurrence options */ @@ -321,19 +322,19 @@ private void bindViewListeners(){ mDestinationSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView parent, View view, int position, long id) { - View recurrenceOptionsView = getView().findViewById(R.id.recurrence_options); + if (view == null) //the item selection is fired twice by the Android framework. Ignore the first one + return; switch (position) { case 0: mExportTarget = ExportParams.ExportTarget.URI; - recurrenceOptionsView.setVisibility(View.VISIBLE); + mRecurrenceOptionsView.setVisibility(View.VISIBLE); if (mExportUri != null) - mTargetUriTextView.setText(mExportUri.toString()); - else - selectExportFile(); + setExportUriText(mExportUri.toString()); + selectExportFile(); break; case 1: //DROPBOX - mTargetUriTextView.setText("Export to /Apps/GnuCash folder on Dropbox"); - recurrenceOptionsView.setVisibility(View.VISIBLE); + setExportUriText(getString(R.string.label_dropbox_export_destination)); + mRecurrenceOptionsView.setVisibility(View.VISIBLE); mExportTarget = ExportParams.ExportTarget.DROPBOX; String dropboxAppKey = getString(R.string.dropbox_app_key, BackupPreferenceFragment.DROPBOX_APP_KEY); String dropboxAppSecret = getString(R.string.dropbox_app_secret, BackupPreferenceFragment.DROPBOX_APP_SECRET); @@ -343,8 +344,8 @@ public void onItemSelected(AdapterView parent, View view, int position, long } break; case 2: - mTargetUriTextView.setText(""); - recurrenceOptionsView.setVisibility(View.VISIBLE); + setExportUriText(null); + mRecurrenceOptionsView.setVisibility(View.VISIBLE); mExportTarget = ExportParams.ExportTarget.OWNCLOUD; if(!(PreferenceManager.getDefaultSharedPreferences(getActivity()) .getBoolean(getString(R.string.key_owncloud_sync), false))) { @@ -353,8 +354,9 @@ public void onItemSelected(AdapterView parent, View view, int position, long } break; case 3: + setExportUriText(getString(R.string.label_select_destination_after_export)); mExportTarget = ExportParams.ExportTarget.SHARING; - recurrenceOptionsView.setVisibility(View.GONE); + mRecurrenceOptionsView.setVisibility(View.GONE); break; default: @@ -478,6 +480,20 @@ public void onClick(View view) { } + /** + * Display the file path of the file where the export will be saved + * @param filepath Path to export file. If {@code null}, the view will be hidden and nothing displayed + */ + private void setExportUriText(String filepath){ + if (filepath == null){ + mTargetUriTextView.setVisibility(View.GONE); + mTargetUriTextView.setText(""); + } else { + mTargetUriTextView.setText(filepath); + mTargetUriTextView.setVisibility(View.VISIBLE); + } + } + /** * Open a chooser for user to pick a file to export to */ 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 ccff3c1bc..08379fa35 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 @@ -22,6 +22,7 @@ import android.content.res.Resources; import android.database.Cursor; import android.graphics.Rect; +import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.support.v4.app.Fragment; @@ -561,9 +562,13 @@ public void bindView(View view, Context context, Cursor cursor) { TextView primaryTextView = (TextView) view.findViewById(R.id.primary_text); ExportParams params = ExportParams.parseCsv(scheduledAction.getTag()); + String exportDestination = params.getExportTarget().getDescription(); + if (params.getExportTarget() == ExportParams.ExportTarget.URI){ + exportDestination = exportDestination + " (" + Uri.parse(params.getExportLocation()).getHost() + ")"; + } primaryTextView.setText(params.getExportFormat().name() + " " + scheduledAction.getActionType().name().toLowerCase() + " to " - + params.getExportTarget().name().toLowerCase()); + + exportDestination); view.findViewById(R.id.right_text).setVisibility(View.GONE); diff --git a/app/src/main/java/org/gnucash/android/ui/util/RecurrenceParser.java b/app/src/main/java/org/gnucash/android/ui/util/RecurrenceParser.java index 15e4346a7..4fbe5d8ed 100644 --- a/app/src/main/java/org/gnucash/android/ui/util/RecurrenceParser.java +++ b/app/src/main/java/org/gnucash/android/ui/util/RecurrenceParser.java @@ -38,10 +38,12 @@ * @author Ngewi Fet */ public class RecurrenceParser { + //these are time millisecond constants which are used for scheduled actions. + //they may not be calendar accurate, but they serve the purpose for scheduling approximate time for background service execution public static final long SECOND_MILLIS = 1000; public static final long MINUTE_MILLIS = 60 * SECOND_MILLIS; public static final long HOUR_MILLIS = 60 * MINUTE_MILLIS; - public static final long DAY_MILLIS = 24 * 60 * MINUTE_MILLIS; + public static final long DAY_MILLIS = 24 * HOUR_MILLIS; public static final long WEEK_MILLIS = 7 * DAY_MILLIS; public static final long MONTH_MILLIS = 30 * DAY_MILLIS; public static final long YEAR_MILLIS = 12 * MONTH_MILLIS; diff --git a/app/src/main/java/org/gnucash/android/ui/util/widget/ReselectSpinner.java b/app/src/main/java/org/gnucash/android/ui/util/widget/ReselectSpinner.java index dc32ef8b5..cf03b65b0 100644 --- a/app/src/main/java/org/gnucash/android/ui/util/widget/ReselectSpinner.java +++ b/app/src/main/java/org/gnucash/android/ui/util/widget/ReselectSpinner.java @@ -1,15 +1,25 @@ package org.gnucash.android.ui.util.widget; import android.content.Context; +import android.support.v7.widget.AppCompatSpinner; import android.util.AttributeSet; -import android.widget.Spinner; +import android.view.View; +import android.widget.AdapterView; + +import org.gnucash.android.ui.export.ExportFormFragment; /** * Spinner which fires OnItemSelectedListener even when an item is reselected. * Normal Spinners only fire item selected notifications when the selected item changes. - *

This is used in {@code ReportsActivity} for the time range

+ *

This is used in {@code ReportsActivity} for the time range and in the {@link ExportFormFragment}

+ *

It could happen that the selected item is fired twice especially if the item is the first in the list. + * The Android system does this internally. In order to capture the first one, check whether the view parameter + * of {@link android.widget.AdapterView.OnItemSelectedListener#onItemSelected(AdapterView, View, int, long)} is null. + * That would represent the first call during initialization of the views. This call can be ignored. + * See {@link ExportFormFragment#bindViewListeners()} for an example + *

*/ -public class ReselectSpinner extends Spinner { +public class ReselectSpinner extends AppCompatSpinner { public ReselectSpinner(Context context) { super(context); } @@ -26,9 +36,10 @@ public ReselectSpinner(Context context, AttributeSet attrs, int defStyleAttr) { public void setSelection(int position) { boolean sameSelected = getSelectedItemPosition() == position; super.setSelection(position); - if (position == 5 && sameSelected){ - getOnItemSelectedListener().onItemSelected(this, getSelectedView(), position, getSelectedItemId()); + if (sameSelected){ + OnItemSelectedListener listener = getOnItemSelectedListener(); + if (listener != null) + listener.onItemSelected(this, getSelectedView(), position, getSelectedItemId()); } - super.setSelection(position); } } diff --git a/app/src/main/res/layout/fragment_export_form.xml b/app/src/main/res/layout/fragment_export_form.xml index 8621e3754..162b3f58f 100644 --- a/app/src/main/res/layout/fragment_export_form.xml +++ b/app/src/main/res/layout/fragment_export_form.xml @@ -42,7 +42,7 @@ android:gravity="center_vertical" style="@style/TextAppearance.EditTransaction_Small" /> - diff --git a/app/src/main/res/layout/list_item_scheduled_trxn.xml b/app/src/main/res/layout/list_item_scheduled_trxn.xml index abb47f835..bc93895e3 100644 --- a/app/src/main/res/layout/list_item_scheduled_trxn.xml +++ b/app/src/main/res/layout/list_item_scheduled_trxn.xml @@ -45,6 +45,7 @@ android:ellipsize="end" android:text="@string/label_transaction_name" style="@style/ListItemText" + android:textSize="15sp" /> 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 From 957a41712f4b103e9ca3ff72bc8500b3dbf6d0a0 Mon Sep 17 00:00:00 2001 From: Ngewi Fet Date: Mon, 24 Apr 2017 22:26:11 +0200 Subject: [PATCH 33/45] Use custom layout for "rename book" dialog Add some padding to input text field Update CHANGELOG Update version number for first beta release --- CHANGELOG.md | 5 ++- app/build.gradle | 2 +- .../ui/settings/BookManagerFragment.java | 19 +++++++----- .../ui/util/widget/CalculatorEditText.java | 4 +-- .../main/res/layout/dialog_rename_book.xml | 31 +++++++++++++++++++ 5 files changed, 50 insertions(+), 11 deletions(-) create mode 100644 app/src/main/res/layout/dialog_rename_book.xml diff --git a/CHANGELOG.md b/CHANGELOG.md index 95c115fcb..c64e22dd9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ Change Log =============================================================================== Version 2.2.0 *(2017-05-xx)* ---------------------------- +* Feature #646: Option to select backup file using Storage Access Framework +* Feature #565: Regular automatic backups (even when not explicitly set by user) * Feature #656: Added Bitcoin (BTC) currency support * Feature #634: Added support for renaming books * Fixed #672: Crash when exporting multi-currency transactions to Google Drive @@ -10,7 +12,8 @@ Version 2.2.0 *(2017-05-xx)* * Fixed #607: Widgets stop functioning after switching books * Improved #635: Improved support for BYN currency * Improved #661: Removed need for WRITE_EXTERNAL_STORAGE permission for Android 4.4 (KitKat) and above - + * This release raises the minimum API level to 19 (KitKat) + Version 2.1.7 *(2017-04-18)* ---------------------------- * Properly handle crashes during migration of backup/export files to new location diff --git a/app/build.gradle b/app/build.gradle index 3cde90a65..eb0847d7b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -7,7 +7,7 @@ apply plugin: 'android-apt' def versionMajor = 2 def versionMinor = 2 def versionPatch = 0 -def versionBuild = 0 +def versionBuild = 1 def buildTime() { def df = new SimpleDateFormat("yyyyMMdd HH:mm 'UTC'") diff --git a/app/src/main/java/org/gnucash/android/ui/settings/BookManagerFragment.java b/app/src/main/java/org/gnucash/android/ui/settings/BookManagerFragment.java index ba850849e..6d4f5cb97 100644 --- a/app/src/main/java/org/gnucash/android/ui/settings/BookManagerFragment.java +++ b/app/src/main/java/org/gnucash/android/ui/settings/BookManagerFragment.java @@ -195,7 +195,7 @@ public void onClick(View v) { public boolean onMenuItemClick(MenuItem item) { switch (item.getItemId()) { case R.id.ctx_menu_rename_book: - return handleMenuRenameBook(context, bookName, bookUID); + return handleMenuRenameBook(bookName, bookUID); case R.id.ctx_menu_sync_book: //TODO implement sync return false; @@ -238,20 +238,24 @@ public void onClick(DialogInterface dialog, int which) { }); } - private boolean handleMenuRenameBook(Context context, String bookName, final String bookUID) { - final EditText nameEditText = new EditText(context); - nameEditText.setText(bookName); - + /** + * Opens a dialog for renaming a book + * @param bookName Current name of the book + * @param bookUID GUID of the book + * @return {@code true} + */ + private boolean handleMenuRenameBook(String bookName, final String bookUID) { AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(getActivity()); dialogBuilder.setTitle(R.string.title_rename_book) - .setView(nameEditText) + .setView(R.layout.dialog_rename_book) .setPositiveButton(R.string.btn_rename, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { + EditText bookTitle = (EditText) ((AlertDialog)dialog).findViewById(R.id.input_book_title); BooksDbAdapter.getInstance() .updateRecord(bookUID, BookEntry.COLUMN_DISPLAY_NAME, - nameEditText.getText().toString()); + bookTitle.getText().toString().trim()); refresh(); } }) @@ -263,6 +267,7 @@ public void onClick(DialogInterface dialog, int which) { }); AlertDialog dialog = dialogBuilder.create(); dialog.show(); + ((TextView)dialog.findViewById(R.id.input_book_title)).setText(bookName); return true; } diff --git a/app/src/main/java/org/gnucash/android/ui/util/widget/CalculatorEditText.java b/app/src/main/java/org/gnucash/android/ui/util/widget/CalculatorEditText.java index 69a778906..67f6a07f0 100644 --- a/app/src/main/java/org/gnucash/android/ui/util/widget/CalculatorEditText.java +++ b/app/src/main/java/org/gnucash/android/ui/util/widget/CalculatorEditText.java @@ -21,6 +21,7 @@ import android.inputmethodservice.KeyboardView; import android.support.annotation.Nullable; import android.support.annotation.XmlRes; +import android.support.v7.widget.AppCompatEditText; import android.text.Editable; import android.text.InputType; import android.text.TextWatcher; @@ -29,7 +30,6 @@ import android.view.MotionEvent; import android.view.View; import android.view.inputmethod.InputMethodManager; -import android.widget.EditText; import com.crashlytics.android.Crashlytics; @@ -54,7 +54,7 @@ * with the view from your layout where the calculator keyboard should be displayed.

* @author Ngewi Fet */ -public class CalculatorEditText extends EditText { +public class CalculatorEditText extends AppCompatEditText { private CalculatorKeyboard mCalculatorKeyboard; private Commodity mCommodity = Commodity.DEFAULT_COMMODITY; diff --git a/app/src/main/res/layout/dialog_rename_book.xml b/app/src/main/res/layout/dialog_rename_book.xml new file mode 100644 index 000000000..7042b5c35 --- /dev/null +++ b/app/src/main/res/layout/dialog_rename_book.xml @@ -0,0 +1,31 @@ + + + + + + From c30292fad16f1c6b6db4b6d2e222659f868d80b0 Mon Sep 17 00:00:00 2001 From: Ngewi Fet Date: Mon, 24 Apr 2017 22:45:06 +0200 Subject: [PATCH 34/45] Code cleanup and refactoring - Remove code branches targeting API levels below KitKat - Remove deprecated layout xml tags --- .../gnucash/android/test/ui/AccountsActivityTest.java | 2 -- .../org/gnucash/android/export/ExportAsyncTask.java | 8 +++----- .../org/gnucash/android/importer/ImportAsyncTask.java | 11 +++++------ .../android/ui/account/AccountsListFragment.java | 8 +++----- .../android/ui/passcode/PasscodeLockActivity.java | 4 +--- .../android/ui/settings/PreferenceActivity.java | 11 +++-------- .../dialog/DeleteAllAccountsConfirmationDialog.java | 1 - .../android/ui/transaction/TransactionsActivity.java | 8 ++------ .../gnucash/android/ui/util/ScrollingFABBehavior.java | 7 +++---- .../android/ui/util/widget/CheckableLinearLayout.java | 1 - .../main/res/layout/account_spinner_dropdown_item.xml | 2 +- app/src/main/res/layout/account_spinner_item.xml | 2 +- .../main/res/layout/activity_transaction_detail.xml | 1 - app/src/main/res/layout/cardview_budget.xml | 4 ++-- app/src/main/res/values/strings.xml | 2 +- 15 files changed, 25 insertions(+), 47 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 bfa7fb14f..c0c61e22f 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 @@ -175,7 +175,6 @@ public static void preventFirstRunDialogs(Context context) { } - @TargetApi(Build.VERSION_CODES.HONEYCOMB) public void testDisplayAccountsList(){ AccountsActivity.createDefaultAccounts("EUR", mAccountsActivity); mAccountsActivity.recreate(); @@ -462,7 +461,6 @@ public void testIntentAccountCreation(){ /** * Tests that the setup wizard is displayed on first run */ - @TargetApi(Build.VERSION_CODES.HONEYCOMB) @Test public void shouldShowWizardOnFirstRun() throws Throwable { Editor editor = PreferenceManager.getDefaultSharedPreferences(mAccountsActivity) 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 812c90083..49d579c60 100644 --- a/app/src/main/java/org/gnucash/android/export/ExportAsyncTask.java +++ b/app/src/main/java/org/gnucash/android/export/ExportAsyncTask.java @@ -117,7 +117,6 @@ public ExportAsyncTask(Context context, SQLiteDatabase db){ } @Override - @TargetApi(11) protected void onPreExecute() { super.onPreExecute(); if (mContext instanceof Activity) { @@ -125,10 +124,9 @@ protected void onPreExecute() { mProgressDialog.setTitle(R.string.title_progress_exporting_transactions); mProgressDialog.setIndeterminate(true); mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.HONEYCOMB) { - mProgressDialog.setProgressNumberFormat(null); - mProgressDialog.setProgressPercentFormat(null); - } + mProgressDialog.setProgressNumberFormat(null); + mProgressDialog.setProgressPercentFormat(null); + mProgressDialog.show(); } } 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 92570468f..7b7c4999b 100644 --- a/app/src/main/java/org/gnucash/android/importer/ImportAsyncTask.java +++ b/app/src/main/java/org/gnucash/android/importer/ImportAsyncTask.java @@ -58,7 +58,6 @@ public ImportAsyncTask(Activity context, TaskDelegate delegate){ this.mDelegate = delegate; } - @TargetApi(11) @Override protected void onPreExecute() { super.onPreExecute(); @@ -67,11 +66,11 @@ protected void onPreExecute() { mProgressDialog.setIndeterminate(true); mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); mProgressDialog.show(); - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.HONEYCOMB){ - //these methods must be called after progressDialog.show() - mProgressDialog.setProgressNumberFormat(null); - mProgressDialog.setProgressPercentFormat(null); - } + + //these methods must be called after progressDialog.show() + mProgressDialog.setProgressNumberFormat(null); + mProgressDialog.setProgressPercentFormat(null); + } 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 f6e9382ac..d099027d2 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 @@ -496,12 +496,10 @@ public void onBindViewHolderCursor(final AccountViewHolder holder, final Cursor holder.description.setVisibility(View.GONE); // add a summary of transactions to the account view - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { + // Make sure the balance task is truly multithread - new AccountBalanceTask(holder.accountBalance).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, accountUID); - } else { - new AccountBalanceTask(holder.accountBalance).execute(accountUID); - } + new AccountBalanceTask(holder.accountBalance).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, accountUID); + String accountColor = cursor.getString(cursor.getColumnIndexOrThrow(DatabaseSchema.AccountEntry.COLUMN_COLOR_CODE)); int colorCode = accountColor == null ? Color.TRANSPARENT : Color.parseColor(accountColor); holder.colorStripView.setBackgroundColor(colorCode); diff --git a/app/src/main/java/org/gnucash/android/ui/passcode/PasscodeLockActivity.java b/app/src/main/java/org/gnucash/android/ui/passcode/PasscodeLockActivity.java index 0934053dc..951695868 100644 --- a/app/src/main/java/org/gnucash/android/ui/passcode/PasscodeLockActivity.java +++ b/app/src/main/java/org/gnucash/android/ui/passcode/PasscodeLockActivity.java @@ -44,9 +44,7 @@ protected void onResume() { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); boolean isPassEnabled = prefs.getBoolean(UxArgument.ENABLED_PASSCODE, false); if (isPassEnabled) { - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) { - getWindow().addFlags(LayoutParams.FLAG_SECURE); - } + getWindow().addFlags(LayoutParams.FLAG_SECURE); } else { getWindow().clearFlags(LayoutParams.FLAG_SECURE); } diff --git a/app/src/main/java/org/gnucash/android/ui/settings/PreferenceActivity.java b/app/src/main/java/org/gnucash/android/ui/settings/PreferenceActivity.java index bb16e00b1..10cf12b29 100644 --- a/app/src/main/java/org/gnucash/android/ui/settings/PreferenceActivity.java +++ b/app/src/main/java/org/gnucash/android/ui/settings/PreferenceActivity.java @@ -93,7 +93,6 @@ public void onPanelClosed(View panel) { actionBar.setDisplayHomeAsUpEnabled(true); } - @TargetApi(Build.VERSION_CODES.KITKAT) //for one of the exceptions caught @Override public boolean onPreferenceStartFragment(PreferenceFragmentCompat caller, Preference pref) { String key = pref.getKey(); @@ -128,13 +127,9 @@ private void loadFragment(Fragment fragment) { public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { - android.app.FragmentManager fm = getFragmentManager(); - if (fm.getBackStackEntryCount() > 0) { - fm.popBackStack(); - } else { - finish(); - } + android.app.FragmentManager fm = getFragmentManager(); + if (fm.getBackStackEntryCount() > 0) { + fm.popBackStack(); } else { finish(); } 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 198b5b817..58c4cd93f 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 @@ -36,7 +36,6 @@ * * @author Ngewi Fet */ -@TargetApi(11) public class DeleteAllAccountsConfirmationDialog extends DialogFragment { public static DeleteAllAccountsConfirmationDialog newInstance() { diff --git a/app/src/main/java/org/gnucash/android/ui/transaction/TransactionsActivity.java b/app/src/main/java/org/gnucash/android/ui/transaction/TransactionsActivity.java index 07051a11d..fb4ba91ef 100644 --- a/app/src/main/java/org/gnucash/android/ui/transaction/TransactionsActivity.java +++ b/app/src/main/java/org/gnucash/android/ui/transaction/TransactionsActivity.java @@ -260,12 +260,8 @@ public void refresh(String accountUID) { if (mPagerAdapter != null) mPagerAdapter.notifyDataSetChanged(); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { - // make sure the account balance task is truely multi-thread - new AccountBalanceTask(mSumTextView).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, mAccountUID); - } else { - new AccountBalanceTask(mSumTextView).execute(mAccountUID); - } + new AccountBalanceTask(mSumTextView).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, mAccountUID); + } @Override diff --git a/app/src/main/java/org/gnucash/android/ui/util/ScrollingFABBehavior.java b/app/src/main/java/org/gnucash/android/ui/util/ScrollingFABBehavior.java index 6a71f62a3..c7953211a 100644 --- a/app/src/main/java/org/gnucash/android/ui/util/ScrollingFABBehavior.java +++ b/app/src/main/java/org/gnucash/android/ui/util/ScrollingFABBehavior.java @@ -50,10 +50,9 @@ public boolean onDependentViewChanged(CoordinatorLayout parent, FloatingActionBu CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams) fab.getLayoutParams(); int fabBottomMargin = lp.bottomMargin; int distanceToScroll = fab.getHeight() + fabBottomMargin; - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.GINGERBREAD_MR1) { - float ratio = (float) dependency.getY() / (float) toolbarHeight; - fab.setTranslationY(-distanceToScroll * ratio); - } + float ratio = (float) dependency.getY() / (float) toolbarHeight; + fab.setTranslationY(-distanceToScroll * ratio); + } return true; } diff --git a/app/src/main/java/org/gnucash/android/ui/util/widget/CheckableLinearLayout.java b/app/src/main/java/org/gnucash/android/ui/util/widget/CheckableLinearLayout.java index 5230e8be3..d7547a837 100644 --- a/app/src/main/java/org/gnucash/android/ui/util/widget/CheckableLinearLayout.java +++ b/app/src/main/java/org/gnucash/android/ui/util/widget/CheckableLinearLayout.java @@ -51,7 +51,6 @@ public CheckableLinearLayout(Context context, AttributeSet attrs) { super(context, attrs); } - @TargetApi(11) public CheckableLinearLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } diff --git a/app/src/main/res/layout/account_spinner_dropdown_item.xml b/app/src/main/res/layout/account_spinner_dropdown_item.xml index f27051994..c32bfdc6a 100644 --- a/app/src/main/res/layout/account_spinner_dropdown_item.xml +++ b/app/src/main/res/layout/account_spinner_dropdown_item.xml @@ -17,7 +17,7 @@ Recommend in Play Store
until %1$s on %1$s - for %1$s times + for %1$d times Compact View Book %1$d never From 6a3962d549a00642346f4b239ab3db3fe3c8fd11 Mon Sep 17 00:00:00 2001 From: Ngewi Fet Date: Mon, 24 Apr 2017 22:50:17 +0200 Subject: [PATCH 35/45] Update Czech translations --- app/src/main/res/values-cs-rCZ/strings.xml | 133 ++++++++++----------- 1 file changed, 66 insertions(+), 67 deletions(-) diff --git a/app/src/main/res/values-cs-rCZ/strings.xml b/app/src/main/res/values-cs-rCZ/strings.xml index ce20f70f4..87d0a42c8 100644 --- a/app/src/main/res/values-cs-rCZ/strings.xml +++ b/app/src/main/res/values-cs-rCZ/strings.xml @@ -39,7 +39,7 @@ Datum & čas Účet DEBET - CREDIT + ÚVĚR Účty Transakce Smazat @@ -73,57 +73,57 @@ Přesunout transakci(e) %1$d Cílový účet Přístup na SD kartu - 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 - Select accounts to create - No accounts exist in GnuCash.\nCreate an account before adding a widget - Build version - License - Apache License v2.0. Click for details - General Preferences - Select Account - There are no transactions available to export - Passcode - Passcode Preferences - Passcode Turned On - Passcode Turned Off - Change Passcode - About GnuCash + Nelze přesunout transakce.\nCílový účet používá jinou měnu než původu účet + Všeobecné + O Produktu + Vyberte výchozí měnu + Výchozí měna + Výchozí měna k novým účtům + Umožňuje záznam transakcí v GnuCash pro Android + Umožňuje vytváření účtů v GnuCash pro Android + Vaše GnuCash data + Číst a upravovat GnuCash data + Záznam transakcí v GnuCash + Vytvořit účty v GnuCash + Zobrazit účet + Skrýt zůstatek účtu ve widget + Vytvořit účet + Vyberte účty, které chcete vytvořit + Neexistují účty v GnuCash.\nVytvořte účet před přidáním widgetu + Verze sestavení + Licence + Licence Apache v2.0. Klikněte pro podrobnosti + Všeobecné předvolby + Vybrat účet + Neexistují žádné transakce pro export + Heslo + Předvolby hesla + Kódový zámek zapnutou + Přístupový kód vypnutý + Změnit heslo + O GnuCash A mobile finance management and expense-tracker designed for Android - About - %1$s file exported to:\n + O programu + %1$s exportovano do souboru:\n 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 + GnuCash Android exportováno z + Transakce + Transakce předvolby + Předvolby účtu + Výchozí typ transakce + Typ transakce pro výchozí použití, KREDITNÍ nebo DEBETNÍ - CREDIT - DEBIT + ÚVĚR + DEBET - Are you sure you want to delete ALL transactions? - Are you sure you want to delete this transaction? + Opravdu chcete odstranit všechny transakce? + Opravdu chcete odstranit tuto transakci? 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. - Transfer Account + Exportovat všechny transakce + Odstranit exportované transakce + Výchozí exportní e-mail + Výchozí e-mailovou adresa pro export. Můžete změnit při exportu. + Přenos účtu Všechny transakce budou převedeny z jednoho účtu na jiný Activate Double Entry Zůstatek @@ -224,26 +224,25 @@ Spend Receive Withdrawal - Deposit - Payment - Charge - Decrease - Increase - Income - Rebate - Expense - Bill - Invoice - Buy - Sell - Repeats - No recent backup found - 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 + Vklad + Platba + Poplatek + Snížení + Zvýšení + Příjem + Sleva + Výdaj + Účet + Faktura + Koupit + Prodej + Se opakuje + Nebyla nalezena žádná poslední záloha + Počáteční zůstatky + Vlastní kapitál + Povolit uložení saldo běžného účtu (před odstraněním transakcí) jako nový počáteční zůstatek po odstranění transakcí + Uložit počáteční zůstatek účet + OFX nepodporuje transakce podvojného účetnictní Generates separate QIF files per currency Transaction splits Imbalance: From 1da977bd380c9f63c96f4b3b3bd0175ebc8618d8 Mon Sep 17 00:00:00 2001 From: Ngewi Fet Date: Mon, 24 Apr 2017 23:13:10 +0200 Subject: [PATCH 36/45] Remove unused resources --- .../appwidget_inner_focused_c.9.png | Bin 158 -> 0 bytes .../appwidget_inner_focused_l.9.png | Bin 216 -> 0 bytes .../appwidget_inner_focused_r.9.png | Bin 219 -> 0 bytes .../appwidget_inner_pressed_c.9.png | Bin 157 -> 0 bytes .../appwidget_inner_pressed_l.9.png | Bin 224 -> 0 bytes .../appwidget_inner_pressed_r.9.png | Bin 226 -> 0 bytes .../main/res/drawable-hdpi/action_about.png | Bin 1764 -> 0 bytes .../main/res/drawable-hdpi/action_help.png | Bin 1544 -> 0 bytes .../appwidget_dark_bg_pressed.9.png | Bin 3279 -> 0 bytes .../appwidget_inner_focused_c.9.png | Bin 221 -> 0 bytes .../appwidget_inner_focused_l.9.png | Bin 400 -> 0 bytes .../appwidget_inner_focused_r.9.png | Bin 423 -> 0 bytes .../appwidget_inner_pressed_c.9.png | Bin 223 -> 0 bytes .../appwidget_inner_pressed_l.9.png | Bin 408 -> 0 bytes .../appwidget_inner_pressed_r.9.png | Bin 419 -> 0 bytes .../drawable-hdpi/content_new_holo_dark.png | Bin 1142 -> 0 bytes .../res/drawable-hdpi/drawer_shadow.9.png | Bin 161 -> 0 bytes .../res/drawable-hdpi/ic_clear_black_24dp.png | Bin 207 -> 0 bytes .../drawable-hdpi/ic_dashboard_black_24dp.png | Bin 126 -> 0 bytes .../res/drawable-hdpi/ic_google_drive.png | Bin 2859 -> 0 bytes .../appwidget_inner_focused_c.9.png | Bin 152 -> 0 bytes .../appwidget_inner_focused_l.9.png | Bin 189 -> 0 bytes .../appwidget_inner_focused_r.9.png | Bin 184 -> 0 bytes .../appwidget_inner_pressed_c.9.png | Bin 151 -> 0 bytes .../appwidget_inner_pressed_l.9.png | Bin 198 -> 0 bytes .../appwidget_inner_pressed_r.9.png | Bin 189 -> 0 bytes .../main/res/drawable-mdpi/action_about.png | Bin 1441 -> 0 bytes .../main/res/drawable-mdpi/action_help.png | Bin 1318 -> 0 bytes .../appwidget_dark_bg_pressed.9.png | Bin 2050 -> 0 bytes .../appwidget_inner_focused_c.9.png | Bin 212 -> 0 bytes .../appwidget_inner_focused_l.9.png | Bin 325 -> 0 bytes .../appwidget_inner_focused_r.9.png | Bin 324 -> 0 bytes .../appwidget_inner_pressed_c.9.png | Bin 212 -> 0 bytes .../appwidget_inner_pressed_l.9.png | Bin 335 -> 0 bytes .../appwidget_inner_pressed_r.9.png | Bin 330 -> 0 bytes .../drawable-mdpi/content_new_holo_dark.png | Bin 1090 -> 0 bytes .../res/drawable-mdpi/drawer_shadow.9.png | Bin 142 -> 0 bytes .../res/drawable-mdpi/ic_clear_black_24dp.png | Bin 164 -> 0 bytes .../drawable-mdpi/ic_dashboard_black_24dp.png | Bin 92 -> 0 bytes .../res/drawable-mdpi/ic_google_drive.png | Bin 1258 -> 0 bytes .../appwidget_inner_focused_c.9.png | Bin 158 -> 0 bytes .../appwidget_inner_focused_l.9.png | Bin 242 -> 0 bytes .../appwidget_inner_focused_r.9.png | Bin 236 -> 0 bytes .../appwidget_inner_pressed_c.9.png | Bin 157 -> 0 bytes .../appwidget_inner_pressed_l.9.png | Bin 258 -> 0 bytes .../appwidget_inner_pressed_r.9.png | Bin 247 -> 0 bytes .../drawable-xhdpi/content_new_holo_dark.png | Bin 1221 -> 0 bytes .../res/drawable-xhdpi/drawer_shadow.9.png | Bin 174 -> 0 bytes .../drawable-xhdpi/ic_clear_black_24dp.png | Bin 235 -> 0 bytes .../ic_dashboard_black_24dp.png | Bin 115 -> 0 bytes .../res/drawable-xhdpi/ic_google_drive.png | Bin 6676 -> 0 bytes .../res/drawable-xxhdpi/drawer_shadow.9.png | Bin 208 -> 0 bytes .../drawable-xxhdpi/ic_clear_black_24dp.png | Bin 309 -> 0 bytes .../ic_dashboard_black_24dp.png | Bin 126 -> 0 bytes .../drawable-xxxhdpi/ic_clear_black_24dp.png | Bin 377 -> 0 bytes .../ic_dashboard_black_24dp.png | Bin 127 -> 0 bytes .../res/drawable/appwidget_button_center.xml | 25 ---- .../res/drawable/appwidget_button_left.xml | 25 ---- .../res/drawable/appwidget_button_right.xml | 25 ---- .../main/res/layout/card_item_date_range.xml | 38 ------ .../main/res/layout/drawer_section_header.xml | 9 -- .../res/layout/fragment_account_detail.xml | 45 -------- .../res/layout/split_account_spinner_item.xml | 26 ----- app/src/main/res/values-af-rZA/strings.xml | 107 +++++------------ app/src/main/res/values-ar-rSA/strings.xml | 107 +++++------------ app/src/main/res/values-ca-rES/strings.xml | 107 +++++------------ app/src/main/res/values-cs-rCZ/strings.xml | 107 +++++------------ app/src/main/res/values-de/strings.xml | 105 +++++------------ app/src/main/res/values-el-rGR/strings.xml | 107 +++++------------ app/src/main/res/values-en-rGB/strings.xml | 107 +++++------------ app/src/main/res/values-es-rMX/strings.xml | 105 +++++------------ app/src/main/res/values-es/strings.xml | 105 +++++------------ app/src/main/res/values-fi-rFI/strings.xml | 107 +++++------------ app/src/main/res/values-fr/strings.xml | 104 +++++------------ app/src/main/res/values-hu-rHU/strings.xml | 107 +++++------------ app/src/main/res/values-in-rID/strings.xml | 107 +++++------------ app/src/main/res/values-it-rIT/strings.xml | 107 +++++------------ app/src/main/res/values-iw-rIL/strings.xml | 107 +++++------------ app/src/main/res/values-ja-rJP/strings.xml | 106 +++++------------ app/src/main/res/values-ko-rKR/strings.xml | 104 +++++------------ app/src/main/res/values-lv-rLV/strings.xml | 107 +++++------------ app/src/main/res/values-nb/strings.xml | 104 +++++------------ app/src/main/res/values-nl-rNL/strings.xml | 107 +++++------------ app/src/main/res/values-no-rNO/strings.xml | 104 +++++------------ app/src/main/res/values-pl-rPL/strings.xml | 104 +++++------------ app/src/main/res/values-pt-rBR/strings.xml | 105 +++++------------ app/src/main/res/values-pt-rPT/strings.xml | 105 +++++------------ app/src/main/res/values-ro-rRO/strings.xml | 107 +++++------------ app/src/main/res/values-ru/strings.xml | 108 +++++------------- app/src/main/res/values-sr-rSP/strings.xml | 107 +++++------------ app/src/main/res/values-sv-rSE/strings.xml | 107 +++++------------ app/src/main/res/values-sw600dp/flags.xml | 1 - app/src/main/res/values-tr-rTR/strings.xml | 107 +++++------------ app/src/main/res/values-uk-rUA/strings.xml | 107 +++++------------ app/src/main/res/values-vi-rVN/strings.xml | 45 -------- app/src/main/res/values-zh-rCN/strings.xml | 105 +++++------------ app/src/main/res/values-zh-rTW/strings.xml | 105 +++++------------ app/src/main/res/values/dimens.xml | 4 - app/src/main/res/values/donottranslate.xml | 5 - app/src/main/res/values/flags.xml | 1 - app/src/main/res/values/strings.xml | 51 +-------- app/src/main/res/values/styles.xml | 4 - .../res/xml/fragment_book_preferences.xml | 4 - app/src/main/res/xml/preference_headers.xml | 32 ------ 104 files changed, 996 insertions(+), 2739 deletions(-) delete mode 100644 app/src/main/res/drawable-hdpi-v14/appwidget_inner_focused_c.9.png delete mode 100644 app/src/main/res/drawable-hdpi-v14/appwidget_inner_focused_l.9.png delete mode 100644 app/src/main/res/drawable-hdpi-v14/appwidget_inner_focused_r.9.png delete mode 100644 app/src/main/res/drawable-hdpi-v14/appwidget_inner_pressed_c.9.png delete mode 100644 app/src/main/res/drawable-hdpi-v14/appwidget_inner_pressed_l.9.png delete mode 100644 app/src/main/res/drawable-hdpi-v14/appwidget_inner_pressed_r.9.png delete mode 100644 app/src/main/res/drawable-hdpi/action_about.png delete mode 100644 app/src/main/res/drawable-hdpi/action_help.png delete mode 100644 app/src/main/res/drawable-hdpi/appwidget_dark_bg_pressed.9.png delete mode 100644 app/src/main/res/drawable-hdpi/appwidget_inner_focused_c.9.png delete mode 100644 app/src/main/res/drawable-hdpi/appwidget_inner_focused_l.9.png delete mode 100644 app/src/main/res/drawable-hdpi/appwidget_inner_focused_r.9.png delete mode 100644 app/src/main/res/drawable-hdpi/appwidget_inner_pressed_c.9.png delete mode 100644 app/src/main/res/drawable-hdpi/appwidget_inner_pressed_l.9.png delete mode 100644 app/src/main/res/drawable-hdpi/appwidget_inner_pressed_r.9.png delete mode 100644 app/src/main/res/drawable-hdpi/content_new_holo_dark.png delete mode 100644 app/src/main/res/drawable-hdpi/drawer_shadow.9.png delete mode 100644 app/src/main/res/drawable-hdpi/ic_clear_black_24dp.png delete mode 100644 app/src/main/res/drawable-hdpi/ic_dashboard_black_24dp.png delete mode 100644 app/src/main/res/drawable-hdpi/ic_google_drive.png delete mode 100644 app/src/main/res/drawable-mdpi-v14/appwidget_inner_focused_c.9.png delete mode 100644 app/src/main/res/drawable-mdpi-v14/appwidget_inner_focused_l.9.png delete mode 100644 app/src/main/res/drawable-mdpi-v14/appwidget_inner_focused_r.9.png delete mode 100644 app/src/main/res/drawable-mdpi-v14/appwidget_inner_pressed_c.9.png delete mode 100644 app/src/main/res/drawable-mdpi-v14/appwidget_inner_pressed_l.9.png delete mode 100644 app/src/main/res/drawable-mdpi-v14/appwidget_inner_pressed_r.9.png delete mode 100644 app/src/main/res/drawable-mdpi/action_about.png delete mode 100644 app/src/main/res/drawable-mdpi/action_help.png delete mode 100644 app/src/main/res/drawable-mdpi/appwidget_dark_bg_pressed.9.png delete mode 100644 app/src/main/res/drawable-mdpi/appwidget_inner_focused_c.9.png delete mode 100644 app/src/main/res/drawable-mdpi/appwidget_inner_focused_l.9.png delete mode 100644 app/src/main/res/drawable-mdpi/appwidget_inner_focused_r.9.png delete mode 100644 app/src/main/res/drawable-mdpi/appwidget_inner_pressed_c.9.png delete mode 100644 app/src/main/res/drawable-mdpi/appwidget_inner_pressed_l.9.png delete mode 100644 app/src/main/res/drawable-mdpi/appwidget_inner_pressed_r.9.png delete mode 100644 app/src/main/res/drawable-mdpi/content_new_holo_dark.png delete mode 100644 app/src/main/res/drawable-mdpi/drawer_shadow.9.png delete mode 100644 app/src/main/res/drawable-mdpi/ic_clear_black_24dp.png delete mode 100644 app/src/main/res/drawable-mdpi/ic_dashboard_black_24dp.png delete mode 100644 app/src/main/res/drawable-mdpi/ic_google_drive.png delete mode 100644 app/src/main/res/drawable-xhdpi-v14/appwidget_inner_focused_c.9.png delete mode 100644 app/src/main/res/drawable-xhdpi-v14/appwidget_inner_focused_l.9.png delete mode 100644 app/src/main/res/drawable-xhdpi-v14/appwidget_inner_focused_r.9.png delete mode 100644 app/src/main/res/drawable-xhdpi-v14/appwidget_inner_pressed_c.9.png delete mode 100644 app/src/main/res/drawable-xhdpi-v14/appwidget_inner_pressed_l.9.png delete mode 100644 app/src/main/res/drawable-xhdpi-v14/appwidget_inner_pressed_r.9.png delete mode 100644 app/src/main/res/drawable-xhdpi/content_new_holo_dark.png delete mode 100644 app/src/main/res/drawable-xhdpi/drawer_shadow.9.png delete mode 100644 app/src/main/res/drawable-xhdpi/ic_clear_black_24dp.png delete mode 100644 app/src/main/res/drawable-xhdpi/ic_dashboard_black_24dp.png delete mode 100644 app/src/main/res/drawable-xhdpi/ic_google_drive.png delete mode 100644 app/src/main/res/drawable-xxhdpi/drawer_shadow.9.png delete mode 100644 app/src/main/res/drawable-xxhdpi/ic_clear_black_24dp.png delete mode 100644 app/src/main/res/drawable-xxhdpi/ic_dashboard_black_24dp.png delete mode 100644 app/src/main/res/drawable-xxxhdpi/ic_clear_black_24dp.png delete mode 100644 app/src/main/res/drawable-xxxhdpi/ic_dashboard_black_24dp.png delete mode 100644 app/src/main/res/drawable/appwidget_button_center.xml delete mode 100644 app/src/main/res/drawable/appwidget_button_left.xml delete mode 100644 app/src/main/res/drawable/appwidget_button_right.xml delete mode 100644 app/src/main/res/layout/card_item_date_range.xml delete mode 100644 app/src/main/res/layout/drawer_section_header.xml delete mode 100644 app/src/main/res/layout/fragment_account_detail.xml delete mode 100644 app/src/main/res/layout/split_account_spinner_item.xml delete mode 100644 app/src/main/res/xml/fragment_book_preferences.xml delete mode 100644 app/src/main/res/xml/preference_headers.xml diff --git a/app/src/main/res/drawable-hdpi-v14/appwidget_inner_focused_c.9.png b/app/src/main/res/drawable-hdpi-v14/appwidget_inner_focused_c.9.png deleted file mode 100644 index 5aafacd9caf7e6ef4e8064df54c0c6e22e14d7c6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 158 zcmeAS@N?(olHy`uVBq!ia0vp^tUxTn!3HE8oq077UXDFUDt9Y<{lfTh?wY`(`%43Qdq&^C2o1Kf>05pri)78&qol`;+0AYAA AYybcN diff --git a/app/src/main/res/drawable-hdpi-v14/appwidget_inner_focused_l.9.png b/app/src/main/res/drawable-hdpi-v14/appwidget_inner_focused_l.9.png deleted file mode 100644 index ab6e8f31e8c262ba3e2626bce4ca4b1769c7b20f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 216 zcmeAS@N?(olHy`uVBq!ia0vp^96&6>!3HEZNY`WoDVAa<&kznEsNqQI0P;BtJR*x3 z7`Qt@n9=;?>9s(?Do+>35RHj@FKpy&br5KISnsOr$ZcS-foUVp>#Ysp4GFwW8`+la za^r5_G3Ud9lkc8!Pjd_{T*Q#HJ};tZ0V9_Jqr~r5)7et9T_t|6n14!O$~{H4O%es4 z1q+s&C>VbD&%6GBAagd)nFBAIHm)yAU^2_w-LXnZ>0U$;W7Z1^k(3ExpMkDm@O1Ta JS?83{1ON=sNg)6L diff --git a/app/src/main/res/drawable-hdpi-v14/appwidget_inner_focused_r.9.png b/app/src/main/res/drawable-hdpi-v14/appwidget_inner_focused_r.9.png deleted file mode 100644 index 6c1b3c624e58b07cba79bb746a45ed9b8fe3ed5d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 219 zcmeAS@N?(olHy`uVBq!ia0vp^96&6>!3HEZNY`WoDVAa<&kznEsNqQI0P;BtJR*x3 z7`Qt@n9=;?>9s(?T2B|p5RHj@FBoziV&GxDFn!VF=6A9-%_ZNNZ1ejsD!))D3BTwo zCC;jOwP}H5eVjtgOwP;{qhN=tCAZzLJ?@E(o*tfJXrZ;?(6X#uM~x+BGrjg_m=>I1 z$NERGXWI|1N8jyhc=LiE{NVgzaUf@d$D9rMuRk$OoT>CEHnNcY#%g(kS8rAPfevBt MboFyt=akR{00VGLHvj+t diff --git a/app/src/main/res/drawable-hdpi-v14/appwidget_inner_pressed_c.9.png b/app/src/main/res/drawable-hdpi-v14/appwidget_inner_pressed_c.9.png deleted file mode 100644 index 470f5c038971950c6b5165355fc1cb0cc5264b2f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 157 zcmeAS@N?(olHy`uVBq!ia0vp^tUxTn!3HEbP0l+XkK^J6cU diff --git a/app/src/main/res/drawable-hdpi-v14/appwidget_inner_pressed_l.9.png b/app/src/main/res/drawable-hdpi-v14/appwidget_inner_pressed_l.9.png deleted file mode 100644 index e3aa8db1a377f5131fa6f7802f275d678245c273..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 224 zcmeAS@N?(olHy`uVBq!ia0vp^96&6>!3HEZNY`WoDVAa<&kznEsNqQI0P;BtJR*x3 z7`Qt@n9=;?>9s(?CQlc~5RHj@FKpyw3glsXps$paS-*Fwg88wtiP;Yw#F~~a4L;GE zxKx_s*s*5knmtYiz8wGEIVR5JnksPk%Dna7Z#Ep_`L!-{`Qn29pU-EV+?~I#HF<-c ztWkq2ugGiv1wT$Py72y~W46hD5XZb+%Hlx~OYL==4MzpH8+M!#5nP&nl>6sGiJ8&o SK2HX^hQZU-&t;ucLK6UYxloV* diff --git a/app/src/main/res/drawable-hdpi-v14/appwidget_inner_pressed_r.9.png b/app/src/main/res/drawable-hdpi-v14/appwidget_inner_pressed_r.9.png deleted file mode 100644 index 9e27d2fdc794f7f3db79699be5e7c4b35d1f6141..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 226 zcmeAS@N?(olHy`uVBq!ia0vp^96&6>!3HEZNY`WoDVAa<&kznEsNqQI0P;BtJR*x3 z7`Qt@n9=;?>9s(?7Ec$)5RHjjFKpyIlpw(R;Jwa~bBu4^s^&cuYkF62gDQ< UU)Zbg0o}vk>FVdQ&MBb@0A_qpc>n+a diff --git a/app/src/main/res/drawable-hdpi/action_about.png b/app/src/main/res/drawable-hdpi/action_about.png deleted file mode 100644 index 8f39c428ac6cacd0ad4fd9e7ab4252a77ce91a53..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1764 zcmaJ?X;2eq7!F0za*QByiN{V56ohOx2O)t#fg~6p2!TRSYh98JNg>%BOA;)tBa9k| z9j`Lrg(DzZ$67$N7MO|x3d${t##%eHf>taPPz$2%1_Aq{bZ2(I?|bKcp7(mb+5Gsp zC^QcMqP5JI>FO+x^_0!~MS2rS7~ zw;~|~g7q4Ck_ZzCVp(ETL4qwlB!fbQqX~qNFoOyfXCN4mhNR1tY~pa;6(S&)u!$*j z0a>7mL}c;}+ciky_P8YR_6#voLJSK9LJTaNK!IQ|U{GW#wJZahIL*t#_ZBlq1g2fE z3^wsyQX)Y-5Q%CKfKDO@h{=Hv5J)3IG%}OM^am(phzvp$kjw~x$SgX8MTUTxhloeh zNTe(whc^=oKe3523{$Z{P_NgM^i&e6Ne3Y&lL?Y3AcYcudjx2+l^ARYP-?wq890bm ztdXlQIjRIKjBpy7g|UfvrteBnsOD&u+L<=t4Fe6Z3WP{xOG&ei0>S^gDim|xT1<$% z&-XuxwMp431Qa4#G)p7K2PgHigi^60H3*EMnj{p>ob6(~48>5b3{?SpmrGbw9y0<84`R?G$Q%gbLQERO^O7T6nE`|k9tzH)zO zYBkUjeP%&pAjG$=wYB5d+u2T9eexpl_lomNYdaRdBP^?oc$9%6WM4 zIeWz3-tJl3P@BKa*wwtey21Lf682#5U`~EcDEg-JfYG7JCe6BP=e>{0ZF-*8oOr7< zS(hgC=0Bg>!S^?&{+XKd$_)D)x<4`|S)3Q|8)uADi!xpANL98!rt?1I`kR*YA2#^j z%`+~dt76BE(gg3f6!WLej1oa_flUIaGwRe^>fIvK&i-WM*W|4(>F&ta54gE~cbJ&5 zP;s_+Q&cm*yXnHj>v($#;eaPO@i*Hn%N`Z#O=2)`q@@c&kl}eoDp}Q|BLlrMkI1Z*a<;vz{^UhU$)M)aI)j%o#hofZ|`kc3k_6#)k;I*Ch<{-CY0?yxT|eIYg}<$ z%`faQG?#3fXc5o&H9?GeZpZM6>OqhUF*tAj6`i|B}|^WXGgjkRaxz9thAG61^D_FQsCYe&)= zve%GOdR=9UC?lp@8}j-dLfgu_ZkDaQ*I;i~RZ@obb{p^d;W1)^M>R1X_BBB-k3S{b z7ayBCFkInP!t-j3}ZcC`IeV^nVC67AM3R#!`@hZ$Dp+Fic?11O}ZO)k|f zOFNYbQIGgFKG*7nN1Qwy86rtav!H0DZvU@UkKwg(J?UO1eZ#Z!!|7hs8~t$4X3t!H zQOp$OT()5%aB>xI{lSmCcy8>Abjem+;YR>ny#6rCQ2)m9s|Y2(6V z-?qSx>gbZ9;a8i#S$~Qosc2Hf?X9T1n6kw=J!V~|cj_aZ2U@pI^^74Zw#Z3>L`qlRr^9$-;)*7r%7WG+gJlVd=BJ4~c(9v`8vcDI& Y6RKUD#(0B`J1qYlJ~xhYK4M$$KMBsYYybcN diff --git a/app/src/main/res/drawable-hdpi/action_help.png b/app/src/main/res/drawable-hdpi/action_help.png deleted file mode 100644 index 459bed76c5b9f546541383271a6c9b9ff9943093..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1544 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 N002ovPDHLkV1lMfCjkHe diff --git a/app/src/main/res/drawable-hdpi/appwidget_inner_focused_c.9.png b/app/src/main/res/drawable-hdpi/appwidget_inner_focused_c.9.png deleted file mode 100644 index a949bd2c3b670aca8fc067d63273f34d0356d824..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 221 zcmeAS@N?(olHy`uVBq!ia0vp^oIvc$!3HEtBK)#|lw^r(L`iUdT1k0gQ7VIDN`6wR zf@f}GdTLN=VoGJ<$y6JlqIypk$B>F!Z^ABeH7M}h@%*p#H^_c>NuZDSCJw1h%m;fP z+uS_Ncg9DGA*eg>OUsr8DOpY`vz}aAlEZPOu|VUkx|;p2x@9HJ*^mFM4my#%Vt>qa zvEFjc$eMGuJ3n6R{T*>w;UHx3vIVCg!00TN#`~Uy| diff --git a/app/src/main/res/drawable-hdpi/appwidget_inner_focused_l.9.png b/app/src/main/res/drawable-hdpi/appwidget_inner_focused_l.9.png deleted file mode 100644 index 4aaca6c504c54d3497ecca13075d86fdb5ed86f2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 400 zcmV;B0dM|^P)1RCwC#SV0QHKn&bezfkZ2p1lbD zfnGcbg5b%U)<5VsY<++z2%goWH$m_L{=hd#%+_t)ma4l25ha01AtYfkU1&Feh{DtW zfYK3CvD^kTXpxtwE0l5@4umMDA|bfa6WXS%%35)i{GLz|7ItqCfTXy{6i)~n!ch$I znnVM@JO*7gYivy_+LMyACN1}Ho&wt88C`3&47;^W0$M6{@Q!l1A@@_0sF z(U3_d_xmIK0RtX^HDC=`L>7@nWRK+AZ*d^d-@+8nM5{(vr)a*y(@oY8 uI^_EVFSQ=YG6>Z{ik1)sOrGMK00RK9D!78uy-_>>0000C8)Fj diff --git a/app/src/main/res/drawable-hdpi/appwidget_inner_focused_r.9.png b/app/src/main/res/drawable-hdpi/appwidget_inner_focused_r.9.png deleted file mode 100644 index 1fc0f900af97e530df8458ae1261fc71e1ddb3bf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 423 zcmV;Y0a*TtP)-a3QN2p+6)lPiU7GIa$##n8bHTtZ*D+> z8)9Z7K|~m0GM1pc!IHS;{uj9W;mS;SRiCVA@YLF%jsy%%$Z6TYGj14xpO|o14ipVo zFvR*xvhl$ya4u?w;G%5ccg{EE%!6&tm_Be`AC$y&Qz@{)ls7oe#mKmM30+|GENU;^ z6ck9sb>OZbf+tn&ppSxI@UoY|T0w(a|1fBaTJ67=a0Uu=3F?7*peCw`YN9>`HBn8} zud{lfKifxB;N@w>>1_M%Xi$qsoOkbms7?Ercq^`VZVDp0#{CCT`)a2kq5!-X@kf#7 zJn~y}h7$c53GKmIdy3P|1iZlPd%F~;iaoL<=f%YQ-WY}DAw&;kZ}GPP0{}BC;C@IZ RzI*@x002ovPDHLkV1n#!x3B;J diff --git a/app/src/main/res/drawable-hdpi/appwidget_inner_pressed_c.9.png b/app/src/main/res/drawable-hdpi/appwidget_inner_pressed_c.9.png deleted file mode 100644 index ca6f16cd1df9ce0e3bd8264544d0d29c4cef5b01..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 223 zcmeAS@N?(olHy`uVBq!ia0vp^oIvc$!3HEtBK)#|lw^r(L`iUdT1k0gQ7VIDN`6wR zf@f}GdTLN=VoGJ<$y6JlqDD^_$B>F!Nq_$Tw`Vp=NJ(Mg`kU?|$gFL3x?`14WFsdt zGxv}*Cb+fO~LBy9brFH%&tF6*k-x%`Z*T!?1Q|zG7KWh42*0HTe~{~ TRfS)G9K+!0>gTe~DWM4fP8&@z diff --git a/app/src/main/res/drawable-hdpi/appwidget_inner_pressed_l.9.png b/app/src/main/res/drawable-hdpi/appwidget_inner_pressed_l.9.png deleted file mode 100644 index 642eb3d326b518f34db902518167f5edc9aa73bc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 408 zcmV;J0cZY+P)45 zRs+s5dIL!iHilwmtOlAeFtDJAoM99Nk~4ugkXTo-AZb1+XXbF7KlX&jxE8*k>-#-io=+S91AJ`?*N^p2gK`9oQV(P z9Lbu^7y={{Km#Hu!z1V}NP{LM@E`;k0F;UW0t^81slj9|c<=rI0000yXq)0P!@`FgT6m1ydXWc$(1_NbW)siC{EiSV6oOO#kl& zl66QTvDm$U5Bz7CjilBXVF2;qH4Yfmjb{`wP^hOnBJi6c8_9Nd3PC>rfbY5P}Ql3^w)^1&PVosU-?Y zsp*+{wo31J?^jaDOtDo8H}y5}EpSfF$n>ZxN)4{^3rViZPPR-@vbR&PsjvbXkegbP zs8ErclUHn2VXFi-*9yo63F|8hm3bwJ6}oxF$}kgLQj3#|G7CyF^YauyCMG83 zmzLNn0bL65LT&-v*t}wBFaZNhzap_f-%!s00+w{G(#^lGsViy(A34k#nsr<$-vFf(ACh=#L&{!#L3yw z&D7A`#K{n**Cju>G&eP`1g19yq1ObbUQlAlEdbi=l3J8mmYU*Ll%J~r_Ow+dZnqfX zG!Lpb1-Dy_aO%|uIz}H9wMbD769T3m5EGtofgE_!Pt60S_ab1z4q0f*3(OQRJY5_^ zDsH`*smOQ8K%~vwiGeZv-~+WZOSZ=B1v}ZkA5bcg&|!bTpuiCPPfc~@%_GO8r_|{G zoD{$H=e4|Q-{R*n(~6#QC^RrIg@$%jp0M7yea4df9|g;jBtNOTHZI@cdUHutO?01w6Wim;H*=-YJSF-6)vTXc4;gaVh((-iXfZZqW+vo+u8V zBVlX)f0A7h-RPgQQ0YmtMO4nhGu#pz1)d)`lqGk1{e=6U^-ld^7jR%;WC0UGRzbha uDrBCXJI|cEda|~f-cO)#|k0wldT1B8LpG*1`DkP61PSG>8J9RwIIzPh7b zJflYMsk(bE^P@Beu4=(eGmA7oyR_x~a$D?{a5p37g|Cs|p&E~Q4@@WZu3cW0RzKfl zOG4W1`7gJ diff --git a/app/src/main/res/drawable-hdpi/ic_dashboard_black_24dp.png b/app/src/main/res/drawable-hdpi/ic_dashboard_black_24dp.png deleted file mode 100644 index b832916f5e97b28900f42852d245eead209cdb4e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 126 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 JPDHLkV1g(1OB( zW}Sf0Q#?F8cTU`zvB0EDVH1n69w$#jIMd<12Apn_dJk!T6rFw~myNA0U&H8v!=L^( aNe1nC!u;#b#fAW_V(@hJb6Mw<&;$Tm$2tE1 diff --git a/app/src/main/res/drawable-mdpi-v14/appwidget_inner_focused_r.9.png b/app/src/main/res/drawable-mdpi-v14/appwidget_inner_focused_r.9.png deleted file mode 100644 index 8d22c56178e0bb2c2629a8defc0d8e3c717cc5d1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 184 zcmeAS@N?(olHy`uVBq!ia0vp^Y(UJ%0U~+k=c)oJmSQK*5Dp-y;YjHK@;M7UB8wRq zxI00Z(fs7;wLrlrPZ!4!iK$aB7;-f@@VH#G@4hQ{SUIA5`8Sj3YKGal$DNKSuoWs? zoxOx(8>3H0E}N&%>un6i7aG+v^%rU^U{ZEy(Kc=Mk4a6T85=FJT7-frc=6y85}Sb4q9e03;nN6aWAK diff --git a/app/src/main/res/drawable-mdpi-v14/appwidget_inner_pressed_l.9.png b/app/src/main/res/drawable-mdpi-v14/appwidget_inner_pressed_l.9.png deleted file mode 100644 index e49e8a9b89d6ff6d24f355fda54edb826e63a775..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 198 zcmeAS@N?(olHy`uVBq!ia0vp^Y(UJ%0U~+k=c)oJmSQK*5Dp-y;YjHK@;M7UB8wRq zxI00Z(fs7;wLrlPPZ!4!iK$aB7;-fS@Gx9hekHhM65m{_${xAptG)T3a$0pA{~+L` zsCQo}zV4vIi4T(ml$25g102oI@PEk&^go|n?is6p=UZ~=s_!-T*?c+qK8o{w&kL9v o$9i9=N44T{^HcSgofDUeDzl1Q5j(&0Aka<*Pgg&ebxsLQ08Vs31poj5 diff --git a/app/src/main/res/drawable-mdpi-v14/appwidget_inner_pressed_r.9.png b/app/src/main/res/drawable-mdpi-v14/appwidget_inner_pressed_r.9.png deleted file mode 100644 index a54ecd0da36154b159c6efb1b4396a1311a8b2eb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 189 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 diff --git a/app/src/main/res/drawable-mdpi/appwidget_inner_focused_c.9.png b/app/src/main/res/drawable-mdpi/appwidget_inner_focused_c.9.png deleted file mode 100644 index 1450e65b11f4c31d454ed6c55dadd6f535b6db87..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 212 zcmeAS@N?(olHy`uVBq!ia0vp^>_BY7!3HGV_9$HdQj#UE5hcO-X(i=}MX3yqDfvmM z3ZA)%>8U}fi7AzZCsS>Jipo4)978H@CH?vT-=3Mrz{rSc=YPqEH5;DzxV3Q2aA9L+ z7G{=XnD~eP=t~FX#2hvztH=jdi4W{eRc1QWIhP%Auzopr09DCJRsaA1 diff --git a/app/src/main/res/drawable-mdpi/appwidget_inner_focused_l.9.png b/app/src/main/res/drawable-mdpi/appwidget_inner_focused_l.9.png deleted file mode 100644 index 6e8f100e4c1e84797c0a13898163a65c76b23cfc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 325 zcmV-L0lNN)P)N~TmdB883+J~h8bX!TcBdCKs=oxs``(qfgem~{r^C$2K)d?o(UArV1fSxG(hE<7)S&pJ8Af6 z_(wp)ztK<{4b~Cl>CyZ!8mt4LfeE=CL6a(r2`carESSwGKwK^QpW!l8u#;E~H9-6q zh|?G{8C!r@5|;)YLVe+SSM2O!=A#4q5c{ztWe1=dU>CP+b101#jR XzO9#AucSuv00000NkvXXu0mjfzD0j1 diff --git a/app/src/main/res/drawable-mdpi/appwidget_inner_focused_r.9.png b/app/src/main/res/drawable-mdpi/appwidget_inner_focused_r.9.png deleted file mode 100644 index bc8757b88c6146c8eded858ae0d7eb89f728ac79..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 324 zcmV-K0lWT*P)&_BY7!3HGV_9$HdQj#UE5hcO-X(i=}MX3yqDfvmM z3ZA)%>8U}fi7AzZCsS>Jipo4)978H@CH?vT-=3Mrz{rSc=YPqEH5;Bty0vi4XwhV5 z7G{=XnD~ePsG5UvVh$UVRpbM!#0U1KDl;AGoXd_lSa$@StoNAopKWh8``mE0*blwO z)bFa!oN#*A!3l2zPcO7w@lN~TmdB883+I-FnJni@_&XF29Ra{8Cro@hQS2r3>b!p zg8|Hj79eH@;#mx)|7-Bs0MYQDK^Mv|$ExZ-rUrg6ZT|lQu^R9LBzY!KI3s%sr-uK? zp+o{8*-67k!#@HV{*8vxNDbBznKVZ8!)UM$fCeVyb_7kTEGDSHN3h^LMgih#(f`6pHRCwBA{Qv(y0}L=SG6K2(85!^Z zkmUdWr?JXC1>&F6chvfgMm*m%!JAx0P#_6zy|&^ltbkWFr9=AnEn3%mFEX>S>PJ*0VH{n zG=SoXiGf5wTPF=_4gUye_%|9#BQ;n@WYQST52L|402)ZDvOw(+8aQblBQKQog*XlW z8G4|sv-ma4VUz@t*+85I#D9Uf2B86=2$x+o3@QJQ5OUHBAU*|dV5K4&SfDl=xJf{4 cumS`a07$p7Cc&{Q%m4rY07*qoM6N<$f}z2IdjJ3c diff --git a/app/src/main/res/drawable-mdpi/content_new_holo_dark.png b/app/src/main/res/drawable-mdpi/content_new_holo_dark.png deleted file mode 100644 index 4d5d484b39978c1b37dec37c77c6b3d9de6ce74e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1090 zcmaJ=O-K|`9G`9R1I+@-A~AYQR$%VTylq#T!A*B}c6Ff)FnCz_QqW8&^GYqz4zz;`~83ak9Vf8 zx4W*Uv4&xox>S#xraSOfbtV0~#o|x8Z6}E=>Bqw)uR4f{>Uao&l&OxOG*b2P{rfkEPKZeOOA%$<j@ z^LZ}6pTo`w4@FVrg&-dc2B=5C9k+;D2w3i}5`&Cf%`t3ZU<-JR>JT0y5tb_blY(il z(OT|mnP|cIf@<@S6TFm4j*9YsSJPbcc1aqo>-{IOn;EwepGGbob2Qqx;ay%RTZ%hK zCD_See5_Q(z8ogl&0!nF`@s zOUuihPr5ejIqj>wx@l(KH_-K|>gm9j#>cTqe|Blr0qHF4)vzWG;HqqD zqVvGwmW%!Yd3y81t{2s)w*FStmj!&PX`yEReb>^-2^BmSXTCMaTaw^j_vC%M@47G9 zT34UGGFWk8hj0G!t%i!vJw2y{53_rtJKs6J+HlLg*HgK-KC0lFQr&V^Jikcta~K>v lH-F>k;f_ny-P3+%!$xNJBes5e+wwl|Ri_fY^6l8*)Nc=SU$6iG diff --git a/app/src/main/res/drawable-mdpi/drawer_shadow.9.png b/app/src/main/res/drawable-mdpi/drawer_shadow.9.png deleted file mode 100644 index ffe3a28d77c72094021013c6442560803b3d344c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 142 zcmeAS@N?(olHy`uVBq!ia0vp^96+qZ!3HFgEN0vWQY^(zo*^7SP{WbZ0pxQQctjR6 zFmQK*Fr)d&(`$i(2A(dCAr_~TfBgS%&&>BhUX5L;x$y=|hic;t)=0q~!&M0(2Uj#r iT+R^X@#lD(V-Z7IzK5^$=Dm!pL5PtJwWQDwD%^7mKX72vfc*YT)xac;A{}-v*=ps zr9JO5s)Jrvt$1~wJ<6?Hv?}9?*Yy)i*G<&jSt@L~vF4xM{qD^_8RvC6y^4F&G81Sm NgQu&X%Q~loCIAp8KMVi> diff --git a/app/src/main/res/drawable-mdpi/ic_dashboard_black_24dp.png b/app/src/main/res/drawable-mdpi/ic_dashboard_black_24dp.png deleted file mode 100644 index c0cb8620dc2c227e1e774f4505011d81c5387510..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 92 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM0wlfaz7_*1WltB!kP60Ri9HYOwT|eStzs0N pB(tWZoBfyd7^36&4Zm}mq{DIuuDq(+t|vT-4n1QOV{YZtXK#uaI0i=mZ8 zcSctl5|vS7TnJKwDaC)SRYOAfGeQe=I`1AA_rBLQl`_bp8(wnfzIl0b&v(A_o$uZe zGh;n3Wu32e2aro|L}bku3&-{yIF|cu^!2CG_r(83!i<3U=fJ`Eg|ElfS}!fVWH)O7 zC>+~Y1716qNI$mccYZN(Ukr%I zu&5v^NPCfa2XCFr({uu=ppj=oS1{n2K}PSZ3*ynfUJ>^CBn4E^a?I!d6#3{v6Ul^_ z29Es5$|Bv1r}y;#|27mxQGrMyWucr0fp>o?aP3Btb%RLIgenq6S%(0Fi{HOey=MTW z5BvH>q${I8w41cYNTf_R^BkEp&=4qqNP!?(hg5;#dlF&k#QrKUEkc!4T&Z{xBRPK& z5XzC&|9I7Ci}TgxEMSJ2!(4(nKV$ZCVb`fzOCl5)7Qrv7;*$v)keniM0dmT`MDLf1 z(LkzsBLp@F=KRwT$gts-N>DnnzZ>CzNC+4}r1ykw5S1B3M)Ex;#|*1s=CieQSjaMf zFzdek-AnzgXka*{!3QMNH&j9dDdjx%CiO0|WzKh3;8-*a-}tFu=}&YZ9F^Xwd#t}d|H4D~mIB}S^48~)GD zC_esX)nHf*5Cap;3<J#3;wg!y6($+9PrbX14d{ zH_!J1wG|8)^kZ~^kurvXIm4U^?> zU!JaW=E5A)7Zmh!VC5GBd|2PiRzC#z0s%=ML8;++lY!~|B4w; z=FVcyND_y0UYO1~EJ?C;^0F}V@q4drUQq&!34>lS-%cql=}s$)dCKRX1`IKWYMx5x zR`SI%8Wd_6e>QjEoFhq$B#6-^j-=VdIhRHoS|vim4yKg*IhciWm^0#EHxu2A!8s%< zQ0dr8u24ZmQBfjQB8`aDe^(VWoE0QHv;Z(Ob+mJA6@bQ}@fkB4%IaTKqGAQb2T@G{D4GUj*+E6Z&gnr6tmOkEW;Wx4ht_pQ`P>%N<`rbJld!rjB-gaz_A-!{c?pvtVBR`OA+`yx4&j+G%^N2hkjvxtifT zQ8+W4h4Pts9W9em*l&jJ>V%EM<6~)$0<=#)hZfr@b#(jSS$n3#g2)^$F|YX4_%Z|r zP@5d-sI|Hs4*-;BpP|^;MEvMBkSOza3)wnzByMRmlZ3?i=iY)@wkL3~pS zJ7N|owDdgN72Zj1k0$ItsD9yW=zGS81sip?{}wV^TCX-AXcmK~tDnm{r-UW|;4?4D diff --git a/app/src/main/res/drawable-xhdpi-v14/appwidget_inner_focused_l.9.png b/app/src/main/res/drawable-xhdpi-v14/appwidget_inner_focused_l.9.png deleted file mode 100644 index ce9decd19c78cff06e3fefb0fc042e57fd1f5ac0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 242 zcmeAS@N?(olHy`uVBq!ia0vp^Ahr|-8<6aKa#jsUu@pObhHwBu4M$1`kk47*5n0T@ zz}*SLjOHg#uLTND@pN$vvFN>e$&l-?0guB){l{!pQ=E=ld7rzG{A7=%1OEf#l2135 zdbc}GIoif^tmt@#;4yyB6K@R8jlpZzoUdeA z7FIv~^HHfS2eS3Q_-<{!6k?rmEO5b3Z7!4W&*iTdABflg^X*W%zE4;vyTJ1eOoA1R j@><5-tCW^8qcvELbp5`3|Kun z14Py>V3>Yw^8Q2HIvNZD1r;4HKl{6&-m%^I)5i@vl~+w<^uBT<>FyTGgEdNpjk4#j zZ0vl$RjajBbI#O5t3 diff --git a/app/src/main/res/drawable-xhdpi-v14/appwidget_inner_pressed_c.9.png b/app/src/main/res/drawable-xhdpi-v14/appwidget_inner_pressed_c.9.png deleted file mode 100644 index defdbb9c00bc91f79df75cf952d9dbd3aa93b694..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 157 zcmeAS@N?(olHy`uVBq!ia0vp^Y(Ol<0V1c|&|LwfSc;uILpXq-h9ji|$mcBZh%9Dc z;O+!rM)Q-W*8&CYJzX3_G$ua1w2_lRLBRQeX(1Prqd>#M`jUf576y9z8eR2%v)n$qAaJ(-va81< zHgL}3*D+fkacmQ}_>(l>M^h4S^v`UR4G~oqaVQF%KTB`}lh^@~8BexL)VvjvV)9O4 z4w-*z*OcoA_T~Rmcsg@yM%(8g3%%BL;iU^C|1+IkeImf;>)RLdhr>1ZOg~htX~VFV zGx>vf0=LcDz9SFTAKeS(opSg8k~T+Wu;PXe@rO_@SkEnB|A2N$(!- pGA$H6^(c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxKsVXI%uvD1M9IxIyg#@@$ndN=gc>^!3Zj z%k|2Q_413-^$jg8E%gnI^o@*kfhu&1EAvVcD|GXUm0>2hq!uR^WfqiV=I1GZOiWD5 zFD$Tv3bSNU;+l1ennz|zM-B0$V)JVzP|XC=H|jx7ncO3BHWAB;NpiyW)Z+ZoqGVvir744~DzI`cN=+=uFAB-e&w+(vKt_H^esM;Afr4|esh**NZ(?$0 z9!LbN!`Ii!Gq1QLF)umQ)5TT^Xog;9W{Q=Yp{a|7i;J77lYyI|p{t>#iJ_&diIcOV zo2j9>iIX8ruSMv>2~2MaT(7GEPQ9SSkXrz>*(J3ovn(~mttdZN0qkk3Ox$j9 z!D${;ZwgMgxVYlfs}FRHJ}7FDq8cUyOg|tdJmCU4@T8xb2Tbopz=Z9QHRk~X1Eaa8 zi(^Q|tv9!Qxegf!v_0$$aeO>+Pjj{Ty#t#&%nKghv#wC7|G@s3W%Gt0l?z)0Kk?Oc zi)nuKlsdgd>iL{0F`)bj1}}DNopmzix@~^e@3RJL!XnFzoEvtTX|sy^CY}h?tI7Py zaKcE4S#u9eJu>}kRr-^C=T>+9>B)~>oeO=2&+p+?Fi3E>pdO#jmsf{h^)Sw@nwUb}i8V z%C7wR$ZLlyN)pD*a+Cf|4880>&wa#)f(I4n0Oe1ucaK-UG2dP67+QSb6Mw<&;$U2b(L5E diff --git a/app/src/main/res/drawable-xhdpi/drawer_shadow.9.png b/app/src/main/res/drawable-xhdpi/drawer_shadow.9.png deleted file mode 100644 index fabe9d96563785c7d6b008bb3d8da25e816c343c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 174 zcmeAS@N?(olHy`uVBq!ia0vp^{6Or)!3HEd1bTh|DVAa<&kznEsNqQI0P;BtJR*x3 z7`Qt@n9=;?>9s(?08bak5Rc<;uP@|nU=UzFz%6x@#ly{E6Z5RU$2Hhw*i>Gs`(~~C zr~2_>(e4Ka`gpa)&de}Ks*u{@U5E@m-v9X3<$3NT3r3p*`<7|@n1Ecw;OXk;vd$@? F2>^8yH@N@+ diff --git a/app/src/main/res/drawable-xhdpi/ic_clear_black_24dp.png b/app/src/main/res/drawable-xhdpi/ic_clear_black_24dp.png deleted file mode 100644 index 6bc437298ab7bfbfbf128acdf5849e304b3c6903..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 235 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA0wn)(8}b0DO`a}}Ar*{or)}gqq9DL3|6^j{ z=hBm&C)~N@b{5Sp4w$_m*}geiNBekToKL{TWpQP5xqRwZuPjq53pI^Z3A|FbUaRcY z{O^J0fmhZ3y?R?;$@JP$iP2W!Cd>8DEZ3V|*s2`1aV~kzx#D@}&nn~61{Xzi+b(jKntiPo%%5fP`-@uPgu|~7wTA~r iR9eY#70Eo`TEl2=&Qt5V_=GOd^$eb_elF{r5}E*WmdKI;Vst0C>J5Hvj+t diff --git a/app/src/main/res/drawable-xhdpi/ic_google_drive.png b/app/src/main/res/drawable-xhdpi/ic_google_drive.png deleted file mode 100644 index 4bbb4b80982f49d2ccd1a709e81d39a26fae0c0f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6676 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 diff --git a/app/src/main/res/drawable-xxhdpi/drawer_shadow.9.png b/app/src/main/res/drawable-xxhdpi/drawer_shadow.9.png deleted file mode 100644 index b91e9d7f285e8110ba3ba4e72cc6f0416eb3a30a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 208 zcmeAS@N?(olHy`uVBq!ia0vp^VnCe4!3HEl*p=S^DajJoh?3y^w370~qErUQl>DSr z1<%~X^wgl##FWaylc_d9MMa)2jv*QM-d<4Ta$*#5x!8O#VbcvCx78OjjCM19-`|n` zlcQ;yJWqK-!X?h+v^9}Es?F+hJ07=b>sdT*QRcgm+^%b8|A7C`|A*gYPjm3$2miRv cpZdzQt0C%<%j~>EK-(ESUHx3vIVCg!0CP)0sQ>@~ diff --git a/app/src/main/res/drawable-xxhdpi/ic_clear_black_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_clear_black_24dp.png deleted file mode 100644 index 51b4401ca053ca8cad6e9903646709a2f44444df..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 309 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY0wn)GsXhaw{&>1LhEy=VysgJ$~x+g}6OvsxLimbaeN-!xQItoH=3nd`|H@dF!kYp_dChV`b9AudQLRn7yg2 zv~UiON9*MGOYAy6D=_*g@;c3(5`S(Hi^X;wk8Ms*3SMP^!WTf z-E_DAd$kLMWY`Z`);MuKF9=d$?_mzLoFj7Xp~|_3ODg!(3;AT&wLoS^O+0tsvcZJ& zD(m_$dNm(!9@n+CTyDa$w$*a^-$!>qK3P{4end8+qbAwoFEAV!JYD@<);T3K0RXh> Be)#|Z diff --git a/app/src/main/res/drawable-xxhdpi/ic_dashboard_black_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_dashboard_black_24dp.png deleted file mode 100644 index ad14dfeb9fcb3669d59c0077ab851f2cc95eff6b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 126 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xf3?%cF6g~}cLi3NSnFL zkGQYSP&@m;*IK6Q-#O#%t5^JG`{BMqf8+n2Z|={&zE4otx%%IU37m2NTYWzKGCBDA zGRyk&LVN6HtbDhUpKVe#i0?1F$Iko1uY_g0mfJ~qyju(6`+^n!Z*4iz5hh+Bv?p(-%zt$M-+!?Iw6Q#P16<$f#A@_7oVT44$rjF6*2U FngA8*uUr5C diff --git a/app/src/main/res/drawable-xxxhdpi/ic_dashboard_black_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_dashboard_black_24dp.png deleted file mode 100644 index 8fad114fe9f3f5a207a2dd9008c5e94ce7f6330d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 127 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeK3?y%aJ*@^(YymzYu0R?HmZtAK52P4Ng8YIR z9G=}s19H?oT^vIy7?UNgH2enw`2#E`=LGUuuw - - - - - - diff --git a/app/src/main/res/drawable/appwidget_button_left.xml b/app/src/main/res/drawable/appwidget_button_left.xml deleted file mode 100644 index 7382f05ff..000000000 --- a/app/src/main/res/drawable/appwidget_button_left.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - diff --git a/app/src/main/res/drawable/appwidget_button_right.xml b/app/src/main/res/drawable/appwidget_button_right.xml deleted file mode 100644 index a81225917..000000000 --- a/app/src/main/res/drawable/appwidget_button_right.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - diff --git a/app/src/main/res/layout/card_item_date_range.xml b/app/src/main/res/layout/card_item_date_range.xml deleted file mode 100644 index 4cc2531af..000000000 --- a/app/src/main/res/layout/card_item_date_range.xml +++ /dev/null @@ -1,38 +0,0 @@ - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/drawer_section_header.xml b/app/src/main/res/layout/drawer_section_header.xml deleted file mode 100644 index 79927e90e..000000000 --- a/app/src/main/res/layout/drawer_section_header.xml +++ /dev/null @@ -1,9 +0,0 @@ - - \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_account_detail.xml b/app/src/main/res/layout/fragment_account_detail.xml deleted file mode 100644 index 9cda56040..000000000 --- a/app/src/main/res/layout/fragment_account_detail.xml +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - - - - -