From ad9877d9459e2006e154a1b478838020e791d49e Mon Sep 17 00:00:00 2001 From: Moshe Date: Mon, 2 Oct 2017 13:03:50 +0300 Subject: [PATCH 001/131] IRR defined double currency. --- app/src/main/res/raw/iso_4217_currencies.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/raw/iso_4217_currencies.xml b/app/src/main/res/raw/iso_4217_currencies.xml index 1312e8db2..27a93a61b 100644 --- a/app/src/main/res/raw/iso_4217_currencies.xml +++ b/app/src/main/res/raw/iso_4217_currencies.xml @@ -1313,7 +1313,7 @@ exchange-code="364" parts-per-unit="1" smallest-fraction="1" - local-symbol="﷼﷼" + local-symbol="﷼" /> From c3754782330cb0f44ce1c93317d16631f3134b82 Mon Sep 17 00:00:00 2001 From: Moshe Date: Mon, 2 Oct 2017 13:22:39 +0300 Subject: [PATCH 002/131] copy currencies from GnuCash desktop. --- app/src/main/res/raw/iso_4217_currencies.xml | 489 ++++++++++--------- 1 file changed, 252 insertions(+), 237 deletions(-) diff --git a/app/src/main/res/raw/iso_4217_currencies.xml b/app/src/main/res/raw/iso_4217_currencies.xml index 27a93a61b..2a1dfbbd2 100644 --- a/app/src/main/res/raw/iso_4217_currencies.xml +++ b/app/src/main/res/raw/iso_4217_currencies.xml @@ -35,7 +35,7 @@ Sort order by ISO codes for simpler maintainance - File source: https://github.com/Gnucash/gnucash/blob/master/src/engine/iso-4217-currencies.xml + File source: https://github.com/Gnucash/gnucash/blob/master/libgnucash/engine/iso-4217-currencies.xml --> @@ -53,7 +53,7 @@ parts-per-unit="100" smallest-fraction="100" local-symbol="₣" - /> + /> @@ -67,7 +67,7 @@ parts-per-unit="100" smallest-fraction="100" local-symbol="₧" - /> + /> @@ -81,7 +81,7 @@ parts-per-unit="100" smallest-fraction="100" local-symbol="Dhs" - /> + /> @@ -95,7 +95,7 @@ parts-per-unit="100" smallest-fraction="100" local-symbol="" - /> + /> + /> + /> + /> @@ -148,7 +148,7 @@ parts-per-unit="100" smallest-fraction="100" local-symbol="NAƒ" - /> + /> + /> @@ -175,7 +175,7 @@ parts-per-unit="100" smallest-fraction="100" local-symbol="" - /> + /> @@ -189,7 +189,7 @@ parts-per-unit="100" smallest-fraction="100" local-symbol="" - /> + /> @@ -203,7 +203,7 @@ parts-per-unit="100" smallest-fraction="100" local-symbol="" - /> + /> + /> @@ -230,7 +230,7 @@ parts-per-unit="100" smallest-fraction="100" local-symbol="öS" - /> + /> + /> @@ -257,7 +257,7 @@ parts-per-unit="100" smallest-fraction="100" local-symbol="Afl." - /> + /> @@ -271,7 +271,7 @@ parts-per-unit="100" smallest-fraction="100" local-symbol="" - /> + /> @@ -285,7 +285,7 @@ parts-per-unit="100" smallest-fraction="100" local-symbol="m" - /> + /> @@ -299,7 +299,7 @@ parts-per-unit="100" smallest-fraction="100" local-symbol="" - /> + /> + /> + /> + /> @@ -352,7 +352,7 @@ parts-per-unit="100" smallest-fraction="100" local-symbol="fr." - /> + /> @@ -366,7 +366,7 @@ parts-per-unit="100" smallest-fraction="100" local-symbol="" - /> + /> @@ -380,7 +380,7 @@ parts-per-unit="100" smallest-fraction="100" local-symbol="лв" - /> + /> @@ -394,7 +394,7 @@ parts-per-unit="1000" smallest-fraction="1000" local-symbol="BD" - /> + /> + /> + /> + /> + /> @@ -460,7 +460,7 @@ parts-per-unit="100" smallest-fraction="100" local-symbol="" - /> + /> @@ -474,7 +474,7 @@ parts-per-unit="100" smallest-fraction="100" local-symbol="" - /> + /> + /> @@ -501,7 +501,7 @@ parts-per-unit="100" smallest-fraction="100" local-symbol="" - /> + /> + /> + /> + /> @@ -554,7 +554,7 @@ parts-per-unit="100" smallest-fraction="1" local-symbol="" - /> + /> @@ -568,7 +568,7 @@ parts-per-unit="1" smallest-fraction="100" local-symbol="Br" - /> + /> + /> + /> + /> + /> @@ -634,7 +634,7 @@ parts-per-unit="100" smallest-fraction="100" local-symbol="" - /> + /> + /> @@ -661,7 +661,7 @@ parts-per-unit="100" smallest-fraction="100" local-symbol="" - /> + /> @@ -675,7 +675,7 @@ parts-per-unit="10000" smallest-fraction="10000" local-symbol="" - /> + /> @@ -689,7 +689,7 @@ parts-per-unit="100" smallest-fraction="1" local-symbol="$" - /> + /> @@ -703,7 +703,7 @@ parts-per-unit="100" smallest-fraction="100" local-symbol="CN¥" - /> + /> + /> + /> + /> + /> + /> + /> @@ -795,7 +795,7 @@ parts-per-unit="100" smallest-fraction="100" local-symbol="£" - /> + /> + /> @@ -822,7 +822,7 @@ parts-per-unit="100" smallest-fraction="100" local-symbol="DM" - /> + /> + /> + /> + /> @@ -875,7 +875,7 @@ parts-per-unit="100" smallest-fraction="100" local-symbol="DA" - /> + /> @@ -889,7 +889,7 @@ parts-per-unit="100" smallest-fraction="100" local-symbol="S/." - /> + /> @@ -903,7 +903,7 @@ parts-per-unit="100" smallest-fraction="100" local-symbol="kr" - /> + /> @@ -917,7 +917,7 @@ parts-per-unit="100" smallest-fraction="100" local-symbol="£E" - /> + /> + /> @@ -944,7 +944,7 @@ parts-per-unit="100" smallest-fraction="100" local-symbol="₧" - /> + /> + /> + /> @@ -984,7 +984,7 @@ parts-per-unit="100" smallest-fraction="100" local-symbol="mk" - /> + /> + /> + /> @@ -1024,7 +1024,7 @@ parts-per-unit="100" smallest-fraction="100" local-symbol="₣" - /> + /> + /> + /> @@ -1064,7 +1064,7 @@ parts-per-unit="100" smallest-fraction="100" local-symbol="" - /> + /> + /> + /> + /> + /> @@ -1130,7 +1130,7 @@ parts-per-unit="100" smallest-fraction="100" local-symbol="Δρ." - /> + /> + /> @@ -1157,7 +1157,7 @@ parts-per-unit="100" smallest-fraction="100" local-symbol="" - /> + /> + /> + /> + /> + /> + /> + /> + /> @@ -1262,7 +1262,7 @@ parts-per-unit="100" smallest-fraction="100" local-symbol="£" - /> + /> + /> + /> + /> + /> + /> @@ -1341,7 +1341,7 @@ parts-per-unit="1" smallest-fraction="1" local-symbol="₤" - /> + /> + /> + /> + /> + /> + /> + /> + /> + /> + /> + /> + /> + /> + /> + /> + /> + /> + /> + /> @@ -1589,7 +1589,7 @@ parts-per-unit="100" smallest-fraction="100" local-symbol="Flux" - /> + /> @@ -1603,7 +1603,7 @@ parts-per-unit="100" smallest-fraction="100" local-symbol="Ls" - /> + /> + /> + /> + /> + /> @@ -1669,7 +1669,7 @@ parts-per-unit="500" smallest-fraction="500" local-symbol="" - /> + /> + /> @@ -1696,7 +1696,7 @@ parts-per-unit="100" smallest-fraction="100" local-symbol="" - /> + /> + /> + /> + /> + /> @@ -1762,7 +1762,7 @@ parts-per-unit="100" smallest-fraction="100" local-symbol="Lm" - /> + /> + local-symbol="₨" + /> + /> + /> @@ -1815,7 +1815,7 @@ parts-per-unit="100" smallest-fraction="100" local-symbol="Mex$" - /> + /> @@ -1829,7 +1829,7 @@ parts-per-unit="100" smallest-fraction="100" local-symbol="" - /> + /> + /> @@ -1856,7 +1856,7 @@ parts-per-unit="100" smallest-fraction="100" local-symbol="" - /> + /> + /> + /> + /> @@ -1909,7 +1909,7 @@ parts-per-unit="100" smallest-fraction="100" local-symbol="" - /> + /> + /> @@ -1936,7 +1936,7 @@ parts-per-unit="100" smallest-fraction="100" local-symbol="" - /> + /> + /> + /> + /> + /> + /> + /> + /> + /> + /> + /> @@ -2080,7 +2080,7 @@ parts-per-unit="100" smallest-fraction="100" local-symbol="$" - /> + /> @@ -2094,7 +2094,7 @@ parts-per-unit="100" smallest-fraction="100" local-symbol="₲" - /> + /> + /> @@ -2121,7 +2121,7 @@ parts-per-unit="100" smallest-fraction="100" local-symbol="" - /> + /> @@ -2135,7 +2135,7 @@ parts-per-unit="100" smallest-fraction="100" local-symbol="" - /> + /> + /> @@ -2161,8 +2161,8 @@ exchange-code="643" parts-per-unit="100" smallest-fraction="100" - local-symbol="руб" - /> + local-symbol="₽" + /> + /> + /> + /> + /> @@ -2228,7 +2228,7 @@ parts-per-unit="100" smallest-fraction="100" local-symbol="" - /> + /> + /> @@ -2255,7 +2255,7 @@ parts-per-unit="100" smallest-fraction="100" local-symbol="" - /> + /> + /> + /> + /> @@ -2308,7 +2308,7 @@ parts-per-unit="100" smallest-fraction="100" local-symbol="" - /> + /> @@ -2322,7 +2322,7 @@ parts-per-unit="100" smallest-fraction="100" local-symbol="" - /> + /> + /> + /> + /> @@ -2375,7 +2375,7 @@ parts-per-unit="100" smallest-fraction="100" local-symbol="" - /> + /> + /> @@ -2402,7 +2402,7 @@ parts-per-unit="100" smallest-fraction="100" local-symbol="" - /> + /> + /> + /> + /> @@ -2455,7 +2455,7 @@ parts-per-unit="1" smallest-fraction="1" local-symbol="" - /> + /> + /> @@ -2482,7 +2482,7 @@ parts-per-unit="100" smallest-fraction="100" local-symbol="" - /> + /> + /> + /> + /> + /> + /> + /> + /> + /> + /> + /> @@ -2626,7 +2626,7 @@ parts-per-unit="100" smallest-fraction="100" local-symbol="$n" - /> + /> @@ -2640,7 +2640,7 @@ parts-per-unit="100" smallest-fraction="100" local-symbol="$s" - /> + /> + /> + /> + /> @@ -2693,7 +2693,7 @@ parts-per-unit="100" smallest-fraction="100" local-symbol="" - /> + /> + /> + /> + /> + /> + /> @@ -2772,7 +2772,7 @@ parts-per-unit="100" smallest-fraction="100" local-symbol="" - /> + /> + /> @@ -2799,7 +2799,7 @@ parts-per-unit="100" smallest-fraction="100" local-symbol="ZK" - /> + /> @@ -2813,7 +2813,7 @@ parts-per-unit="100" smallest-fraction="100" local-symbol="ZK" - /> + /> @@ -2827,7 +2827,7 @@ parts-per-unit="100" smallest-fraction="100" local-symbol="" - /> + /> @@ -2841,7 +2841,7 @@ parts-per-unit="100" smallest-fraction="100" local-symbol="Z.$" - /> + /> @@ -2886,7 +2886,7 @@ parts-per-unit="1" smallest-fraction="1" local-symbol="" - /> + /> @@ -2900,7 +2900,7 @@ parts-per-unit="1" smallest-fraction="100" local-symbol="" - /> + /> @@ -2914,7 +2914,7 @@ parts-per-unit="1" smallest-fraction="100" local-symbol="" - /> + /> @@ -2928,7 +2928,7 @@ parts-per-unit="1" smallest-fraction="100" local-symbol="" - /> + /> @@ -2942,7 +2942,7 @@ parts-per-unit="1" smallest-fraction="100" local-symbol="" - /> + /> @@ -2956,7 +2956,7 @@ parts-per-unit="1" smallest-fraction="1000000" local-symbol="" - /> + /> + /> + isocode="XBT" + fullname="Bitcoin" + unitname="bitcoin" + partname="microbitcoin" + namespace="ISO4217" + exchange-code="nil" + parts-per-unit="1000000" + smallest-fraction="1000000" + local-symbol="₿" + /> + /> + /> - + /> + + + \ No newline at end of file From 3dac05d09dce3aea61c791bc140e398fd3116b7f Mon Sep 17 00:00:00 2001 From: Moshe Date: Tue, 3 Oct 2017 10:15:48 +0300 Subject: [PATCH 003/131] Currencies XML was modified, so re-populate the commodities database table. --- .../gnucash/android/db/DatabaseHelper.java | 4 +- .../gnucash/android/db/DatabaseSchema.java | 2 +- .../gnucash/android/db/MigrationHelper.java | 65 +++++++++++++------ .../importer/CommoditiesXmlHandler.java | 1 + 4 files changed, 48 insertions(+), 24 deletions(-) diff --git a/app/src/main/java/org/gnucash/android/db/DatabaseHelper.java b/app/src/main/java/org/gnucash/android/db/DatabaseHelper.java index c079a7eb6..b8ef6c41f 100644 --- a/app/src/main/java/org/gnucash/android/db/DatabaseHelper.java +++ b/app/src/main/java/org/gnucash/android/db/DatabaseHelper.java @@ -278,9 +278,7 @@ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion){ while(oldVersion < newVersion){ try { Method method = MigrationHelper.class.getDeclaredMethod("upgradeDbToVersion" + (oldVersion+1), SQLiteDatabase.class); - Object result = method.invoke(null, db); - oldVersion = Integer.parseInt(result.toString()); - + oldVersion = (int) method.invoke(null, db); } catch (NoSuchMethodException e) { String msg = String.format("Database upgrade method upgradeToVersion%d(SQLiteDatabase) definition not found ", newVersion); Log.e(LOG_TAG, msg, e); 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 f3dcbeaf8..991029b88 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 = 15; + public static final int DATABASE_VERSION = 16; /** * 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 5acc1d74a..a5717671b 100644 --- a/app/src/main/java/org/gnucash/android/db/MigrationHelper.java +++ b/app/src/main/java/org/gnucash/android/db/MigrationHelper.java @@ -264,7 +264,7 @@ public static int upgradeDbToVersion2(SQLiteDatabase db) { " ADD COLUMN double_account_uid varchar(255)"; //introducing sub accounts - Log.i(DatabaseHelper.LOG_TAG, "Adding column for parent accounts"); + Log.i(LOG_TAG, "Adding column for parent accounts"); String addParentAccountSql = "ALTER TABLE " + AccountEntry.TABLE_NAME + " ADD COLUMN " + AccountEntry.COLUMN_PARENT_ACCOUNT_UID + " varchar(255)"; @@ -273,7 +273,7 @@ public static int upgradeDbToVersion2(SQLiteDatabase db) { //update account types to GnuCash account types //since all were previously CHECKING, now all will be CASH - Log.i(DatabaseHelper.LOG_TAG, "Converting account types to GnuCash compatible types"); + Log.i(LOG_TAG, "Converting account types to GnuCash compatible types"); ContentValues cv = new ContentValues(); cv.put(SplitEntry.COLUMN_TYPE, AccountType.CASH.toString()); db.update(AccountEntry.TABLE_NAME, cv, null, null); @@ -495,7 +495,7 @@ static int upgradeDbToVersion7(SQLiteDatabase db) { * @return New database version (8) if upgrade successful, old version (7) if unsuccessful */ static int upgradeDbToVersion8(SQLiteDatabase db) { - Log.i(DatabaseHelper.LOG_TAG, "Upgrading database to version 8"); + Log.i(LOG_TAG, "Upgrading database to version 8"); int oldVersion = 7; new File(Exporter.LEGACY_BASE_FOLDER_PATH + "/backups/").mkdirs(); new File(Exporter.LEGACY_BASE_FOLDER_PATH + "/exports/").mkdirs(); @@ -505,7 +505,7 @@ static int upgradeDbToVersion8(SQLiteDatabase db) { db.beginTransaction(); try { - Log.i(DatabaseHelper.LOG_TAG, "Creating scheduled actions table"); + Log.i(LOG_TAG, "Creating scheduled actions table"); db.execSQL("CREATE TABLE " + ScheduledActionEntry.TABLE_NAME + " (" + ScheduledActionEntry._ID + " integer primary key autoincrement, " + ScheduledActionEntry.COLUMN_UID + " varchar(255) not null UNIQUE, " @@ -525,7 +525,7 @@ static int upgradeDbToVersion8(SQLiteDatabase db) { //==============================BEGIN TABLE MIGRATIONS ======================================== - Log.i(DatabaseHelper.LOG_TAG, "Migrating accounts table"); + Log.i(LOG_TAG, "Migrating accounts table"); // backup transaction table db.execSQL("ALTER TABLE " + AccountEntry.TABLE_NAME + " RENAME TO " + AccountEntry.TABLE_NAME + "_bak"); // create new transaction table @@ -577,7 +577,7 @@ static int upgradeDbToVersion8(SQLiteDatabase db) { + " FROM " + AccountEntry.TABLE_NAME + "_bak;" ); - Log.i(DatabaseHelper.LOG_TAG, "Migrating transactions table"); + Log.i(LOG_TAG, "Migrating transactions table"); // backup transaction table db.execSQL("ALTER TABLE " + TransactionEntry.TABLE_NAME + " RENAME TO " + TransactionEntry.TABLE_NAME + "_bak"); // create new transaction table @@ -618,7 +618,7 @@ static int upgradeDbToVersion8(SQLiteDatabase db) { + " FROM " + TransactionEntry.TABLE_NAME + "_bak;" ); - Log.i(DatabaseHelper.LOG_TAG, "Migrating splits table"); + Log.i(LOG_TAG, "Migrating splits table"); // backup split table db.execSQL("ALTER TABLE " + SplitEntry.TABLE_NAME + " RENAME TO " + SplitEntry.TABLE_NAME + "_bak"); // create new split table @@ -668,7 +668,7 @@ static int upgradeDbToVersion8(SQLiteDatabase db) { //TransactionsDbAdapter transactionsDbAdapter = new TransactionsDbAdapter(db, splitsDbAdapter); //AccountsDbAdapter accountsDbAdapter = new AccountsDbAdapter(db,transactionsDbAdapter); - Log.i(DatabaseHelper.LOG_TAG, "Creating default root account if none exists"); + Log.i(LOG_TAG, "Creating default root account if none exists"); ContentValues contentValues = new ContentValues(); //assign a root account to all accounts which had null as parent except ROOT (top-level accounts) String rootAccountUID; @@ -706,7 +706,7 @@ static int upgradeDbToVersion8(SQLiteDatabase db) { contentValues.put(AccountEntry.COLUMN_PARENT_ACCOUNT_UID, rootAccountUID); db.update(AccountEntry.TABLE_NAME, contentValues, AccountEntry.COLUMN_PARENT_ACCOUNT_UID + " IS NULL AND " + AccountEntry.COLUMN_TYPE + " != ?", new String[]{"ROOT"}); - Log.i(DatabaseHelper.LOG_TAG, "Migrating existing recurring transactions"); + Log.i(LOG_TAG, "Migrating existing recurring transactions"); cursor = db.query(TransactionEntry.TABLE_NAME + "_bak", null, "recurrence_period > 0", null, null, null, null); long lastRun = System.currentTimeMillis(); while (cursor.moveToNext()){ @@ -753,7 +753,7 @@ static int upgradeDbToVersion8(SQLiteDatabase db) { cursor.close(); //auto-balance existing splits - Log.i(DatabaseHelper.LOG_TAG, "Auto-balancing existing transaction splits"); + Log.i(LOG_TAG, "Auto-balancing existing transaction splits"); cursor = db.query( TransactionEntry.TABLE_NAME + " , " + SplitEntry.TABLE_NAME + " ON " + TransactionEntry.TABLE_NAME + "." + TransactionEntry.COLUMN_UID + "=" + SplitEntry.TABLE_NAME + "." + SplitEntry.COLUMN_TRANSACTION_UID @@ -830,7 +830,7 @@ static int upgradeDbToVersion8(SQLiteDatabase db) { cursor.close(); } - Log.i(DatabaseHelper.LOG_TAG, "Dropping temporary migration tables"); + Log.i(LOG_TAG, "Dropping temporary migration tables"); db.execSQL("DROP TABLE " + SplitEntry.TABLE_NAME + "_bak"); db.execSQL("DROP TABLE " + AccountEntry.TABLE_NAME + "_bak"); db.execSQL("DROP TABLE " + TransactionEntry.TABLE_NAME + "_bak"); @@ -861,7 +861,7 @@ static int upgradeDbToVersion8(SQLiteDatabase db) { * @throws RuntimeException if the default commodities could not be imported */ static int upgradeDbToVersion9(SQLiteDatabase db){ - Log.i(DatabaseHelper.LOG_TAG, "Upgrading database to version 9"); + Log.i(LOG_TAG, "Upgrading database to version 9"); int oldVersion = 8; db.beginTransaction(); @@ -885,7 +885,7 @@ static int upgradeDbToVersion9(SQLiteDatabase db){ try { importCommodities(db); } catch (SAXException | ParserConfigurationException | IOException e) { - Log.e(DatabaseHelper.LOG_TAG, "Error loading currencies into the database", e); + Log.e(LOG_TAG, "Error loading currencies into the database", e); Crashlytics.logException(e); throw new RuntimeException(e); } @@ -1092,7 +1092,7 @@ static int upgradeDbToVersion9(SQLiteDatabase db){ * @return 10 if upgrade was successful, 9 otherwise */ static int upgradeDbToVersion10(SQLiteDatabase db){ - Log.i(DatabaseHelper.LOG_TAG, "Upgrading database to version 9"); + Log.i(LOG_TAG, "Upgrading database to version 9"); int oldVersion = 9; db.beginTransaction(); @@ -1145,7 +1145,7 @@ static int upgradeDbToVersion10(SQLiteDatabase db){ * @return 11 if upgrade was successful, 10 otherwise */ static int upgradeDbToVersion11(SQLiteDatabase db){ - Log.i(DatabaseHelper.LOG_TAG, "Upgrading database to version 9"); + Log.i(LOG_TAG, "Upgrading database to version 9"); int oldVersion = 10; db.beginTransaction(); @@ -1241,7 +1241,7 @@ static int upgradeDbToVersion12(SQLiteDatabase db){ * @return New database version, 13 if migration succeeds, 11 otherwise */ static int upgradeDbToVersion13(SQLiteDatabase db){ - Log.i(DatabaseHelper.LOG_TAG, "Upgrading database to version 13"); + Log.i(LOG_TAG, "Upgrading database to version 13"); int oldVersion = 12; db.beginTransaction(); @@ -1552,7 +1552,7 @@ private static void moveDirectory(File srcDir, File dstDir) throws IOException { * @return New database version */ public static int upgradeDbToVersion14(SQLiteDatabase db){ - Log.i(DatabaseHelper.LOG_TAG, "Upgrading database to version 14"); + Log.i(LOG_TAG, "Upgrading database to version 14"); int oldDbVersion = 13; File backupFolder = new File(Exporter.BASE_FOLDER_PATH); backupFolder.mkdir(); @@ -1585,7 +1585,7 @@ public void run() { } /** - * Upgrades the database to version 14. + * Upgrades the database to version 15. *

