diff --git a/app/build.gradle b/app/build.gradle index de330a8..fd78cf6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -22,6 +22,9 @@ android { "$projectDir/schemas".toString()] } } + sourceSets { + androidTest.assets.srcDirs += files("$projectDir/schemas".toString()) + } } buildTypes { release { @@ -60,7 +63,7 @@ android { dependencies { def lifecycle_version = "2.0.0" - def room_version = "2.1.0-alpha03" + def room_version = "2.1.0-alpha04" def nav_version = "1.0.0-alpha11" def rx_bindings_version = "3.0.0-alpha2" @@ -114,7 +117,7 @@ dependencies { androidTestImplementation 'androidx.test:core:1.1.0' // testImplementation "androidx.arch.core:core-testing:$lifecycle_version" -// testImplementation "androidx.room:room-testing:$room_version" -// androidTestImplementation 'androidx.test:runner:1.1.0' + androidTestImplementation "androidx.room:room-testing:$room_version" + androidTestImplementation 'androidx.test.ext:junit:1.1.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' } diff --git a/app/schemas/cy.agorise.bitsybitshareswallet.database.BitsyDatabase/2.json b/app/schemas/cy.agorise.bitsybitshareswallet.database.BitsyDatabase/2.json index f88ca88..ae78789 100644 --- a/app/schemas/cy.agorise.bitsybitshareswallet.database.BitsyDatabase/2.json +++ b/app/schemas/cy.agorise.bitsybitshareswallet.database.BitsyDatabase/2.json @@ -428,4 +428,4 @@ "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"79a582fa39e989852699b131383e993a\")" ] } -} \ No newline at end of file +} diff --git a/app/schemas/cy.agorise.bitsybitshareswallet.database.BitsyDatabase/3.json b/app/schemas/cy.agorise.bitsybitshareswallet.database.BitsyDatabase/3.json new file mode 100644 index 0000000..7e0f04c --- /dev/null +++ b/app/schemas/cy.agorise.bitsybitshareswallet.database.BitsyDatabase/3.json @@ -0,0 +1,415 @@ +{ + "formatVersion": 1, + "database": { + "version": 3, + "identityHash": "36ac924b7b8d78fb2d937d1ff9ba8897", + "entities": [ + { + "tableName": "assets", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `symbol` TEXT NOT NULL, `precision` INTEGER NOT NULL, `description` TEXT NOT NULL, `bit_asset_id` TEXT NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "precision", + "columnName": "precision", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "bitAssetId", + "columnName": "bit_asset_id", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "authorities", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `user_id` TEXT NOT NULL, `authority_type` INTEGER NOT NULL, `encrypted_wif` TEXT NOT NULL, `encrypted_brain_key` TEXT NOT NULL, `encrypted_sequence_number` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userId", + "columnName": "user_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "authorityType", + "columnName": "authority_type", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "encryptedWIF", + "columnName": "encrypted_wif", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "encryptedBrainKey", + "columnName": "encrypted_brain_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "encryptedSequenceNumber", + "columnName": "encrypted_sequence_number", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "balances", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`asset_id` TEXT NOT NULL, `asset_amount` INTEGER NOT NULL, `last_update` INTEGER NOT NULL, PRIMARY KEY(`asset_id`))", + "fields": [ + { + "fieldPath": "assetId", + "columnName": "asset_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "assetAmount", + "columnName": "asset_amount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lastUpdate", + "columnName": "last_update", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "asset_id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "equivalent_values", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`transfer_id` TEXT NOT NULL, `value` INTEGER NOT NULL, `symbol` TEXT NOT NULL, PRIMARY KEY(`transfer_id`, `symbol`), FOREIGN KEY(`transfer_id`) REFERENCES `transfers`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )", + "fields": [ + { + "fieldPath": "transferId", + "columnName": "transfer_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "transfer_id", + "symbol" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [ + { + "table": "transfers", + "onDelete": "NO ACTION", + "onUpdate": "NO ACTION", + "columns": [ + "transfer_id" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "transfers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `block_number` INTEGER NOT NULL, `timestamp` INTEGER NOT NULL, `fee_amount` INTEGER NOT NULL, `fee_asset_id` TEXT NOT NULL, `source` TEXT NOT NULL, `destination` TEXT NOT NULL, `transfer_amount` INTEGER NOT NULL, `transfer_asset_id` TEXT NOT NULL, `memo` TEXT NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "blockNumber", + "columnName": "block_number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "feeAmount", + "columnName": "fee_amount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "feeAssetId", + "columnName": "fee_asset_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "source", + "columnName": "source", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "destination", + "columnName": "destination", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "transferAmount", + "columnName": "transfer_amount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "transferAssetId", + "columnName": "transfer_asset_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "memo", + "columnName": "memo", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "user_accounts", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `is_ltm` INTEGER NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isLtm", + "columnName": "is_ltm", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "merchants", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `address` TEXT, `lat` REAL NOT NULL, `lon` REAL NOT NULL, `phone` TEXT, `telegram` TEXT, `website` TEXT, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "_id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lat", + "columnName": "lat", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "lon", + "columnName": "lon", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "phone", + "columnName": "phone", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "telegram", + "columnName": "telegram", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "website", + "columnName": "website", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "tellers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `address` TEXT, `lat` REAL NOT NULL, `lon` REAL NOT NULL, `phone` TEXT, `telegram` TEXT, `website` TEXT, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "_id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gt_name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lat", + "columnName": "lat", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "lon", + "columnName": "lon", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "phone", + "columnName": "phone", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "telegram", + "columnName": "telegram", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "url", + "columnName": "website", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"36ac924b7b8d78fb2d937d1ff9ba8897\")" + ] + } +} \ No newline at end of file diff --git a/app/src/androidTest/java/cy/agorise/bitsybitshareswallet/MigrationTest.kt b/app/src/androidTest/java/cy/agorise/bitsybitshareswallet/MigrationTest.kt new file mode 100644 index 0000000..822547d --- /dev/null +++ b/app/src/androidTest/java/cy/agorise/bitsybitshareswallet/MigrationTest.kt @@ -0,0 +1,40 @@ +package cy.agorise.bitsybitshareswallet; + +import androidx.room.testing.MigrationTestHelper +import androidx.sqlite.db.framework.FrameworkSQLiteOpenHelperFactory +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import cy.agorise.bitsybitshareswallet.database.BitsyDatabase +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import java.io.IOException + +@RunWith(AndroidJUnit4::class) +class MigrationTest { + private val TEST_DB = "migration-test" + + @get:Rule + val helper: MigrationTestHelper = MigrationTestHelper( + InstrumentationRegistry.getInstrumentation(), + BitsyDatabase::class.java.canonicalName, + FrameworkSQLiteOpenHelperFactory() + ) + + @Test + @Throws(IOException::class) + fun migrate2To3() { + var db = helper.createDatabase(TEST_DB, 2).apply { + // db has schema version 1. insert some data using SQL queries. + // You cannot use DAO classes because they expect the latest schema. + execSQL("INSERT INTO assets(id, symbol, precision, description, bit_asset_id) VALUES('1.3.0','BTS', 5, '', '')") + execSQL("INSERT INTO transfers(id, block_number, timestamp, fee_amount, fee_asset_id, source, destination, transfer_amount, transfer_asset_id, memo) values(1,0,1500000000,120,'1.3.0','1.2.100','1.2.101',1000,'1.3.121','')") + execSQL("INSERT INTO equivalent_values(id, transfer_id, value, asset_id) values(1, 1, 100, '1.3.0')") + // Prepare for the next version. + close() + } + // Re-open the database with version 2 and provide + // MIGRATION_1_2 as the migration process. + db = helper.runMigrationsAndValidate(TEST_DB, 3, true, BitsyDatabase.MIGRATION_2_3) + } +} diff --git a/app/src/main/java/cy/agorise/bitsybitshareswallet/activities/ConnectedActivity.kt b/app/src/main/java/cy/agorise/bitsybitshareswallet/activities/ConnectedActivity.kt index 83a6fbc..ad498fa 100644 --- a/app/src/main/java/cy/agorise/bitsybitshareswallet/activities/ConnectedActivity.kt +++ b/app/src/main/java/cy/agorise/bitsybitshareswallet/activities/ConnectedActivity.kt @@ -140,7 +140,7 @@ abstract class ConnectedActivity : AppCompatActivity(), ServiceConnection { if (blockNumber != null && blockNumber != blockNumberWithMissingTime) { blockNumberWithMissingTime = blockNumber Log.d(TAG, "Block number: $blockNumber, Time: ${System.currentTimeMillis()}") - mHandler.postDelayed(mRequestBlockMissingTimeTask, 10) + mHandler.post(mRequestBlockMissingTimeTask) } }) diff --git a/app/src/main/java/cy/agorise/bitsybitshareswallet/database/BitsyDatabase.kt b/app/src/main/java/cy/agorise/bitsybitshareswallet/database/BitsyDatabase.kt index 0888d7e..aa344b2 100644 --- a/app/src/main/java/cy/agorise/bitsybitshareswallet/database/BitsyDatabase.kt +++ b/app/src/main/java/cy/agorise/bitsybitshareswallet/database/BitsyDatabase.kt @@ -21,7 +21,7 @@ import cy.agorise.bitsybitshareswallet.database.joins.TransferDetailDao Merchant::class, Teller::class ], - version = 2, + version = 3, exportSchema = true) abstract class BitsyDatabase : RoomDatabase() { @@ -48,18 +48,27 @@ abstract class BitsyDatabase : RoomDatabase() { INSTANCE = Room.databaseBuilder( context.applicationContext, BitsyDatabase::class.java, "BiTSyWallet.db" - ).addMigrations(MIGRATION_1_2).build() + ).addMigrations(MIGRATION_1_2) + .addMigrations(MIGRATION_2_3) + .build() } } return INSTANCE } - private val MIGRATION_1_2 = object : Migration(1, 2) { + val MIGRATION_1_2 = object : Migration(1, 2) { override fun migrate(database: SupportSQLiteDatabase) { database.execSQL("CREATE TABLE IF NOT EXISTS 'merchants' ('id' TEXT NOT NULL PRIMARY KEY, 'name' TEXT NOT NULL, 'address' TEXT, 'lat' REAL NOT NULL, 'lon' REAL NOT NULL, 'phone' TEXT, 'telegram' TEXT, 'website' TEXT)") database.execSQL("CREATE TABLE IF NOT EXISTS 'tellers' ('id' TEXT NOT NULL PRIMARY KEY, 'name' TEXT NOT NULL, 'address' TEXT, 'lat' REAL NOT NULL, 'lon' REAL NOT NULL, 'phone' TEXT, 'telegram' TEXT, 'website' TEXT)") } } + + val MIGRATION_2_3 = object : Migration(2, 3) { + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("DROP TABLE 'equivalent_values'") + database.execSQL("CREATE TABLE IF NOT EXISTS 'equivalent_values' ('transfer_id' TEXT NOT NULL, 'value' INTEGER NOT NULL, 'symbol' TEXT NOT NULL, PRIMARY KEY(transfer_id, symbol), FOREIGN KEY (transfer_id) REFERENCES transfers(id))") + } + } } } diff --git a/app/src/main/java/cy/agorise/bitsybitshareswallet/database/entities/EquivalentValue.kt b/app/src/main/java/cy/agorise/bitsybitshareswallet/database/entities/EquivalentValue.kt index a702950..4105d2b 100644 --- a/app/src/main/java/cy/agorise/bitsybitshareswallet/database/entities/EquivalentValue.kt +++ b/app/src/main/java/cy/agorise/bitsybitshareswallet/database/entities/EquivalentValue.kt @@ -3,23 +3,17 @@ package cy.agorise.bitsybitshareswallet.database.entities import androidx.room.ColumnInfo import androidx.room.Entity import androidx.room.ForeignKey -import androidx.room.PrimaryKey -@Entity(tableName = "equivalent_values",foreignKeys = - [ForeignKey( +@Entity(tableName = "equivalent_values", + primaryKeys = arrayOf("transfer_id", "symbol"), + foreignKeys = [ForeignKey( entity = Transfer::class, parentColumns = ["id"], childColumns = ["transfer_id"] - ), ForeignKey( - entity = Asset::class, - parentColumns = ["id"], - childColumns = ["asset_id"] )] ) data class EquivalentValue ( - @PrimaryKey(autoGenerate = true) - @ColumnInfo(name = "id") val id: Long, @ColumnInfo(name = "transfer_id") val transferId: String, @ColumnInfo(name = "value") val value: Long, - @ColumnInfo(name = "asset_id") val assetId: String + @ColumnInfo(name = "symbol") val symbol: String ) \ No newline at end of file