This migration makes the following changes to the database: *

    *
  • Fixes accounts referencing a default transfer account that no longer @@ -1593,10 +1593,10 @@ public void run() { *
*

* @param db SQLite database to be upgraded - * @return New database version, 14 if migration succeeds, 13 otherwise + * @return New database version, 15 if migration succeeds, 14 otherwise */ static int upgradeDbToVersion15(SQLiteDatabase db) { - Log.i(DatabaseHelper.LOG_TAG, "Upgrading database to version 15"); + Log.i(LOG_TAG, "Upgrading database to version 15"); int dbVersion = 14; db.beginTransaction(); @@ -1628,4 +1628,29 @@ static int upgradeDbToVersion15(SQLiteDatabase db) { rescheduleServiceAlarm(); return dbVersion; } + + /** + * Upgrades the database to version 16. + *

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

    + *
  • Re-populate the commodities table (see #731)
  • + *
+ *

+ * @param db SQLite database to be upgraded + * @return New database version, 16 if migration succeeds, 15 otherwise + */ + static int upgradeDbToVersion16(SQLiteDatabase db) { + Log.i(LOG_TAG, "Upgrading database to version 16"); + int dbVersion = 15; + + try { + importCommodities(db); + dbVersion = 16; + } catch (SAXException | ParserConfigurationException | IOException e) { + Log.e(LOG_TAG, "Error loading currencies into the database", e); + Crashlytics.logException(e); + } + + return dbVersion; + } } diff --git a/app/src/main/java/org/gnucash/android/importer/CommoditiesXmlHandler.java b/app/src/main/java/org/gnucash/android/importer/CommoditiesXmlHandler.java index 977328fa6..afefcc343 100644 --- a/app/src/main/java/org/gnucash/android/importer/CommoditiesXmlHandler.java +++ b/app/src/main/java/org/gnucash/android/importer/CommoditiesXmlHandler.java @@ -82,6 +82,7 @@ public void startElement(String uri, String localName, String qName, Attributes @Override public void endDocument() throws SAXException { + mCommoditiesDbAdapter.deleteAllRecords(); mCommoditiesDbAdapter.bulkAddRecords(mCommodities, DatabaseAdapter.UpdateMethod.insert); } } From 9f293d7289d19b10dc1c37cc8a9795feef7a227c Mon Sep 17 00:00:00 2001 From: Moshe Date: Mon, 11 Jun 2018 22:41:35 +0300 Subject: [PATCH 004/131] Fix issue #790 - text margins for RTL text in transaction details screen. --- app/src/main/res/layout/activity_transaction_detail.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/src/main/res/layout/activity_transaction_detail.xml b/app/src/main/res/layout/activity_transaction_detail.xml index 66a50ed96..f0a2d3739 100644 --- a/app/src/main/res/layout/activity_transaction_detail.xml +++ b/app/src/main/res/layout/activity_transaction_detail.xml @@ -41,6 +41,8 @@ Date: Thu, 13 Sep 2018 13:12:46 -0500 Subject: [PATCH 005/131] Update gradle wrapper to 4.4 and plugin to 3.1.4 --- build.gradle | 2 +- gradle/wrapper/gradle-wrapper.properties | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index 9a8d74969..f6c1f8ae9 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ buildscript { google() } dependencies { - classpath 'com.android.tools.build:gradle:3.0.1' + classpath 'com.android.tools.build:gradle:3.1.4' classpath 'io.fabric.tools:gradle:1.21.6' 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 2eadbb2b3..733dfde0b 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Mon Oct 30 21:17:22 CET 2017 +#Thu Sep 13 12:17:42 CDT 2018 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip From c546904a4b75f6adf22c3ebad5bf321bfdef893a Mon Sep 17 00:00:00 2001 From: Ngewi Fet Date: Thu, 20 Sep 2018 22:14:52 +0200 Subject: [PATCH 006/131] Add privacy policy Update crashlytics dependency version --- app/build.gradle | 2 +- privacy_policy.md | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 privacy_policy.md diff --git a/app/build.gradle b/app/build.gradle index 674ca8088..ac6280960 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -223,7 +223,7 @@ dependencies { exclude module: 'httpclient' } - implementation('com.crashlytics.sdk.android:crashlytics:2.6.7@aar') { + implementation('com.crashlytics.sdk.android:crashlytics:2.9.5@aar') { transitive = true; } diff --git a/privacy_policy.md b/privacy_policy.md new file mode 100644 index 000000000..2ba51946e --- /dev/null +++ b/privacy_policy.md @@ -0,0 +1,15 @@ +GnuCash Android App Privacy Policy +================================== + +Gnucash Android uses the third party service Crashlytics for collecting the crash log data (if enabled). +This information is used by the app developers to fix issues in the app and improve functionality. + +No personally identifiable information is automatically collected as part of this process. +The privacy policy of the Crashlytics service can be found here: http://try.crashlytics.com/terms/privacy-policy.pdf + +By default the crash log submission is turned off. If you opt-in to submit crash logs, +we collect information which includes, but is not limited to, device state information, +unique device identifiers, device hardware and OS information relating to how the application functions. + +For users of the beta version of the app, the collection of log data is switched on by default (with the possibility to opt out). +For all other regular users, the collection of the data is opt-in. \ No newline at end of file From 21fc3cfcc7a80c8943f633646b2f8556bb122135 Mon Sep 17 00:00:00 2001 From: RajatChandel Date: Thu, 14 Feb 2019 12:08:04 +0530 Subject: [PATCH 007/131] fixed add button disappearing after rotaion --- .../ui/transaction/ScheduledActionsListFragment.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/app/src/main/java/org/gnucash/android/ui/transaction/ScheduledActionsListFragment.java b/app/src/main/java/org/gnucash/android/ui/transaction/ScheduledActionsListFragment.java index 01f25340e..abf75287d 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 @@ -25,6 +25,7 @@ import android.net.Uri; import android.os.Build; import android.os.Bundle; +import android.support.annotation.NonNull; import android.support.v4.app.Fragment; import android.support.v4.app.ListFragment; import android.support.v4.app.LoaderManager; @@ -78,6 +79,7 @@ public class ScheduledActionsListFragment extends ListFragment implements * Logging tag */ protected static final String TAG = "ScheduledActionFragment"; + private static final String FRAGMENT_ACTION_KEY = "action_key"; private TransactionsDbAdapter mTransactionsDbAdapter; private SimpleCursorAdapter mCursorAdapter; @@ -175,6 +177,10 @@ public static Fragment getInstance(ScheduledAction.ActionType actionType){ public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + if(savedInstanceState != null && savedInstanceState.containsKey(FRAGMENT_ACTION_KEY)) { + mActionType = (ScheduledAction.ActionType) savedInstanceState.getSerializable(FRAGMENT_ACTION_KEY); + } + mTransactionsDbAdapter = TransactionsDbAdapter.getInstance(); switch (mActionType){ case TRANSACTION: @@ -641,5 +647,10 @@ public Cursor loadInBackground() { } } + @Override + public void onSaveInstanceState(@NonNull Bundle outState) { + outState.putSerializable(FRAGMENT_ACTION_KEY, mActionType); + super.onSaveInstanceState(outState); + } } From f69a6090cbbd7e0c96371a36807535f7f28dfa79 Mon Sep 17 00:00:00 2001 From: Ngewi Fet Date: Fri, 1 Mar 2019 11:43:00 +0100 Subject: [PATCH 008/131] Fixes crash when exporting CSV to 3rd party applications Fixes #809 Closes #814 --- app/src/main/res/xml/filepaths.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/xml/filepaths.xml b/app/src/main/res/xml/filepaths.xml index c4435e624..3c3463fc5 100644 --- a/app/src/main/res/xml/filepaths.xml +++ b/app/src/main/res/xml/filepaths.xml @@ -14,8 +14,9 @@ See the License for the specific language governing permissions and limitations under the License. --> - + + \ No newline at end of file From 15af63281e46d2ee4e949be1dcb59cd7b23aa4af Mon Sep 17 00:00:00 2001 From: milotype <43657314+milotype@users.noreply.github.com> Date: Fri, 1 Mar 2019 17:27:18 +0100 Subject: [PATCH 009/131] Initial release of croatian accounts --- .../main/res/raw-hr/default_accounts.gnucash | 889 ++++++++++++++++++ 1 file changed, 889 insertions(+) create mode 100644 app/src/main/res/raw-hr/default_accounts.gnucash diff --git a/app/src/main/res/raw-hr/default_accounts.gnucash b/app/src/main/res/raw-hr/default_accounts.gnucash new file mode 100644 index 000000000..5dadf8a71 --- /dev/null +++ b/app/src/main/res/raw-hr/default_accounts.gnucash @@ -0,0 +1,889 @@ + + +1 +60 + + ISO4217 + HRK + + currency + + + + template + template + template + template + 1 + + + Root Account + 5341c3d89c4a4163ba2cd13a0f521033 + ROOT + + ISO4217 + HRK + + 100 + + + Rashod + 235ba33cbfbb45b18a14ef690c65a8f0 + EXPENSE + + ISO4217 + HRK + + 100 + Rashod + + + color + #D13E29 + + + placeholder + true + + + 5341c3d89c4a4163ba2cd13a0f521033 + + + Osiguranje + 9892b814fe2a442ba344af98862e5c49 + EXPENSE + + ISO4217 + HRK + + 100 + Osiguranje + + + placeholder + true + + + 235ba33cbfbb45b18a14ef690c65a8f0 + + + Osiguranje kućanstva + 0efe6e487ce149fe9d8d2f6fa6c92838 + EXPENSE + + ISO4217 + HRK + + 100 + Osiguranje stvari kućanstva + 9892b814fe2a442ba344af98862e5c49 + + + Osiguranje imovine + bb3bfe896d7048ecb76a3c453888bfb0 + EXPENSE + + ISO4217 + HRK + + 100 + Osiguranje kuće, stana, apartmana + 9892b814fe2a442ba344af98862e5c49 + + + Zdravstveno osiguranje + e17cc50365084a538b69ff308ae2f7f5 + EXPENSE + + ISO4217 + HRK + + 100 + Zdravstveno osiguranje + 9892b814fe2a442ba344af98862e5c49 + + + Stanovanje + ab80c55c11ea4d37b27ee84ed27b3878 + EXPENSE + + ISO4217 + HRK + + 100 + Troškovi stanovanja + + + placeholder + true + + + 235ba33cbfbb45b18a14ef690c65a8f0 + + + Najamnina + c9803aa724ad41c6864992b4167339da + EXPENSE + + ISO4217 + HRK + + 100 + Najamnina + ab80c55c11ea4d37b27ee84ed27b3878 + + + Režije + 8254099c5c8a4454ae394e5853cc558a + EXPENSE + + ISO4217 + HRK + + 100 + Režije + + + placeholder + true + + + ab80c55c11ea4d37b27ee84ed27b3878 + + + Plin + 8755f7661cf34377ae596daac3df8167 + EXPENSE + + ISO4217 + HRK + + 100 + Plin + 8254099c5c8a4454ae394e5853cc558a + + + Struja + e871041f3aaa4a91afac7c32f7dee6f5 + EXPENSE + + ISO4217 + HRK + + 100 + Struja + 8254099c5c8a4454ae394e5853cc558a + + + Voda + 2d5605f4bf674a868d58299f1b42e634 + EXPENSE + + ISO4217 + HRK + + 100 + Voda + 8254099c5c8a4454ae394e5853cc558a + + + Grijanje + 1b32c949e760400ca39170dbe0e7aeaf + EXPENSE + + ISO4217 + HRK + + 100 + Grijanje + 8254099c5c8a4454ae394e5853cc558a + + + Čistoća + 528973ad80764a8e9cf1c3e7467b5409 + EXPENSE + + ISO4217 + HRK + + 100 + Čistoća + 8254099c5c8a4454ae394e5853cc558a + + + Komunalna naknada + a7f6651f9cf34e2ab31ea164d949ea4a + EXPENSE + + ISO4217 + HRK + + 100 + Komunalna naknada + 8254099c5c8a4454ae394e5853cc558a + + + Pričuva + 55e82a78bd8d49afb58aa4cec4878f3d + EXPENSE + + ISO4217 + HRK + + 100 + Pričuva + 8254099c5c8a4454ae394e5853cc558a + + + Telekomunikacija + 15599741f6414e5bacf6a721123054ce + EXPENSE + + ISO4217 + HRK + + 100 + Telekomunikacija + + + placeholder + true + + + ab80c55c11ea4d37b27ee84ed27b3878 + + + Televizija + 85e284fe8ab74fff844cd37e89ad9701 + EXPENSE + + ISO4217 + HRK + + 100 + RTV pretplata + 15599741f6414e5bacf6a721123054ce + + + Internet + f79d94ab9d014a20a0e00ebce765539a + EXPENSE + + ISO4217 + HRK + + 100 + Internet + 15599741f6414e5bacf6a721123054ce + + + Telefon + d3b95f1b45a84594b513414cfdfe5a34 + EXPENSE + + ISO4217 + HRK + + 100 + Telefon/Mobitel + 15599741f6414e5bacf6a721123054ce + + + Mrežne usluge + cc27256b8391480c95158df6bf86681c + EXPENSE + + ISO4217 + HRK + + 100 + Mrežne usluge + 15599741f6414e5bacf6a721123054ce + + + Obrazovanje + 0a136f6f18af49d7bc9e189da49745eb + EXPENSE + + ISO4217 + HRK + + 100 + Obrazovanje + + + placeholder + true + + + 235ba33cbfbb45b18a14ef690c65a8f0 + + + Pretplate + ce6ec9b02ae7459aa182b0d866ed7e10 + EXPENSE + + ISO4217 + HRK + + 100 + Pretplate, npr. na časopise + 0a136f6f18af49d7bc9e189da49745eb + + + Knjige + 11420c5117c7474d98465f3282288638 + EXPENSE + + ISO4217 + HRK + + 100 + Knjige + 0a136f6f18af49d7bc9e189da49745eb + + + Uredski materijal + e3dbccbd84b14ae095a66df282ab937f + EXPENSE + + ISO4217 + HRK + + 100 + Uredski materijal + 0a136f6f18af49d7bc9e189da49745eb + + + Odjeća i obuća + c5e2ae12932548c8b7999597bb487e4e + EXPENSE + + ISO4217 + HRK + + 100 + Odjeća i obuća + 235ba33cbfbb45b18a14ef690c65a8f0 + + + Namirnice + 07c603cdb09e462983f4c3ce61beb10c + EXPENSE + + ISO4217 + HRK + + 100 + Namirnice + 235ba33cbfbb45b18a14ef690c65a8f0 + + + Lijekovi + 1f649ce81ee9422f87f09822dcb279e6 + EXPENSE + + ISO4217 + HRK + + 100 + Lijekovi + 235ba33cbfbb45b18a14ef690c65a8f0 + + + Razni troškovi + 6b118c004de94d319777f315aac31590 + EXPENSE + + ISO4217 + HRK + + 100 + Razni troškovi + + + placeholder + true + + + 235ba33cbfbb45b18a14ef690c65a8f0 + + + Bankovne naknade + dc31044a0a1f43179ab9917caacb8005 + EXPENSE + + ISO4217 + HRK + + 100 + Bankovne naknade + 6b118c004de94d319777f315aac31590 + + + Razno + 12781eaa4a744c0890d43ebecf3ac02b + EXPENSE + + ISO4217 + HRK + + 100 + Neodređeni troškovi + 6b118c004de94d319777f315aac31590 + + + Izlasci + 77a047c799b841f8a1723d3b0bb82dbd + EXPENSE + + ISO4217 + HRK + + 100 + Koncerti, kino, kafići i sl. + 6b118c004de94d319777f315aac31590 + + + Putovanja + 23f06146e1004ee2abca2711b049d19c + EXPENSE + + ISO4217 + HRK + + 100 + Putovanja, ljetovanje, skijanje + 6b118c004de94d319777f315aac31590 + + + Sport + 05fec015e7464e139c63824f48d11d40 + EXPENSE + + ISO4217 + HRK + + 100 + Sport + 6b118c004de94d319777f315aac31590 + + + Porezi + 874c3bdc5ce649b38526ede38511c6aa + EXPENSE + + ISO4217 + HRK + + 100 + Porezi i prirezi + + + placeholder + true + + + 235ba33cbfbb45b18a14ef690c65a8f0 + + + Ostali porezi + f5f8b06aab9d4e7ab45562b70ff0f38d + EXPENSE + + ISO4217 + HRK + + 100 + Ostali porezi + 874c3bdc5ce649b38526ede38511c6aa + + + Kamate + 397a362bb1854eb282c81b1c55c8c8ed + EXPENSE + + ISO4217 + HRK + + 100 + Kamate + + + placeholder + true + + + 235ba33cbfbb45b18a14ef690c65a8f0 + + + Kamate na kredit + 8e830a56bfd74bf794d3daa0ee4d2e89 + EXPENSE + + ISO4217 + HRK + + 100 + Kamate na kredit + 397a362bb1854eb282c81b1c55c8c8ed + + + Prijevoz + d76be66cd8c74163aa32d78cdf1a8f43 + EXPENSE + + ISO4217 + HRK + + 100 + Javni prijevoz + 235ba33cbfbb45b18a14ef690c65a8f0 + + + Imovina + 8238aeea37da4e83b2e6ebda9a154fed + ASSET + + ISO4217 + HRK + + 100 + Imovina + + + color + #1469EB + + + placeholder + true + + + 5341c3d89c4a4163ba2cd13a0f521033 + + + Trenutačna imovina + 2f65659c8f0a40d5bfacdbc6e97385b2 + ASSET + + ISO4217 + HRK + + 100 + Trenutačna imovina + + + placeholder + true + + + 8238aeea37da4e83b2e6ebda9a154fed + + + Žiro račun + dbc38ec576cb4698be83e2d3b9a069de + BANK + + ISO4217 + HRK + + 100 + Žiro račun + 2f65659c8f0a40d5bfacdbc6e97385b2 + + + Štedni račun + 9c2956c914c24b479c516aa51abc45f3 + BANK + + ISO4217 + HRK + + 100 + Štedni račun + 2f65659c8f0a40d5bfacdbc6e97385b2 + + + Gotovina + 16d490a3ee704af6b561ae27a5c94b08 + CASH + + ISO4217 + HRK + + 100 + Gotovina + 2f65659c8f0a40d5bfacdbc6e97385b2 + + + Tekući račun + 870388eb4aeb44aab610dfdfb64e5923 + BANK + + ISO4217 + HRK + + 100 + Tekući račun + 2f65659c8f0a40d5bfacdbc6e97385b2 + + + Obveze + d06b88ced55b447ca3855c5f45c2ce61 + LIABILITY + + ISO4217 + HRK + + 100 + Obveze + + + color + #B304AD + + + placeholder + true + + + 5341c3d89c4a4163ba2cd13a0f521033 + + + Kreditna kartica + 5bd303fbe2e34d94abd6875f14585779 + CREDIT + + ISO4217 + HRK + + 100 + Kreditna kartica + d06b88ced55b447ca3855c5f45c2ce61 + + + Krediti + ed98b81a714e45278dc67dfdd4af2feb + LIABILITY + + ISO4217 + HRK + + 100 + Krediti/zajmovi + + + placeholder + true + + + d06b88ced55b447ca3855c5f45c2ce61 + + + Kredit + 72cc954449654b15885e64c8acf5d580 + LIABILITY + + ISO4217 + HRK + + 100 + Kredit + ed98b81a714e45278dc67dfdd4af2feb + + + Prihod + d7d12700fe634492a94167d2027b6b20 + INCOME + + ISO4217 + HRK + + 100 + Prihod + + + color + #1B4F15 + + + placeholder + true + + + 5341c3d89c4a4163ba2cd13a0f521033 + + + Dohodak + 8c785ba89f1e42518bd7305abd0a49ca + INCOME + + ISO4217 + HRK + + 100 + Dohodak + + + placeholder + true + + + d7d12700fe634492a94167d2027b6b20 + + + Osobni dohodak + d6afbd8f3f10454e93a0f19012fe62d8 + INCOME + + ISO4217 + HRK + + 100 + Osobni dohodak + 8c785ba89f1e42518bd7305abd0a49ca + + + Dodaci + e9b8d2ff282647ce8a28a54d59d067e1 + INCOME + + ISO4217 + HRK + + 100 + Dodaci, bonusi, dnevnica i sl. + 8c785ba89f1e42518bd7305abd0a49ca + + + Ostalo + 443ecfc3f1f04e59af9858403790dfcb + INCOME + + ISO4217 + HRK + + 100 + Ostali prihodi + d7d12700fe634492a94167d2027b6b20 + + + Kamate + fa2021c3387f49fdad88a82368c4c14b + INCOME + + ISO4217 + HRK + + 100 + Prihod od kamata + + + placeholder + true + + + d7d12700fe634492a94167d2027b6b20 + + + Kamate po žiro računu + be1b142eefef4ec3978fae9294287148 + INCOME + + ISO4217 + HRK + + 100 + Kamate po žiro računu + fa2021c3387f49fdad88a82368c4c14b + + + Kamate po štednom računu + 33d293b2094c4d688614c00ad4921060 + INCOME + + ISO4217 + HRK + + 100 + Kamate po štednom računu + fa2021c3387f49fdad88a82368c4c14b + + + Kamate po tekućem računu + 2027ae3a6f62439a994c98ac58a71d84 + INCOME + + ISO4217 + HRK + + 100 + Kamate po tekućem računu + fa2021c3387f49fdad88a82368c4c14b + + + Kapital + 3892e7fc9d6d41e8b1d03f152f6b782f + EQUITY + + ISO4217 + HRK + + 100 + Kapital + + + color + #EE8600 + + + placeholder + true + + + 5341c3d89c4a4163ba2cd13a0f521033 + + + Početni saldo + 944896a235b64f18966397d346300c01 + EQUITY + + ISO4217 + HRK + + 100 + Početni saldo + 3892e7fc9d6d41e8b1d03f152f6b782f + + + From ef9955598bed042453a2c4adbab289e0707c462e Mon Sep 17 00:00:00 2001 From: Pikrass Date: Sat, 5 Oct 2019 19:23:52 +0200 Subject: [PATCH 010/131] Fixes slow CSV exports For each split in every exported transaction, the code fetched the whole Account instance, including all its transactions with all their splits. This resulted in hundreds of thousands of requests with a medium-sized ledger. There was a cache mechanism, but it was local to one single transaction, thus essentially useless. Now, each split generates two requests, to get the account's name and full name. A better cache mechanism would speed this up further. --- .../export/csv/CsvTransactionsExporter.java | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/org/gnucash/android/export/csv/CsvTransactionsExporter.java b/app/src/main/java/org/gnucash/android/export/csv/CsvTransactionsExporter.java index f0d082e6e..2ad6337a7 100644 --- a/app/src/main/java/org/gnucash/android/export/csv/CsvTransactionsExporter.java +++ b/app/src/main/java/org/gnucash/android/export/csv/CsvTransactionsExporter.java @@ -99,8 +99,6 @@ public List generateExport() throws ExporterException { private void writeSplitsToCsv(@NonNull List splits, @NonNull CsvWriter writer) throws IOException { int index = 0; - Map uidAccountMap = new HashMap<>(); - for (Split split : splits) { if (index++ > 0){ // the first split is on the same line as the transactions. But after that, we writer.write("" + mCsvSeparator + mCsvSeparator + mCsvSeparator + mCsvSeparator @@ -108,18 +106,9 @@ private void writeSplitsToCsv(@NonNull List splits, @NonNull CsvWriter wr } writer.writeToken(split.getMemo()); - //cache accounts so that we do not have to go to the DB each time String accountUID = split.getAccountUID(); - Account account; - if (uidAccountMap.containsKey(accountUID)) { - account = uidAccountMap.get(accountUID); - } else { - account = mAccountsDbAdapter.getRecord(accountUID); - uidAccountMap.put(accountUID, account); - } - - writer.writeToken(account.getFullName()); - writer.writeToken(account.getName()); + writer.writeToken(mAccountsDbAdapter.getAccountFullName(accountUID)); + writer.writeToken(mAccountsDbAdapter.getAccountName(accountUID)); String sign = split.getType() == TransactionType.CREDIT ? "-" : ""; writer.writeToken(sign + split.getQuantity().formattedString()); From a037f438577eadc5507762d52a8b324b7131c629 Mon Sep 17 00:00:00 2001 From: Pikrass Date: Sat, 5 Oct 2019 20:01:37 +0200 Subject: [PATCH 011/131] Cache account names when exporting transactions to CSV Caching allows us to decrease the number of database requests. We did two requests per exported split before this commit; now we only do two requests per unique account in all splits. --- .../export/csv/CsvTransactionsExporter.java | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/org/gnucash/android/export/csv/CsvTransactionsExporter.java b/app/src/main/java/org/gnucash/android/export/csv/CsvTransactionsExporter.java index 2ad6337a7..f298cd201 100644 --- a/app/src/main/java/org/gnucash/android/export/csv/CsvTransactionsExporter.java +++ b/app/src/main/java/org/gnucash/android/export/csv/CsvTransactionsExporter.java @@ -96,7 +96,8 @@ public List generateExport() throws ExporterException { * Write splits to CSV format * @param splits Splits to be written */ - private void writeSplitsToCsv(@NonNull List splits, @NonNull CsvWriter writer) throws IOException { + private void writeSplitsToCsv(@NonNull List splits, @NonNull CsvWriter writer, + Map accountNames, Map accountFullNames) throws IOException { int index = 0; for (Split split : splits) { @@ -107,8 +108,21 @@ private void writeSplitsToCsv(@NonNull List splits, @NonNull CsvWriter wr writer.writeToken(split.getMemo()); String accountUID = split.getAccountUID(); - writer.writeToken(mAccountsDbAdapter.getAccountFullName(accountUID)); - writer.writeToken(mAccountsDbAdapter.getAccountName(accountUID)); + + // Cache account names + String fullName, name; + if (accountNames.containsKey(accountUID)) { + fullName = accountFullNames.get(accountUID); + name = accountNames.get(accountUID); + } else { + fullName = mAccountsDbAdapter.getAccountFullName(accountUID); + name = mAccountsDbAdapter.getAccountName(accountUID); + accountFullNames.put(accountUID, fullName); + accountNames.put(accountUID, name); + } + + writer.writeToken(fullName); + writer.writeToken(name); String sign = split.getType() == TransactionType.CREDIT ? "-" : ""; writer.writeToken(sign + split.getQuantity().formattedString()); @@ -132,6 +146,8 @@ private void generateExport(final CsvWriter csvWriter) throws ExporterException } csvWriter.newLine(); + Map nameCache = new HashMap<>(); + Map fullNameCache = new HashMap<>(); Cursor cursor = mTransactionsDbAdapter.fetchTransactionsModifiedSince(mExportParams.getExportStartTime()); Log.d(LOG_TAG, String.format("Exporting %d transactions to CSV", cursor.getCount())); @@ -148,7 +164,7 @@ private void generateExport(final CsvWriter csvWriter) throws ExporterException csvWriter.writeToken("CURRENCY::" + transaction.getCurrencyCode()); csvWriter.writeToken(null); // Void Reason csvWriter.writeToken(null); // Action - writeSplitsToCsv(transaction.getSplits(), csvWriter); + writeSplitsToCsv(transaction.getSplits(), csvWriter, nameCache, fullNameCache); } PreferencesHelper.setLastExportTime(TimestampHelper.getTimestampFromNow()); From 2a62ab8371dfca25f3b03202d188ccb89063a7bf Mon Sep 17 00:00:00 2001 From: Ngewi Fet Date: Sat, 9 Nov 2019 22:04:46 +0100 Subject: [PATCH 012/131] Upgrades compileSdk, targetSdk, buildTools and gradle versions --- app/build.gradle | 11 ++++++----- build.gradle | 4 ++-- gradle/wrapper/gradle-wrapper.properties | 4 ++-- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index ac6280960..22446adf5 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -20,13 +20,13 @@ static def gitSha() { android { - compileSdkVersion 27 - buildToolsVersion '27.0.3' + compileSdkVersion 29 + buildToolsVersion '29.0.0' defaultConfig { applicationId "org.gnucash.android" testApplicationId 'org.gnucash.android.test' minSdkVersion 19 - targetSdkVersion 27 + targetSdkVersion 29 versionCode versionMajor * 10000 + versionMinor * 1000 + versionPatch * 100 + versionBuild versionName "${versionMajor}.${versionMinor}.${versionPatch}" resValue "string", "app_version_name", "${versionName}" @@ -147,6 +147,7 @@ android { } testOptions { + animationsDisabled true unitTests { includeAndroidResources = true } @@ -223,8 +224,8 @@ dependencies { exclude module: 'httpclient' } - implementation('com.crashlytics.sdk.android:crashlytics:2.9.5@aar') { - transitive = true; + implementation('com.crashlytics.sdk.android:crashlytics:2.10.1@aar') { + transitive = true } diff --git a/build.gradle b/build.gradle index f6c1f8ae9..6e3389eb5 100644 --- a/build.gradle +++ b/build.gradle @@ -5,8 +5,8 @@ buildscript { google() } dependencies { - classpath 'com.android.tools.build:gradle:3.1.4' - classpath 'io.fabric.tools:gradle:1.21.6' + classpath 'com.android.tools.build:gradle:3.5.2' + classpath 'io.fabric.tools:gradle:1.31.2' 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 733dfde0b..c95c2d898 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Thu Sep 13 12:17:42 CDT 2018 +#Sat Nov 09 21:22:07 CET 2019 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.0-all.zip From 448caaecf2778a268ec2cedd1679f5c8783ecd29 Mon Sep 17 00:00:00 2001 From: Ngewi Fet Date: Sat, 9 Nov 2019 23:57:48 +0100 Subject: [PATCH 013/131] Fixes UI (integration) tests Upgrades support library version Migrate legacy tests to use ActivityTestRule --- app/build.gradle | 2 +- .../android/test/ui/AccountsActivityTest.java | 26 +-------- .../test/ui/CalculatorEditTextTest.java | 3 +- .../test/ui/ExportTransactionsTest.java | 57 ++++++------------- .../test/ui/FirstRunWizardActivityTest.java | 17 ++---- .../android/test/ui/OwnCloudExportTest.java | 6 +- .../test/ui/TransactionsActivityTest.java | 5 +- .../ui/util/GnucashAndroidTestRunner.java | 28 ++++++++- 8 files changed, 59 insertions(+), 85 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 22446adf5..15bfc62a2 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -180,7 +180,7 @@ afterEvaluate { } -def androidSupportVersion = "27.0.2" +def androidSupportVersion = "28.0.0" def androidEspressoVersion = "3.0.0" def androidSupportTestVersion = "1.0.0" 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 eb7f43c13..54afddccd 100644 --- a/app/src/androidTest/java/org/gnucash/android/test/ui/AccountsActivityTest.java +++ b/app/src/androidTest/java/org/gnucash/android/test/ui/AccountsActivityTest.java @@ -17,7 +17,6 @@ package org.gnucash.android.test.ui; import android.Manifest; -import android.content.Context; import android.content.Intent; import android.content.SharedPreferences.Editor; import android.database.SQLException; @@ -32,8 +31,6 @@ import android.util.Log; import android.view.View; -import com.kobakei.ratethisapp.RateThisApp; - import org.gnucash.android.R; import org.gnucash.android.app.GnuCashApplication; import org.gnucash.android.db.DatabaseHelper; @@ -51,6 +48,7 @@ import org.gnucash.android.model.Transaction; import org.gnucash.android.receivers.AccountCreator; import org.gnucash.android.test.ui.util.DisableAnimationsRule; +import org.gnucash.android.test.ui.util.GnucashAndroidTestRunner; import org.gnucash.android.ui.account.AccountsActivity; import org.gnucash.android.ui.account.AccountsListFragment; import org.hamcrest.Description; @@ -131,7 +129,7 @@ public AccountsActivityTest() { @BeforeClass public static void prepTest(){ - preventFirstRunDialogs(GnuCashApplication.getAppContext()); + GnucashAndroidTestRunner.preventFirstRunDialogs(GnuCashApplication.getAppContext()); String activeBookUID = BooksDbAdapter.getInstance().getActiveBookUID(); mDbHelper = new DatabaseHelper(GnuCashApplication.getAppContext(), activeBookUID); @@ -163,26 +161,6 @@ public void setUp() throws Exception { } - /** - * Prevents the first-run dialogs (Whats new, Create accounts etc) from being displayed when testing - * @param context Application context - */ - public static void preventFirstRunDialogs(Context context) { - AccountsActivity.rateAppConfig = new RateThisApp.Config(10000, 10000); - Editor editor = PreferenceManager.getDefaultSharedPreferences(context).edit(); - - //do not show first run dialog - editor.putBoolean(context.getString(R.string.key_first_run), false); - editor.putInt(AccountsActivity.LAST_OPEN_TAB_INDEX, AccountsActivity.INDEX_TOP_LEVEL_ACCOUNTS_FRAGMENT); - - //do not show "What's new" dialog - String minorVersion = context.getString(R.string.app_minor_version); - int currentMinor = Integer.parseInt(minorVersion); - editor.putInt(context.getString(R.string.key_previous_minor_version), currentMinor); - editor.commit(); - } - - public void testDisplayAccountsList(){ AccountsActivity.createDefaultAccounts("EUR", mAccountsActivity); mAccountsActivity.recreate(); diff --git a/app/src/androidTest/java/org/gnucash/android/test/ui/CalculatorEditTextTest.java b/app/src/androidTest/java/org/gnucash/android/test/ui/CalculatorEditTextTest.java index f1932b732..6e8b807c4 100644 --- a/app/src/androidTest/java/org/gnucash/android/test/ui/CalculatorEditTextTest.java +++ b/app/src/androidTest/java/org/gnucash/android/test/ui/CalculatorEditTextTest.java @@ -36,6 +36,7 @@ import org.gnucash.android.model.Account; import org.gnucash.android.model.Commodity; import org.gnucash.android.test.ui.util.DisableAnimationsRule; +import org.gnucash.android.test.ui.util.GnucashAndroidTestRunner; import org.gnucash.android.ui.common.UxArgument; import org.gnucash.android.ui.transaction.TransactionsActivity; import org.junit.After; @@ -105,7 +106,7 @@ public static void prepTestCase(){ mTransactionsDbAdapter = TransactionsDbAdapter.getInstance(); mAccountsDbAdapter = AccountsDbAdapter.getInstance(); - AccountsActivityTest.preventFirstRunDialogs(GnuCashApplication.getAppContext()); + GnucashAndroidTestRunner.preventFirstRunDialogs(GnuCashApplication.getAppContext()); } @Before 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 119e4f033..2e8d511b0 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 @@ -17,23 +17,13 @@ package org.gnucash.android.test.ui; import android.Manifest; -import android.app.AlertDialog; -import android.content.SharedPreferences; -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.rule.ActivityTestRule; import android.support.test.rule.GrantPermissionRule; import android.support.test.runner.AndroidJUnit4; -import android.support.v7.preference.PreferenceManager; -import android.test.ActivityInstrumentationTestCase2; import android.util.Log; -import android.widget.CompoundButton; import org.gnucash.android.R; import org.gnucash.android.app.GnuCashApplication; @@ -42,52 +32,38 @@ import org.gnucash.android.db.adapter.BooksDbAdapter; import org.gnucash.android.db.adapter.CommoditiesDbAdapter; import org.gnucash.android.db.adapter.DatabaseAdapter; -import org.gnucash.android.db.adapter.ScheduledActionDbAdapter; 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.Exporter; import org.gnucash.android.model.Account; import org.gnucash.android.model.Commodity; import org.gnucash.android.model.Money; -import org.gnucash.android.model.PeriodType; -import org.gnucash.android.model.ScheduledAction; import org.gnucash.android.model.Split; import org.gnucash.android.model.Transaction; +import org.gnucash.android.test.ui.util.GnucashAndroidTestRunner; 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.BeforeClass; import org.junit.FixMethodOrder; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.MethodSorters; -import java.io.File; -import java.util.List; - import static android.support.test.espresso.Espresso.onView; import static android.support.test.espresso.action.ViewActions.click; import static android.support.test.espresso.action.ViewActions.swipeUp; import static android.support.test.espresso.assertion.ViewAssertions.matches; import static android.support.test.espresso.matcher.RootMatchers.withDecorView; -import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom; import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed; -import static android.support.test.espresso.matcher.ViewMatchers.isEnabled; -import static android.support.test.espresso.matcher.ViewMatchers.withEffectiveVisibility; import static android.support.test.espresso.matcher.ViewMatchers.withId; import static android.support.test.espresso.matcher.ViewMatchers.withText; -import static org.assertj.core.api.Assertions.assertThat; -import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; @RunWith(AndroidJUnit4.class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -public class ExportTransactionsTest extends - ActivityInstrumentationTestCase2 { +public class ExportTransactionsTest { private DatabaseHelper mDbHelper; private SQLiteDatabase mDb; @@ -99,20 +75,23 @@ public class ExportTransactionsTest extends @Rule public GrantPermissionRule animationPermissionsRule = GrantPermissionRule.grant(Manifest.permission.SET_ANIMATION_SCALE); + @Rule + public ActivityTestRule mActivityRule = new ActivityTestRule<>(AccountsActivity.class); + public ExportTransactionsTest() { - super(AccountsActivity.class); } - @Override + @BeforeClass + public static void setupClass(){ + GnucashAndroidTestRunner.preventFirstRunDialogs(GnuCashApplication.getAppContext()); + } + @Before - public void setUp() throws Exception { - super.setUp(); - injectInstrumentation(InstrumentationRegistry.getInstrumentation()); - AccountsActivityTest.preventFirstRunDialogs(getInstrumentation().getTargetContext()); - mAcccountsActivity = getActivity(); + public void setUp() { + mAcccountsActivity = mActivityRule.getActivity(); String activeBookUID = BooksDbAdapter.getInstance().getActiveBookUID(); - mDbHelper = new DatabaseHelper(getActivity(), activeBookUID); + mDbHelper = new DatabaseHelper(mAcccountsActivity.getApplicationContext(), activeBookUID); try { mDb = mDbHelper.getWritableDatabase(); } catch (SQLException e) { @@ -164,16 +143,14 @@ public void testCreateBackup(){ */ private void assertToastDisplayed(int toastString) { onView(withText(toastString)) - .inRoot(withDecorView(not(is(getActivity().getWindow().getDecorView())))) + .inRoot(withDecorView(not(is(mAcccountsActivity.getWindow().getDecorView())))) .check(matches(isDisplayed())); } //todo: add testing of export flag to unit test //todo: add test of ignore exported transactions to unit tests - @Override - @After public void tearDown() throws Exception { + @After public void tearDown(){ mDbHelper.close(); mDb.close(); - super.tearDown(); } } diff --git a/app/src/androidTest/java/org/gnucash/android/test/ui/FirstRunWizardActivityTest.java b/app/src/androidTest/java/org/gnucash/android/test/ui/FirstRunWizardActivityTest.java index 7c4ab6d39..ce335de06 100644 --- a/app/src/androidTest/java/org/gnucash/android/test/ui/FirstRunWizardActivityTest.java +++ b/app/src/androidTest/java/org/gnucash/android/test/ui/FirstRunWizardActivityTest.java @@ -18,10 +18,9 @@ import android.Manifest; import android.database.SQLException; import android.database.sqlite.SQLiteDatabase; -import android.support.test.InstrumentationRegistry; +import android.support.test.rule.ActivityTestRule; import android.support.test.rule.GrantPermissionRule; import android.support.test.runner.AndroidJUnit4; -import android.test.ActivityInstrumentationTestCase2; import android.util.Log; import org.gnucash.android.R; @@ -50,7 +49,7 @@ * @author Ngewi Fet */ @RunWith(AndroidJUnit4.class) -public class FirstRunWizardActivityTest extends ActivityInstrumentationTestCase2{ +public class FirstRunWizardActivityTest { private DatabaseHelper mDbHelper; private SQLiteDatabase mDb; @@ -62,16 +61,12 @@ public class FirstRunWizardActivityTest extends ActivityInstrumentationTestCase2 @Rule public GrantPermissionRule animationPermissionsRule = GrantPermissionRule.grant(Manifest.permission.SET_ANIMATION_SCALE); - public FirstRunWizardActivityTest() { - super(FirstRunWizardActivity.class); - } + @Rule + public ActivityTestRule mActivityRule = new ActivityTestRule<>(FirstRunWizardActivity.class); @Before - public void setUp() throws Exception { - super.setUp(); - injectInstrumentation(InstrumentationRegistry.getInstrumentation()); - - mActivity = getActivity(); + public void setUp() { + mActivity = mActivityRule.getActivity(); mDbHelper = new DatabaseHelper(mActivity, BaseModel.generateUID()); try { mDb = mDbHelper.getWritableDatabase(); 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 b3e512037..008909b11 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 @@ -28,7 +28,6 @@ import android.support.test.rule.ActivityTestRule; import android.support.test.rule.GrantPermissionRule; import android.support.test.runner.AndroidJUnit4; -import android.test.ActivityInstrumentationTestCase2; import android.util.Log; import org.gnucash.android.R; @@ -66,7 +65,7 @@ import static android.support.test.espresso.matcher.ViewMatchers.withText; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertTrue; -import static org.gnucash.android.test.ui.AccountsActivityTest.preventFirstRunDialogs; +import static org.gnucash.android.test.ui.util.GnucashAndroidTestRunner.preventFirstRunDialogs; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; @@ -84,8 +83,7 @@ public class OwnCloudExportTest { private String OC_DIR = "gc_test"; /** - * A JUnit {@link Rule @Rule} to launch your activity under test. This is a replacement - * for {@link ActivityInstrumentationTestCase2}. + * A JUnit {@link Rule @Rule} to launch your activity under test. *

* Rules are interceptors which are executed for each test method and will run before * any of your setup code in the {@link Before @Before} method. 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 6821e4ca2..4395e7386 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 @@ -43,6 +43,7 @@ import org.gnucash.android.model.TransactionType; import org.gnucash.android.receivers.TransactionRecorder; import org.gnucash.android.test.ui.util.DisableAnimationsRule; +import org.gnucash.android.test.ui.util.GnucashAndroidTestRunner; import org.gnucash.android.ui.common.UxArgument; import org.gnucash.android.ui.settings.PreferenceActivity; import org.gnucash.android.ui.transaction.TransactionFormFragment; @@ -57,7 +58,6 @@ import java.math.BigDecimal; import java.text.NumberFormat; -import java.util.Currency; import java.util.Date; import java.util.List; import java.util.Locale; @@ -76,7 +76,6 @@ import static android.support.test.espresso.matcher.ViewMatchers.withText; import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.Matchers.allOf; -import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; @RunWith(AndroidJUnit4.class) @@ -135,7 +134,7 @@ public TransactionsActivityTest() { @BeforeClass public static void prepareTestCase(){ Context context = GnuCashApplication.getAppContext(); - AccountsActivityTest.preventFirstRunDialogs(context); + GnucashAndroidTestRunner.preventFirstRunDialogs(context); mSplitsDbAdapter = SplitsDbAdapter.getInstance(); mTransactionsDbAdapter = TransactionsDbAdapter.getInstance(); diff --git a/app/src/androidTest/java/org/gnucash/android/test/ui/util/GnucashAndroidTestRunner.java b/app/src/androidTest/java/org/gnucash/android/test/ui/util/GnucashAndroidTestRunner.java index 40d9691bb..393e6b0c5 100644 --- a/app/src/androidTest/java/org/gnucash/android/test/ui/util/GnucashAndroidTestRunner.java +++ b/app/src/androidTest/java/org/gnucash/android/test/ui/util/GnucashAndroidTestRunner.java @@ -16,13 +16,20 @@ package org.gnucash.android.test.ui.util; +import android.content.Context; +import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.os.Bundle; import android.os.IBinder; -import android.support.multidex.MultiDex; +import android.preference.PreferenceManager; import android.support.test.runner.AndroidJUnitRunner; import android.util.Log; +import com.kobakei.ratethisapp.RateThisApp; + +import org.gnucash.android.R; +import org.gnucash.android.ui.account.AccountsActivity; + import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -35,6 +42,25 @@ public class GnucashAndroidTestRunner extends AndroidJUnitRunner{ private static final float DISABLED = 0.0f; private static final float DEFAULT = 1.0f; + /** + * Prevents the first-run dialogs (Whats new, Create accounts etc) from being displayed when testing + * @param context Application context + */ + public static void preventFirstRunDialogs(Context context) { + AccountsActivity.rateAppConfig = new RateThisApp.Config(10000, 10000); + SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(context).edit(); + + //do not show first run dialog + editor.putBoolean(context.getString(R.string.key_first_run), false); + editor.putInt(AccountsActivity.LAST_OPEN_TAB_INDEX, AccountsActivity.INDEX_TOP_LEVEL_ACCOUNTS_FRAGMENT); + + //do not show "What's new" dialog + String minorVersion = context.getString(R.string.app_minor_version); + int currentMinor = Integer.parseInt(minorVersion); + editor.putInt(context.getString(R.string.key_previous_minor_version), currentMinor); + editor.commit(); + } + @Override public void onCreate(Bundle args) { super.onCreate(args); From 87e2b1d374786659165a561139bfac32ef9d4634 Mon Sep 17 00:00:00 2001 From: Ngewi Fet Date: Sun, 10 Nov 2019 00:05:30 +0100 Subject: [PATCH 014/131] Upgrade version of robolectric to fix execution of unit tests --- app/build.gradle | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 15bfc62a2..d2c10736f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -229,14 +229,14 @@ dependencies { } - testImplementation 'org.robolectric:robolectric:3.5.1' + testImplementation 'org.robolectric:robolectric:4.3.1' testImplementation( 'junit:junit:4.12', 'joda-time:joda-time:2.9.4', - 'org.assertj:assertj-core:1.7.1' + 'org.assertj:assertj-core:3.14.0' ) - testImplementation 'org.robolectric:shadows-multidex:3.0' + testImplementation 'org.robolectric:shadows-multidex:4.3.1' androidTestImplementation ( 'com.android.support:support-annotations:' + androidSupportVersion, From d2cfae33cd889fca04083e74c94e67bb7930c104 Mon Sep 17 00:00:00 2001 From: Anil Praharaj Date: Thu, 14 Feb 2019 16:52:51 +0530 Subject: [PATCH 015/131] Add Book name as subtitle in Accounts Activity --- .../java/org/gnucash/android/ui/account/AccountsActivity.java | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/org/gnucash/android/ui/account/AccountsActivity.java b/app/src/main/java/org/gnucash/android/ui/account/AccountsActivity.java index 846330af8..2a59bd68f 100644 --- a/app/src/main/java/org/gnucash/android/ui/account/AccountsActivity.java +++ b/app/src/main/java/org/gnucash/android/ui/account/AccountsActivity.java @@ -227,6 +227,7 @@ public void onCreate(Bundle savedInstanceState) { init(); + getSupportActionBar().setSubtitle(BooksDbAdapter.getInstance().getActiveBookDisplayName()); TabLayout tabLayout = (TabLayout) findViewById(R.id.tab_layout); tabLayout.addTab(tabLayout.newTab().setText(R.string.title_recent_accounts)); tabLayout.addTab(tabLayout.newTab().setText(R.string.title_all_accounts)); From 0c7e4e353131c3222b8f682722b079c45fc572fe Mon Sep 17 00:00:00 2001 From: Ngewi Fet Date: Sun, 10 Nov 2019 00:29:26 +0100 Subject: [PATCH 016/131] Update Java source compatibility to Java 8 --- app/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index d2c10736f..5a7ea8efa 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -142,8 +142,8 @@ android { compileOptions { //we want switch with strings during xml parsing encoding "UTF-8" - sourceCompatibility JavaVersion.VERSION_1_7 - targetCompatibility JavaVersion.VERSION_1_7 + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 } testOptions { From 9fa40d4c1fd9d5d1733503ae376de1c264f04242 Mon Sep 17 00:00:00 2001 From: Ngewi Fet Date: Sun, 10 Nov 2019 00:33:27 +0100 Subject: [PATCH 017/131] Update version for 2.4.1 release Update Java source compatibility to Java 8 Update Travis CI build configuration --- .travis.yml | 8 ++++---- app/build.gradle | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9dcd1ce95..94dc52c0a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,17 +5,17 @@ android: - platform-tools - tools - tools #not a typo. Needed for SDK update - - build-tools-27.0.3 + - build-tools-29.0.0 # The SDK version used to compile your project - - android-27 + - android-29 # Additional components - extra-android-support - extra-google-google_play_services - extra-google-m2repository - extra-android-m2repository - - addon-google_apis-google-26 + - addon-google_apis-google-29 # Specify at least one system image, # if you need to run emulator(s) during your tests @@ -23,7 +23,7 @@ android: # XXX: Temporary workaround. Remove once fixed before_install: - - yes | sdkmanager "platforms;android-27" + - yes | sdkmanager "platforms;android-29" # Emulator Management: Create, Start and Wait # Re-enable this when we figure out how to reliably build on Travis diff --git a/app/build.gradle b/app/build.gradle index 5a7ea8efa..74cd220eb 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -5,8 +5,8 @@ apply plugin: 'io.fabric' def versionMajor = 2 def versionMinor = 4 -def versionPatch = 0 -def versionBuild = 3 +def versionPatch = 1 +def versionBuild = 0 static def buildTime() { def df = new SimpleDateFormat("yyyyMMdd HH:mm 'UTC'") From b401372732a34d729b772e601650c27515a3fb15 Mon Sep 17 00:00:00 2001 From: Ngewi Fet Date: Sun, 10 Nov 2019 01:43:51 +0100 Subject: [PATCH 018/131] Update translations from CrowdIn --- .gitignore | 1 + app/src/main/res/values-ar-rSA/strings.xml | 98 ++-- app/src/main/res/values-el-rGR/strings.xml | 18 +- app/src/main/res/values-es-rMX/strings.xml | 4 +- app/src/main/res/values-es/strings.xml | 4 +- app/src/main/res/values-hr/strings.xml | 501 +++++++++++++++++++++ app/src/main/res/values-in-rID/strings.xml | 221 ++++----- app/src/main/res/values-pt-rBR/strings.xml | 16 +- app/src/main/res/values-pt-rPT/strings.xml | 2 +- app/src/main/res/values-ru/strings.xml | 16 +- app/src/main/res/values-sv-rSE/strings.xml | 14 +- app/src/main/res/values-zh-rTW/strings.xml | 89 ++-- crowdin.yml | 2 + 13 files changed, 731 insertions(+), 255 deletions(-) create mode 100644 app/src/main/res/values-hr/strings.xml diff --git a/.gitignore b/.gitignore index bd87d9b4f..ee987b848 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,7 @@ out/ /captures gradle.properties +crowdin.properties #Crashlytics diff --git a/app/src/main/res/values-ar-rSA/strings.xml b/app/src/main/res/values-ar-rSA/strings.xml index f593b1675..7ccf20003 100644 --- a/app/src/main/res/values-ar-rSA/strings.xml +++ b/app/src/main/res/values-ar-rSA/strings.xml @@ -16,58 +16,58 @@ limitations under the License. --> - إنشاء حساب - تعديل الحساب - إضافة حركة جديدة لحساب - عرض تفاصيل الحساب - لا توجد حسابات للعرض - اسم الحساب - إلغاء - حفظ - اختبار - أدخل رمز المرور - رمز المرور خطأ؛ من فضلك حاول مُجددًا - إعداد رمز المرور - الرجاء تأكيد كلمة المرور الخاصة بك - تأكيد رمز المرور غير صحيح. الرجاء المحاولة مرة أخرى - الوصف - المبلغ - معاملة جديدة - لا معاملات لعرض - الخصم - الائتمان - الحسابات - المعاملات - حذف - حذف - إلغاء - تم حذف الحساب - تأكيد الحذف - تحرير المعاملات - إضافة ملاحظة - %1$d محدد - الرصيد: - التصدير إلى: - تصدير المعاملات - بشكل افتراضي، سيتم تصدير المعاملات الجديدة فقط منذ آخر تصدير. حدد هذا الخيار لتصدير جميع المعاملات - خطأ في تصدير ملف %1$s - تصدير - حذف الحركات بعد التصدير - سيتم حذف كافة المعاملات المصدرة عند اكتمال التصدير - إعدادات + Create Account + Edit Account + Add a new transaction to an account + View account details + No accounts to display + Account name + Cancel + Save + Test + Enter Passcode + Wrong passcode, please try again + Passcode set + Please confirm your passcode + Invalid passcode confirmation. Please try again + Description + Amount + New transaction + No transactions to display + DEBIT + CREDIT + Accounts + Transactions + Delete + Delete + Cancel + Account deleted + Confirm delete + Edit Transaction + Add note + %1$d selected + Balance: + Export To: + Export Transactions + By default, only new transactions since last export will be exported. Check this option to export all transactions + Error exporting %1$s file + Export + Delete transactions after export + All exported transactions will be deleted when exporting is completed + Settings - حفظ باسم… + Save As… Dropbox ownCloud - إرسال إلى… + Send to… - نقل - نقل %1$d معاملات + Move + Move %1$d transaction(s) Destination Account Cannot move transactions.\nThe destination account uses a different currency from origin account - عام - حول - اختار عملة إفتراضية + General + About + Choose default currency Default currency Default currency to assign to new accounts Enables recording transactions in GnuCash for Android @@ -124,9 +124,9 @@ - Multiple bug fixes and improvements\n Dismiss - أدخل مبلغ لحفظ الحركة - حدث خطأ أثناء استيراد حسابات GnuCash - تم إستيراد حسابات GnuCash بنجاح + Enter an amount to save the transaction + An error occurred while importing the GnuCash accounts + GnuCash Accounts successfully imported Import account structure exported from GnuCash desktop Import GnuCash XML Delete all accounts in the database. All transactions will be deleted as diff --git a/app/src/main/res/values-el-rGR/strings.xml b/app/src/main/res/values-el-rGR/strings.xml index cf674c732..49f40617d 100644 --- a/app/src/main/res/values-el-rGR/strings.xml +++ b/app/src/main/res/values-el-rGR/strings.xml @@ -206,10 +206,10 @@ Memo Spend Receive - Ανάληψη - Κατάθεση - Πληρωμή - Χρέωση + Withdrawal + Deposit + Payment + Charge Decrease Increase Income @@ -217,8 +217,8 @@ Expense Bill Invoice - Αγορά - Πώληση + Buy + Sell Αρχικά υπόλοιπα Καθαρή Θέση Enable to save the current account balance (before deleting transactions) as new opening balance after deleting transactions @@ -228,14 +228,14 @@ Generates separate QIF files per currency Imbalance: Add split - Αγαπημένο + Favorite Navigation drawer opened Navigation drawer closed - Αναφορές + Reports Pie Chart Line Chart Bar Chart - Προτιμήσεις αναφοράς + Report Preferences Account color in reports Use account color in the bar/pie chart Order by size diff --git a/app/src/main/res/values-es-rMX/strings.xml b/app/src/main/res/values-es-rMX/strings.xml index a6dddb325..1d8d70660 100644 --- a/app/src/main/res/values-es-rMX/strings.xml +++ b/app/src/main/res/values-es-rMX/strings.xml @@ -52,7 +52,7 @@ Por defecto solo las nuevas transacciones desde la última exportación serán exportadas. Seleccione esta opción para exportar todas las transacciones Error exportando datos %1$s Exportar - Borrar transacciones después de exportar + Borrar transacciones despues de exportar Todas las transacciones exportadas serán borradas cuando la exportación haya terminado Ajustes @@ -226,7 +226,7 @@ Mostrar leyenda Mostrar etiquetas Mostrar porcentaje - Mostrar líneas de media + Mostar lineas de media Agrupar porciones pequeñas Datos del gráfico no disponibles Total diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index e83540caf..994bd4661 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -38,7 +38,7 @@ ABONO Cuentas Transacciones - Borrar + BORRAR Borrar Cancelar Cuenta borrada @@ -226,7 +226,7 @@ Mostrar leyenda Mostrar etiquetas Mostrar porcentaje - Mostrar líneas de media + Mostar lineas de media Agrupar porciones pequeñas Datos del gráfico no disponibles Total diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml new file mode 100644 index 000000000..3e7ff0211 --- /dev/null +++ b/app/src/main/res/values-hr/strings.xml @@ -0,0 +1,501 @@ + + + + + Stvori konto + Uredi konto + Dodaj novu transakciju jednom kontu + Prikaži detalje konta + Nema konta + Ime konta + Odustani + Spremi + Ispitaj + Unesi lozinku + Kriva lozinka, pokušaj ponovo + Lozinka je postavljena + Potvrdi lozinku + Nevaljana potvrda lozinke. Pokušaj ponovo + Opis + Iznos + Nova transakcija + Nema transakcija + DUGUJE + POTRAŽUJE + Konti + Transakcije + Izbriši + Izbriši + Odustani + Konto je izbrisan + Potvrdi brisanje + Uredi transakciju + Dodaj napomenu + Odabrano: %1$d + Saldo: + Izvezi u: + Izvezi transakcije + Standardno se izvoze samo nove transakcije, učinjene nakon zadnjeg izvoza. Uključi ovu opciju za izvoz svih transakcija + Greška prilikom izvoza datoteke %1$s + Izvezi + Izbriši transakcije nakon izvoza + Sve izvezene transakcije će biti izbrisane nakon završetka izvoza + Postavke + + Spremi pod … + Dropbox + ownCloud + Pošalji na … + + Premjesti + Premjesti %1$d transakcije(a) + Odredišni konto + Prijenos transakcije nije moguć.\nOdredišni konto koristi drugačiju valutu od izvornog konta + Općenito + O programu + Odaberi standardnu valutu + Standardna valuta + Standardna valuta za nova konta + Omogućava spremanje transakcija u GnuCash za Android + Omogućava stvaranje konta u GnuCashu za Android + Tvoji GnuCash podaci + Čitaj i promijeni GnuCash podatke + Spremi transakcije u GnuCashu + Stvori transakcije u GnuCashu + Prikaži konto + Sakrij saldo konta u alatnom bloku + Stvori konta + U GnuCashu nema konta.\nStvori konto prije dodavanja alatnog bloka + Licenca + Apache License v2.0. Za detalje, klikni + Opće postavke + Odaberi konto + Nema transakcija za izvoz + Postavke za lozinku + Aktiviraj lozinku + Promijeni lozinku + O programu GnuCash + GnuCash Android %1$s izvoz + GnuCash Android izvoz iz + Transakcije + Postavke za transakcije + Postavke za konta + Standardna vrsta transakcije + Standardno korištena vrsta transakcije, POTRAŽUJE ili DUGUJE + + POTRAŽUJE + DUGUJE + + Zaista želiš izbrisati SVE transakcije? + Zaista želiš izbrisati ovu transakciju? + Izvoz + Izvezi sve transakcije + Izbriši izvezene transakcije + Standardna e-pošta za izvoz + Standardna adresa e-pošte, na koju se šalju datoteke izvoza. Adresu možeš promijeniti i prilikom izvoza. + Sve transakcije će se prenijeti s jednog konta na drugi konto + Aktiviraj dvojno knjigovodstvo + Saldo + Za stvaranje konta, unesi ime konta + Valuta + Matični konto + Koristi XML OFX zaglavlje + Aktiviraj ovu opciju za izvoz u neki drugi program, koji nije desktop verzija GnuCasha + Što je novo + - Podržava format za CSV-izvoz \n + - Poboljšana kompatibilnost s datotekama desktop verzije GnuCasha\n + - Ograničavanje slobodnog mjesta za sigurnosne kopije \n + - Razne ispravke grešaka i poboljšanja\n + Odbaci + Za spremanje transakcije, unesi iznos + Došlo je do greške prilikom uvoza GnuCash konta + GnuCash konti su uspješno uvezeni + Uvezi kontni plan iz GnuCash XML-a + Uvezi GnuCash XML + Izbriši sva konta iz baze podataka. Istovremeno se brišu sve transakcije. + + + Izbriši sva konta + Konti + Svi konti su uspješno izbrisani + Zaista želiš izbrisati sva konta i sve transakcije?\n\n + Ovo je nepovratna operacija! + + Izbrisat će se sve transakcije svih konta! + Izbriši sve transakcije + Sve transakcije su uspješno izbrisane! + Uvoz konta + Transakcije + Podkonta + Traži + Standardni format za izvoz + Format koji će se standardno koristiti za izvoz transakcija + Ponavljanje + + Debalans + Izvoz transakcija + Nema ponavljajućih transakcija. + Ponavljajuće transakcije su uspješno izbrisane + Rezervirani konto + Standardni konto za prijenos + + %d podkonto + %d podkonta + %d podkonta + + + GOTOVINA + BANKA + KREDITNA KARTICA + IMOVINA + OBVEZE + PRIHOD + RASHOD + DUGOVANJA + POTRAŽIVANJA + KAPITAL + VALUTA + DIONICE + INVESTICIJSKI FOND + TRGOVANJE + + + CSV + QIF + OFX + XML + + + Odaberi boju + + + Boja i vrsta konta + Izbriši podkonta + Nedavna + Favoriti + Sva + Stvara standardnu, u GnuCashu često korištenu strukturu konta + Stvori standardna konta + Otvorit će se nova knjiga sa standardnim kontima\n\nTvoji postojeći konti i transakcije se neće dirati! + Transakcije + Odaberi odredište za izvoz + Zabilješka + Izdatak + Primitak + Isplata + Uplata + Plaćanje + Terećenje + Smanjenje + Povećanje + Prihod + Popust + Rashod + Ulazni račun + izlazni račun + Kupovina + Prodaja + Početni saldo + Kapital + Aktiviraj, kako bi se trenutačni saldo konta preuzeo kao novi početni saldo nakon brisanja transakcija + + Spremi početni saldo konta + OFX ne pordžava dvojno knjigovodstvo + Za svaku valutu stvara pojedinačnu QIF datoteku + Debalans: + Dodaj podjelu + Favorit + Navigacijska traka je otvorena + Navigacijska traka je zatvorena + Izvještaji + Kružni dijagram + Linijski dijagram + Stupčani dijagram + Postavke za izvještaje + Boja konta u izvještajima + Koristi boje konta u stupčanom/kružnom dijagramu + Razvrstaj prema veličini + Prikaži legendu + Prikaži oznake + Prikaži postotke + Prikaži linije prosjeka + Grupiraj manje odsječke + Nema podataka za izradu dijagrama + Ukupno + Ostali + Postotak za odabranu vrijednost u odnosu na ukupni iznos + Postotak za odabranu vrijednost u odnosu na iznos trenutačnog složenog stupca + Spremi kao predložak + Ovaj konto sadržava transakcije. \nŠto želiš uraditi s tim transakcijama? + Ovaj konto sadržava podkonta. \nŠto želiš uraditi s tim podkontima? + Izbriši transakcije + Za spremanje transakcije, stvori i odredi konto za prijenos ILI deaktiviraj dvojno knjigovodstvo u postavkama + Stvori termine + Učitaj sigurnosnu kopiju … + Stvori sigurnosnu kopiju i izvezi + Aktiviraj DropBox + Aktiviraj ownCloud + Sigurnosna kopija + Aktiviraj izvoz u DropBox + Aktiviraj izvoz u ownCloud + Postavke za sigurnosnu kopiju + Stvori sigurnosnu kopiju + Stvori sigurnosnu kopiju aktivne knjige + Učitaj najnoviju sigurnosnu kopiju aktivne knjige + Sigurnosna kopija je uspješno stvorena + Sigurnosna kopija nije stvorena + Izvozi sva konta i sve transakcije + Instaliraj upravitelja datotekama za odabir datoteka + Odaberi sigurnosnu kopiju, koju želiš ponovo učitati + Favoriti + Otvori … + Izvještaji + Izvezi … + Postavke + Korisničko ime + Lozinka + owncloud + https:// + OC server nije nađen + OC korisničko ime/lozinka ne valja + Nevaljani slovni znakovi: \\ < > : \" | * ? + OC server je u redu + OC korisničko ime/lozinka je u redu + Dir ime je u redu + + Svaki sat + Svaka %d sata + Svakih %d sati + + + Dnevno + Svaka %d dana + Svakih %d dana + + + Tjedno + Svaka %d tjedna + Svakih %d tjedana + + + Mjesečno + Svaka %d mjeseca + Svakih %d mjeseci + + + Godišnje + Svake %d godine + Svakih %d godina + + Aktiviraj dnevnik urušavanja programa + Automatski šalji informacije o aplikacijskim greškama razvijateljima. + Format + Unesi staru lozinku + Unesi novu lozinku + Izvozi + Nema terminiranih izvoza + Stvori terminirane izvoze + Izvezeno u: %1$s + Legenda je pre dugačka + Opis konta + Nema nedavno korištenih konta + Nema favoriziranih konta + Terminirane radnje + "Završeno. Zadnje izvođenje %1$s" + Sljedeće + Gotovo + Standardna valuta + Postavke konta + Odaberi valutu + Opcije za feedback + Stvori standardna konta + Uvezi moja konta + Dozvoli da ja to obavim + Ostalo … + Automatski pošalji izvještaj o urušavanju programa + Deaktiviraj izvještavanje o urušavanju programa + Natrag + Postavi GnuCash + Dobro došla, dobro došao u GnuCash + Prije nego što kreneš s radom, \nnajprije namjesti par stvari\n\nZa nastavak, pritisni Sljedeće + Uređivač podjela + Prije spremanja, provjeri da sve stavke podjele imaju valjani iznos! + Nevaljani izraz! + Terminirana ponavljajuća transakcija + Prenesi sredstva + + Za prikaz detalja, odaberi jedan odsječak + Razdoblje: + Iz: + U: + Za prijenos sredstava, zadaj konvertiranu vrijednost ili tečaj + Tečaj + Dohvati tečaj + Konvertirani iznos + Bilanca + Rashod zadnjih tri mjeseca + Ukupno imovina + Ukupno obveze + Neto vrijednost + Imovina + Obveze + Kapital + Premjesti u: + Grupiraj po + Mjesecu + Kvartalu + Godini + Bilanca + Ukupno: + Google+ zajednica + Prevedi GnuCash + Izmijeni ideje, diskutiraj o promjenama ili prijavi greške + Prevodi ili ispravljaj prijevode na CrowdIn + Nema kompatibilne aplikacije za prihvaćanje izvezenih transakcija! + Premjesti … + Dupliciraj + Novčani tijek + Proračuni + Aktiviraj kompaktni prikaz + Aktiviraj stalni prikaz kompaktnog popisa transakcija + Nevaljani tečaj + npr. 1 %1$s = x.xx %2$s + Nevaljani iznos + + Tekući mjesec + Zadnja 3 mjeseca + Zadnjih 6 mjeseci + Zadnjih 12 mjeseci + Svo vrijeme + Proizvoljno razdoblje … + + + 1 + + + 2 + ABC + 3 + DEF + 4 + GHI + 5 + JKL + 6 + MNO + 7 + PQRS + 8 + TUV + 9 + WXYZ + 0 + + + Upravljaj knjigama + Upravljaj knjigama … + Za prikaz detalja, odaberi bilo koji dio dijagrama + Potvrdi brisanje knjige + Svi konti i sve transakcije ove knjige će biti izbrisane! + Izbriši knjigu + Zadnji izvoz: + Aktiviraj sinkronizaciju + Nova knjiga + Odabrana transakcija nije podijeljena i ne može se otvoriti + Broj podjela: %1$d + u %1$s + + %d konto + %d konta + %d konti + + + %d transakcija + %d transakcije + %d transakcija + + + RASHOD + PRIHOD + + Spoji se s Google Drive + Spajanje s Google Drive nije moguće + Unesi iznos u podjelu + vanjska usluga + Aktualiziraj ponavljanja terminiranih transakcija + Od + Svo vrijeme + Preporuči na Play Store stranici + do %1$s + na %1$s + %1$d puta + Kompaktni prikaz + Knjiga %1$d + nikad + Preimenuj knjigu + Preimenuj + Preimenuj + Odaberi sigurnosnu kopiju + Odaberi datoteku za spremanje sigurnosne kopije + Potvrdi ponovo učitavanje sigurnosne kopije + Otvorit će se nova knjiga sa sadržajem ove sigurnosne kopije. Želiš li nastaviti? + Učitaj sigurnosnu kopiju + Nema sigurnosnih kopija + Ne postoje sigurnosne kopije, koje bi se mogle učitati. + + gnucash_android_backup.gnca + Odaberi odredište nakon što izvoz završi. + Izvezi u Dropbox mapu pod „/Apps/GnuCash Android/„ + Postavke + Da, želim + export_accounts_csv_key + Izvezi sva konta (bez transakcija) u CSV + Izvezi kao CSV + Razdvojnik + Izvezi transakcije kao CSV + + Datum + ID transakcije + Broj + Opis + Napomene + Roba/Valuta + Razlog storniranja + Radnja + Zabilješka + Cjelokupno ime konta + Ime konta + Iznos s oznakom + Numerički iznos + Uskladi + Datum usklađivanja + Tečaj/cijena + + + Vrsta + Cjelokupno ime + Ime + Šifra + Opis + Boja + Napomene + robam + roban + Skriveno + Porez + Rezervirano mjesto + + diff --git a/app/src/main/res/values-in-rID/strings.xml b/app/src/main/res/values-in-rID/strings.xml index 8f9d8d940..32e24d46f 100644 --- a/app/src/main/res/values-in-rID/strings.xml +++ b/app/src/main/res/values-in-rID/strings.xml @@ -20,15 +20,15 @@ Ubah Akun Tambah transaksi baru ke akun Lihat rincian akun - Tak ada akun untuk ditampilkan + Tidak ada akun untuk ditampilkan Nama akun Batal Simpan - Uji coba - Masukkan sandi - Sandi salah, silakan coba lagi - Sandi dibuat - Harap konfirmasi sandi Anda + Tes + Masukkan Kode akses + Kode akses salah, silakan coba lagi + Kode akses dibuat + Harap konfirmasi kode akses Anda Konfirmasi kode akses tidak valid. Silakan cobalagi Deskripsi Jumlah @@ -183,7 +183,7 @@ Semua Buat struktur akun yang paling sering digunakan di GnuCash secara default Buat akun default - Sebuah buku baru akan dibuka dengan akun default\n\nakun dan transaksi Anda saat ini tidak akan diubah! + A new book will be opened with the default accounts\n\nYour current accounts and transactions will not be modified! Transaksi Pilih tujuan untuk ekspor Memo @@ -209,13 +209,10 @@ OFX tidak mendukung transaksi double-entry Menghasilkan file QIF terpisah per mata uang Tidak seimbang: - Tambahkan split -  + Add split Favorit - Laci navigasi dibuka -  - Laci navigasi tertutup -  + Navigation drawer opened + Navigation drawer closed Laporan Bagan Pai Bagan Garis @@ -250,8 +247,7 @@ Pengaturan Cadangan Membuat Cadangan Buat cadangan dari pembukuan yang aktif - Pulihkan cadangan terbaru buku aktif -  + Restore most recent backup of active book Pencadangan berhasil Pencadangan gagal Ekspor seluruh akun dan transaksi @@ -267,15 +263,13 @@ owncloud https:// Server OC tidak ditemukan - Username / kata kunci OC tidak valid -  + OC username/password invalid Karakter tidak valid: \\ < > : \" | * ? Server OC OK - OC username / password OK -  + OC username/password OK Nama Dir OK - Per jam + Every %d hours Setiap %d hari @@ -319,11 +313,9 @@ Kembali Pengaturan GnuCash Selamat datang di GnuCash - Sebelum Anda menyelam, \nlmari kita siapkan beberapa hal yang pertama \n\n Untuk melanjutkan, tekan Next - Editor split -  - Periksa apakah semua split memiliki jumlah yang valid sebelum menyimpannya! - ! + Before you dive in, \nlet\'s setup a few things first\n\nTo continue, press Next + Split Editor + Check that all splits have valid amounts before saving! Ekspresi yang tidak valid! Transaksi berulang yang dijadwalkan Transfer Dana @@ -332,53 +324,46 @@ Periode: Dari: Ke: - Berikan nilai tukar atau nilai tukar yang dikonversi untuk mentransfer dana -  - Kurs -  - Ambil kutipan -  - Jumlah yang Dikonversi -  - Lembar -  - Biaya untuk 3 bulan terakhir -  - Total aset - Jumlah kewajiban - Kekayaan Bersih - Aset - Kewajiban - Ekuitas - Pindah ke: - Kelompok dengan - Bulan - Kuartal - Tahun - Neraca keuangan -  - Jumlah: - Komunitas Google+ - Menerjemahkan GnuCash - Berbagi ide-ide, mendiskusikan perubahan atau melaporkan masalah - Menerjemahkan atau bukti dibaca pada CrowdIn - Tidak ada aplikasi yang kompatibel untuk menerima transaksi diekspor! - Bergerak… - Duplikat - Arus Tunai - Anggaran - Mengaktifkan tampilan kompak - Memungkinkan untuk selalu menggunakan tampilan kompak untuk daftar transaksi - Edit nilai tukar - misalnya 1 %1$s = x.xx %2$s - Nominal tidak sah + Provide either the converted amount or exchange rate in order to transfer funds + Exchange rate + Fetch quote + Converted Amount + Sheet + Expenses for last 3 months + Total Assets + Total Liabilities + Net Worth + Assets + Liabilities + Equity + Move to: + Group By + Month + Quarter + Year + Balance Sheet + Total: + Google+ Community + Translate GnuCash + Share ideas, discuss changes or report problems + Translate or proof-read on CrowdIn + No compatible apps to receive the exported transactions! + Move… + Duplicate + Cash Flow + Budgets + Enable compact view + Enable to always use compact view for transactions list + Invalid exchange rate + e.g. 1 %1$s = x.xx %2$s + Invalid amount - Bulan berjalan - 3 bulan terakhir - 6 bulan terakhir - 12 bulan terakhir - Sepanjang waktu - Rentang ubahsuai… + Current month + Last 3 months + Last 6 months + Last 12 months + All time + Custom range… 1 @@ -402,61 +387,55 @@ WXYZ 0 + - Mengelola buku - Mengelola buku… - Memilih bagian dari tabel untuk melihat rincian - Konfirmasi penghapusan - Semua account dan transaksi dalam buku ini akan dihapus! - Menghapus buku - Terakhir Diekspor Di: - Mengaktifkan sinkronisasi - Buku baru - Transaksi dipilih telah ada perpecahan dan tidak dapat dibuka - perpecahan %1$d - di %1$s + Manage Books + Manage Books… + Select any part of the chart to view details + Confirm delete Book + All accounts and transactions in this book will be deleted! + Delete Book + Last Exported: + Enable Sync + New Book + The selected transaction has no splits and cannot be opened + %1$d splits + in %1$s - %d sub-akun + %d accounts - transaksi%d + %d transactions - PENGELUARAN - PENDAPATAN + EXPENSE + INCOME - Hubungakan ke Google play - Tidak dapat tersambung ke server - Masukkan jumlah - layanan Eksternal - Diperbarui transaksi berkala jadwal - Sejak - Sepanjang waktu - Merekomendasikan di Play Store - sampai %1$s - di %1$s - untuk %1$d kali - Tampilan Sederhana - Buku %1$d - tdk pernah - Mengubah nama buku - Ubah Nama - Ubah Nama - Pilih file backup - Pilih file untuk backup otomatis -  - Yakin memulihkan dari backup - Sebuah buku baru akan dibuka dengan isi backup ini. Apakah Anda ingin melanjutkan? - ? - Mengembalikan - Backup tidak ditemukan - Tidak ada file cadangan yang ada untuk dipulihkan -  + Connected to Google Drive + Unable to connect to Google Drive + Please enter an amount to split + external service + Updated transaction recurring schedule + Since + All time + Recommend in Play Store + until %1$s + on %1$s + for %1$d times + Compact View + Book %1$d + never + Rename Book + Rename + Rename + Select backup file + Select a file for automatic backups + Confirm restore from backup + A new book will be opened with the contents of this backup. Do you wish to proceed? + Restore + No backups found + There are no existing backup files to restore from - gnucash_android_backup.gnca -  - Pilih tujuan setelah ekspor selesai -  - Ekspor ke folder \'/ Apps / GnuCash Android /\' di Dropbox -  - Preferensi + gnucash_android_backup.gnca + Select the destination after export is complete + Export to \'/Apps/GnuCash Android/\' folder on Dropbox + Preferences diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 37681f758..2f3bfca4f 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -138,7 +138,7 @@ Todas as transações apagadas com sucesso! Importando contas Transações - Subcontas + Sub-Contas Procurar Formato de Exportação padrão Formato de arquivo a ser usado por padrão ao exportar transações @@ -151,8 +151,8 @@ Conta não editável Conta para transferências padrão - %d subconta - %d subconta + %d sub-conta + %d sub-contas DINHEIRO @@ -180,9 +180,9 @@ Cor de conta & Tipo - Apagar subcontas + Apagar sub-contas Recentes - Favoritas + Favoritos Todas Cria uma estrutura de contas GnuCash padrão Cria contas padrão @@ -222,8 +222,8 @@ Gráfico de Linhas Gráfico de Barras Preferências de relatórios - Cor da conta nos relatórios - Use cor da conta no gráfico de barras/linhas + Côr da conta nos relatórios + Use côr da conta no gráfico de barras/linhas Ordenar por tamanho Alterna visibilidade da legenda Alterna visibilidade das etiquetas @@ -428,7 +428,7 @@ Neste processo não serão recolhidas informações do utilizador! Agenda recorrente de transação atualizada Desde Desde o início - Recomendar na Play Store + Recomendado na Play Store até %1$s em %1$s por %1$d vezes diff --git a/app/src/main/res/values-pt-rPT/strings.xml b/app/src/main/res/values-pt-rPT/strings.xml index eedcb017a..fed5df41b 100644 --- a/app/src/main/res/values-pt-rPT/strings.xml +++ b/app/src/main/res/values-pt-rPT/strings.xml @@ -428,7 +428,7 @@ Neste processo não serão recolhidas informações do utilizador! Transação atualizada agendamento recorrente Desde Todo o tempo - Recomendar na Play Store + Recomendado na Play Store desde%1$s na %1$s por %1$d vezes diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 1b625cc34..9ba4502df 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -154,7 +154,6 @@ %d дочерний счёт %d шт. дочерних счетов - %d sub-accounts %d шт. дочерних счетов @@ -225,7 +224,7 @@ График Гистограмма Настройки отчётов - Цвет счёта в отчётах + Цвет счёта в отчётых Использовать цвет счёта в отчётах Отсортировать по размеру Показать легенду @@ -278,31 +277,26 @@ Каждый час Каждые %d часа - Every %d hours Каждые %d часов Ежедневно Каждые %d дня - Every %d days Каждые %d дней Еженедельно Каждые %d недели - Every %d weeks Каждые %d недель Ежемесячно Каждые %d месяца - Every %d months Каждые %d месяцев Ежегодно Каждые %d года - Every %d years Каждые %d лет Записывать отказы программы @@ -335,7 +329,7 @@ Назад Настройка GnuCash Добро пожаловать в GnuCash - Прежде, чем вы начнёте,\nдавайте настроим некоторые вещи.\n\nДля продолжения нажмите «Далее». + Прежде, чем вы начныте,\nдавайте настроим некоторые вещи.\n\nДля продолжения нажмите «Далее». Редактор разбиения Проверьте, что все части корректно распределены перед сохранением! Неверное выражение! @@ -347,7 +341,7 @@ С: До: Заполните или обменянную сумму или курс обмена - Курс обмена + EКурс обмена Получить котировки Обмененная сумма Сводка @@ -424,13 +418,11 @@ %d счета %d счета - %d accounts %d счетов %d транзакция %d транзакции - %d transactions %d транзакций @@ -457,7 +449,7 @@ Выберите файл резервной копии Выберите файл для автоматического резервного копирования Подтвердите восстановление из резервной копии - Новая книга будет открыта с содержанием этой резервной копии. Хотите продолжить? + Новая книга будет открыта с содержанием этой резервной копии. Вы хотите продолжить? Восстановить Резервные копии не найдены Нет существующих файлов резервных копий для восстановления diff --git a/app/src/main/res/values-sv-rSE/strings.xml b/app/src/main/res/values-sv-rSE/strings.xml index 71645393c..be8308fd5 100644 --- a/app/src/main/res/values-sv-rSE/strings.xml +++ b/app/src/main/res/values-sv-rSE/strings.xml @@ -20,7 +20,7 @@ Redigera konto Lägg till en transaktion till ett konto Visa kontodetaljer - Inga konton att visa + Det finns inga konton att visa Kontonamn Avbryt Spara @@ -33,7 +33,7 @@ Beskrivning Belopp Lägg till transaktion - Inga transaktioner att visa + Det finns inga transaktioner att visa DEBIT KREDIT Konton @@ -79,7 +79,7 @@ Visa konto Dölja kontosaldo i widget Skapa konton - Inga konton finns i GnuCash.\nSkapa ett konto innan du lägger till en gränssnittskomponent + Inga konton finns i GnuCash.\nSkapa ett konto innan du lägger till en widget Licens Apache License v2.0. Klicka för Detaljer Allmänna inställningar @@ -293,7 +293,7 @@ Ange din gamla lösenkod Ange din nya lösenkod Exporter - Inga schemalagda exporter att visa + Ingen schemalagda exporter att Visa Skapa exportschema Exporterades till: %1$s Förklaringen är för lång @@ -391,8 +391,8 @@ WXYZ 0 + - Hantera bokföringar - Hantera bokföringar… + Hantera böcker + Hantera böcker… Välj någon del av diagrammet för att visa detaljer Bekräfta radera bokföring Alla konton och transaktioner i denna bokföring kommer att raderas! @@ -400,7 +400,7 @@ Senast exporterad: Aktivera synkronisering Ny bokföring - Valda transaktionen har inga delningar och kan inte öppnas + Valda transaktionen har ingen delningar och kan inte öppnas %1$d delar i %1$s diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 8b502ea05..70dd0e16b 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -18,9 +18,9 @@ 新增科目 編輯科目 - 在科目中新增交易 + 给科目添加交易 檢視帳戶詳細資訊 - 沒有科目可顯示 + 没有要显示的科目 科目名稱 取消 存檔 @@ -59,7 +59,7 @@ 另存為… Dropbox ownCloud - 傳送到… + Send to… 移動 移動 %1$d 交易 @@ -78,7 +78,7 @@ 建立會計科目 顯示科目名字 小工具中隱藏帳戶餘額 - 建立科目 + 创建科目 GnuCash裡還没有會計科目信息。\n使用小部件前需要添加會計科目 授權許可 Apache License v2.0,點擊查看詳细(將打開網頁)。 @@ -125,7 +125,7 @@ 知道了 輸入金額才能保存交易 - 匯入GnuCash科目時發生錯誤。 + 匯入GnuCash科目时發生錯誤。 GnuCash科目資料匯入完成。 匯入從GnuCash桌面版匯出的科目設置 匯入GnuCash科目 @@ -177,7 +177,7 @@ XML - 選擇顏色 + 选择一种颜色 科目顏色和類型 @@ -187,7 +187,8 @@ 所有 建立通用的科目結構 建立預設科目 - 這將用預設科目來建立新的帳簿\n\n目前擁有的科目與交易不會受影響 + 将会创建新的账簿附带默认的科目 +现在这个账簿不会受到影响 交易 選擇儲存的位置 描述 @@ -196,7 +197,7 @@ 提款 存款 付款 - 費用 + 费用 減少 增加 收入 @@ -220,10 +221,10 @@ 報表 圓形圖 折線圖 - 長條圖 + 橫條圖 報表設置 用不同顏色区分科目 - 在圓餅圖中使用科目的顏色 + 在饼图中使用科目的颜色 按數量排序 顯示圖例 顯示標籤 @@ -266,20 +267,20 @@ 密碼 ownCloud https:// - 沒有找到 ownCloud 伺服器 + 伺服器找不到。 帳號/密碼無效 無效字元: \\ < > : \" | * ? OC server OK OC username/password OK Dir name OK - 每 %d 個小時 + Every %d hours 每 %d 天 - 每 %d 週 + 每 %d 周 每 %d 月 @@ -311,13 +312,13 @@ No user-identifiable information will be collected as part of this process! 選擇幣種 回饋選項 建立預設科目 - 匯入科目 + 汇入科目 稍后处理 其他... 自動發送故障報告 禁用崩潰報告 後退 - 設定GnuCash + 设置GnuCash 歡迎來到GnuCash 在使用之前,需 \n要设置几个参数\n\n请点击“下一步”繼續 拆分交易 @@ -343,7 +344,7 @@ No user-identifiable information will be collected as part of this process! 負債 財產淨值 移動至 - 分組方式 + 分组方式 季度 @@ -352,13 +353,13 @@ No user-identifiable information will be collected as part of this process! Google+ 社群 翻譯GnuCash 在Google+上提交问题和建议 - 在CrowdIn上協助翻譯或校對GnuCash + 帮忙翻譯或校對( CrowdIn) 没有合适的应用接收汇出的文档 移動... 複製 現金流 預算 - 啟用緊湊視圖 + 啟用紧凑視圖 交易清單總是啟用緊湊視圖 匯率不正確 例如 1 %1$s = x.xx %2$s @@ -393,23 +394,23 @@ No user-identifiable information will be collected as part of this process! WXYZ 0 + - 管理帳簿 - 管理帳簿… + 管理帐簿 + 管理帐簿 選擇該圖表以查看詳細資訊的任何部分 確認删除 - 帳簿中所有科目和交易都將被刪除 ! - 删除帳簿 + 帐簿中所有科目和交易都將被刪除 ! + 删除帐簿 最後匯出︰ 啟用同步 新建帐簿 选择的交易没有拆分 %1$d 项分割 - 於 %1$s + 于 %1$s - %d 個科目 + %d个科目 - %d 個交易 + %d个交易 費用 @@ -419,29 +420,29 @@ No user-identifiable information will be collected as part of this process! 無法連線到伺服器 請輸入要拆分的金額 外部服務 - 排程交易已經更新 + 排程交易已经更新 自從 全部時間 在 Play Store 推薦 直到%1$s 在%1$s - %1$d 次 - 緊湊視圖 - 帳簿 %1$d - 從未 - 重新命名帳簿 - 重新命名 - 重新命名 - 選擇備份檔 - 選擇自動備份時用的檔案 - 確認恢復備份 - 將以這個備份的內容新建一本帳簿。確定要繼續? - 恢復 - 沒有找到備份 - 沒有能恢復的備份 + for %1$d times + 紧凑视图 + 账簿 %1$d + never + Rename Book + Rename + Rename + Select backup file + Select a file for automatic backups + Confirm restore from backup + A new book will be opened with the contents of this backup. Do you wish to proceed? + Restore + No backups found + There are no existing backup files to restore from - gnucash_android_備份.gnca - 匯出後再選擇目的地 - 匯出到 Dropbox 上的 \'/Apps/GnuCash Android/\' 資料夾 - 偏好設定 + gnucash_android_backup.gnca + Select the destination after export is complete + Export to \'/Apps/GnuCash Android/\' folder on Dropbox + Preferences diff --git a/crowdin.yml b/crowdin.yml index 57f6ea49d..b4389d4b2 100644 --- a/crowdin.yml +++ b/crowdin.yml @@ -7,6 +7,8 @@ # "preserve_hierarchy": false +"disable-branches": true + # # Files configuration # From 4fac62785c9de9ed0c3ac8646d4efe170377e97b Mon Sep 17 00:00:00 2001 From: Ngewi Fet Date: Sun, 10 Nov 2019 01:56:44 +0100 Subject: [PATCH 019/131] Update CHANGELOG for v2.4.1 release Update version for first beta release --- CHANGELOG.md | 9 +++++++++ app/build.gradle | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 17a7f8258..44d39ce21 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ Change Log =============================================================================== +Version 2.4.0 *(2018-06-15)* +---------------------------- +* Fixes #809: Crash when exporting CSV +* Fixes #811: Add button in schedule action disappears after rotation +* Fixes #790: Missing transaction description margin +* Improve performance of CSV export +* Improved: Add book name to accounts listing view +* Improved: Update translations + Version 2.4.0 *(2018-06-15)* ---------------------------- * Feature #665: Adds CSV export format diff --git a/app/build.gradle b/app/build.gradle index 74cd220eb..433fd721b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -6,7 +6,7 @@ apply plugin: 'io.fabric' def versionMajor = 2 def versionMinor = 4 def versionPatch = 1 -def versionBuild = 0 +def versionBuild = 1 static def buildTime() { def df = new SimpleDateFormat("yyyyMMdd HH:mm 'UTC'") From a9fcb9fcd16c883d235c59abd0ea8684a2124730 Mon Sep 17 00:00:00 2001 From: Ngewi Fet Date: Thu, 21 Nov 2019 19:11:01 +0100 Subject: [PATCH 020/131] Update version and CHANGELOG for v2.4.1-beta2 release Fix crash during export of transations after upgrade to v2.4.1-beta1 The commodities table was replaced, however, there are references to the commodities in the transactions Remove GnuCash Android Google+ Community Page link (Google+ is dead) --- CHANGELOG.md | 3 +- app/build.gradle | 2 +- .../gnucash/android/db/DatabaseHelper.java | 6 ++-- .../gnucash/android/db/MigrationHelper.java | 10 +++--- .../importer/CommoditiesXmlHandler.java | 36 ++++++++++++++++--- .../res/xml/fragment_about_preferences.xml | 5 --- 6 files changed, 41 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 44d39ce21..97d30e73f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,11 @@ Change Log =============================================================================== -Version 2.4.0 *(2018-06-15)* +Version 2.4.1 *(2019-11-30)* ---------------------------- * Fixes #809: Crash when exporting CSV * Fixes #811: Add button in schedule action disappears after rotation * Fixes #790: Missing transaction description margin +* Fixes crash if during database upgrade triggered by scheduled action * Improve performance of CSV export * Improved: Add book name to accounts listing view * Improved: Update translations diff --git a/app/build.gradle b/app/build.gradle index 433fd721b..ee6d05082 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -6,7 +6,7 @@ apply plugin: 'io.fabric' def versionMajor = 2 def versionMinor = 4 def versionPatch = 1 -def versionBuild = 1 +def versionBuild = 2 static def buildTime() { def df = new SimpleDateFormat("yyyyMMdd HH:mm 'UTC'") diff --git a/app/src/main/java/org/gnucash/android/db/DatabaseHelper.java b/app/src/main/java/org/gnucash/android/db/DatabaseHelper.java index b8ef6c41f..9d4762ef3 100644 --- a/app/src/main/java/org/gnucash/android/db/DatabaseHelper.java +++ b/app/src/main/java/org/gnucash/android/db/DatabaseHelper.java @@ -20,11 +20,9 @@ import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.util.Log; -import android.widget.Toast; import com.crashlytics.android.Crashlytics; -import org.gnucash.android.app.GnuCashApplication; import org.gnucash.android.model.Commodity; import org.xml.sax.SAXException; @@ -264,7 +262,7 @@ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion){ Log.i(LOG_TAG, "Upgrading database from version " + oldVersion + " to " + newVersion); - Toast.makeText(GnuCashApplication.getAppContext(), "Upgrading GnuCash database", Toast.LENGTH_SHORT).show(); + //TODO: Find way to show a progress dialog for long running db upgrades /* * NOTE: In order to modify the database, create a new static method in the MigrationHelper class * called upgradeDbToVersion<#>, e.g. int upgradeDbToVersion10(SQLiteDatabase) in order to upgrade to version 10. @@ -354,7 +352,7 @@ private void createDatabaseTables(SQLiteDatabase db) { db.execSQL(createBudgetAmountUidIndex); try { - MigrationHelper.importCommodities(db); + MigrationHelper.importCommodities(db, true); } catch (SAXException | ParserConfigurationException | IOException e) { Log.e(LOG_TAG, "Error loading currencies into the database"); e.printStackTrace(); 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 a5717671b..dbad1acb9 100644 --- a/app/src/main/java/org/gnucash/android/db/MigrationHelper.java +++ b/app/src/main/java/org/gnucash/android/db/MigrationHelper.java @@ -235,7 +235,7 @@ public void run() { /** * Imports commodities into the database from XML resource file */ - static void importCommodities(SQLiteDatabase db) throws SAXException, ParserConfigurationException, IOException { + static void importCommodities(SQLiteDatabase db, boolean deleteExisting) throws SAXException, ParserConfigurationException, IOException { SAXParserFactory spf = SAXParserFactory.newInstance(); SAXParser sp = spf.newSAXParser(); XMLReader xr = sp.getXMLReader(); @@ -246,7 +246,7 @@ static void importCommodities(SQLiteDatabase db) throws SAXException, ParserConf /** Create handler to handle XML Tags ( extends DefaultHandler ) */ - CommoditiesXmlHandler handler = new CommoditiesXmlHandler(db); + CommoditiesXmlHandler handler = new CommoditiesXmlHandler(db, deleteExisting); xr.setContentHandler(handler); xr.parse(new InputSource(bos)); @@ -883,7 +883,7 @@ static int upgradeDbToVersion9(SQLiteDatabase db){ + "' ON " + CommodityEntry.TABLE_NAME + "(" + CommodityEntry.COLUMN_UID + ")"); try { - importCommodities(db); + importCommodities(db, true); } catch (SAXException | ParserConfigurationException | IOException e) { Log.e(LOG_TAG, "Error loading currencies into the database", e); Crashlytics.logException(e); @@ -1633,7 +1633,7 @@ static int upgradeDbToVersion15(SQLiteDatabase db) { * Upgrades the database to version 16. *

This migration makes the following changes to the database: *

    - *
  • Re-populate the commodities table (see #731)
  • + *
  • Update the commodities table (see #731)
  • *
*

* @param db SQLite database to be upgraded @@ -1644,7 +1644,7 @@ static int upgradeDbToVersion16(SQLiteDatabase db) { int dbVersion = 15; try { - importCommodities(db); + importCommodities(db, false); dbVersion = 16; } catch (SAXException | ParserConfigurationException | IOException e) { Log.e(LOG_TAG, "Error loading currencies into the database", e); diff --git a/app/src/main/java/org/gnucash/android/importer/CommoditiesXmlHandler.java b/app/src/main/java/org/gnucash/android/importer/CommoditiesXmlHandler.java index afefcc343..0cc9b84b4 100644 --- a/app/src/main/java/org/gnucash/android/importer/CommoditiesXmlHandler.java +++ b/app/src/main/java/org/gnucash/android/importer/CommoditiesXmlHandler.java @@ -15,9 +15,11 @@ */ package org.gnucash.android.importer; +import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import org.gnucash.android.app.GnuCashApplication; +import org.gnucash.android.db.DatabaseSchema; import org.gnucash.android.db.adapter.CommoditiesDbAdapter; import org.gnucash.android.db.adapter.DatabaseAdapter; import org.gnucash.android.model.Commodity; @@ -44,17 +46,23 @@ public class CommoditiesXmlHandler extends DefaultHandler { * List of commodities parsed from the XML file. * They will be all added to db at once at the end of the document */ - private List mCommodities; + private List mCommodities = new ArrayList<>(); + + private boolean deleteExisting; private CommoditiesDbAdapter mCommoditiesDbAdapter; - public CommoditiesXmlHandler(SQLiteDatabase db){ + public CommoditiesXmlHandler(SQLiteDatabase db, boolean deleteExisting){ + initAdapter(db); + this.deleteExisting = deleteExisting; + } + + private void initAdapter(SQLiteDatabase db) { if (db == null){ mCommoditiesDbAdapter = GnuCashApplication.getCommoditiesDbAdapter(); } else { mCommoditiesDbAdapter = new CommoditiesDbAdapter(db); } - mCommodities = new ArrayList<>(); } @Override @@ -82,7 +90,25 @@ public void startElement(String uri, String localName, String qName, Attributes @Override public void endDocument() throws SAXException { - mCommoditiesDbAdapter.deleteAllRecords(); - mCommoditiesDbAdapter.bulkAddRecords(mCommodities, DatabaseAdapter.UpdateMethod.insert); + if (this.deleteExisting){ + mCommoditiesDbAdapter.deleteAllRecords(); + mCommoditiesDbAdapter.bulkAddRecords(mCommodities, DatabaseAdapter.UpdateMethod.insert); + } else { + List existingCurrencyCodes = new ArrayList<>(); + + try(Cursor cursor = mCommoditiesDbAdapter.fetchAllRecords()) { + while (cursor.moveToNext()) { + String code = cursor.getString(cursor.getColumnIndexOrThrow(DatabaseSchema.CommodityEntry.COLUMN_MNEMONIC)); + existingCurrencyCodes.add(code); + } + } + for (Commodity commodity : mCommodities) { + if (existingCurrencyCodes.contains(commodity.getCurrencyCode())){ + mCommoditiesDbAdapter.addRecord(commodity, DatabaseAdapter.UpdateMethod.update); + } else { + mCommoditiesDbAdapter.addRecord(commodity, DatabaseAdapter.UpdateMethod.insert); + } + } + } } } diff --git a/app/src/main/res/xml/fragment_about_preferences.xml b/app/src/main/res/xml/fragment_about_preferences.xml index 47e375b21..19e4bbb63 100644 --- a/app/src/main/res/xml/fragment_about_preferences.xml +++ b/app/src/main/res/xml/fragment_about_preferences.xml @@ -18,11 +18,6 @@ - - - Date: Wed, 27 Nov 2019 11:53:29 +0100 Subject: [PATCH 021/131] Fixes crash when exporting after upgrading to v2.4.1-beta1 from v2.4.0 The commudity UIDs across the database were invalidated. A new migration has been added to refresh them after a new import. This issue only affects beta1 users. Users who went directly from v2.4.0 to v2.4.1-beta2 are not affected - Properly refrsh the book name in the accounts activity --- .../gnucash/android/db/DatabaseSchema.java | 2 +- .../gnucash/android/db/MigrationHelper.java | 22 +++++++++++++++++++ .../android/ui/account/AccountsActivity.java | 11 +++++++++- 3 files changed, 33 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 991029b88..eb689affe 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 = 16; + public static final int DATABASE_VERSION = 17; /** * 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 dbad1acb9..f56b72eb8 100644 --- a/app/src/main/java/org/gnucash/android/db/MigrationHelper.java +++ b/app/src/main/java/org/gnucash/android/db/MigrationHelper.java @@ -1653,4 +1653,26 @@ static int upgradeDbToVersion16(SQLiteDatabase db) { return dbVersion; } + + /** + * Upgrades the database to version 17. + * This migration updates the foreign keys to the commodities in the splits table + * after a previous migration which overwrote the db table for commodities, rendering + * all foreign keys to commodity GUIDs invalid. + * + * @param db SQLite database to be upgraded + * @return New database version, 16 if migration succeeds, 15 otherwise + */ + static int upgradeDbToVersion17(SQLiteDatabase db) { + Log.i(LOG_TAG, "Upgrading database to version 17"); + int dbVersion = 16; + + db.beginTransaction(); + db.execSQL("UPDATE accounts SET commodity_uid = (SELECT uid FROM commodities WHERE mnemonic = accounts.currency_code)"); + db.execSQL("UPDATE transactions SET commodity_uid = (SELECT uid FROM commodities WHERE commodities.mnemonic = transactions.currency_code)"); + + db.endTransaction(); + + return dbVersion + 1; + } } diff --git a/app/src/main/java/org/gnucash/android/ui/account/AccountsActivity.java b/app/src/main/java/org/gnucash/android/ui/account/AccountsActivity.java index 2a59bd68f..5e8a94026 100644 --- a/app/src/main/java/org/gnucash/android/ui/account/AccountsActivity.java +++ b/app/src/main/java/org/gnucash/android/ui/account/AccountsActivity.java @@ -37,6 +37,7 @@ import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentPagerAdapter; import android.support.v4.view.ViewPager; +import android.support.v7.app.ActionBar; import android.support.v7.app.AppCompatActivity; import android.support.v7.preference.PreferenceManager; import android.util.Log; @@ -227,7 +228,6 @@ public void onCreate(Bundle savedInstanceState) { init(); - getSupportActionBar().setSubtitle(BooksDbAdapter.getInstance().getActiveBookDisplayName()); TabLayout tabLayout = (TabLayout) findViewById(R.id.tab_layout); tabLayout.addTab(tabLayout.newTab().setText(R.string.title_recent_accounts)); tabLayout.addTab(tabLayout.newTab().setText(R.string.title_all_accounts)); @@ -269,6 +269,15 @@ public void onClick(View v) { }); } + @Override + protected void onResume() { + super.onResume(); + ActionBar actionBar = getSupportActionBar(); + if (actionBar != null) { + actionBar.setSubtitle(BooksDbAdapter.getInstance().getActiveBookDisplayName()); + } + } + @Override protected void onStart() { super.onStart(); From 32ee92744dd203f9406bd213fac3c03a512c7a53 Mon Sep 17 00:00:00 2001 From: Ngewi Fet Date: Thu, 28 Nov 2019 21:46:40 +0100 Subject: [PATCH 022/131] Upgrade version for v2.4.1-beta3 release --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index ee6d05082..b5f0bf1f2 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -6,7 +6,7 @@ apply plugin: 'io.fabric' def versionMajor = 2 def versionMinor = 4 def versionPatch = 1 -def versionBuild = 2 +def versionBuild = 3 static def buildTime() { def df = new SimpleDateFormat("yyyyMMdd HH:mm 'UTC'") From 85704b408b7a89ff4d38d2cdeb15b9bd3cda4679 Mon Sep 17 00:00:00 2001 From: Ngewi Fet Date: Sat, 30 Nov 2019 23:52:18 +0100 Subject: [PATCH 023/131] Upgrade version for v2.4.1 final release --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index b5f0bf1f2..b4483fbfe 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -6,7 +6,7 @@ apply plugin: 'io.fabric' def versionMajor = 2 def versionMinor = 4 def versionPatch = 1 -def versionBuild = 3 +def versionBuild = 4 static def buildTime() { def df = new SimpleDateFormat("yyyyMMdd HH:mm 'UTC'") From 5670ac8f386c5252b4a899903901470e765b70f4 Mon Sep 17 00:00:00 2001 From: JeanGarf Date: Fri, 17 Jan 2020 22:31:24 +0100 Subject: [PATCH 024/131] #110 - Create SearchableSpinner --- .../ArrayAdapterWithContainsFilter.java | 66 ++++ .../SearchableListDialog.java | 361 ++++++++++++++++++ .../searchablespinner/SearchableSpinner.java | 265 +++++++++++++ .../res/layout/searchable_list_dialog.xml | 22 ++ app/src/main/res/values-fr/strings.xml | 1 + app/src/main/res/values/attrs.xml | 4 + app/src/main/res/values/strings.xml | 1 + 7 files changed, 720 insertions(+) create mode 100644 app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/ArrayAdapterWithContainsFilter.java create mode 100644 app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableListDialog.java create mode 100644 app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableSpinner.java create mode 100644 app/src/main/res/layout/searchable_list_dialog.xml diff --git a/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/ArrayAdapterWithContainsFilter.java b/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/ArrayAdapterWithContainsFilter.java new file mode 100644 index 000000000..77a4f0cfd --- /dev/null +++ b/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/ArrayAdapterWithContainsFilter.java @@ -0,0 +1,66 @@ +package org.gnucash.android.ui.util.widget.searchablespinner; + +import android.app.Activity; +import android.support.annotation.NonNull; +import android.widget.ArrayAdapter; +import android.widget.Filter; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +public class ArrayAdapterWithContainsFilter + extends ArrayAdapter { + + private List _fileteredList = null; + private ArrayList _initialList; + + public ArrayAdapterWithContainsFilter(Activity context, + int items_view, + List items) { + + super(context, + items_view, + items); + + // save the initialList + this._initialList = new ArrayList(); + this._initialList.addAll(items); + + // + this._fileteredList = items; + } + +// @NonNull +// @Override +// public Filter getFilter() { +// +// return super.getFilter(); +// } + + // Filter method + public void filter(String textToSearch) { + + textToSearch = textToSearch.toLowerCase(Locale.getDefault()); + + _fileteredList.clear(); + + if (textToSearch.length() == 0) { + + _fileteredList.addAll(_initialList); + + } else { + + for (String item : _initialList) { + + if (item.toLowerCase(Locale.getDefault()) + .contains(textToSearch)) { + // + + _fileteredList.add(item); + } + } + } + notifyDataSetChanged(); + } +} diff --git a/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableListDialog.java b/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableListDialog.java new file mode 100644 index 000000000..1d383dc75 --- /dev/null +++ b/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableListDialog.java @@ -0,0 +1,361 @@ +package org.gnucash.android.ui.util.widget.searchablespinner; + +import android.app.AlertDialog; +import android.app.Dialog; +import android.app.DialogFragment; +import android.app.SearchManager; +import android.content.Context; +import android.content.DialogInterface; +import android.os.Bundle; +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.view.inputmethod.InputMethodManager; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.ListView; +import android.widget.SearchView; + +import org.gnucash.android.R; + +import java.io.Serializable; +import java.util.List; + +public class SearchableListDialog + extends DialogFragment + implements SearchView.OnQueryTextListener, + SearchView.OnCloseListener { + + private static final String ITEMS = "items"; + + private ArrayAdapter listAdapter; + + private ListView _listViewItems; + + private SearchableItem _searchableItem; + + private OnSearchTextChanged _onSearchTextChanged; + + private SearchView _searchView; + + private String _strTitle; + + private String _strPositiveButtonText; + + private DialogInterface.OnClickListener _onClickListener; + + public SearchableListDialog() { + + } + + public static SearchableListDialog newInstance(List items) { + + SearchableListDialog multiSelectExpandableFragment = new SearchableListDialog(); + + Bundle args = new Bundle(); + args.putSerializable(ITEMS, + (Serializable) items); + + multiSelectExpandableFragment.setArguments(args); + + return multiSelectExpandableFragment; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + + super.onCreate(savedInstanceState); + + } + + @Override + public View onCreateView(LayoutInflater inflater, + ViewGroup container, + Bundle savedInstanceState) { + + getDialog().getWindow() + .setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN); + return super.onCreateView(inflater, + container, + savedInstanceState); + } + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + + // Getting the layout inflater to inflate the view in an alert dialog. + LayoutInflater inflater = LayoutInflater.from(getActivity()); + + // Crash on orientation change #7 + // Change Start + // Description: As the instance was re initializing to null on rotating the device, + // getting the instance from the saved instance + if (null != savedInstanceState) { + _searchableItem = (SearchableItem) savedInstanceState.getSerializable("item"); + } + // Change End + + View rootView = inflater.inflate(R.layout.searchable_list_dialog, + null); + + setData(rootView); + + AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(getActivity()); + alertDialogBuilder.setView(rootView); + + String strPositiveButton = _strPositiveButtonText == null + ? "CLOSE" + : _strPositiveButtonText; + alertDialogBuilder.setPositiveButton(strPositiveButton, + _onClickListener); + + String strTitle = _strTitle == null + ? "Select Item" + : _strTitle; + alertDialogBuilder.setTitle(strTitle); + + final AlertDialog dialog = alertDialogBuilder.create(); +// dialog.getWindow() +// .setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN); + + hideKeyboard(_searchView); + return dialog; + } + + // Crash on orientation change #7 + // Change Start + // Description: Saving the instance of searchable item instance. + @Override + public void onSaveInstanceState(Bundle outState) { + + outState.putSerializable("item", + _searchableItem); + super.onSaveInstanceState(outState); + } + // Change End + + public void setTitle(String strTitle) { + + _strTitle = strTitle; + } + + public void setPositiveButton(String strPositiveButtonText) { + + _strPositiveButtonText = strPositiveButtonText; + } + + public void setPositiveButton(String strPositiveButtonText, + DialogInterface.OnClickListener onClickListener) { + + _strPositiveButtonText = strPositiveButtonText; + _onClickListener = onClickListener; + } + + public void setOnSearchableItemClickListener(SearchableItem searchableItem) { + + this._searchableItem = searchableItem; + } + + public void setOnSearchTextChangedListener(OnSearchTextChanged onSearchTextChanged) { + + this._onSearchTextChanged = onSearchTextChanged; + } + + private void setData(View rootView) { + + SearchManager searchManager = (SearchManager) getActivity().getSystemService(Context.SEARCH_SERVICE); + + _searchView = (SearchView) rootView.findViewById(R.id.search); + _searchView.setSearchableInfo(searchManager.getSearchableInfo(getActivity().getComponentName())); + _searchView.setIconifiedByDefault(false); + _searchView.setOnQueryTextListener(this); + _searchView.setOnCloseListener(this); + _searchView.clearFocus(); + + // Hide Soft Keybord +// InputMethodManager keyboard = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE); +// keyboard.hideSoftInputFromWindow(_searchView.getWindowToken(), +// 0); + + List items = (List) getArguments().getSerializable(ITEMS); + + _listViewItems = (ListView) rootView.findViewById(R.id.listItems); + + //create the adapter by passing your ArrayList data +// listAdapter = new ArrayAdapter(getActivity(), +// android.R.layout.simple_list_item_1, +// items); + listAdapter = new ArrayAdapterWithContainsFilter(getActivity(), + android.R.layout.simple_list_item_1, + items); + //attach the adapter to the list + _listViewItems.setAdapter(listAdapter); + + _listViewItems.setTextFilterEnabled(true); + + _listViewItems.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView parent, + View view, + int position, + long id) { + + _searchableItem.onSearchableItemClicked(listAdapter.getItem(position), + position); + getDialog().dismiss(); + } + }); + } + + + @Override + public boolean onClose() { + + return false; + } + + @Override + public void onPause() { + + super.onPause(); + dismiss(); + } + + @Override + public boolean onQueryTextSubmit(String s) { + + _searchView.clearFocus(); + return true; + } + + @Override + public boolean onQueryTextChange(String s) { + + if (TextUtils.isEmpty(s)) { + +// ((ArrayAdapter) _listViewItems.getAdapter()).getFilter() +// .filter(null); + ((ArrayAdapterWithContainsFilter) _listViewItems.getAdapter()).filter(null); + } else { +// ((ArrayAdapter) _listViewItems.getAdapter()).getFilter() +// .filter(s); + ((ArrayAdapterWithContainsFilter) _listViewItems.getAdapter()).filter(s); + } + + if (null != _onSearchTextChanged) { + _onSearchTextChanged.onSearchTextChanged(s); + } + return true; + } + + public interface SearchableItem + extends Serializable { + void onSearchableItemClicked(T item, + int position); + } + + public interface OnSearchTextChanged { + void onSearchTextChanged(String strText); + } + + private void hideKeyboard(final View ettext) { + + ettext.requestFocus(); + + // Delay the keyboard hiding + ettext.postDelayed(new Runnable() { + @Override + public void run() { + + // + // Hide keyboard + // + + InputMethodManager keyboard = (InputMethodManager) ettext.getContext() + .getSystemService(Context.INPUT_METHOD_SERVICE); + + keyboard.hideSoftInputFromWindow(ettext.getWindowToken(), + 0); + } + }, + 200); + } + +// /** +// *

An array filter constrains the content of the array adapter with +// * items containing a text.

+// */ +// private class ArrayTextFilter +// extends Filter { +// +// @Override +// protected FilterResults performFiltering(CharSequence prefix) { +// +// final FilterResults results = new FilterResults(); +// +// if (mOriginalValues == null) { +// synchronized (mLock) { +// mOriginalValues = new ArrayList<>(mObjects); +// } +// } +// +// if (prefix == null || prefix.length() == 0) { +// final ArrayList list; +// synchronized (mLock) { +// list = new ArrayList<>(mOriginalValues); +// } +// results.values = list; +// results.count = list.size(); +// } else { +// final String prefixString = prefix.toString() +// .toLowerCase(); +// +// final ArrayList values; +// synchronized (mLock) { +// values = new ArrayList<>(mOriginalValues); +// } +// +// final int count = values.size(); +// final ArrayList newValues = new ArrayList<>(); +// +// for (int i = 0; i < count; i++) { +// final T value = values.get(i); +// final String valueText = value.toString() +// .toLowerCase(); +// +// // First match against the whole, non-splitted value +// if (valueText.startsWith(prefixString)) { +// newValues.add(value); +// } else { +// final String[] words = valueText.split(" "); +// for (String word : words) { +// if (word.startsWith(prefixString)) { +// newValues.add(value); +// break; +// } +// } +// } +// } +// +// results.values = newValues; +// results.count = newValues.size(); +// } +// +// return results; +// } +// +// @Override +// protected void publishResults(CharSequence constraint, +// FilterResults results) { +// //noinspection unchecked +// mObjects = (List) results.values; +// if (results.count > 0) { +// notifyDataSetChanged(); +// } else { +// notifyDataSetInvalidated(); +// } +// } +// } +} diff --git a/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableSpinner.java b/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableSpinner.java new file mode 100644 index 000000000..85b55f347 --- /dev/null +++ b/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableSpinner.java @@ -0,0 +1,265 @@ +package org.gnucash.android.ui.util.widget.searchablespinner; + +import android.app.Activity; +import android.content.Context; +import android.content.ContextWrapper; +import android.content.DialogInterface; +import android.content.res.TypedArray; +import android.database.Cursor; +import android.support.v4.widget.CursorAdapter; +import android.text.TextUtils; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; +import android.view.inputmethod.InputMethodManager; +import android.widget.ArrayAdapter; +import android.widget.EditText; +import android.widget.SpinnerAdapter; + +import org.gnucash.android.R; +import org.gnucash.android.db.DatabaseSchema; + +import java.util.ArrayList; +import java.util.List; + +public class SearchableSpinner + extends android.support.v7.widget.AppCompatSpinner + implements View.OnTouchListener, + SearchableListDialog.SearchableItem { + + public static final int NO_ITEM_SELECTED = -1; + private Context _context; + private List _items; + private SearchableListDialog _searchableListDialog; + + private boolean _isDirty; + private CursorAdapter _cursorAdapter; + private String _strHintText; + private boolean _isFromInit; + + public SearchableSpinner(Context context) { + + super(context); + this._context = context; + init(); + } + + public SearchableSpinner(Context context, + AttributeSet attrs) { + + super(context, + attrs); + this._context = context; + TypedArray a = context.obtainStyledAttributes(attrs, + R.styleable.SearchableSpinner); + final int N = a.getIndexCount(); + for (int i = 0; i < N; ++i) { + int attr = a.getIndex(i); + if (attr == R.styleable.SearchableSpinner_hintText) { + _strHintText = a.getString(attr); + } + } + a.recycle(); + init(); + } + + public SearchableSpinner(Context context, + AttributeSet attrs, + int defStyleAttr) { + + super(context, + attrs, + defStyleAttr); + this._context = context; + init(); + } + + private void init() { + + _items = new ArrayList(); + _searchableListDialog = SearchableListDialog.newInstance(_items); + + // S'abonner aux clicks sur un item + _searchableListDialog.setOnSearchableItemClickListener(this); + + // S'abonner aux évènements onTouch + setOnTouchListener(this); + + _cursorAdapter = (CursorAdapter) getAdapter(); + + // TODO TW C 2020-01-17 : Supprimer la partie ArrayAdapter + if (!TextUtils.isEmpty(_strHintText)) { + ArrayAdapter arrayAdapter = new ArrayAdapter(_context, + android.R.layout.simple_list_item_1, + new String[]{_strHintText}); + _isFromInit = true; + setAdapter(arrayAdapter); + } + } + + @Override + public boolean onTouch(View v, + MotionEvent event) { + + if (_searchableListDialog.isAdded()) { + return true; + } + if (event.getAction() == MotionEvent.ACTION_UP) { + + if (null != _cursorAdapter) { + + // Refresh content #6 + // Change Start + // Description: The items were only set initially, not reloading the data in the + // spinner every time it is loaded with items in the adapter. + _items.clear(); + + for (int i = 0; i < _cursorAdapter.getCount(); i++) { + + Cursor cursorOnRow = (Cursor) _cursorAdapter.getItem(i); + + final String accountFullName = cursorOnRow.getString(cursorOnRow.getColumnIndexOrThrow(DatabaseSchema.AccountEntry.COLUMN_FULL_NAME)); + + // TODO TW C 2020-01-17 : Ajouter l'étoile pour les Favoris + + _items.add(accountFullName); + } + // Change end. + + _searchableListDialog.show(scanForActivity(_context).getFragmentManager(), + "TAG"); + } + } + return true; + } + + @Override + public void setAdapter(SpinnerAdapter adapter) { + + if (!_isFromInit) { + + _cursorAdapter = (CursorAdapter) adapter; + + // TODO TW C 2020-01-17 : Supprimer la partie ArrayAdapter + if (!TextUtils.isEmpty(_strHintText) && !_isDirty) { + // + + // + ArrayAdapter arrayAdapter = new ArrayAdapter(_context, + android.R.layout.simple_list_item_1, + new String[]{_strHintText}); + super.setAdapter(arrayAdapter); + + } else { + super.setAdapter(adapter); + } + + } else { + _isFromInit = false; + super.setAdapter(adapter); + } + } + + @Override + public void onSearchableItemClicked(Object item, + int position) { + + setSelection(_items.indexOf(item)); + + if (!_isDirty) { + _isDirty = true; + setAdapter(_cursorAdapter); + setSelection(_items.indexOf(item)); + } + } + + public void setTitle(String strTitle) { + + _searchableListDialog.setTitle(strTitle); + } + + public void setPositiveButton(String strPositiveButtonText) { + + _searchableListDialog.setPositiveButton(strPositiveButtonText); + } + + public void setPositiveButton(String strPositiveButtonText, + DialogInterface.OnClickListener onClickListener) { + + _searchableListDialog.setPositiveButton(strPositiveButtonText, + onClickListener); + } + + public void setOnSearchTextChangedListener(SearchableListDialog.OnSearchTextChanged onSearchTextChanged) { + + _searchableListDialog.setOnSearchTextChangedListener(onSearchTextChanged); + } + + private Activity scanForActivity(Context cont) { + + if (cont == null) { + return null; + } else if (cont instanceof Activity) { + return (Activity) cont; + } else if (cont instanceof ContextWrapper) { + return scanForActivity(((ContextWrapper) cont).getBaseContext()); + } + + return null; + } + + @Override + public int getSelectedItemPosition() { + + if (!TextUtils.isEmpty(_strHintText) && !_isDirty) { + return NO_ITEM_SELECTED; + } else { + return super.getSelectedItemPosition(); + } + } + + @Override + public Object getSelectedItem() { + + if (!TextUtils.isEmpty(_strHintText) && !_isDirty) { + return null; + } else { + return super.getSelectedItem(); + } + } + + private void showKeyboard(final EditText ettext) { + + ettext.requestFocus(); + ettext.postDelayed(new Runnable() { + @Override + public void run() { + + InputMethodManager keyboard = + (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + + keyboard.showSoftInput(ettext, + 0); + } + }, + 200); + } + + private void hideKeyboard(final EditText ettext) { + + ettext.requestFocus(); + ettext.postDelayed(new Runnable() { + @Override + public void run() { + + InputMethodManager keyboard = + (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + + keyboard.hideSoftInputFromWindow(ettext.getWindowToken(), + 0); + } + }, + 200); + } + +} diff --git a/app/src/main/res/layout/searchable_list_dialog.xml b/app/src/main/res/layout/searchable_list_dialog.xml new file mode 100644 index 000000000..e93fbcbb9 --- /dev/null +++ b/app/src/main/res/layout/searchable_list_dialog.xml @@ -0,0 +1,22 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 9ab498501..6f12d3a24 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -446,4 +446,5 @@ Sélectionnez la destination une fois l\'exportation terminée Exporter dans le dossier \'/Apps/GnuCash Android/\' sur Dropbox Préférences + Selectionner le Compte associé diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml index 958872756..d0734ea24 100644 --- a/app/src/main/res/values/attrs.xml +++ b/app/src/main/res/values/attrs.xml @@ -19,4 +19,8 @@ + + + + \ 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 4ef7ae17e..54c2b9d61 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -500,4 +500,5 @@ Tax Placeholder + Select Transfer Account From 7fa63f4562ee848c2bced5f1fc20d2ced8bf7242 Mon Sep 17 00:00:00 2001 From: JeanGarf Date: Fri, 17 Jan 2020 22:32:53 +0100 Subject: [PATCH 025/131] #110 - Use SearchableSpinner in Transactions --- .../ui/transaction/TransactionFormFragment.java | 12 +++++++++--- .../main/res/layout/fragment_transaction_form.xml | 3 ++- 2 files changed, 11 insertions(+), 4 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 ad9f36e03..4e34627b3 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 @@ -46,7 +46,6 @@ import android.widget.EditText; import android.widget.FilterQueryProvider; import android.widget.ImageView; -import android.widget.Spinner; import android.widget.TextView; import android.widget.Toast; @@ -82,6 +81,7 @@ import org.gnucash.android.ui.util.RecurrenceViewClickListener; import org.gnucash.android.ui.util.widget.CalculatorEditText; import org.gnucash.android.ui.util.widget.TransactionTypeSwitch; +import org.gnucash.android.ui.util.widget.searchablespinner.SearchableSpinner; import org.gnucash.android.util.QualifiedAccountNameCursorAdapter; import java.math.BigDecimal; @@ -180,7 +180,8 @@ public class TransactionFormFragment extends Fragment implements /** * Spinner for selecting the transfer account */ - @BindView(R.id.input_transfer_account_spinner) Spinner mTransferAccountSpinner; + @BindView(R.id.input_transfer_account_spinner) + SearchableSpinner mTransferAccountSpinner; /** * Checkbox indicating if this transaction should be saved as a template or not @@ -318,8 +319,10 @@ public void onActivityCreated(Bundle savedInstanceState) { } setListeners(); + //updateTransferAccountsList must only be called after initializing mAccountsDbAdapter updateTransferAccountsList(); + mTransferAccountSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { /** * Flag for ignoring first call to this listener. @@ -359,6 +362,8 @@ public void onNothingSelected(AdapterView adapterView) { } }); + mTransferAccountSpinner.setTitle(getString(R.string.select_transfer_account)); + ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar(); assert actionBar != null; // actionBar.setSubtitle(mAccountsDbAdapter.getFullyQualifiedAccountName(mAccountUID)); @@ -596,7 +601,8 @@ private void updateTransferAccountsList(){ mCursor = mAccountsDbAdapter.fetchAccountsOrderedByFavoriteAndFullName(conditions, new String[]{mAccountUID, AccountType.ROOT.name()}); mAccountCursorAdapter = new QualifiedAccountNameCursorAdapter(getActivity(), mCursor); - mTransferAccountSpinner.setAdapter(mAccountCursorAdapter); + + mTransferAccountSpinner.setAdapter(mAccountCursorAdapter); } /** diff --git a/app/src/main/res/layout/fragment_transaction_form.xml b/app/src/main/res/layout/fragment_transaction_form.xml index a011a6df4..796ae13f6 100644 --- a/app/src/main/res/layout/fragment_transaction_form.xml +++ b/app/src/main/res/layout/fragment_transaction_form.xml @@ -100,7 +100,8 @@ - Date: Sat, 18 Jan 2020 01:24:21 +0100 Subject: [PATCH 026/131] #110 - WithContainsFilterArrayAdapter based on ArrayTextFilter --- .../ArrayAdapterWithContainsFilter.java | 66 ------ .../SearchableListDialog.java | 222 ++++++------------ .../searchablespinner/SearchableSpinner.java | 86 ++++--- .../WithContainsFilterArrayAdapter.java | 187 +++++++++++++++ 4 files changed, 320 insertions(+), 241 deletions(-) delete mode 100644 app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/ArrayAdapterWithContainsFilter.java create mode 100644 app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/WithContainsFilterArrayAdapter.java diff --git a/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/ArrayAdapterWithContainsFilter.java b/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/ArrayAdapterWithContainsFilter.java deleted file mode 100644 index 77a4f0cfd..000000000 --- a/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/ArrayAdapterWithContainsFilter.java +++ /dev/null @@ -1,66 +0,0 @@ -package org.gnucash.android.ui.util.widget.searchablespinner; - -import android.app.Activity; -import android.support.annotation.NonNull; -import android.widget.ArrayAdapter; -import android.widget.Filter; - -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; - -public class ArrayAdapterWithContainsFilter - extends ArrayAdapter { - - private List _fileteredList = null; - private ArrayList _initialList; - - public ArrayAdapterWithContainsFilter(Activity context, - int items_view, - List items) { - - super(context, - items_view, - items); - - // save the initialList - this._initialList = new ArrayList(); - this._initialList.addAll(items); - - // - this._fileteredList = items; - } - -// @NonNull -// @Override -// public Filter getFilter() { -// -// return super.getFilter(); -// } - - // Filter method - public void filter(String textToSearch) { - - textToSearch = textToSearch.toLowerCase(Locale.getDefault()); - - _fileteredList.clear(); - - if (textToSearch.length() == 0) { - - _fileteredList.addAll(_initialList); - - } else { - - for (String item : _initialList) { - - if (item.toLowerCase(Locale.getDefault()) - .contains(textToSearch)) { - // - - _fileteredList.add(item); - } - } - } - notifyDataSetChanged(); - } -} diff --git a/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableListDialog.java b/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableListDialog.java index 1d383dc75..3023fbb48 100644 --- a/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableListDialog.java +++ b/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableListDialog.java @@ -30,13 +30,15 @@ public class SearchableListDialog private static final String ITEMS = "items"; - private ArrayAdapter listAdapter; + private ArrayAdapter withContainsFilterArrayAdapter; - private ListView _listViewItems; + private ListView _listView; - private SearchableItem _searchableItem; + private OnSearchTextChangedListener _onSearchTextChangedListener; - private OnSearchTextChanged _onSearchTextChanged; + private OnSearchableItemClickedListener _onSearchableItemClickedListener; + + private DialogInterface.OnClickListener _onPositiveBtnClickListener; private SearchView _searchView; @@ -44,7 +46,6 @@ public class SearchableListDialog private String _strPositiveButtonText; - private DialogInterface.OnClickListener _onClickListener; public SearchableListDialog() { @@ -93,7 +94,7 @@ public Dialog onCreateDialog(Bundle savedInstanceState) { // Description: As the instance was re initializing to null on rotating the device, // getting the instance from the saved instance if (null != savedInstanceState) { - _searchableItem = (SearchableItem) savedInstanceState.getSerializable("item"); + _onSearchableItemClickedListener = (OnSearchableItemClickedListener) savedInstanceState.getSerializable("item"); } // Change End @@ -109,7 +110,7 @@ public Dialog onCreateDialog(Bundle savedInstanceState) { ? "CLOSE" : _strPositiveButtonText; alertDialogBuilder.setPositiveButton(strPositiveButton, - _onClickListener); + _onPositiveBtnClickListener); String strTitle = _strTitle == null ? "Select Item" @@ -117,50 +118,12 @@ public Dialog onCreateDialog(Bundle savedInstanceState) { alertDialogBuilder.setTitle(strTitle); final AlertDialog dialog = alertDialogBuilder.create(); -// dialog.getWindow() -// .setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN); + // dialog.getWindow() + // .setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN); hideKeyboard(_searchView); - return dialog; - } - - // Crash on orientation change #7 - // Change Start - // Description: Saving the instance of searchable item instance. - @Override - public void onSaveInstanceState(Bundle outState) { - - outState.putSerializable("item", - _searchableItem); - super.onSaveInstanceState(outState); - } - // Change End - - public void setTitle(String strTitle) { - - _strTitle = strTitle; - } - public void setPositiveButton(String strPositiveButtonText) { - - _strPositiveButtonText = strPositiveButtonText; - } - - public void setPositiveButton(String strPositiveButtonText, - DialogInterface.OnClickListener onClickListener) { - - _strPositiveButtonText = strPositiveButtonText; - _onClickListener = onClickListener; - } - - public void setOnSearchableItemClickListener(SearchableItem searchableItem) { - - this._searchableItem = searchableItem; - } - - public void setOnSearchTextChangedListener(OnSearchTextChanged onSearchTextChanged) { - - this._onSearchTextChanged = onSearchTextChanged; + return dialog; } private void setData(View rootView) { @@ -181,34 +144,74 @@ private void setData(View rootView) { List items = (List) getArguments().getSerializable(ITEMS); - _listViewItems = (ListView) rootView.findViewById(R.id.listItems); + _listView = (ListView) rootView.findViewById(R.id.listItems); //create the adapter by passing your ArrayList data -// listAdapter = new ArrayAdapter(getActivity(), -// android.R.layout.simple_list_item_1, -// items); - listAdapter = new ArrayAdapterWithContainsFilter(getActivity(), - android.R.layout.simple_list_item_1, - items); + withContainsFilterArrayAdapter = new WithContainsFilterArrayAdapter(getActivity(), + android.R.layout.simple_list_item_1, + items); //attach the adapter to the list - _listViewItems.setAdapter(listAdapter); + _listView.setAdapter(withContainsFilterArrayAdapter); + + _listView.setTextFilterEnabled(true); - _listViewItems.setTextFilterEnabled(true); + _listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { - _listViewItems.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView parent, View view, int position, long id) { - _searchableItem.onSearchableItemClicked(listAdapter.getItem(position), - position); + final Object accountFullName = withContainsFilterArrayAdapter.getItem(position); + + // Call Listener + _onSearchableItemClickedListener.onSearchableItemClicked(accountFullName, + position); getDialog().dismiss(); } }); } + // Crash on orientation change #7 + // Change Start + // Description: Saving the instance of searchable item instance. + @Override + public void onSaveInstanceState(Bundle outState) { + + outState.putSerializable("item", + _onSearchableItemClickedListener); + super.onSaveInstanceState(outState); + } + // Change End + + public void setTitle(String strTitle) { + + _strTitle = strTitle; + } + + public void setPositiveButton(String strPositiveButtonText) { + + _strPositiveButtonText = strPositiveButtonText; + } + + public void setPositiveButton(String strPositiveButtonText, + DialogInterface.OnClickListener onClickListener) { + + _strPositiveButtonText = strPositiveButtonText; + _onPositiveBtnClickListener = onClickListener; + } + + public void setOnSearchableItemClickListener(OnSearchableItemClickedListener onSearchableItemClickedListener) { + + this._onSearchableItemClickedListener = onSearchableItemClickedListener; + } + + public void setOnSearchTextChangedListener(OnSearchTextChangedListener onSearchTextChangedListener) { + + this._onSearchTextChangedListener = onSearchTextChangedListener; + } + @Override public boolean onClose() { @@ -235,28 +238,32 @@ public boolean onQueryTextChange(String s) { if (TextUtils.isEmpty(s)) { -// ((ArrayAdapter) _listViewItems.getAdapter()).getFilter() -// .filter(null); - ((ArrayAdapterWithContainsFilter) _listViewItems.getAdapter()).filter(null); + ((WithContainsFilterArrayAdapter) _listView.getAdapter()).getFilter() + .filter(null); + } else { -// ((ArrayAdapter) _listViewItems.getAdapter()).getFilter() -// .filter(s); - ((ArrayAdapterWithContainsFilter) _listViewItems.getAdapter()).filter(s); + + ((WithContainsFilterArrayAdapter) _listView.getAdapter()).getFilter() + .filter(s); } - if (null != _onSearchTextChanged) { - _onSearchTextChanged.onSearchTextChanged(s); + if (null != _onSearchTextChangedListener) { + + // Call Listener + _onSearchTextChangedListener.onSearchTextChanged(s); } + return true; } - public interface SearchableItem + public interface OnSearchableItemClickedListener extends Serializable { + void onSearchableItemClicked(T item, int position); } - public interface OnSearchTextChanged { + public interface OnSearchTextChangedListener { void onSearchTextChanged(String strText); } @@ -283,79 +290,4 @@ public void run() { 200); } -// /** -// *

An array filter constrains the content of the array adapter with -// * items containing a text.

-// */ -// private class ArrayTextFilter -// extends Filter { -// -// @Override -// protected FilterResults performFiltering(CharSequence prefix) { -// -// final FilterResults results = new FilterResults(); -// -// if (mOriginalValues == null) { -// synchronized (mLock) { -// mOriginalValues = new ArrayList<>(mObjects); -// } -// } -// -// if (prefix == null || prefix.length() == 0) { -// final ArrayList list; -// synchronized (mLock) { -// list = new ArrayList<>(mOriginalValues); -// } -// results.values = list; -// results.count = list.size(); -// } else { -// final String prefixString = prefix.toString() -// .toLowerCase(); -// -// final ArrayList values; -// synchronized (mLock) { -// values = new ArrayList<>(mOriginalValues); -// } -// -// final int count = values.size(); -// final ArrayList newValues = new ArrayList<>(); -// -// for (int i = 0; i < count; i++) { -// final T value = values.get(i); -// final String valueText = value.toString() -// .toLowerCase(); -// -// // First match against the whole, non-splitted value -// if (valueText.startsWith(prefixString)) { -// newValues.add(value); -// } else { -// final String[] words = valueText.split(" "); -// for (String word : words) { -// if (word.startsWith(prefixString)) { -// newValues.add(value); -// break; -// } -// } -// } -// } -// -// results.values = newValues; -// results.count = newValues.size(); -// } -// -// return results; -// } -// -// @Override -// protected void publishResults(CharSequence constraint, -// FilterResults results) { -// //noinspection unchecked -// mObjects = (List) results.values; -// if (results.count > 0) { -// notifyDataSetChanged(); -// } else { -// notifyDataSetInvalidated(); -// } -// } -// } } diff --git a/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableSpinner.java b/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableSpinner.java index 85b55f347..a9c5235bd 100644 --- a/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableSpinner.java +++ b/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableSpinner.java @@ -25,15 +25,23 @@ public class SearchableSpinner extends android.support.v7.widget.AppCompatSpinner implements View.OnTouchListener, - SearchableListDialog.SearchableItem { + SearchableListDialog.OnSearchableItemClickedListener { public static final int NO_ITEM_SELECTED = -1; + + // TODO TW C 2020-01-17 : a remplacer par getContext() private Context _context; + private List _items; + private List _allItems; + private SearchableListDialog _searchableListDialog; private boolean _isDirty; + + // Adpater for Spinner based on data in a DB Cursor private CursorAdapter _cursorAdapter; + private String _strHintText; private boolean _isFromInit; @@ -49,7 +57,9 @@ public SearchableSpinner(Context context, super(context, attrs); + this._context = context; + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SearchableSpinner); final int N = a.getIndexCount(); @@ -70,12 +80,16 @@ public SearchableSpinner(Context context, super(context, attrs, defStyleAttr); + this._context = context; + init(); } private void init() { + _allItems=new ArrayList(); + _items = new ArrayList(); _searchableListDialog = SearchableListDialog.newInstance(_items); @@ -85,16 +99,16 @@ private void init() { // S'abonner aux évènements onTouch setOnTouchListener(this); - _cursorAdapter = (CursorAdapter) getAdapter(); +// _cursorAdapter = (CursorAdapter) getAdapter(); // TODO TW C 2020-01-17 : Supprimer la partie ArrayAdapter - if (!TextUtils.isEmpty(_strHintText)) { - ArrayAdapter arrayAdapter = new ArrayAdapter(_context, - android.R.layout.simple_list_item_1, - new String[]{_strHintText}); - _isFromInit = true; - setAdapter(arrayAdapter); - } +// if (!TextUtils.isEmpty(_strHintText)) { +// ArrayAdapter arrayAdapter = new ArrayAdapter(_context, +// android.R.layout.simple_list_item_1, +// new String[]{_strHintText}); +// _isFromInit = true; +// setAdapter(arrayAdapter); +// } } @Override @@ -113,6 +127,11 @@ public boolean onTouch(View v, // Description: The items were only set initially, not reloading the data in the // spinner every time it is loaded with items in the adapter. _items.clear(); + _allItems.clear(); + + // + // Add items from DB Cursor + // for (int i = 0; i < _cursorAdapter.getCount(); i++) { @@ -123,8 +142,10 @@ public boolean onTouch(View v, // TODO TW C 2020-01-17 : Ajouter l'étoile pour les Favoris _items.add(accountFullName); - } - // Change end. + + } // for + + _allItems.addAll(_items); _searchableListDialog.show(scanForActivity(_context).getFragmentManager(), "TAG"); @@ -133,12 +154,30 @@ public boolean onTouch(View v, return true; } + @Override + public void onSearchableItemClicked(Object item, + int position) { + +// String accountFullName = (String) item; + +// setSelection(_items.indexOf(item)); + setSelection(_allItems.indexOf(item)); + + if (!_isDirty) { + _isDirty = true; + setAdapter(_cursorAdapter); +// setSelection(_items.indexOf(item)); + setSelection(_allItems.indexOf(item)); + } + } + + @Override public void setAdapter(SpinnerAdapter adapter) { - if (!_isFromInit) { + _cursorAdapter = (CursorAdapter) adapter; - _cursorAdapter = (CursorAdapter) adapter; + if (!_isFromInit) { // TODO TW C 2020-01-17 : Supprimer la partie ArrayAdapter if (!TextUtils.isEmpty(_strHintText) && !_isDirty) { @@ -160,19 +199,6 @@ public void setAdapter(SpinnerAdapter adapter) { } } - @Override - public void onSearchableItemClicked(Object item, - int position) { - - setSelection(_items.indexOf(item)); - - if (!_isDirty) { - _isDirty = true; - setAdapter(_cursorAdapter); - setSelection(_items.indexOf(item)); - } - } - public void setTitle(String strTitle) { _searchableListDialog.setTitle(strTitle); @@ -184,15 +210,15 @@ public void setPositiveButton(String strPositiveButtonText) { } public void setPositiveButton(String strPositiveButtonText, - DialogInterface.OnClickListener onClickListener) { + DialogInterface.OnClickListener onPositiveBtnClickListener) { _searchableListDialog.setPositiveButton(strPositiveButtonText, - onClickListener); + onPositiveBtnClickListener); } - public void setOnSearchTextChangedListener(SearchableListDialog.OnSearchTextChanged onSearchTextChanged) { + public void setOnSearchTextChangedListener(SearchableListDialog.OnSearchTextChangedListener onSearchTextChangedListener) { - _searchableListDialog.setOnSearchTextChangedListener(onSearchTextChanged); + _searchableListDialog.setOnSearchTextChangedListener(onSearchTextChangedListener); } private Activity scanForActivity(Context cont) { diff --git a/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/WithContainsFilterArrayAdapter.java b/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/WithContainsFilterArrayAdapter.java new file mode 100644 index 000000000..08d6aab66 --- /dev/null +++ b/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/WithContainsFilterArrayAdapter.java @@ -0,0 +1,187 @@ +package org.gnucash.android.ui.util.widget.searchablespinner; + +import android.app.Activity; +import android.support.annotation.NonNull; +import android.widget.ArrayAdapter; +import android.widget.Filter; + +import java.util.ArrayList; +import java.util.List; + +public class WithContainsFilterArrayAdapter + extends ArrayAdapter { + + /** + *

An array filter constrains the content of the array adapter with + * items containing a text.

+ */ + private class ArrayTextFilter + extends Filter { + + @Override + protected FilterResults performFiltering(CharSequence textToSearch) { + + final FilterResults results = new FilterResults(); + +// if (mOriginalValues == null) { +// synchronized (mLock) { +// mOriginalValues = new ArrayList<>(mObjects); +// } +// } + + if (textToSearch == null || textToSearch.length() == 0) { + // Nothing to search + + // Get copy of all values + final ArrayList values = getCopyOfOriginalValues(); + + results.values = values; + results.count = values.size(); + + } else { + // There is something to search + + final String textToSearchLowerCase = textToSearch.toString() + .toLowerCase(); + + // Get copy of all values + final ArrayList values = getCopyOfOriginalValues(); + + // + // Filter values + // + + final ArrayList filteredValues = new ArrayList<>(); + + final int count = values.size(); + + for (int i = 0; i < count; i++) { + + final T value = values.get(i); + + final String valueTextLowerCase = value.toString() + .toLowerCase(); + + // First match against the whole, non-splitted value + if (valueTextLowerCase.contains(textToSearchLowerCase)) { + // It matches + + filteredValues.add(value); + + } else { + // + +// final String[] words = valueText.split(" "); +// for (String word : words) { +// if (word.startsWith(textToSearchLowerCase)) { +// newValues.add(value); +// break; +// } +// } + } + } + + results.values = filteredValues; + results.count = filteredValues.size(); + } + + return results; + } + + @Override + protected void publishResults(CharSequence constraint, + FilterResults results) { + + clear(); + addAll((List) results.values); + +// mObjects = (List) results.values; + + if (results.count > 0) { + notifyDataSetChanged(); + } else { + notifyDataSetInvalidated(); + } + } + + private ArrayList getCopyOfOriginalValues() { + + final ArrayList values; + synchronized (mLock) { + values = new ArrayList<>(mOriginalValues); + } + return values; + } + + } + + /** + * Lock used to modify the content of {@link #mOriginalValues}. Any write operation + * performed on the array should be synchronized on this lock. This lock is also + * used by the filter (see {@link #getFilter()} to make a synchronized copy of + * the original array of data. + */ + private final Object mLock = new Object(); + + private List mOriginalValues; +// private List mObjects; + + private ArrayTextFilter _arrayTextFilter = new ArrayTextFilter(); + + /** + * Constructor + * + * @param context + * @param items_view + * @param items + */ + public WithContainsFilterArrayAdapter(Activity context, + int items_view, + List items) { + + super(context, + items_view, + items); + + // save the initialList + this.mOriginalValues = new ArrayList(); + this.mOriginalValues.addAll(items); + + // +// this.mObjects = items; + } + + @NonNull + @Override + public Filter getFilter() { + +// return super.getFilter(); + return _arrayTextFilter; + } + +// // Filter method +// public void filter(String textToSearch) { +// +// textToSearch = textToSearch.toLowerCase(Locale.getDefault()); +// +// mObjects.clear(); +// +// if (textToSearch.length() == 0) { +// +// mObjects.addAll(mOriginalValues); +// +// } else { +// +// for (T item : mOriginalValues) { +// +// if (item.toString().toLowerCase(Locale.getDefault()) +// .contains(textToSearch)) { +// // +// +// mObjects.add(item); +// } +// } +// } +// notifyDataSetChanged(); +// } +} From 564a0f0971529b94105c1910edb52cde14b90289 Mon Sep 17 00:00:00 2001 From: JeanGarf Date: Sat, 18 Jan 2020 01:26:43 +0100 Subject: [PATCH 027/131] #110 - Add empty lines to be more readable --- .../android/ui/transaction/TransactionFormFragment.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) 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 4e34627b3..615190636 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 @@ -332,9 +332,12 @@ public void onActivityCreated(Bundle savedInstanceState) { @Override public void onItemSelected(AdapterView adapterView, View view, int position, long id) { + removeFavoriteIconFromSelectedView((TextView) view); - if (mSplitsList.size() == 2) { //when handling simple transfer to one account + if (mSplitsList.size() == 2) { + //when handling simple transfer to one account + for (Split split : mSplitsList) { if (!split.getAccountUID().equals(mAccountUID)) { split.setAccountUID(mAccountsDbAdapter.getUID(id)); @@ -342,10 +345,12 @@ public void onItemSelected(AdapterView adapterView, View view, int position, // else case is handled when saving the transactions } } + if (!userInteraction) { userInteraction = true; return; } + startTransferFunds(); } From 704886370e6ed1fc974fcef10a8efbe0b7190665 Mon Sep 17 00:00:00 2001 From: JeanGarf Date: Sat, 18 Jan 2020 01:53:48 +0100 Subject: [PATCH 028/131] #110 - Renaming and comments --- .../SearchableListDialog.java | 22 +-- ...thContaingTextArrayFilterArrayAdapter.java | 149 ++++++++++++++ .../WithContainsFilterArrayAdapter.java | 187 ------------------ 3 files changed, 160 insertions(+), 198 deletions(-) create mode 100644 app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/WithContaingTextArrayFilterArrayAdapter.java delete mode 100644 app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/WithContainsFilterArrayAdapter.java diff --git a/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableListDialog.java b/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableListDialog.java index 3023fbb48..5c238f257 100644 --- a/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableListDialog.java +++ b/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableListDialog.java @@ -30,7 +30,7 @@ public class SearchableListDialog private static final String ITEMS = "items"; - private ArrayAdapter withContainsFilterArrayAdapter; + private ArrayAdapter withContaingTextArrayFilterArrayAdapter; private ListView _listView; @@ -146,12 +146,12 @@ private void setData(View rootView) { _listView = (ListView) rootView.findViewById(R.id.listItems); - //create the adapter by passing your ArrayList data - withContainsFilterArrayAdapter = new WithContainsFilterArrayAdapter(getActivity(), - android.R.layout.simple_list_item_1, - items); + // Create an ArrayAdapter for items, with filtering capablity based on item containing a text + withContaingTextArrayFilterArrayAdapter = new WithContaingTextArrayFilterArrayAdapter(getActivity(), + android.R.layout.simple_list_item_1, + items); //attach the adapter to the list - _listView.setAdapter(withContainsFilterArrayAdapter); + _listView.setAdapter(withContaingTextArrayFilterArrayAdapter); _listView.setTextFilterEnabled(true); @@ -163,7 +163,7 @@ public void onItemClick(AdapterView parent, int position, long id) { - final Object accountFullName = withContainsFilterArrayAdapter.getItem(position); + final Object accountFullName = withContaingTextArrayFilterArrayAdapter.getItem(position); // Call Listener _onSearchableItemClickedListener.onSearchableItemClicked(accountFullName, @@ -238,13 +238,13 @@ public boolean onQueryTextChange(String s) { if (TextUtils.isEmpty(s)) { - ((WithContainsFilterArrayAdapter) _listView.getAdapter()).getFilter() - .filter(null); + ((WithContaingTextArrayFilterArrayAdapter) _listView.getAdapter()).getFilter() + .filter(null); } else { - ((WithContainsFilterArrayAdapter) _listView.getAdapter()).getFilter() - .filter(s); + ((WithContaingTextArrayFilterArrayAdapter) _listView.getAdapter()).getFilter() + .filter(s); } if (null != _onSearchTextChangedListener) { diff --git a/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/WithContaingTextArrayFilterArrayAdapter.java b/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/WithContaingTextArrayFilterArrayAdapter.java new file mode 100644 index 000000000..0103a5d5a --- /dev/null +++ b/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/WithContaingTextArrayFilterArrayAdapter.java @@ -0,0 +1,149 @@ +package org.gnucash.android.ui.util.widget.searchablespinner; + +import android.app.Activity; +import android.support.annotation.NonNull; +import android.widget.ArrayAdapter; +import android.widget.Filter; + +import java.util.ArrayList; +import java.util.List; + +public class WithContaingTextArrayFilterArrayAdapter + extends ArrayAdapter { + + /** + * Array item filter which keeps only items containing a text + */ + private class ItemContaingTextArrayFilter + extends Filter { + + @Override + protected FilterResults performFiltering(CharSequence textToSearch) { + + final FilterResults results = new FilterResults(); + + if (textToSearch == null || textToSearch.length() == 0) { + // Nothing to search + + // Get copy of all items + final ArrayList allItems = getCopyOfAllItems(); + + results.values = allItems; + results.count = allItems.size(); + + } else { + // There is something to search + + final String textToSearchLowerCase = textToSearch.toString() + .toLowerCase(); + + // Get copy of all items + final ArrayList allItems = getCopyOfAllItems(); + + // + // Filter items + // + + final ArrayList filteredItems = new ArrayList<>(); + + final int count = allItems.size(); + + for (int i = 0; i < count; i++) { + + final T item = allItems.get(i); + + final String itemTextLowerCase = item.toString() + .toLowerCase(); + + // First match against the whole, non-splitted value + if (itemTextLowerCase.contains(textToSearchLowerCase)) { + // It matches + + filteredItems.add(item); + + } else { + // It doesen't match + + // NTD + } + } + + results.values = filteredItems; + results.count = filteredItems.size(); + } + + return results; + } + + @Override + protected void publishResults(CharSequence constraint, + FilterResults results) { + + // Replace items in ArrayAdapter with filtered ones + clear(); + addAll((List) results.values); + + if (results.count > 0) { + notifyDataSetChanged(); + } else { + notifyDataSetInvalidated(); + } + } + + private ArrayList getCopyOfAllItems() { + + final ArrayList values; + synchronized (_lock) { + values = new ArrayList<>(_allItems); + } + return values; + } + + } + + /** + * Array item filter which keeps only items containing a text + */ + private final ItemContaingTextArrayFilter _itemContaingTextArrayFilter = new ItemContaingTextArrayFilter(); + + /** + * Lock used to modify the content of {@link #_allItems}. Any write operation + * performed on the array should be synchronized on this lock. This lock is also + * used by the filter (see {@link #getFilter()} to make a synchronized copy of + * the original array of data. + */ + private final Object _lock = new Object(); + + /** + * Copy of all items before filtering + */ + private List _allItems; + + /** + * Constructor + * + * @param context + * @param items_view + * @param items + */ + public WithContaingTextArrayFilterArrayAdapter(Activity context, + int items_view, + List items) { + + super(context, + items_view, + items); + + // save all the items (before filtering) + this._allItems = new ArrayList(); + this._allItems.addAll(items); + } + + @NonNull + @Override + public Filter getFilter() { + + return _itemContaingTextArrayFilter; + } + +} diff --git a/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/WithContainsFilterArrayAdapter.java b/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/WithContainsFilterArrayAdapter.java deleted file mode 100644 index 08d6aab66..000000000 --- a/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/WithContainsFilterArrayAdapter.java +++ /dev/null @@ -1,187 +0,0 @@ -package org.gnucash.android.ui.util.widget.searchablespinner; - -import android.app.Activity; -import android.support.annotation.NonNull; -import android.widget.ArrayAdapter; -import android.widget.Filter; - -import java.util.ArrayList; -import java.util.List; - -public class WithContainsFilterArrayAdapter - extends ArrayAdapter { - - /** - *

An array filter constrains the content of the array adapter with - * items containing a text.

- */ - private class ArrayTextFilter - extends Filter { - - @Override - protected FilterResults performFiltering(CharSequence textToSearch) { - - final FilterResults results = new FilterResults(); - -// if (mOriginalValues == null) { -// synchronized (mLock) { -// mOriginalValues = new ArrayList<>(mObjects); -// } -// } - - if (textToSearch == null || textToSearch.length() == 0) { - // Nothing to search - - // Get copy of all values - final ArrayList values = getCopyOfOriginalValues(); - - results.values = values; - results.count = values.size(); - - } else { - // There is something to search - - final String textToSearchLowerCase = textToSearch.toString() - .toLowerCase(); - - // Get copy of all values - final ArrayList values = getCopyOfOriginalValues(); - - // - // Filter values - // - - final ArrayList filteredValues = new ArrayList<>(); - - final int count = values.size(); - - for (int i = 0; i < count; i++) { - - final T value = values.get(i); - - final String valueTextLowerCase = value.toString() - .toLowerCase(); - - // First match against the whole, non-splitted value - if (valueTextLowerCase.contains(textToSearchLowerCase)) { - // It matches - - filteredValues.add(value); - - } else { - // - -// final String[] words = valueText.split(" "); -// for (String word : words) { -// if (word.startsWith(textToSearchLowerCase)) { -// newValues.add(value); -// break; -// } -// } - } - } - - results.values = filteredValues; - results.count = filteredValues.size(); - } - - return results; - } - - @Override - protected void publishResults(CharSequence constraint, - FilterResults results) { - - clear(); - addAll((List) results.values); - -// mObjects = (List) results.values; - - if (results.count > 0) { - notifyDataSetChanged(); - } else { - notifyDataSetInvalidated(); - } - } - - private ArrayList getCopyOfOriginalValues() { - - final ArrayList values; - synchronized (mLock) { - values = new ArrayList<>(mOriginalValues); - } - return values; - } - - } - - /** - * Lock used to modify the content of {@link #mOriginalValues}. Any write operation - * performed on the array should be synchronized on this lock. This lock is also - * used by the filter (see {@link #getFilter()} to make a synchronized copy of - * the original array of data. - */ - private final Object mLock = new Object(); - - private List mOriginalValues; -// private List mObjects; - - private ArrayTextFilter _arrayTextFilter = new ArrayTextFilter(); - - /** - * Constructor - * - * @param context - * @param items_view - * @param items - */ - public WithContainsFilterArrayAdapter(Activity context, - int items_view, - List items) { - - super(context, - items_view, - items); - - // save the initialList - this.mOriginalValues = new ArrayList(); - this.mOriginalValues.addAll(items); - - // -// this.mObjects = items; - } - - @NonNull - @Override - public Filter getFilter() { - -// return super.getFilter(); - return _arrayTextFilter; - } - -// // Filter method -// public void filter(String textToSearch) { -// -// textToSearch = textToSearch.toLowerCase(Locale.getDefault()); -// -// mObjects.clear(); -// -// if (textToSearch.length() == 0) { -// -// mObjects.addAll(mOriginalValues); -// -// } else { -// -// for (T item : mOriginalValues) { -// -// if (item.toString().toLowerCase(Locale.getDefault()) -// .contains(textToSearch)) { -// // -// -// mObjects.add(item); -// } -// } -// } -// notifyDataSetChanged(); -// } -} From 697ed1d6ff61cfb8536356b7e3ab184bcc873deb Mon Sep 17 00:00:00 2001 From: JeanGarf Date: Sat, 18 Jan 2020 02:21:33 +0100 Subject: [PATCH 029/131] #110 - Change PositiveButton label to "Cancel" --- .../gnucash/android/ui/transaction/TransactionFormFragment.java | 1 + 1 file changed, 1 insertion(+) 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 615190636..9a81b4e85 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 @@ -368,6 +368,7 @@ public void onNothingSelected(AdapterView adapterView) { }); mTransferAccountSpinner.setTitle(getString(R.string.select_transfer_account)); + mTransferAccountSpinner.setPositiveButton(getString(R.string.alert_dialog_cancel)); ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar(); assert actionBar != null; From 84891c742188cfbffb69b94ca4287d2ce9bed9c7 Mon Sep 17 00:00:00 2001 From: JeanGarf Date: Sat, 18 Jan 2020 11:07:43 +0100 Subject: [PATCH 030/131] #110 - Reformat code to be more readable --- .../ui/account/AccountFormFragment.java | 3 + .../QualifiedAccountNameCursorAdapter.java | 96 ++++++++++++++----- 2 files changed, 74 insertions(+), 25 deletions(-) diff --git a/app/src/main/java/org/gnucash/android/ui/account/AccountFormFragment.java b/app/src/main/java/org/gnucash/android/ui/account/AccountFormFragment.java index 35d5f3e7b..62da9f5b1 100644 --- a/app/src/main/java/org/gnucash/android/ui/account/AccountFormFragment.java +++ b/app/src/main/java/org/gnucash/android/ui/account/AccountFormFragment.java @@ -155,6 +155,7 @@ public class AccountFormFragment extends Fragment { /** * Spinner for parent account list */ + // TODO TW C 2020-01-18 : A remplacer par un SearchableSpinner @BindView(R.id.input_parent_account) Spinner mParentAccountSpinner; /** @@ -177,6 +178,8 @@ public class AccountFormFragment extends Fragment { /** * Spinner for selecting the default transfer account */ + // TODO TW C 2020-01-18 : A remplacer par un SearchableSpinner + Vérifier exclure Placeholder + Etoile des Favoris + 1 + // seule ligne @BindView(R.id.input_default_transfer_account) Spinner mDefaultTransferAccountSpinner; /** diff --git a/app/src/main/java/org/gnucash/android/util/QualifiedAccountNameCursorAdapter.java b/app/src/main/java/org/gnucash/android/util/QualifiedAccountNameCursorAdapter.java index 77bc86674..e8b2cd9b1 100644 --- a/app/src/main/java/org/gnucash/android/util/QualifiedAccountNameCursorAdapter.java +++ b/app/src/main/java/org/gnucash/android/util/QualifiedAccountNameCursorAdapter.java @@ -35,57 +35,103 @@ * * @author Ngewi Fet */ -public class QualifiedAccountNameCursorAdapter extends SimpleCursorAdapter { +public class QualifiedAccountNameCursorAdapter + extends SimpleCursorAdapter { /** * Initialize the Cursor adapter for account names using default spinner views - * @param context Application context - * @param cursor Cursor to accounts + * + * @param context + * Application context + * @param cursor + * Cursor to accounts */ - public QualifiedAccountNameCursorAdapter(Context context, Cursor cursor) { - super(context, android.R.layout.simple_spinner_item, cursor, - new String[]{DatabaseSchema.AccountEntry.COLUMN_FULL_NAME}, - new int[]{android.R.id.text1}, 0); + public QualifiedAccountNameCursorAdapter(Context context, + Cursor cursor) { + + super(context, + android.R.layout.simple_spinner_item, + cursor, + new String[]{DatabaseSchema.AccountEntry.COLUMN_FULL_NAME}, + new int[]{android.R.id.text1}, + 0); + setDropDownViewResource(R.layout.account_spinner_dropdown_item); } /** * Overloaded constructor. Specifies the view to use for displaying selected spinner text - * @param context Application context - * @param cursor Cursor to account data - * @param selectedSpinnerItem Layout resource for selected item text + * + * @param context + * Application context + * @param cursor + * Cursor to account data + * @param selectedSpinnerItem + * Layout resource for selected item text */ - public QualifiedAccountNameCursorAdapter(Context context, Cursor cursor, + public QualifiedAccountNameCursorAdapter(Context context, + Cursor cursor, @LayoutRes int selectedSpinnerItem) { - super(context, selectedSpinnerItem, cursor, - new String[]{DatabaseSchema.AccountEntry.COLUMN_FULL_NAME}, - new int[]{android.R.id.text1}, 0); + + super(context, + selectedSpinnerItem, + cursor, + new String[]{DatabaseSchema.AccountEntry.COLUMN_FULL_NAME}, + new int[]{android.R.id.text1}, + 0); + setDropDownViewResource(R.layout.account_spinner_dropdown_item); } @Override - public void bindView(View view, Context context, Cursor cursor) { - super.bindView(view, context, cursor); - TextView textView = (TextView) view.findViewById(android.R.id.text1); - textView.setEllipsize(TextUtils.TruncateAt.MIDDLE); + public void bindView(View view, + Context context, + Cursor cursor) { + + super.bindView(view, + context, + cursor); + + // item text + TextView text1 = (TextView) view.findViewById(android.R.id.text1); + + // TODO TW C 2020-01-19 : A mettre ? +// // item on single line +// text1.setSingleLine(); + + // Add "..." in the middle of the item if too long for one line + text1.setEllipsize(TextUtils.TruncateAt.MIDDLE); + Integer isFavorite = cursor.getInt(cursor.getColumnIndex(DatabaseSchema.AccountEntry.COLUMN_FAVORITE)); - if(isFavorite == 0) { - textView.setCompoundDrawablesWithIntrinsicBounds(0,0,0,0); + + if (isFavorite == 0) { + text1.setCompoundDrawablesWithIntrinsicBounds(0, + 0, + 0, + 0); } else { - textView.setCompoundDrawablesWithIntrinsicBounds(0,0,R.drawable.ic_star_black_18dp,0); + text1.setCompoundDrawablesWithIntrinsicBounds(0, + 0, + R.drawable.ic_star_black_18dp, + 0); } } /** * Returns the position of a given account in the adapter - * @param accountUID GUID of the account + * + * @param accountUID + * GUID of the account + * * @return Position of the account or -1 if the account is not found */ - public int getPosition(@NonNull String accountUID){ - long accountId = AccountsDbAdapter.getInstance().getID(accountUID); + public int getPosition(@NonNull String accountUID) { + + long accountId = AccountsDbAdapter.getInstance() + .getID(accountUID); for (int pos = 0; pos < getCount(); pos++) { - if (getItemId(pos) == accountId){ + if (getItemId(pos) == accountId) { return pos; } } From a0fb4014dc0feb04f0c26eb596c651446f314424 Mon Sep 17 00:00:00 2001 From: JeanGarf Date: Sun, 19 Jan 2020 04:24:13 +0100 Subject: [PATCH 031/131] #110 - Replace maxLines by singleLine --- .../res/layout/account_spinner_dropdown_item.xml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) 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 c32bfdc6a..9116e1baf 100644 --- a/app/src/main/res/layout/account_spinner_dropdown_item.xml +++ b/app/src/main/res/layout/account_spinner_dropdown_item.xml @@ -15,10 +15,10 @@ limitations under the License. --> \ No newline at end of file + android:id="@android:id/text1" + style="?android:attr/spinnerDropDownItemStyle" + android:textColor="@android:color/black" + android:layout_width="match_parent" + android:layout_height="?attr/dropdownListPreferredItemHeight" + android:ellipsize="marquee" + android:singleLine="true" /> \ No newline at end of file From d7052ac469f31c407829238e16cdef66c0c1572f Mon Sep 17 00:00:00 2001 From: JeanGarf Date: Sun, 19 Jan 2020 04:25:57 +0100 Subject: [PATCH 032/131] #110 - Add comments --- .../SearchableListDialog.java | 158 ++++++++++++------ .../searchablespinner/SearchableSpinner.java | 55 +++--- .../QualifiedAccountNameCursorAdapter.java | 2 +- 3 files changed, 142 insertions(+), 73 deletions(-) diff --git a/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableListDialog.java b/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableListDialog.java index 5c238f257..eac5376a3 100644 --- a/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableListDialog.java +++ b/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableListDialog.java @@ -28,40 +28,76 @@ public class SearchableListDialog implements SearchView.OnQueryTextListener, SearchView.OnCloseListener { + /** + * Listener to call when user clicks on an item + * + * @param + */ + public interface OnSearchableItemClickedListener + extends Serializable { + + void onSearchableItemClicked(T item, + int position); + } + + /** + * Listener to call when Search text change + */ + public interface OnSearchTextChangedListener { + void onSearchTextChanged(String strText); + } + + private static final String ITEMS = "items"; - private ArrayAdapter withContaingTextArrayFilterArrayAdapter; + // Dialog Title + private String _strTitle; + // Search Edit text zone + private SearchView _searchTextEditView; + + // Item list private ListView _listView; - private OnSearchTextChangedListener _onSearchTextChangedListener; + // Bottom right button to close the pop-up + private String _strPositiveButtonText; - private OnSearchableItemClickedListener _onSearchableItemClickedListener; - private DialogInterface.OnClickListener _onPositiveBtnClickListener; + private ArrayAdapter withContaingTextArrayFilterArrayAdapter; - private SearchView _searchView; + private OnSearchTextChangedListener _onSearchTextChangedListener; - private String _strTitle; + private OnSearchableItemClickedListener _onSearchableItemClickedListener; - private String _strPositiveButtonText; + private DialogInterface.OnClickListener _onPositiveBtnClickListener; + /** + * Constructor + */ public SearchableListDialog() { } + /** + * Factory + * + * @param items + * + * @return + */ public static SearchableListDialog newInstance(List items) { - SearchableListDialog multiSelectExpandableFragment = new SearchableListDialog(); + SearchableListDialog searchableListDialog = new SearchableListDialog(); Bundle args = new Bundle(); + args.putSerializable(ITEMS, (Serializable) items); - multiSelectExpandableFragment.setArguments(args); + searchableListDialog.setArguments(args); - return multiSelectExpandableFragment; + return searchableListDialog; } @Override @@ -78,6 +114,7 @@ public View onCreateView(LayoutInflater inflater, getDialog().getWindow() .setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN); + return super.onCreateView(inflater, container, savedInstanceState); @@ -98,59 +135,85 @@ public Dialog onCreateDialog(Bundle savedInstanceState) { } // Change End - View rootView = inflater.inflate(R.layout.searchable_list_dialog, - null); + // + // Prepare the searchableListView + // + + // Instantiate the searchableListView from XML + View searchableListRootView = inflater.inflate(R.layout.searchable_list_dialog, + null); - setData(rootView); + // Configure the searchableListView + configureView(searchableListRootView); + + // + // Create dialog builder + // AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(getActivity()); - alertDialogBuilder.setView(rootView); + + // Indicate to put the searchableListView in the alertDialog + alertDialogBuilder.setView(searchableListRootView); + + // Title + + String strTitle = _strTitle == null + ? "Select Item" + : _strTitle; + + alertDialogBuilder.setTitle(strTitle); + + // Positive Button String strPositiveButton = _strPositiveButtonText == null ? "CLOSE" : _strPositiveButtonText; + alertDialogBuilder.setPositiveButton(strPositiveButton, _onPositiveBtnClickListener); - String strTitle = _strTitle == null - ? "Select Item" - : _strTitle; - alertDialogBuilder.setTitle(strTitle); + // + // Create searchableListDialog + // - final AlertDialog dialog = alertDialogBuilder.create(); + final AlertDialog searchableListDialog = alertDialogBuilder.create(); - // dialog.getWindow() - // .setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN); - hideKeyboard(_searchView); + // Hide Soft Keybord +// hideKeyboard(_searchTextEditView); + hideKeyboard(searchableListRootView); - return dialog; + return searchableListDialog; } - private void setData(View rootView) { + private void configureView(View searchableListRootView) { SearchManager searchManager = (SearchManager) getActivity().getSystemService(Context.SEARCH_SERVICE); - _searchView = (SearchView) rootView.findViewById(R.id.search); - _searchView.setSearchableInfo(searchManager.getSearchableInfo(getActivity().getComponentName())); - _searchView.setIconifiedByDefault(false); - _searchView.setOnQueryTextListener(this); - _searchView.setOnCloseListener(this); - _searchView.clearFocus(); + // + // Search Edit text zone + // - // Hide Soft Keybord -// InputMethodManager keyboard = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE); -// keyboard.hideSoftInputFromWindow(_searchView.getWindowToken(), -// 0); + _searchTextEditView = (SearchView) searchableListRootView.findViewById(R.id.search); - List items = (List) getArguments().getSerializable(ITEMS); + _searchTextEditView.setSearchableInfo(searchManager.getSearchableInfo(getActivity().getComponentName())); + _searchTextEditView.setIconifiedByDefault(false); + _searchTextEditView.setOnQueryTextListener(this); + _searchTextEditView.setOnCloseListener(this); + _searchTextEditView.clearFocus(); + + // + // Items list + // - _listView = (ListView) rootView.findViewById(R.id.listItems); + _listView = (ListView) searchableListRootView.findViewById(R.id.listItems); + + List items = (List) getArguments().getSerializable(ITEMS); // Create an ArrayAdapter for items, with filtering capablity based on item containing a text withContaingTextArrayFilterArrayAdapter = new WithContaingTextArrayFilterArrayAdapter(getActivity(), android.R.layout.simple_list_item_1, items); - //attach the adapter to the list + // Attach the adapter to the list _listView.setAdapter(withContaingTextArrayFilterArrayAdapter); _listView.setTextFilterEnabled(true); @@ -229,13 +292,17 @@ public void onPause() { @Override public boolean onQueryTextSubmit(String s) { - _searchView.clearFocus(); + _searchTextEditView.clearFocus(); return true; } @Override public boolean onQueryTextChange(String s) { + // + // Filter item list + // + if (TextUtils.isEmpty(s)) { ((WithContaingTextArrayFilterArrayAdapter) _listView.getAdapter()).getFilter() @@ -247,6 +314,10 @@ public boolean onQueryTextChange(String s) { .filter(s); } + // + // Call Search Text Change Listener + // + if (null != _onSearchTextChangedListener) { // Call Listener @@ -256,17 +327,6 @@ public boolean onQueryTextChange(String s) { return true; } - public interface OnSearchableItemClickedListener - extends Serializable { - - void onSearchableItemClicked(T item, - int position); - } - - public interface OnSearchTextChangedListener { - void onSearchTextChanged(String strText); - } - private void hideKeyboard(final View ettext) { ettext.requestFocus(); diff --git a/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableSpinner.java b/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableSpinner.java index a9c5235bd..bb26a767c 100644 --- a/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableSpinner.java +++ b/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableSpinner.java @@ -89,8 +89,9 @@ public SearchableSpinner(Context context, private void init() { _allItems=new ArrayList(); - _items = new ArrayList(); + + // Create Dialog instance _searchableListDialog = SearchableListDialog.newInstance(_items); // S'abonner aux clicks sur un item @@ -116,41 +117,49 @@ public boolean onTouch(View v, MotionEvent event) { if (_searchableListDialog.isAdded()) { - return true; - } - if (event.getAction() == MotionEvent.ACTION_UP) { - if (null != _cursorAdapter) { + // NTD - // Refresh content #6 - // Change Start - // Description: The items were only set initially, not reloading the data in the - // spinner every time it is loaded with items in the adapter. - _items.clear(); - _allItems.clear(); + } else { + // dialog is not visible - // - // Add items from DB Cursor - // + if (event.getAction() == MotionEvent.ACTION_UP) { + + if (null != _cursorAdapter) { + + // Refresh content #6 + // Change Start + // Description: The items were only set initially, not reloading the data in the + // spinner every time it is loaded with items in the adapter. + _items.clear(); + _allItems.clear(); - for (int i = 0; i < _cursorAdapter.getCount(); i++) { + // + // Add items from DB Cursor + // - Cursor cursorOnRow = (Cursor) _cursorAdapter.getItem(i); + for (int i = 0; i < _cursorAdapter.getCount(); i++) { - final String accountFullName = cursorOnRow.getString(cursorOnRow.getColumnIndexOrThrow(DatabaseSchema.AccountEntry.COLUMN_FULL_NAME)); + Cursor cursorOnRow = (Cursor) _cursorAdapter.getItem(i); - // TODO TW C 2020-01-17 : Ajouter l'étoile pour les Favoris + final String accountFullName = cursorOnRow.getString(cursorOnRow.getColumnIndexOrThrow(DatabaseSchema.AccountEntry.COLUMN_FULL_NAME)); - _items.add(accountFullName); + // TODO TW C 2020-01-17 : Ajouter l'étoile pour les Favoris - } // for + _items.add(accountFullName); - _allItems.addAll(_items); + } // for - _searchableListDialog.show(scanForActivity(_context).getFragmentManager(), - "TAG"); + // Create a copy of the items + _allItems.addAll(_items); + + // Display SearchableListDialog + _searchableListDialog.show(scanForActivity(_context).getFragmentManager(), + "TAG"); + } } } + return true; } diff --git a/app/src/main/java/org/gnucash/android/util/QualifiedAccountNameCursorAdapter.java b/app/src/main/java/org/gnucash/android/util/QualifiedAccountNameCursorAdapter.java index e8b2cd9b1..c00e441d4 100644 --- a/app/src/main/java/org/gnucash/android/util/QualifiedAccountNameCursorAdapter.java +++ b/app/src/main/java/org/gnucash/android/util/QualifiedAccountNameCursorAdapter.java @@ -95,7 +95,7 @@ public void bindView(View view, // item text TextView text1 = (TextView) view.findViewById(android.R.id.text1); - // TODO TW C 2020-01-19 : A mettre ? + // TODO TW C 2020-01-19 : A mettre ici ou dans xml avec android:ellipsize="marquee" ? // // item on single line // text1.setSingleLine(); From 6cf55425b5875a62f373b71bab5e33df36949ff8 Mon Sep 17 00:00:00 2001 From: JeanGarf Date: Sun, 19 Jan 2020 04:29:55 +0100 Subject: [PATCH 033/131] #110 - Customize item appearance for searchableListView --- ...thContaingTextArrayFilterArrayAdapter.java | 48 +++++++++++++++++-- 1 file changed, 45 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/WithContaingTextArrayFilterArrayAdapter.java b/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/WithContaingTextArrayFilterArrayAdapter.java index 0103a5d5a..daf80f0e4 100644 --- a/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/WithContaingTextArrayFilterArrayAdapter.java +++ b/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/WithContaingTextArrayFilterArrayAdapter.java @@ -2,8 +2,12 @@ import android.app.Activity; import android.support.annotation.NonNull; +import android.text.TextUtils; +import android.view.View; +import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.Filter; +import android.widget.TextView; import java.util.ArrayList; import java.util.List; @@ -123,15 +127,15 @@ private ArrayList getCopyOfAllItems() { * Constructor * * @param context - * @param items_view + * @param itemView * @param items */ public WithContaingTextArrayFilterArrayAdapter(Activity context, - int items_view, + int itemView, List items) { super(context, - items_view, + itemView, items); // save all the items (before filtering) @@ -146,4 +150,42 @@ public Filter getFilter() { return _itemContaingTextArrayFilter; } + @NonNull + @Override + public View getView(int position, + View convertView, + ViewGroup parent) { + + View itemView = super.getView(position, + convertView, + parent); + + // item text + TextView text1 = (TextView) itemView.findViewById(android.R.id.text1); + + // item on single line + text1.setSingleLine(); + + // Add "..." in the middle of the item if too long for one line + text1.setEllipsize(TextUtils.TruncateAt.MIDDLE); + +// text1.setTextColor(getContext().getResources().getColor(R.color.account_red)); + + // TODO TW C 2020-01-19 : Handle favorite star +// Integer isFavorite = cursor.getInt(cursor.getColumnIndex(DatabaseSchema.AccountEntry.COLUMN_FAVORITE)); +// +// if (isFavorite == 0) { +// text1.setCompoundDrawablesWithIntrinsicBounds(0, +// 0, +// 0, +// 0); +// } else { +// text1.setCompoundDrawablesWithIntrinsicBounds(0, +// 0, +// R.drawable.ic_star_black_18dp, +// 0); +// } + + return itemView; + } } From a2dd297138530604709af247a3ed70401209028e Mon Sep 17 00:00:00 2001 From: JeanGarf Date: Sun, 19 Jan 2020 23:32:55 +0100 Subject: [PATCH 034/131] #110 - After test, it's better to allow multi-line --- .../WithContaingTextArrayFilterArrayAdapter.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/WithContaingTextArrayFilterArrayAdapter.java b/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/WithContaingTextArrayFilterArrayAdapter.java index daf80f0e4..3a65bdad2 100644 --- a/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/WithContaingTextArrayFilterArrayAdapter.java +++ b/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/WithContaingTextArrayFilterArrayAdapter.java @@ -163,11 +163,11 @@ public View getView(int position, // item text TextView text1 = (TextView) itemView.findViewById(android.R.id.text1); - // item on single line - text1.setSingleLine(); - - // Add "..." in the middle of the item if too long for one line - text1.setEllipsize(TextUtils.TruncateAt.MIDDLE); +// // item on single line +// text1.setSingleLine(); +// +// // Add "..." in the middle of the item if too long for one line +// text1.setEllipsize(TextUtils.TruncateAt.MIDDLE); // text1.setTextColor(getContext().getResources().getColor(R.color.account_red)); From cf3b090cd5e31a6485c70d5678e4a9361bdb585c Mon Sep 17 00:00:00 2001 From: JeanGarf Date: Wed, 22 Jan 2020 23:45:37 +0100 Subject: [PATCH 035/131] #110 - use singleLine instead of maxLines=1 --- app/src/main/res/layout/account_spinner_item.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/layout/account_spinner_item.xml b/app/src/main/res/layout/account_spinner_item.xml index 485d7d26b..a0119acc3 100644 --- a/app/src/main/res/layout/account_spinner_item.xml +++ b/app/src/main/res/layout/account_spinner_item.xml @@ -17,7 +17,7 @@ Date: Wed, 22 Jan 2020 23:53:18 +0100 Subject: [PATCH 036/131] #110 - Rename account_spinner_item into transaction_account_spinner_item --- .../android/ui/transaction/TransactionsActivity.java | 7 ++++--- ...inner_item.xml => transaction_account_spinner_item.xml} | 0 2 files changed, 4 insertions(+), 3 deletions(-) rename app/src/main/res/layout/{account_spinner_item.xml => transaction_account_spinner_item.xml} (100%) 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 fb4ba91ef..8f37c7409 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 @@ -380,10 +380,11 @@ private void setupActionBarNavigation() { } mAccountsCursor = mAccountsDbAdapter.fetchAllRecordsOrderedByFullName(); - SpinnerAdapter mSpinnerAdapter = new QualifiedAccountNameCursorAdapter( - getSupportActionBar().getThemedContext(), mAccountsCursor, R.layout.account_spinner_item); + SpinnerAdapter qualifiedAccountNameCursorAdapter = new QualifiedAccountNameCursorAdapter(getSupportActionBar().getThemedContext(), + mAccountsCursor, + R.layout.transaction_account_spinner_item); - mToolbarSpinner.setAdapter(mSpinnerAdapter); + mToolbarSpinner.setAdapter(qualifiedAccountNameCursorAdapter); mToolbarSpinner.setOnItemSelectedListener(mTransactionListNavigationListener); getSupportActionBar().setDisplayHomeAsUpEnabled(true); diff --git a/app/src/main/res/layout/account_spinner_item.xml b/app/src/main/res/layout/transaction_account_spinner_item.xml similarity index 100% rename from app/src/main/res/layout/account_spinner_item.xml rename to app/src/main/res/layout/transaction_account_spinner_item.xml From 0351d7b1f77fb149ea2f1e47d5e316d69282e992 Mon Sep 17 00:00:00 2001 From: JeanGarf Date: Sat, 25 Jan 2020 11:42:13 +0100 Subject: [PATCH 037/131] #110 - Improve code quality --- .../android/db/adapter/AccountsDbAdapter.java | 21 ++++--- .../ui/account/AccountFormFragment.java | 12 ++-- ...ontainingTextArrayFilterArrayAdapter.java} | 59 ++++++++----------- .../util/CommoditiesCursorAdapter.java | 15 +++-- .../QualifiedAccountNameCursorAdapter.java | 37 ++++++------ .../main/res/layout/toolbar_with_spinner.xml | 1 + 6 files changed, 74 insertions(+), 71 deletions(-) rename app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/{WithContaingTextArrayFilterArrayAdapter.java => WithContainingTextArrayFilterArrayAdapter.java} (73%) 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 e19138306..c924732dc 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 @@ -702,15 +702,20 @@ public Cursor fetchAllRecords(){ * GnuCash ROOT accounts and hidden accounts will not be included in the result set. * @return {@link Cursor} to all account records */ - public Cursor fetchAllRecordsOrderedByFullName(){ - Log.v(LOG_TAG, "Fetching all accounts from db"); - String selection = AccountEntry.COLUMN_HIDDEN + " = 0 AND " + AccountEntry.COLUMN_TYPE + " != ?" ; + public Cursor fetchAllRecordsOrderedByFullName() { + + Log.v(LOG_TAG, + "Fetching all accounts from db"); + + String selection = AccountEntry.COLUMN_HIDDEN + " = 0 AND " + AccountEntry.COLUMN_TYPE + " != ?"; + return mDb.query(AccountEntry.TABLE_NAME, - null, - selection, - new String[]{AccountType.ROOT.name()}, - null, null, - AccountEntry.COLUMN_FULL_NAME + " ASC"); + null, + selection, + new String[]{AccountType.ROOT.name()}, + null, + null, + AccountEntry.COLUMN_FULL_NAME + " ASC"); } /** diff --git a/app/src/main/java/org/gnucash/android/ui/account/AccountFormFragment.java b/app/src/main/java/org/gnucash/android/ui/account/AccountFormFragment.java index 62da9f5b1..ba743b500 100644 --- a/app/src/main/java/org/gnucash/android/ui/account/AccountFormFragment.java +++ b/app/src/main/java/org/gnucash/android/ui/account/AccountFormFragment.java @@ -325,8 +325,8 @@ public void onClick(View view) { public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); - CommoditiesCursorAdapter commoditiesAdapter = new CommoditiesCursorAdapter( - getActivity(), android.R.layout.simple_spinner_item); + CommoditiesCursorAdapter commoditiesAdapter = new CommoditiesCursorAdapter(getActivity(), + android.R.layout.simple_spinner_item); commoditiesAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); mCurrencySpinner.setAdapter(commoditiesAdapter); @@ -586,7 +586,7 @@ private void loadDefaultTransferAccountList(){ } mDefaultTransferAccountCursorAdapter = new QualifiedAccountNameCursorAdapter(getActivity(), - defaultTransferAccountCursor); + defaultTransferAccountCursor); mDefaultTransferAccountSpinner.setAdapter(mDefaultTransferAccountCursorAdapter); } @@ -626,10 +626,10 @@ private void loadParentAccountList(AccountType accountType){ view.findViewById(R.id.label_parent_account).setVisibility(View.VISIBLE); } - mParentAccountCursorAdapter = new QualifiedAccountNameCursorAdapter( - getActivity(), mParentAccountCursor); + mParentAccountCursorAdapter = new QualifiedAccountNameCursorAdapter(getActivity(), + mParentAccountCursor); mParentAccountSpinner.setAdapter(mParentAccountCursorAdapter); - } + } /** * Returns a comma separated list of account types which can be parent accounts for the specified type. diff --git a/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/WithContaingTextArrayFilterArrayAdapter.java b/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/WithContainingTextArrayFilterArrayAdapter.java similarity index 73% rename from app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/WithContaingTextArrayFilterArrayAdapter.java rename to app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/WithContainingTextArrayFilterArrayAdapter.java index 3a65bdad2..873e5a0f0 100644 --- a/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/WithContaingTextArrayFilterArrayAdapter.java +++ b/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/WithContainingTextArrayFilterArrayAdapter.java @@ -2,7 +2,6 @@ import android.app.Activity; import android.support.annotation.NonNull; -import android.text.TextUtils; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; @@ -12,7 +11,7 @@ import java.util.ArrayList; import java.util.List; -public class WithContaingTextArrayFilterArrayAdapter +public class WithContainingTextArrayFilterArrayAdapter extends ArrayAdapter { /** @@ -24,16 +23,16 @@ private class ItemContaingTextArrayFilter @Override protected FilterResults performFiltering(CharSequence textToSearch) { - final FilterResults results = new FilterResults(); + final FilterResults filteredItems = new FilterResults(); + + // Get copy of all items + final ArrayList allItems = getCopyOfAllItems(); if (textToSearch == null || textToSearch.length() == 0) { // Nothing to search - // Get copy of all items - final ArrayList allItems = getCopyOfAllItems(); - - results.values = allItems; - results.count = allItems.size(); + filteredItems.values = allItems; + filteredItems.count = allItems.size(); } else { // There is something to search @@ -41,14 +40,11 @@ protected FilterResults performFiltering(CharSequence textToSearch) { final String textToSearchLowerCase = textToSearch.toString() .toLowerCase(); - // Get copy of all items - final ArrayList allItems = getCopyOfAllItems(); - // // Filter items // - final ArrayList filteredItems = new ArrayList<>(); + final ArrayList tmpFilteredItems = new ArrayList<>(); final int count = allItems.size(); @@ -57,13 +53,13 @@ protected FilterResults performFiltering(CharSequence textToSearch) { final T item = allItems.get(i); final String itemTextLowerCase = item.toString() - .toLowerCase(); + .toLowerCase(); // First match against the whole, non-splitted value if (itemTextLowerCase.contains(textToSearchLowerCase)) { // It matches - filteredItems.add(item); + tmpFilteredItems.add(item); } else { // It doesen't match @@ -72,22 +68,22 @@ protected FilterResults performFiltering(CharSequence textToSearch) { } } - results.values = filteredItems; - results.count = filteredItems.size(); + filteredItems.values = tmpFilteredItems; + filteredItems.count = tmpFilteredItems.size(); } - return results; + return filteredItems; } @Override protected void publishResults(CharSequence constraint, - FilterResults results) { + FilterResults filteredItems) { // Replace items in ArrayAdapter with filtered ones clear(); - addAll((List) results.values); + addAll((List) filteredItems.values); - if (results.count > 0) { + if (filteredItems.count > 0) { notifyDataSetChanged(); } else { notifyDataSetInvalidated(); @@ -96,11 +92,12 @@ protected void publishResults(CharSequence constraint, private ArrayList getCopyOfAllItems() { - final ArrayList values; + final ArrayList allItemsCopy; + synchronized (_lock) { - values = new ArrayList<>(_allItems); + allItemsCopy = new ArrayList<>(_allItems); } - return values; + return allItemsCopy; } } @@ -130,9 +127,9 @@ private ArrayList getCopyOfAllItems() { * @param itemView * @param items */ - public WithContaingTextArrayFilterArrayAdapter(Activity context, - int itemView, - List items) { + public WithContainingTextArrayFilterArrayAdapter(Activity context, + int itemView, + List items) { super(context, itemView, @@ -156,6 +153,8 @@ public View getView(int position, View convertView, ViewGroup parent) { + // TODO TW C 2020-01-25 : Optimiser en utilisant un ViewHolder + View itemView = super.getView(position, convertView, parent); @@ -163,14 +162,6 @@ public View getView(int position, // item text TextView text1 = (TextView) itemView.findViewById(android.R.id.text1); -// // item on single line -// text1.setSingleLine(); -// -// // Add "..." in the middle of the item if too long for one line -// text1.setEllipsize(TextUtils.TruncateAt.MIDDLE); - -// text1.setTextColor(getContext().getResources().getColor(R.color.account_red)); - // TODO TW C 2020-01-19 : Handle favorite star // Integer isFavorite = cursor.getInt(cursor.getColumnIndex(DatabaseSchema.AccountEntry.COLUMN_FAVORITE)); // diff --git a/app/src/main/java/org/gnucash/android/util/CommoditiesCursorAdapter.java b/app/src/main/java/org/gnucash/android/util/CommoditiesCursorAdapter.java index 03f16608c..41b59e6c1 100644 --- a/app/src/main/java/org/gnucash/android/util/CommoditiesCursorAdapter.java +++ b/app/src/main/java/org/gnucash/android/util/CommoditiesCursorAdapter.java @@ -35,11 +35,16 @@ */ public class CommoditiesCursorAdapter extends SimpleCursorAdapter { - public CommoditiesCursorAdapter(Context context, @LayoutRes int itemLayoutResource) { - super(context, itemLayoutResource, - CommoditiesDbAdapter.getInstance().fetchAllRecords(DatabaseSchema.CommodityEntry.COLUMN_MNEMONIC + " ASC"), - new String[]{DatabaseSchema.CommodityEntry.COLUMN_FULLNAME}, - new int[] {android.R.id.text1}, 0); + public CommoditiesCursorAdapter(Context context, + @LayoutRes int itemLayoutResource) { + + super(context, + itemLayoutResource, + CommoditiesDbAdapter.getInstance() + .fetchAllRecords(DatabaseSchema.CommodityEntry.COLUMN_MNEMONIC + " ASC"), + new String[]{DatabaseSchema.CommodityEntry.COLUMN_FULLNAME}, + new int[]{android.R.id.text1}, + 0); } @Override diff --git a/app/src/main/java/org/gnucash/android/util/QualifiedAccountNameCursorAdapter.java b/app/src/main/java/org/gnucash/android/util/QualifiedAccountNameCursorAdapter.java index c00e441d4..53839e458 100644 --- a/app/src/main/java/org/gnucash/android/util/QualifiedAccountNameCursorAdapter.java +++ b/app/src/main/java/org/gnucash/android/util/QualifiedAccountNameCursorAdapter.java @@ -21,7 +21,6 @@ import android.support.annotation.LayoutRes; import android.support.annotation.NonNull; import android.support.v4.widget.SimpleCursorAdapter; -import android.text.TextUtils; import android.view.View; import android.widget.TextView; @@ -49,14 +48,20 @@ public class QualifiedAccountNameCursorAdapter public QualifiedAccountNameCursorAdapter(Context context, Cursor cursor) { - super(context, - android.R.layout.simple_spinner_item, - cursor, - new String[]{DatabaseSchema.AccountEntry.COLUMN_FULL_NAME}, - new int[]{android.R.id.text1}, - 0); - - setDropDownViewResource(R.layout.account_spinner_dropdown_item); + this(context, + cursor, + android.R.layout.simple_spinner_item // Layout of the closed spinner item + ); + +// super(context, +// android.R.layout.simple_spinner_item, // Layout of the closed spinner item +// cursor, +// new String[]{DatabaseSchema.AccountEntry.COLUMN_FULL_NAME}, +// new int[]{android.R.id.text1}, +// 0); +// +// // Define layout of each item in the open drop down of the spinner +// setDropDownViewResource(R.layout.account_spinner_dropdown_item); } /** @@ -74,12 +79,13 @@ public QualifiedAccountNameCursorAdapter(Context context, @LayoutRes int selectedSpinnerItem) { super(context, - selectedSpinnerItem, + selectedSpinnerItem, // Layout of the closed spinner item cursor, new String[]{DatabaseSchema.AccountEntry.COLUMN_FULL_NAME}, new int[]{android.R.id.text1}, 0); + // Define layout of each item in the open drop down of the spinner setDropDownViewResource(R.layout.account_spinner_dropdown_item); } @@ -95,14 +101,6 @@ public void bindView(View view, // item text TextView text1 = (TextView) view.findViewById(android.R.id.text1); - // TODO TW C 2020-01-19 : A mettre ici ou dans xml avec android:ellipsize="marquee" ? -// // item on single line -// text1.setSingleLine(); - - // Add "..." in the middle of the item if too long for one line - text1.setEllipsize(TextUtils.TruncateAt.MIDDLE); - - Integer isFavorite = cursor.getInt(cursor.getColumnIndex(DatabaseSchema.AccountEntry.COLUMN_FAVORITE)); if (isFavorite == 0) { @@ -130,11 +128,14 @@ public int getPosition(@NonNull String accountUID) { long accountId = AccountsDbAdapter.getInstance() .getID(accountUID); + for (int pos = 0; pos < getCount(); pos++) { + if (getItemId(pos) == accountId) { return pos; } } + return -1; } } diff --git a/app/src/main/res/layout/toolbar_with_spinner.xml b/app/src/main/res/layout/toolbar_with_spinner.xml index b823bc1fb..4294b2476 100644 --- a/app/src/main/res/layout/toolbar_with_spinner.xml +++ b/app/src/main/res/layout/toolbar_with_spinner.xml @@ -32,6 +32,7 @@ android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" app:popupTheme="@style/ThemeOverlay.AppCompat.Light" tools:showIn="@layout/activity_form"> + Date: Sat, 25 Jan 2020 11:44:39 +0100 Subject: [PATCH 038/131] #110 - Rename SearchableSpinner into SearchableSpinnerView --- .../transaction/TransactionFormFragment.java | 31 +-- ...pinner.java => SearchableSpinnerView.java} | 181 +++++++++--------- .../res/layout/fragment_transaction_form.xml | 2 +- app/src/main/res/values/attrs.xml | 2 +- 4 files changed, 112 insertions(+), 104 deletions(-) rename app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/{SearchableSpinner.java => SearchableSpinnerView.java} (57%) 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 9a81b4e85..bd3d5a8ed 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 @@ -81,7 +81,7 @@ import org.gnucash.android.ui.util.RecurrenceViewClickListener; import org.gnucash.android.ui.util.widget.CalculatorEditText; import org.gnucash.android.ui.util.widget.TransactionTypeSwitch; -import org.gnucash.android.ui.util.widget.searchablespinner.SearchableSpinner; +import org.gnucash.android.ui.util.widget.searchablespinner.SearchableSpinnerView; import org.gnucash.android.util.QualifiedAccountNameCursorAdapter; import java.math.BigDecimal; @@ -181,7 +181,7 @@ public class TransactionFormFragment extends Fragment implements * Spinner for selecting the transfer account */ @BindView(R.id.input_transfer_account_spinner) - SearchableSpinner mTransferAccountSpinner; + SearchableSpinnerView mTransferAccountSpinnerView; /** * Checkbox indicating if this transaction should be saved as a template or not @@ -271,7 +271,7 @@ public void onClick(View v) { */ private void startTransferFunds() { Commodity fromCommodity = Commodity.getInstance((mTransactionsDbAdapter.getAccountCurrencyCode(mAccountUID))); - long id = mTransferAccountSpinner.getSelectedItemId(); + long id = mTransferAccountSpinnerView.getSelectedItemId(); String targetCurrencyCode = mAccountsDbAdapter.getCurrencyCode(mAccountsDbAdapter.getUID(id)); if (fromCommodity.equals(Commodity.getInstance(targetCurrencyCode)) @@ -323,7 +323,7 @@ public void onActivityCreated(Bundle savedInstanceState) { //updateTransferAccountsList must only be called after initializing mAccountsDbAdapter updateTransferAccountsList(); - mTransferAccountSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + mTransferAccountSpinnerView.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { /** * Flag for ignoring first call to this listener. * The first call is during layout, but we want it called only in response to user interaction @@ -367,8 +367,8 @@ public void onNothingSelected(AdapterView adapterView) { } }); - mTransferAccountSpinner.setTitle(getString(R.string.select_transfer_account)); - mTransferAccountSpinner.setPositiveButton(getString(R.string.alert_dialog_cancel)); + mTransferAccountSpinnerView.setTitle(getString(R.string.select_transfer_account)); + mTransferAccountSpinnerView.setPositiveButton(getString(R.string.alert_dialog_cancel)); ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar(); assert actionBar != null; @@ -604,11 +604,14 @@ private void updateTransferAccountsList(){ if (mCursor != null) { mCursor.close(); } - mCursor = mAccountsDbAdapter.fetchAccountsOrderedByFavoriteAndFullName(conditions, new String[]{mAccountUID, AccountType.ROOT.name()}); + mCursor = mAccountsDbAdapter.fetchAccountsOrderedByFavoriteAndFullName(conditions, + new String[]{mAccountUID, + AccountType.ROOT.name()}); - mAccountCursorAdapter = new QualifiedAccountNameCursorAdapter(getActivity(), mCursor); + mAccountCursorAdapter = new QualifiedAccountNameCursorAdapter(getActivity(), + mCursor); - mTransferAccountSpinner.setAdapter(mAccountCursorAdapter); + mTransferAccountSpinnerView.setAdapter(mAccountCursorAdapter); } /** @@ -706,7 +709,7 @@ public void onClick(View v) { private void setSelectedTransferAccount(long accountId){ int position = mAccountCursorAdapter.getPosition(mAccountsDbAdapter.getUID(accountId)); if (position >= 0) - mTransferAccountSpinner.setSelection(position); + mTransferAccountSpinnerView.setSelection(position); } /** @@ -778,7 +781,7 @@ private List extractSplitsFromView(){ private @NonNull String getTransferAccountUID() { String transferAcctUID; if (mUseDoubleEntry) { - long transferAcctId = mTransferAccountSpinner.getSelectedItemId(); + long transferAcctId = mTransferAccountSpinnerView.getSelectedItemId(); transferAcctUID = mAccountsDbAdapter.getUID(transferAcctId); } else { Commodity baseCommodity = mAccountsDbAdapter.getRecord(mAccountUID).getCommodity(); @@ -835,7 +838,7 @@ private boolean isMultiCurrencyTransaction(){ if (!mUseDoubleEntry) return false; - String transferAcctUID = mAccountsDbAdapter.getUID(mTransferAccountSpinner.getSelectedItemId()); + String transferAcctUID = mAccountsDbAdapter.getUID(mTransferAccountSpinnerView.getSelectedItemId()); String currencyCode = mAccountsDbAdapter.getAccountCurrencyCode(mAccountUID); String transferCurrencyCode = mAccountsDbAdapter.getCurrencyCode(transferAcctUID); @@ -961,7 +964,7 @@ public boolean onOptionsItemSelected(MenuItem item) { if (mAmountEditText.getValue() == null) { Toast.makeText(getActivity(), R.string.toast_transanction_amount_required, Toast.LENGTH_SHORT).show(); } - if (mUseDoubleEntry && mTransferAccountSpinner.getCount() == 0){ + if (mUseDoubleEntry && mTransferAccountSpinnerView.getCount() == 0){ Toast.makeText(getActivity(), R.string.toast_disable_double_entry_to_save_transaction, Toast.LENGTH_LONG).show(); @@ -981,7 +984,7 @@ public boolean onOptionsItemSelected(MenuItem item) { */ private boolean canSave(){ return (mUseDoubleEntry && mAmountEditText.isInputValid() - && mTransferAccountSpinner.getCount() > 0) + && mTransferAccountSpinnerView.getCount() > 0) || (!mUseDoubleEntry && mAmountEditText.isInputValid()); } diff --git a/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableSpinner.java b/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableSpinnerView.java similarity index 57% rename from app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableSpinner.java rename to app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableSpinnerView.java index bb26a767c..062a12ccb 100644 --- a/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableSpinner.java +++ b/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableSpinnerView.java @@ -5,27 +5,24 @@ import android.content.ContextWrapper; import android.content.DialogInterface; import android.content.res.TypedArray; -import android.database.Cursor; import android.support.v4.widget.CursorAdapter; import android.text.TextUtils; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import android.view.inputmethod.InputMethodManager; -import android.widget.ArrayAdapter; import android.widget.EditText; import android.widget.SpinnerAdapter; import org.gnucash.android.R; -import org.gnucash.android.db.DatabaseSchema; import java.util.ArrayList; import java.util.List; -public class SearchableSpinner +public class SearchableSpinnerView extends android.support.v7.widget.AppCompatSpinner implements View.OnTouchListener, - SearchableListDialog.OnSearchableItemClickedListener { + SearchableListDialogFragment.OnSearchableItemClickedListener { public static final int NO_ITEM_SELECTED = -1; @@ -35,25 +32,25 @@ public class SearchableSpinner private List _items; private List _allItems; - private SearchableListDialog _searchableListDialog; + private SearchableListDialogFragment _searchableListDialogFragment; private boolean _isDirty; - // Adpater for Spinner based on data in a DB Cursor - private CursorAdapter _cursorAdapter; +// // Adpater for Spinner based on data in a DB Cursor +// private CursorAdapter _cursorAdapter; private String _strHintText; private boolean _isFromInit; - public SearchableSpinner(Context context) { + public SearchableSpinnerView(Context context) { super(context); this._context = context; init(); } - public SearchableSpinner(Context context, - AttributeSet attrs) { + public SearchableSpinnerView(Context context, + AttributeSet attrs) { super(context, attrs); @@ -61,11 +58,11 @@ public SearchableSpinner(Context context, this._context = context; TypedArray a = context.obtainStyledAttributes(attrs, - R.styleable.SearchableSpinner); + R.styleable.SearchableSpinnerView); final int N = a.getIndexCount(); for (int i = 0; i < N; ++i) { int attr = a.getIndex(i); - if (attr == R.styleable.SearchableSpinner_hintText) { + if (attr == R.styleable.SearchableSpinnerView_hintText) { _strHintText = a.getString(attr); } } @@ -73,9 +70,9 @@ public SearchableSpinner(Context context, init(); } - public SearchableSpinner(Context context, - AttributeSet attrs, - int defStyleAttr) { + public SearchableSpinnerView(Context context, + AttributeSet attrs, + int defStyleAttr) { super(context, attrs, @@ -92,10 +89,11 @@ private void init() { _items = new ArrayList(); // Create Dialog instance - _searchableListDialog = SearchableListDialog.newInstance(_items); + // TODO TW C 2020-01-25 : Supprimer _items + _searchableListDialogFragment = SearchableListDialogFragment.makeInstance(this, _items); // S'abonner aux clicks sur un item - _searchableListDialog.setOnSearchableItemClickListener(this); + _searchableListDialogFragment.setOnSearchableItemClickListener(this); // S'abonner aux évènements onTouch setOnTouchListener(this); @@ -116,7 +114,8 @@ private void init() { public boolean onTouch(View v, MotionEvent event) { - if (_searchableListDialog.isAdded()) { + if (_searchableListDialogFragment.isAdded()) { + // dialog is already visible // NTD @@ -124,39 +123,41 @@ public boolean onTouch(View v, // dialog is not visible if (event.getAction() == MotionEvent.ACTION_UP) { - - if (null != _cursorAdapter) { - - // Refresh content #6 - // Change Start - // Description: The items were only set initially, not reloading the data in the - // spinner every time it is loaded with items in the adapter. - _items.clear(); - _allItems.clear(); - - // - // Add items from DB Cursor - // - - for (int i = 0; i < _cursorAdapter.getCount(); i++) { - - Cursor cursorOnRow = (Cursor) _cursorAdapter.getItem(i); - - final String accountFullName = cursorOnRow.getString(cursorOnRow.getColumnIndexOrThrow(DatabaseSchema.AccountEntry.COLUMN_FULL_NAME)); - - // TODO TW C 2020-01-17 : Ajouter l'étoile pour les Favoris - - _items.add(accountFullName); - - } // for - - // Create a copy of the items - _allItems.addAll(_items); - - // Display SearchableListDialog - _searchableListDialog.show(scanForActivity(_context).getFragmentManager(), - "TAG"); - } + // User has just clicked on the spinner + +// if (null != _cursorAdapter) { +// +// // +// // Open Search & List Dialog +// // +// +// // Refresh content #6 +// // Change Start +// // Description: The items were only set initially, not reloading the data in the +// // spinner every time it is loaded with items in the adapter. +// _items.clear(); +// _allItems.clear(); +// +// // Create items from DB Cursor +// for (int i = 0; i < _cursorAdapter.getCount(); i++) { +// +// Cursor cursorOnRow = (Cursor) _cursorAdapter.getItem(i); +// +// final String accountFullName = cursorOnRow.getString(cursorOnRow.getColumnIndexOrThrow(DatabaseSchema.AccountEntry.COLUMN_FULL_NAME)); +// +// // TODO TW C 2020-01-17 : Ajouter l'étoile pour les Favoris +// +// _items.add(accountFullName); +// +// } // for +// +// // Create a copy of the items +// _allItems.addAll(_items); + + // Display SearchableListDialogFragment + _searchableListDialogFragment.show(scanForActivity(_context).getFragmentManager(), + "TAG"); +// } } } @@ -170,64 +171,68 @@ public void onSearchableItemClicked(Object item, // String accountFullName = (String) item; // setSelection(_items.indexOf(item)); - setSelection(_allItems.indexOf(item)); - - if (!_isDirty) { - _isDirty = true; - setAdapter(_cursorAdapter); -// setSelection(_items.indexOf(item)); - setSelection(_allItems.indexOf(item)); - } +// setSelection(_allItems.indexOf(item)); + setSelection(position); + +// if (!_isDirty) { +// _isDirty = true; +// setAdapter(_cursorAdapter); +//// setSelection(_items.indexOf(item)); +// setSelection(_allItems.indexOf(item)); +// } } @Override public void setAdapter(SpinnerAdapter adapter) { - _cursorAdapter = (CursorAdapter) adapter; - - if (!_isFromInit) { - - // TODO TW C 2020-01-17 : Supprimer la partie ArrayAdapter - if (!TextUtils.isEmpty(_strHintText) && !_isDirty) { - // - - // - ArrayAdapter arrayAdapter = new ArrayAdapter(_context, - android.R.layout.simple_list_item_1, - new String[]{_strHintText}); - super.setAdapter(arrayAdapter); - - } else { - super.setAdapter(adapter); - } - - } else { - _isFromInit = false; - super.setAdapter(adapter); - } + super.setAdapter(adapter); + +// _cursorAdapter = (CursorAdapter) adapter; +// _searchableListDialogFragment.setCursorAdapter((CursorAdapter) adapter); + +// if (!_isFromInit) { +// +// // TODO TW C 2020-01-17 : Supprimer la partie ArrayAdapter +// if (!TextUtils.isEmpty(_strHintText) && !_isDirty) { +// // +// +// // +// ArrayAdapter arrayAdapter = new ArrayAdapter(_context, +// android.R.layout.simple_list_item_1, +// new String[]{_strHintText}); +// super.setAdapter(arrayAdapter); +// +// } else { +// super.setAdapter(adapter); +// } +// +// } else { +// _isFromInit = false; +// super.setAdapter(adapter); +// } } public void setTitle(String strTitle) { - _searchableListDialog.setTitle(strTitle); + _searchableListDialogFragment.setTitle(strTitle); } public void setPositiveButton(String strPositiveButtonText) { - _searchableListDialog.setPositiveButton(strPositiveButtonText); + _searchableListDialogFragment.setPositiveButton(strPositiveButtonText); } public void setPositiveButton(String strPositiveButtonText, DialogInterface.OnClickListener onPositiveBtnClickListener) { - _searchableListDialog.setPositiveButton(strPositiveButtonText, - onPositiveBtnClickListener); + _searchableListDialogFragment.setPositiveButton(strPositiveButtonText, + onPositiveBtnClickListener); } - public void setOnSearchTextChangedListener(SearchableListDialog.OnSearchTextChangedListener onSearchTextChangedListener) { + public void setOnSearchTextChangedListener(SearchableListDialogFragment.OnSearchTextChangedListener onSearchTextChangedListener) { - _searchableListDialog.setOnSearchTextChangedListener(onSearchTextChangedListener); + _searchableListDialogFragment.setOnSearchTextChangedListener(onSearchTextChangedListener); } private Activity scanForActivity(Context cont) { diff --git a/app/src/main/res/layout/fragment_transaction_form.xml b/app/src/main/res/layout/fragment_transaction_form.xml index 796ae13f6..504583aca 100644 --- a/app/src/main/res/layout/fragment_transaction_form.xml +++ b/app/src/main/res/layout/fragment_transaction_form.xml @@ -101,7 +101,7 @@ - - + From e1625da8cebfb45b25f8cf90071783743586556d Mon Sep 17 00:00:00 2001 From: JeanGarf Date: Sat, 25 Jan 2020 11:48:23 +0100 Subject: [PATCH 039/131] #110 - Use Parent Adapter --- ...java => SearchableListDialogFragment.java} | 61 +++++++++++++------ 1 file changed, 42 insertions(+), 19 deletions(-) rename app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/{SearchableListDialog.java => SearchableListDialogFragment.java} (79%) diff --git a/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableListDialog.java b/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableListDialogFragment.java similarity index 79% rename from app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableListDialog.java rename to app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableListDialogFragment.java index eac5376a3..9ca373543 100644 --- a/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableListDialog.java +++ b/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableListDialogFragment.java @@ -6,7 +6,9 @@ import android.app.SearchManager; import android.content.Context; import android.content.DialogInterface; +import android.database.Cursor; import android.os.Bundle; +import android.support.v4.widget.CursorAdapter; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; @@ -14,16 +16,17 @@ import android.view.WindowManager; import android.view.inputmethod.InputMethodManager; import android.widget.AdapterView; -import android.widget.ArrayAdapter; +import android.widget.ListAdapter; import android.widget.ListView; import android.widget.SearchView; import org.gnucash.android.R; +import org.gnucash.android.db.DatabaseSchema; import java.io.Serializable; import java.util.List; -public class SearchableListDialog +public class SearchableListDialogFragment extends DialogFragment implements SearchView.OnQueryTextListener, SearchView.OnCloseListener { @@ -53,6 +56,9 @@ public interface OnSearchTextChangedListener { // Dialog Title private String _strTitle; + // Parent View + private AdapterView _parentAdapterView; + // Search Edit text zone private SearchView _searchTextEditView; @@ -62,8 +68,9 @@ public interface OnSearchTextChangedListener { // Bottom right button to close the pop-up private String _strPositiveButtonText; - - private ArrayAdapter withContaingTextArrayFilterArrayAdapter; + // Adpater for Spinner based on data in a DB Cursor +// private CursorAdapter _cursorAdapter; +// private ArrayAdapter _withContainingTextArrayFilterArrayAdapter; private OnSearchTextChangedListener _onSearchTextChangedListener; @@ -75,7 +82,7 @@ public interface OnSearchTextChangedListener { /** * Constructor */ - public SearchableListDialog() { + public SearchableListDialogFragment() { } @@ -86,18 +93,20 @@ public SearchableListDialog() { * * @return */ - public static SearchableListDialog newInstance(List items) { + public static SearchableListDialogFragment makeInstance(AdapterView parentAdapterView, List items) { + + SearchableListDialogFragment searchableListDialogFragment = new SearchableListDialogFragment(); - SearchableListDialog searchableListDialog = new SearchableListDialog(); + searchableListDialogFragment.setParentAdapterView(parentAdapterView); Bundle args = new Bundle(); args.putSerializable(ITEMS, (Serializable) items); - searchableListDialog.setArguments(args); + searchableListDialogFragment.setArguments(args); - return searchableListDialog; + return searchableListDialogFragment; } @Override @@ -209,12 +218,12 @@ private void configureView(View searchableListRootView) { List items = (List) getArguments().getSerializable(ITEMS); - // Create an ArrayAdapter for items, with filtering capablity based on item containing a text - withContaingTextArrayFilterArrayAdapter = new WithContaingTextArrayFilterArrayAdapter(getActivity(), - android.R.layout.simple_list_item_1, - items); +// // Create an ArrayAdapter for items, with filtering capablity based on item containing a text +// _withContainingTextArrayFilterArrayAdapter = new WithContainingTextArrayFilterArrayAdapter(getActivity(), +// android.R.layout.simple_list_item_1, +// items); // Attach the adapter to the list - _listView.setAdapter(withContaingTextArrayFilterArrayAdapter); + _listView.setAdapter((ListAdapter) getParentAdapterView().getAdapter()); _listView.setTextFilterEnabled(true); @@ -226,7 +235,10 @@ public void onItemClick(AdapterView parent, int position, long id) { - final Object accountFullName = withContaingTextArrayFilterArrayAdapter.getItem(position); +// final Object accountFullName = _withContainingTextArrayFilterArrayAdapter.getItem(position); + final CursorAdapter cursorAdapter = (CursorAdapter) getParentAdapterView().getAdapter(); + final Cursor cursor = (Cursor) cursorAdapter.getItem(position); + final String accountFullName = cursor.getString(cursor.getColumnIndexOrThrow(DatabaseSchema.AccountEntry.COLUMN_FULL_NAME)); // Call Listener _onSearchableItemClickedListener.onSearchableItemClicked(accountFullName, @@ -303,15 +315,17 @@ public boolean onQueryTextChange(String s) { // Filter item list // + final WithContainingTextArrayFilterArrayAdapter listViewAdapter = (WithContainingTextArrayFilterArrayAdapter) _listView.getAdapter(); + if (TextUtils.isEmpty(s)) { - ((WithContaingTextArrayFilterArrayAdapter) _listView.getAdapter()).getFilter() - .filter(null); + listViewAdapter.getFilter() + .filter(null); } else { - ((WithContaingTextArrayFilterArrayAdapter) _listView.getAdapter()).getFilter() - .filter(s); + listViewAdapter.getFilter() + .filter(s); } // @@ -350,4 +364,13 @@ public void run() { 200); } + public AdapterView getParentAdapterView() { + + return _parentAdapterView; + } + + public void setParentAdapterView(AdapterView parentAdapterView) { + + _parentAdapterView = parentAdapterView; + } } From 73c2c65ffff879929f67be41871625c7c5b4f255 Mon Sep 17 00:00:00 2001 From: JeanGarf Date: Wed, 29 Jan 2020 23:33:17 +0100 Subject: [PATCH 040/131] #110 - Use QualifiedAccountNameCursorAdapter and a FilterQueryProvider to filter item and select it automatically if only one is remaining --- .../SearchableListDialogFragment.java | 100 +++++++++++++++--- 1 file changed, 84 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableListDialogFragment.java b/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableListDialogFragment.java index 9ca373543..f6ccf6c55 100644 --- a/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableListDialogFragment.java +++ b/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableListDialogFragment.java @@ -7,6 +7,7 @@ import android.content.Context; import android.content.DialogInterface; import android.database.Cursor; +import android.database.DataSetObserver; import android.os.Bundle; import android.support.v4.widget.CursorAdapter; import android.text.TextUtils; @@ -16,16 +17,22 @@ import android.view.WindowManager; import android.view.inputmethod.InputMethodManager; import android.widget.AdapterView; +import android.widget.FilterQueryProvider; import android.widget.ListAdapter; import android.widget.ListView; import android.widget.SearchView; import org.gnucash.android.R; import org.gnucash.android.db.DatabaseSchema; +import org.gnucash.android.db.adapter.AccountsDbAdapter; +import org.gnucash.android.util.QualifiedAccountNameCursorAdapter; import java.io.Serializable; import java.util.List; +/** + * Pop-up that display a ListView with a search text field + */ public class SearchableListDialogFragment extends DialogFragment implements SearchView.OnQueryTextListener, @@ -68,10 +75,6 @@ public interface OnSearchTextChangedListener { // Bottom right button to close the pop-up private String _strPositiveButtonText; - // Adpater for Spinner based on data in a DB Cursor -// private CursorAdapter _cursorAdapter; -// private ArrayAdapter _withContainingTextArrayFilterArrayAdapter; - private OnSearchTextChangedListener _onSearchTextChangedListener; private OnSearchableItemClickedListener _onSearchableItemClickedListener; @@ -218,15 +221,16 @@ private void configureView(View searchableListRootView) { List items = (List) getArguments().getSerializable(ITEMS); -// // Create an ArrayAdapter for items, with filtering capablity based on item containing a text -// _withContainingTextArrayFilterArrayAdapter = new WithContainingTextArrayFilterArrayAdapter(getActivity(), -// android.R.layout.simple_list_item_1, -// items); // Attach the adapter to the list _listView.setAdapter((ListAdapter) getParentAdapterView().getAdapter()); - _listView.setTextFilterEnabled(true); +// // Enable filtering based on search text field +// _listView.setTextFilterEnabled(false); + + // Simulate an empty search text field to build the full accounts list + onQueryTextChange(null); + // On item click listener _listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override @@ -235,7 +239,7 @@ public void onItemClick(AdapterView parent, int position, long id) { -// final Object accountFullName = _withContainingTextArrayFilterArrayAdapter.getItem(position); + // TODO TW C 2020-01-29 : A améliorer pour fonctionner aussi avec un ArrayAdapter final CursorAdapter cursorAdapter = (CursorAdapter) getParentAdapterView().getAdapter(); final Cursor cursor = (Cursor) cursorAdapter.getItem(position); final String accountFullName = cursor.getString(cursor.getColumnIndexOrThrow(DatabaseSchema.AccountEntry.COLUMN_FULL_NAME)); @@ -315,24 +319,88 @@ public boolean onQueryTextChange(String s) { // Filter item list // - final WithContainingTextArrayFilterArrayAdapter listViewAdapter = (WithContainingTextArrayFilterArrayAdapter) _listView.getAdapter(); + final QualifiedAccountNameCursorAdapter listViewCursorAdapter = (QualifiedAccountNameCursorAdapter) _listView.getAdapter(); + + // + // Set a filter that rebuild Cursor by running a new query based on a LIKE criteria + // + + // TODO TW C 2020-01-29 : A améliorer pour fonctionner aussi avec un ArrayAdapter + listViewCursorAdapter.setFilterQueryProvider(new FilterQueryProvider() { + + public Cursor runQuery(CharSequence constraint) { + + final AccountsDbAdapter accountsDbAdapter = AccountsDbAdapter.getInstance(); + + final Cursor accountsCursor = accountsDbAdapter.fetchAccountsOrderedByFavoriteAndFullName("(" + + DatabaseSchema.AccountEntry.COLUMN_FULL_NAME + + " LIKE ?" + + " AND " + + DatabaseSchema.AccountEntry.COLUMN_PLACEHOLDER + + " = 0" + + ")", + new String[]{"%" + + ((constraint + != null) + ? constraint.toString() + : "") + + "%"}); + return accountsCursor; + } + }); + + // + // Register a Listener to close dialog if there is only one item remaining in the filtered list, and select it + // automatically + // + + listViewCursorAdapter.registerDataSetObserver(new DataSetObserver() { + @Override + public void onChanged() { + + super.onChanged(); + + final Cursor accountsCursor = listViewCursorAdapter.getCursor(); + + if (accountsCursor.getCount() == 1) { + // only one account + + accountsCursor.moveToFirst(); + + // Simulate a onSearchableItemClicked + _onSearchableItemClickedListener.onSearchableItemClicked(accountsCursor.getString(accountsCursor.getColumnIndex(DatabaseSchema.AccountEntry.COLUMN_FULL_NAME)), + 1); + getDialog().dismiss(); + + } else { + // only one account n' pas + + // RAF + } + + } + }); + + // + // Start filtering thread + // if (TextUtils.isEmpty(s)) { - listViewAdapter.getFilter() - .filter(null); + listViewCursorAdapter.getFilter() + .filter(null); } else { - listViewAdapter.getFilter() - .filter(s); + listViewCursorAdapter.getFilter() + .filter(s); } // // Call Search Text Change Listener // - if (null != _onSearchTextChangedListener) { + if (_onSearchTextChangedListener != null) { // Call Listener _onSearchTextChangedListener.onSearchTextChanged(s); From dbe91e086acd8afe80abfb1364281b5677734cd1 Mon Sep 17 00:00:00 2001 From: JeanGarf Date: Wed, 29 Jan 2020 23:36:11 +0100 Subject: [PATCH 041/131] #110 - Renamings --- .../android/db/adapter/AccountsDbAdapter.java | 16 ++++++++++---- .../transaction/TransactionFormFragment.java | 22 +++++++++---------- 2 files changed, 23 insertions(+), 15 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 c924732dc..fe69c0c3f 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 @@ -759,11 +759,19 @@ public Cursor fetchAccountsOrderedByFullName(String where, String[] whereArgs) { * @param whereArgs where args * @return Cursor set of accounts which fulfill where */ - public Cursor fetchAccountsOrderedByFavoriteAndFullName(String where, String[] whereArgs) { - Log.v(LOG_TAG, "Fetching all accounts from db where " + where + " order by Favorite then Name"); + public Cursor fetchAccountsOrderedByFavoriteAndFullName(String where, + String[] whereArgs) { + + Log.v(LOG_TAG, + "Fetching all accounts from db where " + where + " order by Favorite then Name"); + return mDb.query(AccountEntry.TABLE_NAME, - null, where, whereArgs, null, null, - AccountEntry.COLUMN_FAVORITE + " DESC, " + AccountEntry.COLUMN_FULL_NAME + " ASC"); + null, + where, + whereArgs, + null, + null, + AccountEntry.COLUMN_FAVORITE + " DESC, " + AccountEntry.COLUMN_FULL_NAME + " ASC"); } /** 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 bd3d5a8ed..7de0fce15 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 @@ -181,7 +181,7 @@ public class TransactionFormFragment extends Fragment implements * Spinner for selecting the transfer account */ @BindView(R.id.input_transfer_account_spinner) - SearchableSpinnerView mTransferAccountSpinnerView; + SearchableSpinnerView mTransferAccountSearchableSpinnerView; /** * Checkbox indicating if this transaction should be saved as a template or not @@ -271,7 +271,7 @@ public void onClick(View v) { */ private void startTransferFunds() { Commodity fromCommodity = Commodity.getInstance((mTransactionsDbAdapter.getAccountCurrencyCode(mAccountUID))); - long id = mTransferAccountSpinnerView.getSelectedItemId(); + long id = mTransferAccountSearchableSpinnerView.getSelectedItemId(); String targetCurrencyCode = mAccountsDbAdapter.getCurrencyCode(mAccountsDbAdapter.getUID(id)); if (fromCommodity.equals(Commodity.getInstance(targetCurrencyCode)) @@ -323,7 +323,7 @@ public void onActivityCreated(Bundle savedInstanceState) { //updateTransferAccountsList must only be called after initializing mAccountsDbAdapter updateTransferAccountsList(); - mTransferAccountSpinnerView.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + mTransferAccountSearchableSpinnerView.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { /** * Flag for ignoring first call to this listener. * The first call is during layout, but we want it called only in response to user interaction @@ -367,8 +367,8 @@ public void onNothingSelected(AdapterView adapterView) { } }); - mTransferAccountSpinnerView.setTitle(getString(R.string.select_transfer_account)); - mTransferAccountSpinnerView.setPositiveButton(getString(R.string.alert_dialog_cancel)); + mTransferAccountSearchableSpinnerView.setTitle(getString(R.string.select_transfer_account)); + mTransferAccountSearchableSpinnerView.setPositiveButton(getString(R.string.alert_dialog_cancel)); ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar(); assert actionBar != null; @@ -611,7 +611,7 @@ private void updateTransferAccountsList(){ mAccountCursorAdapter = new QualifiedAccountNameCursorAdapter(getActivity(), mCursor); - mTransferAccountSpinnerView.setAdapter(mAccountCursorAdapter); + mTransferAccountSearchableSpinnerView.setAdapter(mAccountCursorAdapter); } /** @@ -709,7 +709,7 @@ public void onClick(View v) { private void setSelectedTransferAccount(long accountId){ int position = mAccountCursorAdapter.getPosition(mAccountsDbAdapter.getUID(accountId)); if (position >= 0) - mTransferAccountSpinnerView.setSelection(position); + mTransferAccountSearchableSpinnerView.setSelection(position); } /** @@ -781,7 +781,7 @@ private List extractSplitsFromView(){ private @NonNull String getTransferAccountUID() { String transferAcctUID; if (mUseDoubleEntry) { - long transferAcctId = mTransferAccountSpinnerView.getSelectedItemId(); + long transferAcctId = mTransferAccountSearchableSpinnerView.getSelectedItemId(); transferAcctUID = mAccountsDbAdapter.getUID(transferAcctId); } else { Commodity baseCommodity = mAccountsDbAdapter.getRecord(mAccountUID).getCommodity(); @@ -838,7 +838,7 @@ private boolean isMultiCurrencyTransaction(){ if (!mUseDoubleEntry) return false; - String transferAcctUID = mAccountsDbAdapter.getUID(mTransferAccountSpinnerView.getSelectedItemId()); + String transferAcctUID = mAccountsDbAdapter.getUID(mTransferAccountSearchableSpinnerView.getSelectedItemId()); String currencyCode = mAccountsDbAdapter.getAccountCurrencyCode(mAccountUID); String transferCurrencyCode = mAccountsDbAdapter.getCurrencyCode(transferAcctUID); @@ -964,7 +964,7 @@ public boolean onOptionsItemSelected(MenuItem item) { if (mAmountEditText.getValue() == null) { Toast.makeText(getActivity(), R.string.toast_transanction_amount_required, Toast.LENGTH_SHORT).show(); } - if (mUseDoubleEntry && mTransferAccountSpinnerView.getCount() == 0){ + if (mUseDoubleEntry && mTransferAccountSearchableSpinnerView.getCount() == 0){ Toast.makeText(getActivity(), R.string.toast_disable_double_entry_to_save_transaction, Toast.LENGTH_LONG).show(); @@ -984,7 +984,7 @@ public boolean onOptionsItemSelected(MenuItem item) { */ private boolean canSave(){ return (mUseDoubleEntry && mAmountEditText.isInputValid() - && mTransferAccountSpinnerView.getCount() > 0) + && mTransferAccountSearchableSpinnerView.getCount() > 0) || (!mUseDoubleEntry && mAmountEditText.isInputValid()); } From 48d738da168ec94b9d7e00acec71bcee7a82f7c6 Mon Sep 17 00:00:00 2001 From: JeanGarf Date: Thu, 30 Jan 2020 00:18:43 +0100 Subject: [PATCH 042/131] #110 - Do not hide keyboard --- .../SearchableListDialogFragment.java | 27 ++++++++++++++----- .../SearchableSpinnerView.java | 17 ------------ 2 files changed, 21 insertions(+), 23 deletions(-) diff --git a/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableListDialogFragment.java b/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableListDialogFragment.java index f6ccf6c55..302cea283 100644 --- a/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableListDialogFragment.java +++ b/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableListDialogFragment.java @@ -14,7 +14,6 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.view.WindowManager; import android.view.inputmethod.InputMethodManager; import android.widget.AdapterView; import android.widget.FilterQueryProvider; @@ -124,8 +123,9 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - getDialog().getWindow() - .setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN); + // Hide Keyboard +// getDialog().getWindow() +// .setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN); return super.onCreateView(inflater, container, @@ -191,8 +191,7 @@ public Dialog onCreateDialog(Bundle savedInstanceState) { final AlertDialog searchableListDialog = alertDialogBuilder.create(); // Hide Soft Keybord -// hideKeyboard(_searchTextEditView); - hideKeyboard(searchableListRootView); +// hideKeyboard(searchableListRootView); return searchableListDialog; } @@ -211,7 +210,21 @@ private void configureView(View searchableListRootView) { _searchTextEditView.setIconifiedByDefault(false); _searchTextEditView.setOnQueryTextListener(this); _searchTextEditView.setOnCloseListener(this); - _searchTextEditView.clearFocus(); + + // TODO TW C 2020-01-30 : Add a Preference to choose + if (true) { + // Want to open keyboard + + // Set Focus on searchTextEditView to open cursor + _searchTextEditView.setFocusable(true); + _searchTextEditView.requestFocus(); + + } else { + // Do not want to open keyboard + + // Clear Focus + _searchTextEditView.clearFocus(); + } // // Items list @@ -250,6 +263,7 @@ public void onItemClick(AdapterView parent, getDialog().dismiss(); } }); + } // Crash on orientation change #7 @@ -432,6 +446,7 @@ public void run() { 200); } + public AdapterView getParentAdapterView() { return _parentAdapterView; diff --git a/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableSpinnerView.java b/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableSpinnerView.java index 062a12ccb..53c1a9e95 100644 --- a/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableSpinnerView.java +++ b/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableSpinnerView.java @@ -285,21 +285,4 @@ public void run() { 200); } - private void hideKeyboard(final EditText ettext) { - - ettext.requestFocus(); - ettext.postDelayed(new Runnable() { - @Override - public void run() { - - InputMethodManager keyboard = - (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE); - - keyboard.hideSoftInputFromWindow(ettext.getWindowToken(), - 0); - } - }, - 200); - } - } From f7c517e938e5ec02d86ac20673c3c6b10ebb4543 Mon Sep 17 00:00:00 2001 From: JeanGarf Date: Thu, 30 Jan 2020 01:11:55 +0100 Subject: [PATCH 043/131] #110 - Use account_spinner_dropdown_item for DropDown items --- .../SearchableListDialogFragment.java | 22 ++++++++++++------- .../layout/account_spinner_dropdown_item.xml | 7 +++--- .../transaction_account_spinner_item.xml | 4 ++-- 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableListDialogFragment.java b/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableListDialogFragment.java index 302cea283..5d9fc4b74 100644 --- a/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableListDialogFragment.java +++ b/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableListDialogFragment.java @@ -95,6 +95,7 @@ public SearchableListDialogFragment() { * * @return */ + // TODO TW C 2020-01-30 : Supprimer items public static SearchableListDialogFragment makeInstance(AdapterView parentAdapterView, List items) { SearchableListDialogFragment searchableListDialogFragment = new SearchableListDialogFragment(); @@ -232,10 +233,15 @@ private void configureView(View searchableListRootView) { _listView = (ListView) searchableListRootView.findViewById(R.id.listItems); + // TODO TW C 2020-01-30 : A supprimer List items = (List) getArguments().getSerializable(ITEMS); // Attach the adapter to the list - _listView.setAdapter((ListAdapter) getParentAdapterView().getAdapter()); + // TODO TW C 2020-01-30 : A nettoyer +// _listView.setAdapter((ListAdapter) getParentAdapterView().getAdapter()); + _listView.setAdapter((ListAdapter) new QualifiedAccountNameCursorAdapter(getActivity(), + null, + R.layout.account_spinner_dropdown_item)); // // Enable filtering based on search text field // _listView.setTextFilterEnabled(false); @@ -423,12 +429,12 @@ public void onChanged() { return true; } - private void hideKeyboard(final View ettext) { + public static void hideKeyboard(final View editTextView) { - ettext.requestFocus(); + editTextView.requestFocus(); // Delay the keyboard hiding - ettext.postDelayed(new Runnable() { + editTextView.postDelayed(new Runnable() { @Override public void run() { @@ -436,14 +442,14 @@ public void run() { // Hide keyboard // - InputMethodManager keyboard = (InputMethodManager) ettext.getContext() - .getSystemService(Context.INPUT_METHOD_SERVICE); + InputMethodManager keyboard = (InputMethodManager) editTextView.getContext() + .getSystemService(Context.INPUT_METHOD_SERVICE); - keyboard.hideSoftInputFromWindow(ettext.getWindowToken(), + keyboard.hideSoftInputFromWindow(editTextView.getWindowToken(), 0); } }, - 200); + 200); } 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 9116e1baf..09af55502 100644 --- a/app/src/main/res/layout/account_spinner_dropdown_item.xml +++ b/app/src/main/res/layout/account_spinner_dropdown_item.xml @@ -16,9 +16,10 @@ --> \ No newline at end of file diff --git a/app/src/main/res/layout/transaction_account_spinner_item.xml b/app/src/main/res/layout/transaction_account_spinner_item.xml index a0119acc3..e8546d41f 100644 --- a/app/src/main/res/layout/transaction_account_spinner_item.xml +++ b/app/src/main/res/layout/transaction_account_spinner_item.xml @@ -17,9 +17,9 @@ \ No newline at end of file From a49b55f5c404eed16be016fa350a1b1fe408efd0 Mon Sep 17 00:00:00 2001 From: JeanGarf Date: Sat, 1 Feb 2020 16:39:16 +0100 Subject: [PATCH 044/131] #110 - Handle keyboard hiding and add colors to debug --- .../transaction/TransactionFormFragment.java | 24 +++-- .../ui/transaction/TransactionsActivity.java | 23 +++-- .../SearchableListDialogFragment.java | 93 ++++++++++++------- .../QualifiedAccountNameCursorAdapter.java | 55 ++++++++--- .../layout/account_spinner_dropdown_item.xml | 3 +- .../transaction_account_spinner_item.xml | 1 + 6 files changed, 138 insertions(+), 61 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 7de0fce15..2c2d6ae34 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 @@ -252,19 +252,28 @@ public class TransactionFormFragment extends Fragment implements * Create the view and retrieve references to the UI elements */ @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - View v = inflater.inflate(R.layout.fragment_transaction_form, container, false); - ButterKnife.bind(this, v); + public View onCreateView(LayoutInflater inflater, + ViewGroup container, + Bundle savedInstanceState) { + + View v = inflater.inflate(R.layout.fragment_transaction_form, + container, + false); + + ButterKnife.bind(this, + v); + mAmountEditText.bindListeners(mKeyboardView); + mOpenSplitEditor.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { + openSplitEditor(); } }); return v; - } + } /** * Starts the transfer of funds from one currency to another @@ -609,7 +618,10 @@ private void updateTransferAccountsList(){ AccountType.ROOT.name()}); mAccountCursorAdapter = new QualifiedAccountNameCursorAdapter(getActivity(), - mCursor); + mCursor, +// R.layout.account_spinner_dropdown_item); +// android.R.layout.simple_spinner_item); + R.layout.transaction_account_spinner_item); mTransferAccountSearchableSpinnerView.setAdapter(mAccountCursorAdapter); } 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 8f37c7409..0168a9cd6 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 @@ -120,7 +120,7 @@ public class TransactionsActivity extends BaseDrawerActivity implements private SparseArray mFragmentPageReferenceMap = new SparseArray<>(); /** - * Flag for determining is the currently displayed account is a placeholder account or not. + * Flag for determining if the currently displayed account is a placeholder account or not. * This will determine if the transactions tab is displayed or not */ private boolean mIsPlaceholderAccount; @@ -378,14 +378,17 @@ private void setupActionBarNavigation() { if (mAccountsCursor != null) { mAccountsCursor.close(); } - mAccountsCursor = mAccountsDbAdapter.fetchAllRecordsOrderedByFullName(); +// mAccountsCursor = mAccountsDbAdapter.fetchAllRecordsOrderedByFullName(); + mAccountsCursor = mAccountsDbAdapter.fetchAccountsOrderedByFavoriteAndFullName(null,null); SpinnerAdapter qualifiedAccountNameCursorAdapter = new QualifiedAccountNameCursorAdapter(getSupportActionBar().getThemedContext(), mAccountsCursor, R.layout.transaction_account_spinner_item); mToolbarSpinner.setAdapter(qualifiedAccountNameCursorAdapter); + mToolbarSpinner.setOnItemSelectedListener(mTransactionListNavigationListener); + getSupportActionBar().setDisplayHomeAsUpEnabled(true); updateNavigationSelection(); @@ -395,12 +398,18 @@ private void setupActionBarNavigation() { * Updates the action bar navigation list selection to that of the current account * whose transactions are being displayed/manipulated */ - public void updateNavigationSelection() { - // set the selected item in the spinner - int i = 0; - Cursor accountsCursor = mAccountsDbAdapter.fetchAllRecordsOrderedByFullName(); + public void updateNavigationSelection() { + // set the selected item in the spinner + int i = 0; + +// Cursor accountsCursor = mAccountsDbAdapter.fetchAllRecordsOrderedByFullName(); + Cursor accountsCursor = mAccountsDbAdapter.fetchAccountsOrderedByFavoriteAndFullName(null, + null); + while (accountsCursor.moveToNext()) { + String uid = accountsCursor.getString(accountsCursor.getColumnIndexOrThrow(DatabaseSchema.AccountEntry.COLUMN_UID)); + if (mAccountUID.equals(uid)) { mToolbarSpinner.setSelection(i); break; @@ -408,7 +417,7 @@ public void updateNavigationSelection() { ++i; } accountsCursor.close(); - } + } @Override public boolean onPrepareOptionsMenu(Menu menu) { diff --git a/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableListDialogFragment.java b/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableListDialogFragment.java index 5d9fc4b74..95bedde9a 100644 --- a/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableListDialogFragment.java +++ b/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableListDialogFragment.java @@ -27,7 +27,6 @@ import org.gnucash.android.util.QualifiedAccountNameCursorAdapter; import java.io.Serializable; -import java.util.List; /** * Pop-up that display a ListView with a search text field @@ -57,7 +56,7 @@ public interface OnSearchTextChangedListener { } - private static final String ITEMS = "items"; +// private static final String ITEMS = "items"; // Dialog Title private String _strTitle; @@ -91,23 +90,23 @@ public SearchableListDialogFragment() { /** * Factory * - * @param items +// * @param items * * @return */ // TODO TW C 2020-01-30 : Supprimer items - public static SearchableListDialogFragment makeInstance(AdapterView parentAdapterView, List items) { + public static SearchableListDialogFragment makeInstance(AdapterView parentAdapterView) { SearchableListDialogFragment searchableListDialogFragment = new SearchableListDialogFragment(); searchableListDialogFragment.setParentAdapterView(parentAdapterView); - Bundle args = new Bundle(); - - args.putSerializable(ITEMS, - (Serializable) items); - - searchableListDialogFragment.setArguments(args); +// Bundle args = new Bundle(); +// +// args.putSerializable(ITEMS, +// (Serializable) items); +// +// searchableListDialogFragment.setArguments(args); return searchableListDialogFragment; } @@ -125,6 +124,7 @@ public View onCreateView(LayoutInflater inflater, Bundle savedInstanceState) { // Hide Keyboard +// hideKeyboard(); // getDialog().getWindow() // .setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN); @@ -182,6 +182,7 @@ public Dialog onCreateDialog(Bundle savedInstanceState) { ? "CLOSE" : _strPositiveButtonText; + // TODO TW C 2020-02-01 : Negative button alertDialogBuilder.setPositiveButton(strPositiveButton, _onPositiveBtnClickListener); @@ -208,7 +209,7 @@ private void configureView(View searchableListRootView) { _searchTextEditView = (SearchView) searchableListRootView.findViewById(R.id.search); _searchTextEditView.setSearchableInfo(searchManager.getSearchableInfo(getActivity().getComponentName())); - _searchTextEditView.setIconifiedByDefault(false); +// _searchTextEditView.setIconifiedByDefault(false); // Déjà fait dans le xml _searchTextEditView.setOnQueryTextListener(this); _searchTextEditView.setOnCloseListener(this); @@ -234,14 +235,23 @@ private void configureView(View searchableListRootView) { _listView = (ListView) searchableListRootView.findViewById(R.id.listItems); // TODO TW C 2020-01-30 : A supprimer - List items = (List) getArguments().getSerializable(ITEMS); +// List items = (List) getArguments().getSerializable(ITEMS); // Attach the adapter to the list // TODO TW C 2020-01-30 : A nettoyer -// _listView.setAdapter((ListAdapter) getParentAdapterView().getAdapter()); - _listView.setAdapter((ListAdapter) new QualifiedAccountNameCursorAdapter(getActivity(), - null, - R.layout.account_spinner_dropdown_item)); + _listView.setAdapter((ListAdapter) getParentAdapterView().getAdapter()); +// QualifiedAccountNameCursorAdapter parentCursorAdapter = +// (QualifiedAccountNameCursorAdapter) getParentAdapterView().getAdapter(); +// +// parentCursorAdapter.getCursor().moveToFirst(); +// +// _listView.setAdapter((ListAdapter) new QualifiedAccountNameCursorAdapter(getActivity(), +// parentCursorAdapter.getCursor(), +// parentCursorAdapter.getDropDownItemLayout(), +// // ListView utilise uniquement le Layout +// // ci-dessus pour les items +// parentCursorAdapter.getDropDownItemLayout() // +// )); // // Enable filtering based on search text field // _listView.setTextFilterEnabled(false); @@ -266,7 +276,8 @@ public void onItemClick(AdapterView parent, // Call Listener _onSearchableItemClickedListener.onSearchableItemClicked(accountFullName, position); - getDialog().dismiss(); + + dismissDialog(); } }); @@ -390,7 +401,8 @@ public void onChanged() { // Simulate a onSearchableItemClicked _onSearchableItemClickedListener.onSearchableItemClicked(accountsCursor.getString(accountsCursor.getColumnIndex(DatabaseSchema.AccountEntry.COLUMN_FULL_NAME)), 1); - getDialog().dismiss(); + + dismissDialog(); } else { // only one account n' pas @@ -429,27 +441,42 @@ public void onChanged() { return true; } + protected void dismissDialog() { + + hideKeyboard(_searchTextEditView); + + getDialog().dismiss(); + } + + // TODO TW C 2020-02-01 : A déplacer dans une classe utilitaire public static void hideKeyboard(final View editTextView) { - editTextView.requestFocus(); + // + // Hide keyboard + // - // Delay the keyboard hiding - editTextView.postDelayed(new Runnable() { - @Override - public void run() { + InputMethodManager keyboard = (InputMethodManager) editTextView.getContext() + .getSystemService(Context.INPUT_METHOD_SERVICE); - // - // Hide keyboard - // + keyboard.hideSoftInputFromWindow(editTextView.getWindowToken(), + 0); + } + + public static void hideKeyboard(final View editTextView, + final long delay) { - InputMethodManager keyboard = (InputMethodManager) editTextView.getContext() - .getSystemService(Context.INPUT_METHOD_SERVICE); +// editTextView.requestFocus(); - keyboard.hideSoftInputFromWindow(editTextView.getWindowToken(), - 0); - } - }, - 200); + // Delay the keyboard hiding + editTextView.postDelayed(new Runnable() { + @Override + public void run() { + + // Hide keyboard + hideKeyboard(editTextView); + } + }, + delay); } diff --git a/app/src/main/java/org/gnucash/android/util/QualifiedAccountNameCursorAdapter.java b/app/src/main/java/org/gnucash/android/util/QualifiedAccountNameCursorAdapter.java index 53839e458..39e57c145 100644 --- a/app/src/main/java/org/gnucash/android/util/QualifiedAccountNameCursorAdapter.java +++ b/app/src/main/java/org/gnucash/android/util/QualifiedAccountNameCursorAdapter.java @@ -37,6 +37,9 @@ public class QualifiedAccountNameCursorAdapter extends SimpleCursorAdapter { + // + private int _DropDownItemLayout; + /** * Initialize the Cursor adapter for account names using default spinner views * @@ -52,16 +55,27 @@ public QualifiedAccountNameCursorAdapter(Context context, cursor, android.R.layout.simple_spinner_item // Layout of the closed spinner item ); + } -// super(context, -// android.R.layout.simple_spinner_item, // Layout of the closed spinner item -// cursor, -// new String[]{DatabaseSchema.AccountEntry.COLUMN_FULL_NAME}, -// new int[]{android.R.id.text1}, -// 0); -// -// // Define layout of each item in the open drop down of the spinner -// setDropDownViewResource(R.layout.account_spinner_dropdown_item); + /** + * Overloaded constructor. Specifies the view to use for displaying selected spinner text + * + * @param context + * Application context + * @param cursor + * Cursor to account data + * @param selectedSpinnerItemLayout + * Layout resource for selected item text + */ + public QualifiedAccountNameCursorAdapter(Context context, + Cursor cursor, + @LayoutRes int selectedSpinnerItemLayout) { + + this(context, + cursor, + selectedSpinnerItemLayout, // Layout of the closed spinner item + R.layout.account_spinner_dropdown_item + ); } /** @@ -71,22 +85,24 @@ public QualifiedAccountNameCursorAdapter(Context context, * Application context * @param cursor * Cursor to account data - * @param selectedSpinnerItem + * @param selectedSpinnerItemLayout * Layout resource for selected item text */ public QualifiedAccountNameCursorAdapter(Context context, Cursor cursor, - @LayoutRes int selectedSpinnerItem) { + @LayoutRes int selectedSpinnerItemLayout, + @LayoutRes int dropdownSpinnerItemLayout + ) { super(context, - selectedSpinnerItem, // Layout of the closed spinner item + selectedSpinnerItemLayout, // Layout of the closed spinner item cursor, new String[]{DatabaseSchema.AccountEntry.COLUMN_FULL_NAME}, new int[]{android.R.id.text1}, 0); // Define layout of each item in the open drop down of the spinner - setDropDownViewResource(R.layout.account_spinner_dropdown_item); + setDropDownItemLayout(dropdownSpinnerItemLayout); } @Override @@ -138,4 +154,17 @@ public int getPosition(@NonNull String accountUID) { return -1; } + + public int getDropDownItemLayout() { + + return _DropDownItemLayout; + } + + public void setDropDownItemLayout(int dropDownItemLayout) { + + _DropDownItemLayout = dropDownItemLayout; + + setDropDownViewResource(getDropDownItemLayout()); + } + } 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 09af55502..d9bf77f30 100644 --- a/app/src/main/res/layout/account_spinner_dropdown_item.xml +++ b/app/src/main/res/layout/account_spinner_dropdown_item.xml @@ -19,7 +19,6 @@ android:layout_width="match_parent" android:layout_height="?attr/dropdownListPreferredItemHeight" style="?android:attr/spinnerDropDownItemStyle" - android:textColor="@android:color/black" - android:textSize="14sp" + android:textColor="@android:color/holo_green_dark" android:ellipsize="start" android:singleLine="true" /> \ No newline at end of file diff --git a/app/src/main/res/layout/transaction_account_spinner_item.xml b/app/src/main/res/layout/transaction_account_spinner_item.xml index e8546d41f..f9626e8cd 100644 --- a/app/src/main/res/layout/transaction_account_spinner_item.xml +++ b/app/src/main/res/layout/transaction_account_spinner_item.xml @@ -19,6 +19,7 @@ style="?android:attr/spinnerItemStyle" android:layout_width="match_parent" android:layout_height="wrap_content" + android:textColor="@android:color/holo_red_dark" android:textSize="18sp" android:singleLine="true" android:ellipsize="start" From 1bf4837a1621335c96947e5ed3b903df65fd6c33 Mon Sep 17 00:00:00 2001 From: JeanGarf Date: Sat, 1 Feb 2020 17:57:13 +0100 Subject: [PATCH 045/131] #110 - Comment unused code --- .../SearchableSpinnerView.java | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableSpinnerView.java b/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableSpinnerView.java index 53c1a9e95..0937ca614 100644 --- a/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableSpinnerView.java +++ b/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableSpinnerView.java @@ -26,11 +26,10 @@ public class SearchableSpinnerView public static final int NO_ITEM_SELECTED = -1; - // TODO TW C 2020-01-17 : a remplacer par getContext() - private Context _context; +// private Context _context; - private List _items; - private List _allItems; +// private List _items; +// private List _allItems; private SearchableListDialogFragment _searchableListDialogFragment; @@ -40,12 +39,12 @@ public class SearchableSpinnerView // private CursorAdapter _cursorAdapter; private String _strHintText; - private boolean _isFromInit; +// private boolean _isFromInit; public SearchableSpinnerView(Context context) { super(context); - this._context = context; +// this._context = context; init(); } @@ -55,7 +54,7 @@ public SearchableSpinnerView(Context context, super(context, attrs); - this._context = context; +// this._context = context; TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SearchableSpinnerView); @@ -78,19 +77,20 @@ public SearchableSpinnerView(Context context, attrs, defStyleAttr); - this._context = context; +// this._context = context; init(); } private void init() { - _allItems=new ArrayList(); - _items = new ArrayList(); + // TODO TW C 2020-02-01 : A enlever +// _allItems=new ArrayList(); +// _items = new ArrayList(); // Create Dialog instance // TODO TW C 2020-01-25 : Supprimer _items - _searchableListDialogFragment = SearchableListDialogFragment.makeInstance(this, _items); + _searchableListDialogFragment = SearchableListDialogFragment.makeInstance(this); // S'abonner aux clicks sur un item _searchableListDialogFragment.setOnSearchableItemClickListener(this); @@ -155,7 +155,7 @@ public boolean onTouch(View v, // _allItems.addAll(_items); // Display SearchableListDialogFragment - _searchableListDialogFragment.show(scanForActivity(_context).getFragmentManager(), + _searchableListDialogFragment.show(scanForActivity(getContext()).getFragmentManager(), "TAG"); // } } @@ -186,6 +186,7 @@ public void onSearchableItemClicked(Object item, @Override public void setAdapter(SpinnerAdapter adapter) { + // Use given adapter for spinner item (not drop down) super.setAdapter(adapter); // _cursorAdapter = (CursorAdapter) adapter; From 86a2720f75d7a2326934f32cc05450ef375bc68c Mon Sep 17 00:00:00 2001 From: JeanGarf Date: Sat, 1 Feb 2020 21:44:38 +0100 Subject: [PATCH 046/131] #110 - Commit WithContainingTextArrayFilterArrayAdapter before deletion --- ...ContainingTextArrayFilterArrayAdapter.java | 21 +++++-------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/WithContainingTextArrayFilterArrayAdapter.java b/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/WithContainingTextArrayFilterArrayAdapter.java index 873e5a0f0..85c91bb61 100644 --- a/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/WithContainingTextArrayFilterArrayAdapter.java +++ b/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/WithContainingTextArrayFilterArrayAdapter.java @@ -11,6 +11,11 @@ import java.util.ArrayList; import java.util.List; +/** + * ArrayAdapter to filter Array items that contains the text to search + * + * @param + */ public class WithContainingTextArrayFilterArrayAdapter extends ArrayAdapter { @@ -154,7 +159,6 @@ public View getView(int position, ViewGroup parent) { // TODO TW C 2020-01-25 : Optimiser en utilisant un ViewHolder - View itemView = super.getView(position, convertView, parent); @@ -162,21 +166,6 @@ public View getView(int position, // item text TextView text1 = (TextView) itemView.findViewById(android.R.id.text1); - // TODO TW C 2020-01-19 : Handle favorite star -// Integer isFavorite = cursor.getInt(cursor.getColumnIndex(DatabaseSchema.AccountEntry.COLUMN_FAVORITE)); -// -// if (isFavorite == 0) { -// text1.setCompoundDrawablesWithIntrinsicBounds(0, -// 0, -// 0, -// 0); -// } else { -// text1.setCompoundDrawablesWithIntrinsicBounds(0, -// 0, -// R.drawable.ic_star_black_18dp, -// 0); -// } - return itemView; } } From ecc99b6db79a529aafcd6f3c32a9614ef9cef1bb Mon Sep 17 00:00:00 2001 From: JeanGarf Date: Sat, 1 Feb 2020 21:47:56 +0100 Subject: [PATCH 047/131] #110 - Delete WithContainingTextArrayFilterArrayAdapter --- ...ContainingTextArrayFilterArrayAdapter.java | 171 ------------------ 1 file changed, 171 deletions(-) delete mode 100644 app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/WithContainingTextArrayFilterArrayAdapter.java diff --git a/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/WithContainingTextArrayFilterArrayAdapter.java b/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/WithContainingTextArrayFilterArrayAdapter.java deleted file mode 100644 index 85c91bb61..000000000 --- a/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/WithContainingTextArrayFilterArrayAdapter.java +++ /dev/null @@ -1,171 +0,0 @@ -package org.gnucash.android.ui.util.widget.searchablespinner; - -import android.app.Activity; -import android.support.annotation.NonNull; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ArrayAdapter; -import android.widget.Filter; -import android.widget.TextView; - -import java.util.ArrayList; -import java.util.List; - -/** - * ArrayAdapter to filter Array items that contains the text to search - * - * @param - */ -public class WithContainingTextArrayFilterArrayAdapter - extends ArrayAdapter { - - /** - * Array item filter which keeps only items containing a text - */ - private class ItemContaingTextArrayFilter - extends Filter { - - @Override - protected FilterResults performFiltering(CharSequence textToSearch) { - - final FilterResults filteredItems = new FilterResults(); - - // Get copy of all items - final ArrayList allItems = getCopyOfAllItems(); - - if (textToSearch == null || textToSearch.length() == 0) { - // Nothing to search - - filteredItems.values = allItems; - filteredItems.count = allItems.size(); - - } else { - // There is something to search - - final String textToSearchLowerCase = textToSearch.toString() - .toLowerCase(); - - // - // Filter items - // - - final ArrayList tmpFilteredItems = new ArrayList<>(); - - final int count = allItems.size(); - - for (int i = 0; i < count; i++) { - - final T item = allItems.get(i); - - final String itemTextLowerCase = item.toString() - .toLowerCase(); - - // First match against the whole, non-splitted value - if (itemTextLowerCase.contains(textToSearchLowerCase)) { - // It matches - - tmpFilteredItems.add(item); - - } else { - // It doesen't match - - // NTD - } - } - - filteredItems.values = tmpFilteredItems; - filteredItems.count = tmpFilteredItems.size(); - } - - return filteredItems; - } - - @Override - protected void publishResults(CharSequence constraint, - FilterResults filteredItems) { - - // Replace items in ArrayAdapter with filtered ones - clear(); - addAll((List) filteredItems.values); - - if (filteredItems.count > 0) { - notifyDataSetChanged(); - } else { - notifyDataSetInvalidated(); - } - } - - private ArrayList getCopyOfAllItems() { - - final ArrayList allItemsCopy; - - synchronized (_lock) { - allItemsCopy = new ArrayList<>(_allItems); - } - return allItemsCopy; - } - - } - - /** - * Array item filter which keeps only items containing a text - */ - private final ItemContaingTextArrayFilter _itemContaingTextArrayFilter = new ItemContaingTextArrayFilter(); - - /** - * Lock used to modify the content of {@link #_allItems}. Any write operation - * performed on the array should be synchronized on this lock. This lock is also - * used by the filter (see {@link #getFilter()} to make a synchronized copy of - * the original array of data. - */ - private final Object _lock = new Object(); - - /** - * Copy of all items before filtering - */ - private List _allItems; - - /** - * Constructor - * - * @param context - * @param itemView - * @param items - */ - public WithContainingTextArrayFilterArrayAdapter(Activity context, - int itemView, - List items) { - - super(context, - itemView, - items); - - // save all the items (before filtering) - this._allItems = new ArrayList(); - this._allItems.addAll(items); - } - - @NonNull - @Override - public Filter getFilter() { - - return _itemContaingTextArrayFilter; - } - - @NonNull - @Override - public View getView(int position, - View convertView, - ViewGroup parent) { - - // TODO TW C 2020-01-25 : Optimiser en utilisant un ViewHolder - View itemView = super.getView(position, - convertView, - parent); - - // item text - TextView text1 = (TextView) itemView.findViewById(android.R.id.text1); - - return itemView; - } -} From 2491a606fdcfc2c6518f624d6b137b15c6132923 Mon Sep 17 00:00:00 2001 From: JeanGarf Date: Sat, 1 Feb 2020 22:07:18 +0100 Subject: [PATCH 048/131] #110 - Cleaning code --- .../transaction/TransactionFormFragment.java | 1 + .../SearchableListDialogFragment.java | 96 +++++---------- .../SearchableSpinnerView.java | 111 +----------------- .../gnucash/android/util/KeyboardUtils.java | 54 +++++++++ .../QualifiedAccountNameCursorAdapter.java | 74 +++++++----- .../main/res/layout/fragment_account_form.xml | 8 +- .../res/layout/fragment_transaction_form.xml | 3 +- 7 files changed, 143 insertions(+), 204 deletions(-) create mode 100644 app/src/main/java/org/gnucash/android/util/KeyboardUtils.java 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 2c2d6ae34..246591cb3 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 @@ -82,6 +82,7 @@ import org.gnucash.android.ui.util.widget.CalculatorEditText; import org.gnucash.android.ui.util.widget.TransactionTypeSwitch; import org.gnucash.android.ui.util.widget.searchablespinner.SearchableSpinnerView; +import org.gnucash.android.util.KeyboardUtils; import org.gnucash.android.util.QualifiedAccountNameCursorAdapter; import java.math.BigDecimal; diff --git a/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableListDialogFragment.java b/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableListDialogFragment.java index 95bedde9a..b6333bf80 100644 --- a/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableListDialogFragment.java +++ b/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableListDialogFragment.java @@ -24,6 +24,7 @@ import org.gnucash.android.R; import org.gnucash.android.db.DatabaseSchema; import org.gnucash.android.db.adapter.AccountsDbAdapter; +import org.gnucash.android.util.KeyboardUtils; import org.gnucash.android.util.QualifiedAccountNameCursorAdapter; import java.io.Serializable; @@ -55,15 +56,9 @@ public interface OnSearchTextChangedListener { void onSearchTextChanged(String strText); } - -// private static final String ITEMS = "items"; - // Dialog Title private String _strTitle; - // Parent View - private AdapterView _parentAdapterView; - // Search Edit text zone private SearchView _searchTextEditView; @@ -79,6 +74,9 @@ public interface OnSearchTextChangedListener { private DialogInterface.OnClickListener _onPositiveBtnClickListener; + // Parent View + private AdapterView _parentAdapterView; + /** * Constructor @@ -90,24 +88,15 @@ public SearchableListDialogFragment() { /** * Factory * -// * @param items - * * @return */ - // TODO TW C 2020-01-30 : Supprimer items public static SearchableListDialogFragment makeInstance(AdapterView parentAdapterView) { SearchableListDialogFragment searchableListDialogFragment = new SearchableListDialogFragment(); + // Store a link to the Parent SearchableSpinnerView which holds the CursorAdapter searchableListDialogFragment.setParentAdapterView(parentAdapterView); -// Bundle args = new Bundle(); -// -// args.putSerializable(ITEMS, -// (Serializable) items); -// -// searchableListDialogFragment.setArguments(args); - return searchableListDialogFragment; } @@ -192,9 +181,6 @@ public Dialog onCreateDialog(Bundle savedInstanceState) { final AlertDialog searchableListDialog = alertDialogBuilder.create(); - // Hide Soft Keybord -// hideKeyboard(searchableListRootView); - return searchableListDialog; } @@ -209,7 +195,7 @@ private void configureView(View searchableListRootView) { _searchTextEditView = (SearchView) searchableListRootView.findViewById(R.id.search); _searchTextEditView.setSearchableInfo(searchManager.getSearchableInfo(getActivity().getComponentName())); -// _searchTextEditView.setIconifiedByDefault(false); // Déjà fait dans le xml +// _searchTextEditView.setIconifiedByDefault(false); // Already done in xml _searchTextEditView.setOnQueryTextListener(this); _searchTextEditView.setOnCloseListener(this); @@ -234,23 +220,29 @@ private void configureView(View searchableListRootView) { _listView = (ListView) searchableListRootView.findViewById(R.id.listItems); - // TODO TW C 2020-01-30 : A supprimer -// List items = (List) getArguments().getSerializable(ITEMS); + + // + // Put temporarily DropDownItemLayout in selectedItemView, + // because ListView use only selectedItemView for list item + // (this is only a workaround because the setAdapter below does not work) + // + + QualifiedAccountNameCursorAdapter parentCursorAdapter = + (QualifiedAccountNameCursorAdapter) getParentAdapterView().getAdapter(); + + parentCursorAdapter.setViewResource(parentCursorAdapter.getSpinnerDropDownItemLayout()); + // Attach the adapter to the list - // TODO TW C 2020-01-30 : A nettoyer - _listView.setAdapter((ListAdapter) getParentAdapterView().getAdapter()); -// QualifiedAccountNameCursorAdapter parentCursorAdapter = -// (QualifiedAccountNameCursorAdapter) getParentAdapterView().getAdapter(); -// -// parentCursorAdapter.getCursor().moveToFirst(); -// + _listView.setAdapter((ListAdapter) parentCursorAdapter); + + // This does not work // _listView.setAdapter((ListAdapter) new QualifiedAccountNameCursorAdapter(getActivity(), // parentCursorAdapter.getCursor(), -// parentCursorAdapter.getDropDownItemLayout(), +// parentCursorAdapter.getSpinnerDropDownItemLayout(), // // ListView utilise uniquement le Layout // // ci-dessus pour les items -// parentCursorAdapter.getDropDownItemLayout() // +// parentCursorAdapter.getSpinnerDropDownItemLayout() // // )); // // Enable filtering based on search text field @@ -268,9 +260,8 @@ public void onItemClick(AdapterView parent, int position, long id) { - // TODO TW C 2020-01-29 : A améliorer pour fonctionner aussi avec un ArrayAdapter - final CursorAdapter cursorAdapter = (CursorAdapter) getParentAdapterView().getAdapter(); - final Cursor cursor = (Cursor) cursorAdapter.getItem(position); + final CursorAdapter parentCursorAdapter = (CursorAdapter) getParentAdapterView().getAdapter(); + final Cursor cursor = (Cursor) parentCursorAdapter.getItem(position); final String accountFullName = cursor.getString(cursor.getColumnIndexOrThrow(DatabaseSchema.AccountEntry.COLUMN_FULL_NAME)); // Call Listener @@ -280,7 +271,6 @@ public void onItemClick(AdapterView parent, dismissDialog(); } }); - } // Crash on orientation change #7 @@ -356,7 +346,6 @@ public boolean onQueryTextChange(String s) { // Set a filter that rebuild Cursor by running a new query based on a LIKE criteria // - // TODO TW C 2020-01-29 : A améliorer pour fonctionner aussi avec un ArrayAdapter listViewCursorAdapter.setFilterQueryProvider(new FilterQueryProvider() { public Cursor runQuery(CharSequence constraint) { @@ -386,6 +375,7 @@ public Cursor runQuery(CharSequence constraint) { // listViewCursorAdapter.registerDataSetObserver(new DataSetObserver() { + @Override public void onChanged() { @@ -443,43 +433,21 @@ public void onChanged() { protected void dismissDialog() { - hideKeyboard(_searchTextEditView); - - getDialog().dismiss(); - } - - // TODO TW C 2020-02-01 : A déplacer dans une classe utilitaire - public static void hideKeyboard(final View editTextView) { - // - // Hide keyboard + // Restore original Spinner Selected Item Layout // - InputMethodManager keyboard = (InputMethodManager) editTextView.getContext() - .getSystemService(Context.INPUT_METHOD_SERVICE); + QualifiedAccountNameCursorAdapter parentCursorAdapter = + (QualifiedAccountNameCursorAdapter) getParentAdapterView().getAdapter(); - keyboard.hideSoftInputFromWindow(editTextView.getWindowToken(), - 0); - } + parentCursorAdapter.setViewResource(parentCursorAdapter.getSpinnerSelectedItemLayout()); - public static void hideKeyboard(final View editTextView, - final long delay) { -// editTextView.requestFocus(); + KeyboardUtils.hideKeyboard(_searchTextEditView); - // Delay the keyboard hiding - editTextView.postDelayed(new Runnable() { - @Override - public void run() { - - // Hide keyboard - hideKeyboard(editTextView); - } - }, - delay); + getDialog().dismiss(); } - public AdapterView getParentAdapterView() { return _parentAdapterView; diff --git a/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableSpinnerView.java b/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableSpinnerView.java index 0937ca614..447384041 100644 --- a/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableSpinnerView.java +++ b/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableSpinnerView.java @@ -5,7 +5,6 @@ import android.content.ContextWrapper; import android.content.DialogInterface; import android.content.res.TypedArray; -import android.support.v4.widget.CursorAdapter; import android.text.TextUtils; import android.util.AttributeSet; import android.view.MotionEvent; @@ -16,9 +15,6 @@ import org.gnucash.android.R; -import java.util.ArrayList; -import java.util.List; - public class SearchableSpinnerView extends android.support.v7.widget.AppCompatSpinner implements View.OnTouchListener, @@ -77,19 +73,12 @@ public SearchableSpinnerView(Context context, attrs, defStyleAttr); -// this._context = context; - init(); } private void init() { - // TODO TW C 2020-02-01 : A enlever -// _allItems=new ArrayList(); -// _items = new ArrayList(); - // Create Dialog instance - // TODO TW C 2020-01-25 : Supprimer _items _searchableListDialogFragment = SearchableListDialogFragment.makeInstance(this); // S'abonner aux clicks sur un item @@ -97,17 +86,6 @@ private void init() { // S'abonner aux évènements onTouch setOnTouchListener(this); - -// _cursorAdapter = (CursorAdapter) getAdapter(); - - // TODO TW C 2020-01-17 : Supprimer la partie ArrayAdapter -// if (!TextUtils.isEmpty(_strHintText)) { -// ArrayAdapter arrayAdapter = new ArrayAdapter(_context, -// android.R.layout.simple_list_item_1, -// new String[]{_strHintText}); -// _isFromInit = true; -// setAdapter(arrayAdapter); -// } } @Override @@ -125,39 +103,9 @@ public boolean onTouch(View v, if (event.getAction() == MotionEvent.ACTION_UP) { // User has just clicked on the spinner -// if (null != _cursorAdapter) { -// -// // -// // Open Search & List Dialog -// // -// -// // Refresh content #6 -// // Change Start -// // Description: The items were only set initially, not reloading the data in the -// // spinner every time it is loaded with items in the adapter. -// _items.clear(); -// _allItems.clear(); -// -// // Create items from DB Cursor -// for (int i = 0; i < _cursorAdapter.getCount(); i++) { -// -// Cursor cursorOnRow = (Cursor) _cursorAdapter.getItem(i); -// -// final String accountFullName = cursorOnRow.getString(cursorOnRow.getColumnIndexOrThrow(DatabaseSchema.AccountEntry.COLUMN_FULL_NAME)); -// -// // TODO TW C 2020-01-17 : Ajouter l'étoile pour les Favoris -// -// _items.add(accountFullName); -// -// } // for -// -// // Create a copy of the items -// _allItems.addAll(_items); - - // Display SearchableListDialogFragment - _searchableListDialogFragment.show(scanForActivity(getContext()).getFragmentManager(), + // Display SearchableListDialogFragment + _searchableListDialogFragment.show(scanForActivity(getContext()).getFragmentManager(), "TAG"); -// } } } @@ -168,18 +116,7 @@ public boolean onTouch(View v, public void onSearchableItemClicked(Object item, int position) { -// String accountFullName = (String) item; - -// setSelection(_items.indexOf(item)); -// setSelection(_allItems.indexOf(item)); setSelection(position); - -// if (!_isDirty) { -// _isDirty = true; -// setAdapter(_cursorAdapter); -//// setSelection(_items.indexOf(item)); -// setSelection(_allItems.indexOf(item)); -// } } @@ -188,30 +125,6 @@ public void setAdapter(SpinnerAdapter adapter) { // Use given adapter for spinner item (not drop down) super.setAdapter(adapter); - -// _cursorAdapter = (CursorAdapter) adapter; -// _searchableListDialogFragment.setCursorAdapter((CursorAdapter) adapter); - -// if (!_isFromInit) { -// -// // TODO TW C 2020-01-17 : Supprimer la partie ArrayAdapter -// if (!TextUtils.isEmpty(_strHintText) && !_isDirty) { -// // -// -// // -// ArrayAdapter arrayAdapter = new ArrayAdapter(_context, -// android.R.layout.simple_list_item_1, -// new String[]{_strHintText}); -// super.setAdapter(arrayAdapter); -// -// } else { -// super.setAdapter(adapter); -// } -// -// } else { -// _isFromInit = false; -// super.setAdapter(adapter); -// } } public void setTitle(String strTitle) { @@ -240,8 +153,10 @@ private Activity scanForActivity(Context cont) { if (cont == null) { return null; + } else if (cont instanceof Activity) { return (Activity) cont; + } else if (cont instanceof ContextWrapper) { return scanForActivity(((ContextWrapper) cont).getBaseContext()); } @@ -268,22 +183,4 @@ public Object getSelectedItem() { return super.getSelectedItem(); } } - - private void showKeyboard(final EditText ettext) { - - ettext.requestFocus(); - ettext.postDelayed(new Runnable() { - @Override - public void run() { - - InputMethodManager keyboard = - (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE); - - keyboard.showSoftInput(ettext, - 0); - } - }, - 200); - } - } diff --git a/app/src/main/java/org/gnucash/android/util/KeyboardUtils.java b/app/src/main/java/org/gnucash/android/util/KeyboardUtils.java new file mode 100644 index 000000000..2894d5076 --- /dev/null +++ b/app/src/main/java/org/gnucash/android/util/KeyboardUtils.java @@ -0,0 +1,54 @@ +package org.gnucash.android.util; + +import android.content.Context; +import android.view.View; +import android.view.inputmethod.InputMethodManager; + +/** + * Created by JeanGarf on 2020-02-01. + */ +public class KeyboardUtils { + + /** + * Hide keyboard + * + * @param editTextView + */ + public static void hideKeyboard(final View editTextView) { + + // + // Hide keyboard + // + + InputMethodManager keyboard = (InputMethodManager) editTextView.getContext() + .getSystemService(Context.INPUT_METHOD_SERVICE); + + keyboard.hideSoftInputFromWindow(editTextView.getWindowToken(), + 0); + } + + /** + * Hide keyboard after a delay + * + * @param editTextView + * @param delay + */ + public static void hideKeyboard(final View editTextView, + final long delay) { + +// editTextView.requestFocus(); + + // Delay the keyboard hiding + editTextView.postDelayed(new Runnable() { + @Override + public void run() { + + // Hide keyboard + hideKeyboard(editTextView); + } + }, + delay); + } + + +} diff --git a/app/src/main/java/org/gnucash/android/util/QualifiedAccountNameCursorAdapter.java b/app/src/main/java/org/gnucash/android/util/QualifiedAccountNameCursorAdapter.java index 39e57c145..f611991dd 100644 --- a/app/src/main/java/org/gnucash/android/util/QualifiedAccountNameCursorAdapter.java +++ b/app/src/main/java/org/gnucash/android/util/QualifiedAccountNameCursorAdapter.java @@ -38,23 +38,37 @@ public class QualifiedAccountNameCursorAdapter extends SimpleCursorAdapter { // - private int _DropDownItemLayout; + private int _spinnerSelectedItemLayout; + private int _spinnerDropDownItemLayout; /** - * Initialize the Cursor adapter for account names using default spinner views + * Overloaded constructor. Specifies the view to use for displaying selected spinner text * * @param context * Application context * @param cursor - * Cursor to accounts + * Cursor to account data + * @param spinnerSelectedItemLayout + * Layout resource for selected item text */ public QualifiedAccountNameCursorAdapter(Context context, - Cursor cursor) { + Cursor cursor, + @LayoutRes int spinnerSelectedItemLayout, + @LayoutRes int spinnerDropDownItemLayout + ) { - this(context, - cursor, - android.R.layout.simple_spinner_item // Layout of the closed spinner item - ); + super(context, + spinnerSelectedItemLayout, // Layout of the closed spinner item + cursor, + new String[]{DatabaseSchema.AccountEntry.COLUMN_FULL_NAME}, + new int[]{android.R.id.text1}, + 0); + + // Store layout of each item in the open drop down of the spinner + setSpinnerSelectedItemLayout(spinnerSelectedItemLayout); + + // Store layout of each item in the open drop down of the spinner + setSpinnerDropDownItemLayout(spinnerDropDownItemLayout); } /** @@ -79,30 +93,20 @@ public QualifiedAccountNameCursorAdapter(Context context, } /** - * Overloaded constructor. Specifies the view to use for displaying selected spinner text + * Initialize the Cursor adapter for account names using default spinner views * * @param context * Application context * @param cursor - * Cursor to account data - * @param selectedSpinnerItemLayout - * Layout resource for selected item text + * Cursor to accounts */ public QualifiedAccountNameCursorAdapter(Context context, - Cursor cursor, - @LayoutRes int selectedSpinnerItemLayout, - @LayoutRes int dropdownSpinnerItemLayout - ) { - - super(context, - selectedSpinnerItemLayout, // Layout of the closed spinner item - cursor, - new String[]{DatabaseSchema.AccountEntry.COLUMN_FULL_NAME}, - new int[]{android.R.id.text1}, - 0); + Cursor cursor) { - // Define layout of each item in the open drop down of the spinner - setDropDownItemLayout(dropdownSpinnerItemLayout); + this(context, + cursor, + android.R.layout.simple_spinner_item // Layout of the closed spinner item + ); } @Override @@ -155,16 +159,26 @@ public int getPosition(@NonNull String accountUID) { return -1; } - public int getDropDownItemLayout() { + public int getSpinnerSelectedItemLayout() { + + return _spinnerSelectedItemLayout; + } + + public void setSpinnerSelectedItemLayout(int spinnerSelectedItemLayout) { + + _spinnerSelectedItemLayout = spinnerSelectedItemLayout; + } + + public int getSpinnerDropDownItemLayout() { - return _DropDownItemLayout; + return _spinnerDropDownItemLayout; } - public void setDropDownItemLayout(int dropDownItemLayout) { + public void setSpinnerDropDownItemLayout(int spinnerDropDownItemLayout) { - _DropDownItemLayout = dropDownItemLayout; + _spinnerDropDownItemLayout = spinnerDropDownItemLayout; - setDropDownViewResource(getDropDownItemLayout()); + setDropDownViewResource(getSpinnerDropDownItemLayout()); } } diff --git a/app/src/main/res/layout/fragment_account_form.xml b/app/src/main/res/layout/fragment_account_form.xml index 62230c94e..b51a4a5fc 100644 --- a/app/src/main/res/layout/fragment_account_form.xml +++ b/app/src/main/res/layout/fragment_account_form.xml @@ -86,7 +86,7 @@ @@ -135,7 +135,10 @@ + android:layout_height="wrap_content" + android:spinnerMode="dropdown" + android:dropDownWidth="match_parent" + /> + /> From 2c7bfee41839c623b6054eae7706b9c45f9536e6 Mon Sep 17 00:00:00 2001 From: JeanGarf Date: Sat, 1 Feb 2020 22:12:17 +0100 Subject: [PATCH 049/131] #110 - Set Focus onto Amount at first --- .../ui/transaction/TransactionFormFragment.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 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 246591cb3..686a00343 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 @@ -81,6 +81,7 @@ import org.gnucash.android.ui.util.RecurrenceViewClickListener; import org.gnucash.android.ui.util.widget.CalculatorEditText; import org.gnucash.android.ui.util.widget.TransactionTypeSwitch; +import org.gnucash.android.ui.util.widget.searchablespinner.SearchableListDialogFragment; import org.gnucash.android.ui.util.widget.searchablespinner.SearchableSpinnerView; import org.gnucash.android.util.KeyboardUtils; import org.gnucash.android.util.QualifiedAccountNameCursorAdapter; @@ -388,11 +389,18 @@ public void onNothingSelected(AdapterView adapterView) { actionBar.setTitle(R.string.title_add_transaction); initalizeViews(); initTransactionNameAutocomplete(); + } else { actionBar.setTitle(R.string.title_edit_transaction); - initializeViewsWithTransaction(); + initializeViewsWithTransaction(); mEditMode = true; - } + } + + // Set Focus onto Amount at first +// mDescriptionEditText.clearFocus(); + KeyboardUtils.hideKeyboard(mDescriptionEditText, + 200); + mAmountEditText.requestFocus(); getActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE); } From 2e9bbcd57c0c6abfd8479adc92cb9eda564b467a Mon Sep 17 00:00:00 2001 From: JeanGarf Date: Sat, 1 Feb 2020 22:34:20 +0100 Subject: [PATCH 050/131] #110 - Restore black color --- .../ui/transaction/TransactionFormFragment.java | 2 +- .../SearchableSpinnerView.java | 17 ++++++++++++++--- .../layout/account_spinner_dropdown_item.xml | 2 +- .../layout/transaction_account_spinner_item.xml | 3 +-- 4 files changed, 17 insertions(+), 7 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 686a00343..f057108af 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 @@ -630,7 +630,7 @@ private void updateTransferAccountsList(){ mCursor, // R.layout.account_spinner_dropdown_item); // android.R.layout.simple_spinner_item); - R.layout.transaction_account_spinner_item); + R.layout.account_spinner_dropdown_item); mTransferAccountSearchableSpinnerView.setAdapter(mAccountCursorAdapter); } diff --git a/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableSpinnerView.java b/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableSpinnerView.java index 447384041..70d7390f1 100644 --- a/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableSpinnerView.java +++ b/app/src/main/java/org/gnucash/android/ui/util/widget/searchablespinner/SearchableSpinnerView.java @@ -9,8 +9,6 @@ import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; -import android.view.inputmethod.InputMethodManager; -import android.widget.EditText; import android.widget.SpinnerAdapter; import org.gnucash.android.R; @@ -50,18 +48,31 @@ public SearchableSpinnerView(Context context, super(context, attrs); -// this._context = context; + // + // Retrieve attribute value + // TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SearchableSpinnerView); + final int N = a.getIndexCount(); + for (int i = 0; i < N; ++i) { + int attr = a.getIndex(i); + if (attr == R.styleable.SearchableSpinnerView_hintText) { + _strHintText = a.getString(attr); } } + a.recycle(); + + // + // Init + // + init(); } 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 d9bf77f30..a31c4ee14 100644 --- a/app/src/main/res/layout/account_spinner_dropdown_item.xml +++ b/app/src/main/res/layout/account_spinner_dropdown_item.xml @@ -19,6 +19,6 @@ android:layout_width="match_parent" android:layout_height="?attr/dropdownListPreferredItemHeight" style="?android:attr/spinnerDropDownItemStyle" - android:textColor="@android:color/holo_green_dark" + android:textColor="@android:color/black" android:ellipsize="start" android:singleLine="true" /> \ No newline at end of file diff --git a/app/src/main/res/layout/transaction_account_spinner_item.xml b/app/src/main/res/layout/transaction_account_spinner_item.xml index f9626e8cd..d9b99c3eb 100644 --- a/app/src/main/res/layout/transaction_account_spinner_item.xml +++ b/app/src/main/res/layout/transaction_account_spinner_item.xml @@ -16,10 +16,9 @@ --> Date: Sun, 2 Feb 2020 01:06:33 +0100 Subject: [PATCH 051/131] #110 - Use Theme to configure spinners --- .../layout/account_spinner_dropdown_item.xml | 4 +--- .../main/res/layout/toolbar_with_spinner.xml | 2 ++ .../transaction_account_spinner_item.xml | 5 +---- app/src/main/res/values/styles.xml | 20 +++++++++++++++++++ app/src/main/res/values/themes.xml | 11 ++++++++-- 5 files changed, 33 insertions(+), 9 deletions(-) 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 a31c4ee14..6d53e5d7e 100644 --- a/app/src/main/res/layout/account_spinner_dropdown_item.xml +++ b/app/src/main/res/layout/account_spinner_dropdown_item.xml @@ -19,6 +19,4 @@ android:layout_width="match_parent" android:layout_height="?attr/dropdownListPreferredItemHeight" style="?android:attr/spinnerDropDownItemStyle" - android:textColor="@android:color/black" - android:ellipsize="start" - android:singleLine="true" /> \ No newline at end of file +/> \ No newline at end of file diff --git a/app/src/main/res/layout/toolbar_with_spinner.xml b/app/src/main/res/layout/toolbar_with_spinner.xml index 4294b2476..0bf48d2ce 100644 --- a/app/src/main/res/layout/toolbar_with_spinner.xml +++ b/app/src/main/res/layout/toolbar_with_spinner.xml @@ -37,5 +37,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/> + + \ No newline at end of file diff --git a/app/src/main/res/layout/transaction_account_spinner_item.xml b/app/src/main/res/layout/transaction_account_spinner_item.xml index d9b99c3eb..633761a80 100644 --- a/app/src/main/res/layout/transaction_account_spinner_item.xml +++ b/app/src/main/res/layout/transaction_account_spinner_item.xml @@ -18,8 +18,5 @@ android:id="@android:id/text1" android:layout_width="match_parent" android:layout_height="wrap_content" - style="?android:attr/spinnerDropDownItemStyle" - android:textSize="18sp" - android:singleLine="true" - android:ellipsize="start" + style="?android:attr/spinnerItemStyle" android:textAlignment="inherit"/> \ No newline at end of file diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index a67c2f893..ae5c13eca 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -16,9 +16,29 @@ --> + + + + + + + + + + - + + @@ -52,7 +57,8 @@ @android:color/black center_vertical - + + diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index 17cb6ad70..b8a8461ea 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -40,8 +40,8 @@ - -