From a91029cb0326f2e30a11d0feeb2530d7a5b5040b Mon Sep 17 00:00:00 2001 From: WooKeyWallet Date: Tue, 7 May 2019 11:15:09 +0800 Subject: [PATCH] Optimize UX --- app/build.gradle | 17 +- .../io.wookey.wallet.data.AppDatabase/1.json | 236 ++++++++++++ .../io.wookey.wallet.data.AppDatabase/2.json | 346 ++++++++++++++++++ .../java/io/wookey/wallet/SplashActivity.kt | 18 +- .../io/wookey/wallet/base/BaseActivity.kt | 8 + .../io/wookey/wallet/core/XMRRepository.kt | 70 +++- .../wookey/wallet/core/XMRWalletController.kt | 9 +- .../java/io/wookey/wallet/data/AppDatabase.kt | 41 ++- .../io/wookey/wallet/data/dao/AssetDao.kt | 8 + .../java/io/wookey/wallet/data/dao/NodeDao.kt | 8 + .../wallet/data/dao/TransactionInfoDao.kt | 40 ++ .../io/wookey/wallet/data/dao/WalletDao.kt | 16 + .../wallet/data/entity/TransactionInfo.kt | 21 ++ .../wookey/wallet/dialog/BakMnemonicDialog.kt | 4 - .../feature/asset/AssetDetailActivity.kt | 16 +- .../feature/asset/AssetDetailViewModel.kt | 166 ++++++--- .../generate/GenerateWalletViewModel.kt | 5 +- .../generate/create/BackupMnemonicActivity.kt | 8 +- .../generate/create/VerifyMnemonicActivity.kt | 4 +- .../recovery/RecoveryMnemonicFragment.kt | 21 +- .../recovery/RecoveryMnemonicViewModel.kt | 14 +- .../recovery/RecoveryPrivateKeyFragment.kt | 16 + .../recovery/RecoveryPrivateKeyViewModel.kt | 14 +- .../recovery/RecoveryWalletActivity.kt | 4 +- .../feature/setting/NodeListActivity.kt | 9 +- .../feature/setting/NodeListViewModel.kt | 40 +- .../feature/setting/NodeSettingViewModel.kt | 8 +- .../feature/wallet/BackupKeyActivity.kt | 4 +- .../feature/wallet/WalletDetailActivity.kt | 23 ++ .../feature/wallet/WalletDetailViewModel.kt | 63 ++++ .../java/io/wookey/wallet/support/Constant.kt | 9 + .../wallet/support/extensions/Extensions.kt | 1 + .../io/wookey/wallet/widget/IOSDialog.java | 39 +- .../main/res/drawable-xhdpi/icon_close.png | Bin 2311 -> 526 bytes .../res/drawable-xhdpi/icon_switch_node.png | Bin 0 -> 1116 bytes .../res/layout/activity_wallet_detail.xml | 19 + .../main/res/layout/dialog_bak_mnemonic.xml | 7 - app/src/main/res/layout/dialog_node_edit.xml | 40 +- app/src/main/res/layout/dialog_password.xml | 41 ++- .../res/layout/fragment_recovery_mnemonic.xml | 43 +-- .../layout/fragment_recovery_private_key.xml | 44 +-- app/src/main/res/values-zh-rCN/strings.xml | 8 + app/src/main/res/values/strings.xml | 8 + .../io/wookey/monero/util/RestoreHeight.java | 1 + 44 files changed, 1305 insertions(+), 212 deletions(-) create mode 100644 app/schemas/io.wookey.wallet.data.AppDatabase/1.json create mode 100644 app/schemas/io.wookey.wallet.data.AppDatabase/2.json create mode 100644 app/src/main/java/io/wookey/wallet/data/dao/TransactionInfoDao.kt create mode 100755 app/src/main/res/drawable-xhdpi/icon_switch_node.png diff --git a/app/build.gradle b/app/build.gradle index 7701108..3b948c7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -4,7 +4,13 @@ apply plugin: 'kotlin-android-extensions' apply plugin: 'kotlin-kapt' def computeVersionCode() { - 80 + def cmd = 'git rev-list HEAD --first-parent --count' + def trim = cmd.execute().text.trim() + if (trim == null || trim.length() == 0) { + trim = 1 + } + trim.toInteger() + 103 } def gitVersionTag() { @@ -24,7 +30,7 @@ def gitVersionTag() { } def computeVersionName() { - return "1.0.2" + return "1.0.3" } android { @@ -37,6 +43,13 @@ android { versionName computeVersionName() testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + javaCompileOptions { + annotationProcessorOptions { + arguments = ["room.schemaLocation": + "$projectDir/schemas".toString()] + } + } + } buildTypes { debug { diff --git a/app/schemas/io.wookey.wallet.data.AppDatabase/1.json b/app/schemas/io.wookey.wallet.data.AppDatabase/1.json new file mode 100644 index 0000000..7c0152f --- /dev/null +++ b/app/schemas/io.wookey.wallet.data.AppDatabase/1.json @@ -0,0 +1,236 @@ +{ + "formatVersion": 1, + "database": { + "version": 1, + "identityHash": "4501b5285edaa545e5ab39a9b659387e", + "entities": [ + { + "tableName": "wallets", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `symbol` TEXT NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `balance` TEXT NOT NULL, `passwordPrompt` TEXT NOT NULL, `restoreHeight` INTEGER NOT NULL, `isActive` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "balance", + "columnName": "balance", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "passwordPrompt", + "columnName": "passwordPrompt", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "restoreHeight", + "columnName": "restoreHeight", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isActive", + "columnName": "isActive", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "_id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_wallets_symbol_name", + "unique": true, + "columnNames": [ + "symbol", + "name" + ], + "createSql": "CREATE UNIQUE INDEX `index_wallets_symbol_name` ON `${TABLE_NAME}` (`symbol`, `name`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "assets", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `walletId` INTEGER NOT NULL, `token` TEXT NOT NULL, `balance` TEXT NOT NULL, `contractAddress` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "walletId", + "columnName": "walletId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "token", + "columnName": "token", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "balance", + "columnName": "balance", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "contractAddress", + "columnName": "contractAddress", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "_id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_assets_walletId_contractAddress", + "unique": true, + "columnNames": [ + "walletId", + "contractAddress" + ], + "createSql": "CREATE UNIQUE INDEX `index_assets_walletId_contractAddress` ON `${TABLE_NAME}` (`walletId`, `contractAddress`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "nodes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `symbol` TEXT NOT NULL, `url` TEXT NOT NULL, `isSelected` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isSelected", + "columnName": "isSelected", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "_id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_nodes_symbol_url", + "unique": true, + "columnNames": [ + "symbol", + "url" + ], + "createSql": "CREATE UNIQUE INDEX `index_nodes_symbol_url` ON `${TABLE_NAME}` (`symbol`, `url`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "address_books", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `symbol` TEXT NOT NULL, `address` TEXT NOT NULL, `notes` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "notes", + "columnName": "notes", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "_id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_address_books_symbol_address_notes", + "unique": true, + "columnNames": [ + "symbol", + "address", + "notes" + ], + "createSql": "CREATE UNIQUE INDEX `index_address_books_symbol_address_notes` ON `${TABLE_NAME}` (`symbol`, `address`, `notes`)" + } + ], + "foreignKeys": [] + } + ], + "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, \"4501b5285edaa545e5ab39a9b659387e\")" + ] + } +} \ No newline at end of file diff --git a/app/schemas/io.wookey.wallet.data.AppDatabase/2.json b/app/schemas/io.wookey.wallet.data.AppDatabase/2.json new file mode 100644 index 0000000..7e135de --- /dev/null +++ b/app/schemas/io.wookey.wallet.data.AppDatabase/2.json @@ -0,0 +1,346 @@ +{ + "formatVersion": 1, + "database": { + "version": 2, + "identityHash": "480e97bc03ef5a1b7c533a36161eb35d", + "entities": [ + { + "tableName": "wallets", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `symbol` TEXT NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `balance` TEXT NOT NULL, `passwordPrompt` TEXT NOT NULL, `restoreHeight` INTEGER NOT NULL, `isActive` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "balance", + "columnName": "balance", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "passwordPrompt", + "columnName": "passwordPrompt", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "restoreHeight", + "columnName": "restoreHeight", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isActive", + "columnName": "isActive", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "_id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_wallets_symbol_name", + "unique": true, + "columnNames": [ + "symbol", + "name" + ], + "createSql": "CREATE UNIQUE INDEX `index_wallets_symbol_name` ON `${TABLE_NAME}` (`symbol`, `name`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "assets", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `walletId` INTEGER NOT NULL, `token` TEXT NOT NULL, `balance` TEXT NOT NULL, `contractAddress` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "walletId", + "columnName": "walletId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "token", + "columnName": "token", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "balance", + "columnName": "balance", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "contractAddress", + "columnName": "contractAddress", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "_id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_assets_walletId_contractAddress", + "unique": true, + "columnNames": [ + "walletId", + "contractAddress" + ], + "createSql": "CREATE UNIQUE INDEX `index_assets_walletId_contractAddress` ON `${TABLE_NAME}` (`walletId`, `contractAddress`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "nodes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `symbol` TEXT NOT NULL, `url` TEXT NOT NULL, `isSelected` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isSelected", + "columnName": "isSelected", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "_id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_nodes_symbol_url", + "unique": true, + "columnNames": [ + "symbol", + "url" + ], + "createSql": "CREATE UNIQUE INDEX `index_nodes_symbol_url` ON `${TABLE_NAME}` (`symbol`, `url`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "address_books", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `symbol` TEXT NOT NULL, `address` TEXT NOT NULL, `notes` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "notes", + "columnName": "notes", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "_id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_address_books_symbol_address_notes", + "unique": true, + "columnNames": [ + "symbol", + "address", + "notes" + ], + "createSql": "CREATE UNIQUE INDEX `index_address_books_symbol_address_notes` ON `${TABLE_NAME}` (`symbol`, `address`, `notes`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "transactionInfo", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `token` TEXT NOT NULL, `assetId` INTEGER NOT NULL, `walletId` INTEGER NOT NULL, `direction` INTEGER NOT NULL, `isPending` INTEGER NOT NULL, `isFailed` INTEGER NOT NULL, `amount` TEXT, `fee` TEXT, `blockHeight` INTEGER NOT NULL, `confirmations` INTEGER NOT NULL, `hash` TEXT, `timestamp` INTEGER NOT NULL, `paymentId` TEXT, `txKey` TEXT, `address` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "token", + "columnName": "token", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "assetId", + "columnName": "assetId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "walletId", + "columnName": "walletId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "direction", + "columnName": "direction", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isPending", + "columnName": "isPending", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isFailed", + "columnName": "isFailed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "amount", + "columnName": "amount", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "fee", + "columnName": "fee", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "blockHeight", + "columnName": "blockHeight", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "confirmations", + "columnName": "confirmations", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hash", + "columnName": "hash", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "paymentId", + "columnName": "paymentId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "txKey", + "columnName": "txKey", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "_id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + } + ], + "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, \"480e97bc03ef5a1b7c533a36161eb35d\")" + ] + } +} \ No newline at end of file diff --git a/app/src/main/java/io/wookey/wallet/SplashActivity.kt b/app/src/main/java/io/wookey/wallet/SplashActivity.kt index a8a0f7e..7fb99c3 100644 --- a/app/src/main/java/io/wookey/wallet/SplashActivity.kt +++ b/app/src/main/java/io/wookey/wallet/SplashActivity.kt @@ -21,16 +21,22 @@ class SplashActivity : BaseActivity() { delay(1500) val walletId = sharedPreferences().getInt("walletId", -1) val activeWallet = withContext(Dispatchers.IO) { - val activeWallets = AppDatabase.getInstance().walletDao().getActiveWallets() - if (activeWallets.isNullOrEmpty()) { + val wallets = AppDatabase.getInstance().walletDao().getWallets() + if (wallets.isNullOrEmpty()) { null } else { - activeWallets.forEachIndexed { index, wallet -> - if (index > 0) { - AppDatabase.getInstance().walletDao().updateWallets(wallet.apply { isActive = false }) + val activeWallets = AppDatabase.getInstance().walletDao().getActiveWallets() + if (activeWallets.isNullOrEmpty()) { + AppDatabase.getInstance().walletDao().updateWallets(wallets[0].apply { isActive = true }) + wallets[0] + } else { + activeWallets.forEachIndexed { index, wallet -> + if (index > 0) { + AppDatabase.getInstance().walletDao().updateWallets(wallet.apply { isActive = false }) + } } + activeWallets[0] } - activeWallets[0] } } val walletAddress = activeWallet?.address diff --git a/app/src/main/java/io/wookey/wallet/base/BaseActivity.kt b/app/src/main/java/io/wookey/wallet/base/BaseActivity.kt index 38a8cca..547a328 100644 --- a/app/src/main/java/io/wookey/wallet/base/BaseActivity.kt +++ b/app/src/main/java/io/wookey/wallet/base/BaseActivity.kt @@ -1,7 +1,9 @@ package io.wookey.wallet.base import android.content.Context +import android.os.Bundle import android.support.v7.app.AppCompatActivity +import android.view.WindowManager import io.wookey.wallet.dialog.LoadingDialog import io.wookey.wallet.support.extensions.getLocale import io.wookey.wallet.support.extensions.setLocale @@ -12,6 +14,12 @@ open class BaseActivity : AppCompatActivity() { super.attachBaseContext(setLocale(newBase, getLocale())) } + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + // 禁止截图 + window.setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE) + } + var loadingDialog: LoadingDialog? = null open fun showLoading() { diff --git a/app/src/main/java/io/wookey/wallet/core/XMRRepository.kt b/app/src/main/java/io/wookey/wallet/core/XMRRepository.kt index 794fb61..88e7a11 100644 --- a/app/src/main/java/io/wookey/wallet/core/XMRRepository.kt +++ b/app/src/main/java/io/wookey/wallet/core/XMRRepository.kt @@ -4,7 +4,11 @@ import android.app.Application import io.wookey.wallet.App import io.wookey.wallet.data.AppDatabase import io.wookey.wallet.data.entity.Asset +import io.wookey.wallet.data.entity.Node import io.wookey.wallet.data.entity.Wallet +import io.wookey.wallet.support.ZH_CN +import io.wookey.wallet.support.extensions.getCurrentLocale +import io.wookey.wallet.support.nodeArray import java.io.File class XMRRepository(val context: Application = App.instance) { @@ -37,21 +41,33 @@ class XMRRepository(val context: Application = App.instance) { } fun recoveryWallet(walletName: String, password: String, mnemonic: String, restoreHeight: Long?): Wallet? { - return XMRWalletController.recoveryWallet(generateXMRFile(walletName), password, mnemonic, restoreHeight - ?: 0) + return XMRWalletController.recoveryWallet( + generateXMRFile(walletName), password, mnemonic, restoreHeight + ?: 0 + ) } - fun createWalletWithKeys(walletName: String, password: String, address: String, viewKey: String, - spendKey: String, restoreHeight: Long?): Wallet? { - return XMRWalletController.createWalletWithKeys(generateXMRFile(walletName), password, restoreHeight - ?: 0, address, viewKey, spendKey) + fun createWalletWithKeys( + walletName: String, password: String, address: String, viewKey: String, + spendKey: String, restoreHeight: Long? + ): Wallet? { + return XMRWalletController.createWalletWithKeys( + generateXMRFile(walletName), password, restoreHeight + ?: 0, address, viewKey, spendKey + ) } fun saveWallet(wallet: Wallet): Wallet? { val database = AppDatabase.getInstance() - val count = database.walletDao().countWallets() - wallet.isActive = count == 0 + val actWallets = database.walletDao().getActiveWallets() + if (!actWallets.isNullOrEmpty()) { + actWallets.forEach { + it.isActive = false + } + database.walletDao().updateWallets(*actWallets.toTypedArray()) + } + wallet.isActive = true database.walletDao().insertWallet(wallet) val insert = database.walletDao().getWalletsByName(wallet.symbol, wallet.name) if (insert != null) { @@ -60,14 +76,42 @@ class XMRRepository(val context: Application = App.instance) { return insert } - fun cancelCreate(name: String) { + fun deleteWallet(name: String): Boolean { + var success = false val dir = "${context.filesDir.absolutePath}${File.separator}wallet${File.separator}xmr${File.separator}$name" val walletFolder = File(dir) if (walletFolder.exists() && walletFolder.isDirectory) { - walletFolder.listFiles().forEach { - if (it.isFile) { - it.delete() - } + success = walletFolder.deleteRecursively() + } + return success + } + + fun insertNodes() { + // 兼容旧版 + val nodes = AppDatabase.getInstance().nodeDao().getSymbolNodes("XMR") + var zhNode: Node? = null + nodes?.forEach { + if (it.url == "124.160.224.28:18081") { + zhNode = it + return@forEach + } + } + AppDatabase.getInstance().nodeDao().insertNodes(nodes = *nodeArray) + val node = AppDatabase.getInstance().nodeDao().getSymbolNode("XMR") + val locale = context.getCurrentLocale() + if (locale == ZH_CN && zhNode == null && node != null && node.url == "node.moneroworld.com:18089") { + // 兼容旧版,修改中文区默认节点 + val filter = AppDatabase.getInstance().nodeDao().getSymbolNodes("XMR")?.filter { + it.url == "124.160.224.28:18081" + } + if (!filter.isNullOrEmpty()) { + AppDatabase.getInstance().nodeDao().updateNodes( + node.apply { + isSelected = false + }, + filter[0].apply { + isSelected = true + }) } } } diff --git a/app/src/main/java/io/wookey/wallet/core/XMRWalletController.kt b/app/src/main/java/io/wookey/wallet/core/XMRWalletController.kt index 7a4369d..d8c0aa9 100644 --- a/app/src/main/java/io/wookey/wallet/core/XMRWalletController.kt +++ b/app/src/main/java/io/wookey/wallet/core/XMRWalletController.kt @@ -151,6 +151,11 @@ object XMRWalletController { observer.onWalletOpened() + if (startRefresh(wallet, restoreHeight, observer)) return + observer.onWalletStarted() + } + + fun startRefresh(wallet: Wallet, restoreHeight: Long, observer: Observer): Boolean { wallet.init(0) wallet.restoreHeight = restoreHeight @@ -159,7 +164,7 @@ object XMRWalletController { if (wallet.connectionStatus != Wallet.ConnectionStatus.ConnectionStatus_Connected) { val error = wallet.errorString observer.onWalletStartFailed(error) - return + return true } wallet.setListener(object : WalletListener { @@ -210,7 +215,7 @@ object XMRWalletController { } }) wallet.startRefresh() - observer.onWalletStarted() + return false } fun refreshWallet() { diff --git a/app/src/main/java/io/wookey/wallet/data/AppDatabase.kt b/app/src/main/java/io/wookey/wallet/data/AppDatabase.kt index 75ab158..6ca8f2a 100644 --- a/app/src/main/java/io/wookey/wallet/data/AppDatabase.kt +++ b/app/src/main/java/io/wookey/wallet/data/AppDatabase.kt @@ -1,32 +1,52 @@ package io.wookey.wallet.data +import android.arch.persistence.db.SupportSQLiteDatabase import android.arch.persistence.room.Database import android.arch.persistence.room.Room import android.arch.persistence.room.RoomDatabase +import android.arch.persistence.room.migration.Migration import android.content.Context import io.wookey.wallet.App -import io.wookey.wallet.data.dao.AddressBookDao -import io.wookey.wallet.data.dao.AssetDao -import io.wookey.wallet.data.dao.NodeDao -import io.wookey.wallet.data.dao.WalletDao -import io.wookey.wallet.data.entity.AddressBook -import io.wookey.wallet.data.entity.Asset -import io.wookey.wallet.data.entity.Node -import io.wookey.wallet.data.entity.Wallet - -@Database(entities = [Wallet::class, Asset::class, Node::class, AddressBook::class], version = 1) +import io.wookey.wallet.data.dao.* +import io.wookey.wallet.data.entity.* + +@Database(entities = [Wallet::class, Asset::class, Node::class, AddressBook::class, TransactionInfo::class], version = 2) abstract class AppDatabase : RoomDatabase() { abstract fun walletDao(): WalletDao abstract fun assetDao(): AssetDao abstract fun nodeDao(): NodeDao abstract fun addressBookDao(): AddressBookDao + abstract fun transactionInfoDao(): TransactionInfoDao companion object { @Volatile private var INSTANCE: AppDatabase? = null + private val MIGRATION_1_2 = object : Migration(1, 2) { + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("CREATE TABLE `transactionInfo` " + + "(`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, " + + "`token` TEXT NOT NULL, " + + "`assetId` INTEGER NOT NULL, " + + "`walletId` INTEGER NOT NULL, " + + "`direction` INTEGER NOT NULL, " + + "`isPending` INTEGER NOT NULL, " + + "`isFailed` INTEGER NOT NULL, " + + "`amount` TEXT, " + + "`fee` TEXT, " + + "`blockHeight` INTEGER NOT NULL, " + + "`confirmations` INTEGER NOT NULL, " + + "`hash` TEXT, " + + "`timestamp` INTEGER NOT NULL," + + " `paymentId` TEXT," + + " `txKey` TEXT, " + + "`address` TEXT)") + + } + } + fun getInstance(context: Context = App.instance): AppDatabase = INSTANCE ?: synchronized(this) { INSTANCE ?: buildDatabase(context).also { INSTANCE = it } @@ -35,6 +55,7 @@ abstract class AppDatabase : RoomDatabase() { private fun buildDatabase(context: Context) = Room.databaseBuilder(context.applicationContext, AppDatabase::class.java, "Wallet.db") + .addMigrations(MIGRATION_1_2) .build() } diff --git a/app/src/main/java/io/wookey/wallet/data/dao/AssetDao.kt b/app/src/main/java/io/wookey/wallet/data/dao/AssetDao.kt index 81bf906..61d54d3 100644 --- a/app/src/main/java/io/wookey/wallet/data/dao/AssetDao.kt +++ b/app/src/main/java/io/wookey/wallet/data/dao/AssetDao.kt @@ -62,4 +62,12 @@ interface AssetDao { */ @Update(onConflict = OnConflictStrategy.REPLACE) fun updateAsset(asset: Asset) + + /** + * Delete assets in the database + * + * @param assets the assets to be deleted. + */ + @Delete + fun deleteAssets(vararg assets: Asset) } \ No newline at end of file diff --git a/app/src/main/java/io/wookey/wallet/data/dao/NodeDao.kt b/app/src/main/java/io/wookey/wallet/data/dao/NodeDao.kt index a015599..1ae4c51 100644 --- a/app/src/main/java/io/wookey/wallet/data/dao/NodeDao.kt +++ b/app/src/main/java/io/wookey/wallet/data/dao/NodeDao.kt @@ -15,6 +15,14 @@ interface NodeDao { @Insert(onConflict = OnConflictStrategy.IGNORE) fun insertNodes(vararg nodes: Node) + /** + * Insert nodes in the database. If the nodes already exists, ignore it. + * + * @param nodes the nodes to be inserted. + */ + @Insert(onConflict = OnConflictStrategy.REPLACE) + fun insertNodesReplace(vararg nodes: Node) + /** * Select all nodes is selected from the nodes table. * diff --git a/app/src/main/java/io/wookey/wallet/data/dao/TransactionInfoDao.kt b/app/src/main/java/io/wookey/wallet/data/dao/TransactionInfoDao.kt new file mode 100644 index 0000000..e0b8d33 --- /dev/null +++ b/app/src/main/java/io/wookey/wallet/data/dao/TransactionInfoDao.kt @@ -0,0 +1,40 @@ +package io.wookey.wallet.data.dao + +import android.arch.persistence.room.* +import io.wookey.wallet.data.entity.TransactionInfo + +@Dao +interface TransactionInfoDao { + + /** + * Insert a transactionInfo in the database. If the transactionInfo already exists, replace it. + * + * @param transactionInfo the transactionInfo to be inserted. + */ + @Insert(onConflict = OnConflictStrategy.REPLACE) + fun insertTransactionInfo(vararg transactionInfo: TransactionInfo) + + /** + * Select all transactionInfo by assetId from the transactionInfo table. + * + * @return all transactionInfo by assetId. + */ + @Query("SELECT * FROM transactionInfo WHERE assetId = :assetId") + fun getTransactionInfoByAssetId(assetId: Int): List + + /** + * Select all transactionInfo by walletId from the transactionInfo table. + * + * @return all transactionInfo by walletId. + */ + @Query("SELECT * FROM transactionInfo WHERE walletId = :walletId") + fun getTransactionInfoByWalletId(walletId: Int): List + + /** + * Delete transactionInfo in the database + * + * @param transactionInfo the transactionInfo to be deleted. + */ + @Delete + fun deleteTransactionInfo(vararg transactionInfo: TransactionInfo) +} \ No newline at end of file diff --git a/app/src/main/java/io/wookey/wallet/data/dao/WalletDao.kt b/app/src/main/java/io/wookey/wallet/data/dao/WalletDao.kt index 40b16c9..8aa6218 100644 --- a/app/src/main/java/io/wookey/wallet/data/dao/WalletDao.kt +++ b/app/src/main/java/io/wookey/wallet/data/dao/WalletDao.kt @@ -39,6 +39,14 @@ interface WalletDao { @Query("SELECT COUNT(*) FROM wallets WHERE symbol = :symbol AND name = :name") fun countWalletsByName(symbol: String, name: String): Int + /** + * Select wallets from the wallets table. + * + * @return wallets. + */ + @Query("SELECT * FROM wallets") + fun getWallets(): List + /** * Select the active wallet from the wallets table. * @@ -94,4 +102,12 @@ interface WalletDao { */ @Update(onConflict = OnConflictStrategy.REPLACE) fun updateWallets(vararg wallets: Wallet) + + /** + * Delete wallets in the database + * + * @param wallets the wallets to be deleted. + */ + @Delete + fun deleteWallets(vararg wallets: Wallet) } \ No newline at end of file diff --git a/app/src/main/java/io/wookey/wallet/data/entity/TransactionInfo.kt b/app/src/main/java/io/wookey/wallet/data/entity/TransactionInfo.kt index f317726..78bf379 100644 --- a/app/src/main/java/io/wookey/wallet/data/entity/TransactionInfo.kt +++ b/app/src/main/java/io/wookey/wallet/data/entity/TransactionInfo.kt @@ -1,22 +1,43 @@ package io.wookey.wallet.data.entity +import android.arch.persistence.room.* import android.os.Parcelable import kotlinx.android.parcel.Parcelize @Parcelize +@Entity(tableName = "transactionInfo") data class TransactionInfo( + @PrimaryKey(autoGenerate = true) + @ColumnInfo(name = "_id") + var id: Int = 0, + @ColumnInfo var token: String = "", + @ColumnInfo var assetId: Int = 0, + @ColumnInfo + var walletId: Int = 0, + @ColumnInfo var direction: Int = 0,// 0 接收 1 发送 + @ColumnInfo var isPending: Boolean = false, + @ColumnInfo var isFailed: Boolean = false, + @ColumnInfo var amount: String? = "", + @ColumnInfo var fee: String? = "", + @ColumnInfo var blockHeight: Long = 0, + @ColumnInfo var confirmations: Long = 0, + @ColumnInfo var hash: String? = "", + @ColumnInfo var timestamp: Long = 0, + @ColumnInfo var paymentId: String? = "", + @ColumnInfo var txKey: String? = "", + @ColumnInfo var address: String? = "" ) : Parcelable \ No newline at end of file diff --git a/app/src/main/java/io/wookey/wallet/dialog/BakMnemonicDialog.kt b/app/src/main/java/io/wookey/wallet/dialog/BakMnemonicDialog.kt index 4ed628f..5e719c4 100644 --- a/app/src/main/java/io/wookey/wallet/dialog/BakMnemonicDialog.kt +++ b/app/src/main/java/io/wookey/wallet/dialog/BakMnemonicDialog.kt @@ -43,10 +43,6 @@ class BakMnemonicDialog : DialogFragment() { dismiss() } - cancel.setOnClickListener { - cancelListener?.invoke() - dismiss() - } } companion object { diff --git a/app/src/main/java/io/wookey/wallet/feature/asset/AssetDetailActivity.kt b/app/src/main/java/io/wookey/wallet/feature/asset/AssetDetailActivity.kt index c2fdd02..2b2a6c1 100644 --- a/app/src/main/java/io/wookey/wallet/feature/asset/AssetDetailActivity.kt +++ b/app/src/main/java/io/wookey/wallet/feature/asset/AssetDetailActivity.kt @@ -12,7 +12,9 @@ import io.wookey.wallet.R import io.wookey.wallet.base.BaseTitleSecondActivity import io.wookey.wallet.data.AppDatabase import io.wookey.wallet.dialog.PasswordDialog +import io.wookey.wallet.feature.setting.NodeListActivity import io.wookey.wallet.support.BackgroundHelper +import io.wookey.wallet.support.REQUEST_SELECT_NODE import io.wookey.wallet.support.extensions.copy import io.wookey.wallet.support.extensions.dp2px import io.wookey.wallet.support.extensions.formatterAmountStrip @@ -42,9 +44,15 @@ class AssetDetailActivity : BaseTitleSecondActivity() { return } - setRightIcon(R.drawable.icon_refresh) + viewModel.setAssetId(assetId) + + setRightIcon(R.drawable.icon_switch_node) setRightIconClick(View.OnClickListener { - viewModel.refreshWallet() + val symbol = viewModel.activeWallet.value?.symbol ?: return@OnClickListener + startActivityForResult(Intent(this, NodeListActivity::class.java).apply { + putExtra("symbol", symbol) + putExtra("canDelete", false) + }, REQUEST_SELECT_NODE) }) viewModel.loadWallet(password) @@ -199,4 +207,8 @@ class AssetDetailActivity : BaseTitleSecondActivity() { }) } + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + viewModel.handleResult(requestCode, resultCode, data) + } } \ No newline at end of file diff --git a/app/src/main/java/io/wookey/wallet/feature/asset/AssetDetailViewModel.kt b/app/src/main/java/io/wookey/wallet/feature/asset/AssetDetailViewModel.kt index b61ca22..46f2370 100644 --- a/app/src/main/java/io/wookey/wallet/feature/asset/AssetDetailViewModel.kt +++ b/app/src/main/java/io/wookey/wallet/feature/asset/AssetDetailViewModel.kt @@ -1,6 +1,8 @@ package io.wookey.wallet.feature.asset +import android.app.Activity import android.arch.lifecycle.MutableLiveData +import android.content.Intent import android.util.Log import io.wookey.wallet.R import io.wookey.wallet.base.BaseViewModel @@ -8,9 +10,10 @@ import io.wookey.wallet.core.XMRRepository import io.wookey.wallet.core.XMRWalletController import io.wookey.wallet.data.AppDatabase import io.wookey.wallet.data.entity.Asset +import io.wookey.wallet.data.entity.Node import io.wookey.wallet.data.entity.TransactionInfo import io.wookey.wallet.data.entity.Wallet -import io.wookey.wallet.support.nodeArray +import io.wookey.wallet.support.REQUEST_SELECT_NODE import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -44,12 +47,61 @@ class AssetDetailViewModel : BaseViewModel() { var password: String? = null var activeAsset: Asset? = null private var refreshEnabled = false + private var currentNode: Node? = null + + private val observer = object : XMRWalletController.Observer { + var firstBlock = 0L + + override fun onWalletOpened() { + receiveEnabled.postValue(true) + } + + override fun onWalletOpenFailed(error: String?) { + failed() + } + + override fun onWalletStarted() {} + + override fun onWalletStartFailed(error: String?) { + failed() + } + + override fun onRefreshed(height: Long?) { + firstBlock = refresh(firstBlock) + } + } override fun onCleared() { super.onCleared() XMRWalletController.stopRefresh() } + fun setAssetId(assetId: Int) { + uiScope.launch { + try { + withContext(Dispatchers.IO) { + val dao = AppDatabase.getInstance().transactionInfoDao() + val list = dao.getTransactionInfoByAssetId(assetId) + convertData(list) + } + } catch (e: Exception) { + e.printStackTrace() + } + } + } + + private fun convertData(list: List) { + val allInfo = mutableListOf() + val inInfo = mutableListOf() + val outInfo = mutableListOf() + allInfo.addAll(list) + allTransfers.postValue(allInfo) + inInfo.addAll(list.filter { it.direction == 0 }) + inTransfers.postValue(inInfo) + outInfo.addAll(list.filter { it.direction == 1 }) + outTransfers.postValue(outInfo) + } + fun loadWallet(pwd: String) { password = pwd @@ -65,34 +117,14 @@ class AssetDetailViewModel : BaseViewModel() { ?: throw IllegalStateException() activeWallet.postValue(wallet) - AppDatabase.getInstance().nodeDao().insertNodes(nodes = *nodeArray) + repository.insertNodes() val node = AppDatabase.getInstance().nodeDao().getSymbolNode(wallet.symbol) ?: throw IllegalStateException() - + currentNode = node val split = node.url.split(":") XMRWalletController.setNode(split[0], split[1].toInt()) val path = repository.getWalletFilePath(wallet.name) - val observer = object : XMRWalletController.Observer { - var firstBlock = 0L - - override fun onWalletOpened() { - receiveEnabled.postValue(true) - } - override fun onWalletOpenFailed(error: String?) { - failed() - } - - override fun onWalletStarted() {} - - override fun onWalletStartFailed(error: String?) { - failed() - } - - override fun onRefreshed(height: Long?) { - firstBlock = refresh(firstBlock) - } - } XMRWalletController.startWallet(path, pwd, wallet.restoreHeight, observer) } } catch (e: Exception) { @@ -117,13 +149,16 @@ class AssetDetailViewModel : BaseViewModel() { sendEnabled.postValue(true) synchronized.postValue(R.string.block_synchronized) synchronizeProgress.postValue(100) + updateBalance() + updateHistory() } else { sendEnabled.postValue(false) // calculate progress val daemonHeight = XMRWalletController.getDaemonBlockChainHeight() val blockChainHeight = XMRWalletController.getBlockChainHeight() val n = daemonHeight - blockChainHeight - Log.d("refresh", "daemonHeight: $daemonHeight, blockChainHeight: $blockChainHeight, n: $n, daemonBlockChainTargetHeight: ${XMRWalletController.getDaemonBlockChainTargetHeight()}") + Log.d("refresh", "daemonHeight: $daemonHeight, blockChainHeight: $blockChainHeight, n: $n, " + + "daemonBlockChainTargetHeight: ${XMRWalletController.getDaemonBlockChainTargetHeight()}") if (n >= 0) { if (firstBlockHeight == 0L) { firstBlockHeight = blockChainHeight @@ -137,8 +172,7 @@ class AssetDetailViewModel : BaseViewModel() { synchronizeProgress.postValue(100) } } - updateBalance() - updateHistory() + return firstBlockHeight } @@ -161,35 +195,57 @@ class AssetDetailViewModel : BaseViewModel() { @Synchronized private fun updateHistory() { val asset = activeAsset ?: return + val wallet = activeWallet.value ?: return val value = allTransfers.value XMRWalletController.refreshTransactionHistory() - if (value.isNullOrEmpty() || value.size < XMRWalletController.getTransactionHistoryCount()) { - val list = XMRWalletController.getTransactionHistory() - list.forEach { - it.token = asset.token - it.assetId = asset.id - } - val allInfo = mutableListOf() - val inInfo = mutableListOf() - val outInfo = mutableListOf() - allInfo.addAll(list) - allTransfers.postValue(allInfo) - inInfo.addAll(list.filter { it.direction == 0 }) - inTransfers.postValue(inInfo) - outInfo.addAll(list.filter { it.direction == 1 }) - outTransfers.postValue(outInfo) + val list = XMRWalletController.getTransactionHistory() + list.forEach { + it.token = asset.token + it.assetId = asset.id + it.walletId = wallet.id + } + if (value.isNullOrEmpty() || !(value.toTypedArray() contentDeepEquals list.toTypedArray())) { + val dao = AppDatabase.getInstance().transactionInfoDao() + val info = dao.getTransactionInfoByAssetId(asset.id) + dao.deleteTransactionInfo(*info.toTypedArray()) + dao.insertTransactionInfo(*list.toTypedArray()) + convertData(list) } } - fun refreshWallet() { - if (!refreshEnabled) { - return - } - refreshEnabled = false - if (password.isNullOrBlank()) { - showPasswordDialog.postValue(true) + private fun switchNode(node: Node) { + + val activeWallet = activeWallet.value ?: return + val wallet = XMRWalletController.getWallet() + // 异常处理 + if (wallet == null) { + if (password.isNullOrBlank()) { + showPasswordDialog.postValue(true) + } else { + loadWallet(password!!) + } } else { - refreshWallet.postValue(true) + // 相同节点直接返回 + val curNode = currentNode + if (curNode != null && curNode.id == node.id) { + return + } + currentNode = node + uiScope.launch { + try { + withContext(Dispatchers.IO) { + XMRWalletController.stopRefresh() + indeterminate.postValue(null) + connecting.postValue(R.string.block_connecting) + val split = node.url.split(":") + XMRWalletController.setNode(split[0], split[1].toInt()) + XMRWalletController.startRefresh(wallet, activeWallet.restoreHeight, observer) + } + } catch (e: Exception) { + e.printStackTrace() + failed() + } + } } } @@ -201,4 +257,16 @@ class AssetDetailViewModel : BaseViewModel() { openReceive.value = true } + fun handleResult(requestCode: Int, resultCode: Int, data: Intent?) { + if (resultCode != Activity.RESULT_OK) { + return + } + when (requestCode) { + REQUEST_SELECT_NODE -> { + val node = data?.getParcelableExtra("node") ?: return + switchNode(node) + } + } + } + } \ No newline at end of file diff --git a/app/src/main/java/io/wookey/wallet/feature/generate/GenerateWalletViewModel.kt b/app/src/main/java/io/wookey/wallet/feature/generate/GenerateWalletViewModel.kt index e52228a..9914ee2 100644 --- a/app/src/main/java/io/wookey/wallet/feature/generate/GenerateWalletViewModel.kt +++ b/app/src/main/java/io/wookey/wallet/feature/generate/GenerateWalletViewModel.kt @@ -77,6 +77,7 @@ class GenerateWalletViewModel : BaseViewModel() { walletValidJob?.cancel() if (it.isNullOrBlank()) { walletNameError.value = null + walletName.value = it } else { walletValidJob = uiScope.launch { withContext(Dispatchers.IO) { @@ -87,10 +88,10 @@ class GenerateWalletViewModel : BaseViewModel() { } else { walletNameError.postValue(false) } + walletName.postValue(it) } } } - walletName.value = it } fun setPassword(it: String) { @@ -237,7 +238,7 @@ class GenerateWalletViewModel : BaseViewModel() { }) } catch (e: Exception) { e.printStackTrace() - repository.cancelCreate(name) + repository.deleteWallet(name) hideLoading.postValue(true) toast.postValue(e.message) } finally { diff --git a/app/src/main/java/io/wookey/wallet/feature/generate/create/BackupMnemonicActivity.kt b/app/src/main/java/io/wookey/wallet/feature/generate/create/BackupMnemonicActivity.kt index 6f2c463..ee1465c 100644 --- a/app/src/main/java/io/wookey/wallet/feature/generate/create/BackupMnemonicActivity.kt +++ b/app/src/main/java/io/wookey/wallet/feature/generate/create/BackupMnemonicActivity.kt @@ -6,7 +6,10 @@ import android.content.Intent import android.os.Bundle import android.support.v7.widget.GridLayoutManager import android.support.v7.widget.RecyclerView -import android.view.* +import android.view.KeyEvent +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup import io.wookey.wallet.R import io.wookey.wallet.base.BaseTitleSecondActivity import io.wookey.wallet.dialog.BakMnemonicDialog @@ -28,8 +31,7 @@ class BackupMnemonicActivity : BaseTitleSecondActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - // 禁止截图 - window.setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE) + setContentView(R.layout.activity_backup_mnemonic) walletId = intent.getIntExtra("walletId", -1) diff --git a/app/src/main/java/io/wookey/wallet/feature/generate/create/VerifyMnemonicActivity.kt b/app/src/main/java/io/wookey/wallet/feature/generate/create/VerifyMnemonicActivity.kt index 19a30f3..e1baa3e 100644 --- a/app/src/main/java/io/wookey/wallet/feature/generate/create/VerifyMnemonicActivity.kt +++ b/app/src/main/java/io/wookey/wallet/feature/generate/create/VerifyMnemonicActivity.kt @@ -7,7 +7,6 @@ import android.support.v7.widget.RecyclerView import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.view.WindowManager import com.google.android.flexbox.* import io.wookey.wallet.ActivityStackManager import io.wookey.wallet.MainActivity @@ -28,8 +27,7 @@ class VerifyMnemonicActivity : BaseTitleSecondActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - // 禁止截图 - window.setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE) + setContentView(R.layout.activity_verify_mnemonic) setCenterTitle(R.string.create_wallet) diff --git a/app/src/main/java/io/wookey/wallet/feature/generate/recovery/RecoveryMnemonicFragment.kt b/app/src/main/java/io/wookey/wallet/feature/generate/recovery/RecoveryMnemonicFragment.kt index 941c513..478517f 100644 --- a/app/src/main/java/io/wookey/wallet/feature/generate/recovery/RecoveryMnemonicFragment.kt +++ b/app/src/main/java/io/wookey/wallet/feature/generate/recovery/RecoveryMnemonicFragment.kt @@ -13,10 +13,8 @@ import io.wookey.wallet.R import io.wookey.wallet.base.BaseFragment import io.wookey.wallet.feature.wallet.WalletManagerActivity import io.wookey.wallet.support.BackgroundHelper -import io.wookey.wallet.support.extensions.afterTextChanged -import io.wookey.wallet.support.extensions.formatterDate -import io.wookey.wallet.support.extensions.showTimePicker -import io.wookey.wallet.support.extensions.toast +import io.wookey.wallet.support.extensions.* +import io.wookey.wallet.widget.IOSDialog import kotlinx.android.synthetic.main.fragment_recovery_mnemonic.* @@ -88,6 +86,21 @@ class RecoveryMnemonicFragment : BaseFragment() { blockHeight.error = null } }) + + viewModel.showDialog.observe(this, Observer { + IOSDialog(context) + .radius(dp2px(5)) + .titleText("") + .contentText(getString(R.string.dialog_block_height_content)) + .contentTextSize(16) + .contentTextBold(true) + .leftText(getString(R.string.dialog_block_height_cancel)) + .rightText(getString(R.string.dialog_block_height_confirm)) + .setIOSDialogLeftListener { viewModel.create() } + .cancelAble(true) + .layout() + .show() + }) } private fun navigation() { diff --git a/app/src/main/java/io/wookey/wallet/feature/generate/recovery/RecoveryMnemonicViewModel.kt b/app/src/main/java/io/wookey/wallet/feature/generate/recovery/RecoveryMnemonicViewModel.kt index 7b04314..274f35d 100644 --- a/app/src/main/java/io/wookey/wallet/feature/generate/recovery/RecoveryMnemonicViewModel.kt +++ b/app/src/main/java/io/wookey/wallet/feature/generate/recovery/RecoveryMnemonicViewModel.kt @@ -24,6 +24,8 @@ class RecoveryMnemonicViewModel : BaseViewModel() { val blockHeightError = MutableLiveData() + val showDialog = MutableLiveData() + private val repository = XMRRepository() private lateinit var walletName: String @@ -87,6 +89,16 @@ class RecoveryMnemonicViewModel : BaseViewModel() { } fun next() { + enabled.value = false + if (blockHeight <= 0L) { + showDialog.value = true + enabled.value = true + return + } + create() + } + + fun create() { enabled.value = false showLoading.postValue(true) uiScope.launch { @@ -99,7 +111,7 @@ class RecoveryMnemonicViewModel : BaseViewModel() { navigation.postValue(true) } catch (e: Exception) { e.printStackTrace() - repository.cancelCreate(walletName) + repository.deleteWallet(walletName) hideLoading.postValue(true) toast.postValue(e.message) } finally { diff --git a/app/src/main/java/io/wookey/wallet/feature/generate/recovery/RecoveryPrivateKeyFragment.kt b/app/src/main/java/io/wookey/wallet/feature/generate/recovery/RecoveryPrivateKeyFragment.kt index afe169f..eda249c 100644 --- a/app/src/main/java/io/wookey/wallet/feature/generate/recovery/RecoveryPrivateKeyFragment.kt +++ b/app/src/main/java/io/wookey/wallet/feature/generate/recovery/RecoveryPrivateKeyFragment.kt @@ -14,6 +14,7 @@ import io.wookey.wallet.base.BaseFragment import io.wookey.wallet.feature.wallet.WalletManagerActivity import io.wookey.wallet.support.BackgroundHelper import io.wookey.wallet.support.extensions.* +import io.wookey.wallet.widget.IOSDialog import kotlinx.android.synthetic.main.fragment_recovery_private_key.* class RecoveryPrivateKeyFragment : BaseFragment() { @@ -95,6 +96,21 @@ class RecoveryPrivateKeyFragment : BaseFragment() { blockHeight.error = null } }) + + viewModel.showDialog.observe(this, Observer { + IOSDialog(context) + .radius(dp2px(5)) + .titleText("") + .contentText(getString(R.string.dialog_block_height_content)) + .contentTextSize(16) + .contentTextBold(true) + .leftText(getString(R.string.dialog_block_height_cancel)) + .rightText(getString(R.string.dialog_block_height_confirm)) + .setIOSDialogLeftListener { viewModel.create() } + .cancelAble(true) + .layout() + .show() + }) } private fun navigation() { diff --git a/app/src/main/java/io/wookey/wallet/feature/generate/recovery/RecoveryPrivateKeyViewModel.kt b/app/src/main/java/io/wookey/wallet/feature/generate/recovery/RecoveryPrivateKeyViewModel.kt index 86aec98..1365f7e 100644 --- a/app/src/main/java/io/wookey/wallet/feature/generate/recovery/RecoveryPrivateKeyViewModel.kt +++ b/app/src/main/java/io/wookey/wallet/feature/generate/recovery/RecoveryPrivateKeyViewModel.kt @@ -26,6 +26,8 @@ class RecoveryPrivateKeyViewModel : BaseViewModel() { val blockHeightError = MutableLiveData() + val showDialog = MutableLiveData() + private val repository = XMRRepository() private lateinit var walletName: String @@ -97,6 +99,16 @@ class RecoveryPrivateKeyViewModel : BaseViewModel() { } fun next() { + enabled.value = false + if (blockHeight <= 0L) { + showDialog.value = true + enabled.value = true + return + } + create() + } + + fun create() { enabled.value = false showLoading.postValue(true) uiScope.launch { @@ -109,7 +121,7 @@ class RecoveryPrivateKeyViewModel : BaseViewModel() { navigation.postValue(true) } catch (e: Exception) { e.printStackTrace() - repository.cancelCreate(walletName) + repository.deleteWallet(walletName) hideLoading.postValue(true) toast.postValue(e.message) } finally { diff --git a/app/src/main/java/io/wookey/wallet/feature/generate/recovery/RecoveryWalletActivity.kt b/app/src/main/java/io/wookey/wallet/feature/generate/recovery/RecoveryWalletActivity.kt index 7cafa00..5b1ca8d 100644 --- a/app/src/main/java/io/wookey/wallet/feature/generate/recovery/RecoveryWalletActivity.kt +++ b/app/src/main/java/io/wookey/wallet/feature/generate/recovery/RecoveryWalletActivity.kt @@ -3,7 +3,6 @@ package io.wookey.wallet.feature.generate.recovery import android.os.Bundle import android.support.v4.app.Fragment import android.support.v4.app.FragmentPagerAdapter -import android.view.WindowManager.LayoutParams import io.wookey.wallet.R import io.wookey.wallet.base.BaseTitleSecondActivity import kotlinx.android.synthetic.main.activity_recovery_wallet.* @@ -12,8 +11,7 @@ class RecoveryWalletActivity : BaseTitleSecondActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - // 禁止截图 - window.setFlags(LayoutParams.FLAG_SECURE, LayoutParams.FLAG_SECURE) + setContentView(R.layout.activity_recovery_wallet) setCenterTitle(R.string.recovery_wallet) diff --git a/app/src/main/java/io/wookey/wallet/feature/setting/NodeListActivity.kt b/app/src/main/java/io/wookey/wallet/feature/setting/NodeListActivity.kt index d5193c8..4d97302 100644 --- a/app/src/main/java/io/wookey/wallet/feature/setting/NodeListActivity.kt +++ b/app/src/main/java/io/wookey/wallet/feature/setting/NodeListActivity.kt @@ -1,5 +1,6 @@ package io.wookey.wallet.feature.setting +import android.app.Activity import android.arch.lifecycle.Observer import android.arch.lifecycle.ViewModelProviders import android.content.Intent @@ -32,6 +33,8 @@ class NodeListActivity : BaseTitleSecondActivity() { val viewModel = ViewModelProviders.of(this).get(NodeListViewModel::class.java) val symbol = intent.getStringExtra("symbol") + val canDelete = intent.getBooleanExtra("canDelete", true) + viewModel.setCanDelete(canDelete) setCenterTitle("$symbol ${getString(R.string.node_setting)}") setRightIcon(R.drawable.icon_add) setRightIconClick(View.OnClickListener { _ -> @@ -80,13 +83,17 @@ class NodeListActivity : BaseTitleSecondActivity() { viewModel.showLoading.observe(this, Observer { showLoading() }) viewModel.hideLoading.observe(this, Observer { hideLoading() }) - + viewModel.toast.observe(this, Observer { toast(it) }) viewModel.dataChanged.observe(this, Observer { adapter.notifyDataSetChanged() }) more.setOnClickListener { openBrowser("https://wallet.wookey.io/monero-nodes/app.html") } + viewModel.finish.observe(this, Observer { + setResult(Activity.RESULT_OK, Intent().apply { putExtra("node", it) }) + finish() + }) } private fun openBrowser(url: String) { diff --git a/app/src/main/java/io/wookey/wallet/feature/setting/NodeListViewModel.kt b/app/src/main/java/io/wookey/wallet/feature/setting/NodeListViewModel.kt index c163391..9c73e17 100644 --- a/app/src/main/java/io/wookey/wallet/feature/setting/NodeListViewModel.kt +++ b/app/src/main/java/io/wookey/wallet/feature/setting/NodeListViewModel.kt @@ -7,6 +7,7 @@ import io.wookey.wallet.core.XMRWalletController import io.wookey.wallet.data.AppDatabase import io.wookey.wallet.data.entity.Node import io.wookey.wallet.support.nodeArray +import io.wookey.wallet.support.viewmodel.SingleLiveEvent import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -17,10 +18,14 @@ class NodeListViewModel : BaseViewModel() { val showLoading = MutableLiveData() val hideLoading = MutableLiveData() + val toast = MutableLiveData() val toastRes = MutableLiveData() val dataChanged = MutableLiveData() + val finish = SingleLiveEvent() + private var deleteNode: Node? = null + private var canDelete = true fun insertNode(node: Node) { uiScope.launch { @@ -33,27 +38,36 @@ class NodeListViewModel : BaseViewModel() { fun updateNode(nodes: List, node: Node) { uiScope.launch { - withContext(Dispatchers.IO) { - val nodeDao = AppDatabase.getInstance().nodeDao() - val filter = nodes.filter { - it.isSelected - } - if (filter.isNullOrEmpty()) { - return@withContext - } - if (filter.size != 1) { - return@withContext + try { + withContext(Dispatchers.IO) { + val nodeDao = AppDatabase.getInstance().nodeDao() + val filter = nodes.filter { + it.isSelected + } + if (filter.isNullOrEmpty()) { + return@withContext + } + if (filter.size != 1) { + return@withContext + } + nodeDao.updateNodes(filter[0].apply { isSelected = false }, node.apply { isSelected = true }) } - nodeDao.updateNodes(filter[0].apply { isSelected = false }, node.apply { isSelected = true }) + finish.postValue(node) + } catch (e: Exception) { + toast.postValue(e.message) } } } fun onLongClick(node: Node) { + + if (!canDelete) return + var isDefault = false nodeArray.forEach { if (it.symbol == node.symbol && it.url == node.url) { isDefault = true + return@forEach } } if (isDefault) { @@ -105,4 +119,8 @@ class NodeListViewModel : BaseViewModel() { dataChanged.postValue(true) } } + + fun setCanDelete(canDelete: Boolean) { + this.canDelete = canDelete + } } \ No newline at end of file diff --git a/app/src/main/java/io/wookey/wallet/feature/setting/NodeSettingViewModel.kt b/app/src/main/java/io/wookey/wallet/feature/setting/NodeSettingViewModel.kt index f30452a..9d2da97 100644 --- a/app/src/main/java/io/wookey/wallet/feature/setting/NodeSettingViewModel.kt +++ b/app/src/main/java/io/wookey/wallet/feature/setting/NodeSettingViewModel.kt @@ -1,17 +1,19 @@ package io.wookey.wallet.feature.setting import io.wookey.wallet.base.BaseViewModel -import io.wookey.wallet.data.AppDatabase -import io.wookey.wallet.support.nodeArray +import io.wookey.wallet.core.XMRRepository import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext class NodeSettingViewModel : BaseViewModel() { + + private val repository = XMRRepository() + init { uiScope.launch { withContext(Dispatchers.IO) { - AppDatabase.getInstance().nodeDao().insertNodes(nodes = *nodeArray) + repository.insertNodes() } } } diff --git a/app/src/main/java/io/wookey/wallet/feature/wallet/BackupKeyActivity.kt b/app/src/main/java/io/wookey/wallet/feature/wallet/BackupKeyActivity.kt index e337565..7516338 100644 --- a/app/src/main/java/io/wookey/wallet/feature/wallet/BackupKeyActivity.kt +++ b/app/src/main/java/io/wookey/wallet/feature/wallet/BackupKeyActivity.kt @@ -3,7 +3,6 @@ package io.wookey.wallet.feature.wallet import android.arch.lifecycle.Observer import android.arch.lifecycle.ViewModelProviders import android.os.Bundle -import android.view.WindowManager import io.wookey.wallet.R import io.wookey.wallet.base.BaseTitleSecondActivity import io.wookey.wallet.support.extensions.copy @@ -13,8 +12,7 @@ import kotlinx.android.synthetic.main.activity_backup_key.* class BackupKeyActivity : BaseTitleSecondActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - // 禁止截图 - window.setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE) + setContentView(R.layout.activity_backup_key) setCenterTitle(R.string.backup_key) diff --git a/app/src/main/java/io/wookey/wallet/feature/wallet/WalletDetailActivity.kt b/app/src/main/java/io/wookey/wallet/feature/wallet/WalletDetailActivity.kt index 3d8d49a..f59e14c 100644 --- a/app/src/main/java/io/wookey/wallet/feature/wallet/WalletDetailActivity.kt +++ b/app/src/main/java/io/wookey/wallet/feature/wallet/WalletDetailActivity.kt @@ -2,16 +2,19 @@ package io.wookey.wallet.feature.wallet import android.arch.lifecycle.Observer import android.arch.lifecycle.ViewModelProviders +import android.content.Intent import android.os.Bundle import io.wookey.wallet.R import io.wookey.wallet.base.BaseTitleSecondActivity import io.wookey.wallet.dialog.PasswordDialog import io.wookey.wallet.dialog.PasswordPromptDialog +import io.wookey.wallet.feature.generate.WalletActivity import io.wookey.wallet.feature.generate.create.BackupMnemonicActivity import io.wookey.wallet.support.BackgroundHelper import io.wookey.wallet.support.extensions.copy import io.wookey.wallet.support.extensions.dp2px import io.wookey.wallet.support.extensions.formatterAmountStrip +import io.wookey.wallet.support.extensions.toast import kotlinx.android.synthetic.main.activity_wallet_detail.* class WalletDetailActivity : BaseTitleSecondActivity() { @@ -44,6 +47,7 @@ class WalletDetailActivity : BaseTitleSecondActivity() { passwordPrompt.setOnClickListener { viewModel.onPasswordPromptClick() } backupMnemonic.setOnClickListener { viewModel.onBackupMnemonicClick() } backupKey.setOnClickListener { viewModel.onBackupKeyClick() } + delete.setOnClickListener { viewModel.onDeleteClick() } viewModel.showPasswordPrompt.observe(this, Observer { value -> value?.let { @@ -78,5 +82,24 @@ class WalletDetailActivity : BaseTitleSecondActivity() { }) } }) + + viewModel.deleteWallet.observe(this, Observer { + PasswordDialog.display(supportFragmentManager, walletId) { value -> + viewModel.deleteWallet() + } + }) + + viewModel.showLoading.observe(this, Observer { showLoading() }) + viewModel.hideLoading.observe(this, Observer { hideLoading() }) + + viewModel.toast.observe(this, Observer { toast(it) }) + viewModel.toastRes.observe(this, Observer { toast(it) }) + viewModel.finish.observe(this, Observer { finish() }) + viewModel.restart.observe(this, Observer { + startActivity(Intent(this, WalletActivity::class.java).apply { + flags = Intent.FLAG_ACTIVITY_NEW_TASK.or(Intent.FLAG_ACTIVITY_CLEAR_TASK) + }) + finish() + }) } } \ No newline at end of file diff --git a/app/src/main/java/io/wookey/wallet/feature/wallet/WalletDetailViewModel.kt b/app/src/main/java/io/wookey/wallet/feature/wallet/WalletDetailViewModel.kt index 8d3f9b1..9a51871 100644 --- a/app/src/main/java/io/wookey/wallet/feature/wallet/WalletDetailViewModel.kt +++ b/app/src/main/java/io/wookey/wallet/feature/wallet/WalletDetailViewModel.kt @@ -2,7 +2,10 @@ package io.wookey.wallet.feature.wallet import android.arch.lifecycle.MutableLiveData import android.content.Intent +import io.wookey.wallet.R import io.wookey.wallet.base.BaseViewModel +import io.wookey.wallet.core.XMRRepository +import io.wookey.wallet.core.XMRWalletController import io.wookey.wallet.data.AppDatabase import io.wookey.wallet.data.entity.Wallet import io.wookey.wallet.support.viewmodel.SingleLiveEvent @@ -16,11 +19,21 @@ class WalletDetailViewModel : BaseViewModel() { val showPasswordPrompt = MutableLiveData() val backupMnemonic = SingleLiveEvent() val backupKey = SingleLiveEvent() + val deleteWallet = SingleLiveEvent() val openBackupMnemonic = MutableLiveData() val openBackupKey = MutableLiveData() + val showLoading = MutableLiveData() + val hideLoading = MutableLiveData() + val toast = MutableLiveData() + val toastRes = MutableLiveData() + val finish = SingleLiveEvent() + val restart = SingleLiveEvent() + private var walletId = -1 + private val repository = XMRRepository() + fun setWalletId(value: Int) { walletId = value uiScope.launch { @@ -56,4 +69,54 @@ class WalletDetailViewModel : BaseViewModel() { putExtra("password", it) } } + + fun onDeleteClick() { + deleteWallet.call() + } + + fun deleteWallet() { + showLoading.value = true + uiScope.launch { + try { + withContext(Dispatchers.IO) { + val wallet = AppDatabase.getInstance().walletDao().getWalletById(walletId) + if (wallet == null) { + toastRes.postValue(R.string.data_exception) + return@withContext + } + // 停止钱包 + XMRWalletController.stopWallet() + if (repository.deleteWallet(wallet.name)) { + // 删除交易记录 + val info = AppDatabase.getInstance().transactionInfoDao().getTransactionInfoByWalletId(walletId) + AppDatabase.getInstance().transactionInfoDao().deleteTransactionInfo(*info.toTypedArray()) + // 删除资产 + val assets = AppDatabase.getInstance().assetDao().getAssetsByWalletId(walletId) + AppDatabase.getInstance().assetDao().deleteAssets(*assets.toTypedArray()) + // 删除钱包 + AppDatabase.getInstance().walletDao().deleteWallets(wallet) + + val wallets = AppDatabase.getInstance().walletDao().getWallets() + if (wallet.isActive && wallets.isNotEmpty()) { + AppDatabase.getInstance().walletDao().updateWallets(wallets[0].apply { isActive = true }) + } + toastRes.postValue(R.string.delete_success) + hideLoading.postValue(true) + if (wallets.isNotEmpty()) { + finish.postValue(true) + } else { + restart.postValue(true) + } + } else { + toastRes.postValue(R.string.delete_failed) + hideLoading.postValue(true) + } + } + } catch (e: Exception) { + e.printStackTrace() + toast.postValue(e.message) + hideLoading.postValue(true) + } + } + } } diff --git a/app/src/main/java/io/wookey/wallet/support/Constant.kt b/app/src/main/java/io/wookey/wallet/support/Constant.kt index 153ad7b..2063bf8 100644 --- a/app/src/main/java/io/wookey/wallet/support/Constant.kt +++ b/app/src/main/java/io/wookey/wallet/support/Constant.kt @@ -15,9 +15,13 @@ const val SELECT_ADDRESS = 1 const val REQUEST_SCAN_ADDRESS = 100 const val REQUEST_SELECT_COIN = 101 const val REQUEST_SELECT_ADDRESS = 102 +const val REQUEST_SELECT_NODE = 103 const val REQUEST_CODE_PERMISSION_CAMERA = 501 +const val ZH_CN = "zh-CN" +const val EN = "en" + val coinList = listOf( Coin("XMR", "Monero") ) @@ -37,5 +41,10 @@ val nodeArray = arrayOf( symbol = "XMR" url = "uwillrunanodesoon.moneroworld.com:18089" isSelected = false + }, + Node().apply { + symbol = "XMR" + url = "124.160.224.28:18081" + isSelected = false } ) diff --git a/app/src/main/java/io/wookey/wallet/support/extensions/Extensions.kt b/app/src/main/java/io/wookey/wallet/support/extensions/Extensions.kt index 0872639..67d4613 100644 --- a/app/src/main/java/io/wookey/wallet/support/extensions/Extensions.kt +++ b/app/src/main/java/io/wookey/wallet/support/extensions/Extensions.kt @@ -238,6 +238,7 @@ fun SpannableString.clickableSpan(range: IntRange, color: Int, listener: (View) fun View.showTimePicker(startDate: Calendar = Calendar.getInstance().apply { set(2014, 4, 1) }, listener: (Date) -> Unit) { setOnClickListener { + hideKeyboard() TimePickerBuilder(context) { date, v -> listener(date) } .setRangDate(startDate, Calendar.getInstance()) .setLabel("", "", "", "", "", "") diff --git a/app/src/main/java/io/wookey/wallet/widget/IOSDialog.java b/app/src/main/java/io/wookey/wallet/widget/IOSDialog.java index 42e3b25..395f229 100644 --- a/app/src/main/java/io/wookey/wallet/widget/IOSDialog.java +++ b/app/src/main/java/io/wookey/wallet/widget/IOSDialog.java @@ -91,10 +91,11 @@ public class IOSDialog implements View.OnClickListener { private String mContentText = "内容"; private int mContentTextSize = 14; //sp private int mContentTextColor = Color.BLACK; - private int mContentViewPaddingLeft = 15; //dp - private int mContentViewPaddingRight = 15; //dp + private boolean mContentTextBold = false; + private int mContentViewPaddingLeft = 20; //dp + private int mContentViewPaddingRight = 20; //dp private int mContentViewPaddingTop = 0; //dp - private int mContentViewPaddingBottom = 10; //dp + private int mContentViewPaddingBottom = 20; //dp /** @@ -220,6 +221,9 @@ public IOSDialog layout() { mContentView = new TextView(mContext); mContentView.setText(mContentText); + if (mContentTextBold) { + mContentView.setTypeface(Typeface.defaultFromStyle(Typeface.BOLD)); + } mContentView.setTextColor(mContentTextColor); mContentView.setTextSize(mContentTextSize); mContentView.setGravity(Gravity.CENTER_HORIZONTAL); @@ -233,6 +237,9 @@ public IOSDialog layout() { } else { //内容不可滑动 mContentView = new TextView(mContext); mContentView.setText(mContentText); + if (mContentTextBold) { + mContentView.setTypeface(Typeface.defaultFromStyle(Typeface.BOLD)); + } mContentView.setId(mContentView_ID); mContentView.setTextColor(mContentTextColor); mContentView.setTextSize(mContentTextSize); @@ -387,11 +394,9 @@ public IOSDialog radius(int radius) { * 标题相关-------------------- */ public IOSDialog titleText(String title) { - if (!TextUtils.isEmpty(title)) { - mTitleText = title; - if (mTitleView != null) { - mTitleView.setText(mTitleText); - } + mTitleText = title; + if (mTitleView != null) { + mTitleView.setText(mTitleText); } return this; } @@ -464,6 +469,24 @@ public IOSDialog contentTextColor(@ColorInt int textColor) { return this; } + public IOSDialog contentTextSize(int textSize) { + mContentTextSize = textSize; + if (mContentView != null) { + mContentView.setTextSize(mContentTextSize); + } + return this; + } + + public IOSDialog contentTextBold(boolean contentTextBold) { + mContentTextBold = contentTextBold; + if (mContentView != null) { + if (mContentTextBold) { + mContentView.setTypeface(Typeface.defaultFromStyle(Typeface.BOLD)); + } + } + return this; + } + public IOSDialog contentViewTextGravity(int gravity) { if (mContentView != null) { mContentView.setGravity(gravity); diff --git a/app/src/main/res/drawable-xhdpi/icon_close.png b/app/src/main/res/drawable-xhdpi/icon_close.png index 851e85eabd07008930e466179a9e8c392be7016c..f35840db8612fd66849c498b6f7abf4efb86944f 100755 GIT binary patch literal 526 zcmV+p0`dKcP)gs2!gZ2x z=Wwq%$@=;O^@ige5Jm*71DJyNEEZ~pxdl*T;H#E!D?|N)ROomuc>}PaMI{PK$7p?h z(s#P8QsqBFqwy&LH%c7~Wx-5|ao%k=?o_{XIn=YD^a`PQ<%7wA)E_ZWz8E8@YLo= zN@2ti%CHuc{w{ONf$Jsp=i&%?pn@^M5mFT`39KG1AGA`RY^9RAXeW>o$h4n|Htc=2 zB|c?bBhG7-Njj30HQ}6~kPbrXWVk|5nTA8_T0x-=>YpK`4I?O|AzQLa+sc8Y4Y)>) zYnPB6e~dX41LIixa?6bKpeUW#iX!Io!i7iaDlv{o8IIBmmklv6CX~8t0Av5S-FVGy zKhzBf5gv(gxtMG|$tx){`+Qf-ILI)@{m?QmtOkS0R;N>cuVlw4iH4-?58|}6G#6)= Qpa1{>07*qoM6N<$ft<8 literal 2311 zcmV+i3HbJjP)1^@s6$I7^K000QiNkljk7{))BEjuauPKmJ$C3}q)VU(B@gBF>Jq)3|v|1{=TSw^;GY(Io3d$MO6Bx4;z zWXX~}lo-NguAlpx?|a_&yl4A<&-;51_rK?y@AEv@d7k^Z@9Vnnub6|`zAo@UU`OB{ zz+Hjc09ye!2CfDC6ZjMGd*D*whroA%uK^cGy0Y)7s;>cT-vM|s@Ce``KmeK6{sDXm z_$cspNk5(TL96|)Q!s3A0Xzvf2-q38Mh*Ti;M=vJi-7L|KL&mWTrTM<+t;pLvNdpL z;O@Y7z=MH%4?OF0z*~TKN%~t?z;ptJ?dt*01dau6Rg=3M_yF)h;4_l4@Ttspj?*r{ zV}ZR3v^?uqz)OMGO8VOBfukT0^a~$2%Ia)vrKB7-`dXH9|N2O+_!%7@$67ZKN{y5gWs77 z4BH0)uLf=e{1JEoaJr=UhPj&U_@!q6F9vQ3Tmd{=(#Aad%Uve^V9pDqd zK9aIqM%q|l*xs`4_y+*L3Hp+9?{Ai7JJ)e`?Ja<>0FRRNhf$Lq35M;P03WLe@+ZLV zk`f(m)@D0V?k9me6(D;^N`yPqRt3X$w%U2XuD~V0PLh7MS)1G)J=4Ln-X z--peA6d1WNo(23iA+@B7*9#EMJZ*0Sd=a=g@ES=+ObmwYeac}5$(C=`~)AA15H0)CmGtcg9^U?dYwruWVAw!Np}YXx>)65&>p`XAI}xNR`vVIK}0 zEa}Z_HPLyEYx`i}jm5%+MPm~fw(kgp5czXh>pmE&c`b6>af&eu3xC6e>yj>Mz&HWe zP|WlG<34cI_iRT7NIrA!sPB&Yyc}PGzm1YUKkEDAd~Q2e>r;UfB%RWL@u>>39xo}W zZzpE^tndlI)2Cscnn~D>u5u=@Ux5+!eP**ABItcd;w0^+5_pp9XTYsTrMZ*LppIXB zQI6rEl5$2TvbyEoQJ}sP*uSeQzX+?#fao$?28=k(O!z`cA#XahN~4oLCyh>?^ZIcI zC?K-C@(*T9KdmbuTDvafPn5o00>;rm0*?^7iOxDV+qW$~VO!vcaU$CSA~fS(KyJ4~ zx&|T(wH>ZBgz~Wg<4r+4I)tQ@&-`0= zlk|h$kf;Y&<`{|~`ss0!UeY?h*DpzO_e!RsfAks&+m5Ciwf~}k5os=GIyXtm0eUyv zq2Qu^B^*9c(gxcBBJoHZdX7+zQm;Ui&&b+?!KDEs%uj-&Emk8-yGE$QJ4+d8Rj7{iE8YvreJ;BmrBJ}J~&puUB#HqR3)flYR&ZsRKjy29J z#VoN6l1`i_5*2u*eB!D#@fj^3a!eBFomxjEO4wX?M_u`0y)K*kZBjr)u56CSizJ;? zNnLiyLsRb|DXd%X*1AnDo@<;@K%@#MK!kSNbd$8M)}+$`B#v}0H_Bj~kx(~TV6PEq z0nxrvkC&9=vBU1?MNe$NS-h0lB~v#2Cee{ohq0=SEgThlof3gG?WUJBY5!@PQw~#Ad`AMOL?JAiF`YpS2 zp;e$azwX5WK+^V<7(Wesu_Sj9gyvRRT-j&(T9b;$qQ`_upQ>$aqGPg>_pzODJw+!e zzK^BZH%IB+ebkc{LzSw(_=cvrHzfr}4-2M7InL(unZYPu5}hO2U^JMOdg0HXrS@Y) zFMR6J(hL8WaTh)8d!2yMx`LaxB!p6nMA&{y!nam-T4*Eh7GCeY`(o*!CNEv&ucICN$zwMCe4g=>ciKStYFxiE_VF hjyZ2uNnLxd_HTl-#p)KkWq<$x002ovPDHLkV1nY9U>5)Y diff --git a/app/src/main/res/drawable-xhdpi/icon_switch_node.png b/app/src/main/res/drawable-xhdpi/icon_switch_node.png new file mode 100755 index 0000000000000000000000000000000000000000..ff63fb0c2cede3f62ab803ea72eaf2e9a8b3e750 GIT binary patch literal 1116 zcmV-i1f%Nv>W`=pU$x6$BqeE7zlnm<>TiEu2a2v`?m0Xe~CI!)p6tD;U8)FRg#L z>{e>~pgo^j@3QfrTB;n`6w%Ti{=r94P^lKK3f>*PWOrnfCbygJUXpFrWZ#mV-^^#{ zH#5JPABk~^(Sb)(WpYvzPNd!=rWY;m{s!<36DAk+(!_<&jxMZZo_q(4Nr#BA&jsQV zF-wKKGU*2CJ76S{&M@<9D|v}QuNKwpn@%#m1Bx-}6%gKP8b!>zwY>6Squh0X0Sc{a zrff+ltqJJ(#VY3(gIl+bty;WU56zrZ<{uf6)?YP*K_Vjw#&!ULMaLosjC+ccM}D^I zy$;aLkabZsv3IS`r7Nufx_zt|Pd^UuG=N}DhCm+|)ae!x5AmKcKJ{Osv0{_ep zbq%mus8cTi^o|4I5(~op#i{Y{x(Zm$fz%m*9$*rRHOfSv0+=i7`lhd^Klr^~2c48gqnY5$ zOs4GBpqF*kBM+Zzs&dM<-Hpn`c{e_=SzZK;U~b9*Fs= zm1YI6AnmTR0+N`J&VupqH6?Qf)4k{N*+1Gi zXuFon+MCH1X?+5eOqgeC49tAo>yhURU?h>=$;`(rPP(#SldS3ud8AS`5F6m~VjL(LHyOF8m0f-vVTSaDrKw zSS1*Uj_yZR#<`0WB}T-3nk+T`mHyEq^$k zK1kp6j8Om&09aeE ilf>^AbIM*n0sjNRCOdhY*?o literal 0 HcmV?d00001 diff --git a/app/src/main/res/layout/activity_wallet_detail.xml b/app/src/main/res/layout/activity_wallet_detail.xml index 515249c..3a92f78 100644 --- a/app/src/main/res/layout/activity_wallet_detail.xml +++ b/app/src/main/res/layout/activity_wallet_detail.xml @@ -141,6 +141,25 @@ app:sRightIconMarginRight="25dp" app:sRightIconRes="@drawable/arrow_right" app:sUseRipple="false" /> + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_bak_mnemonic.xml b/app/src/main/res/layout/dialog_bak_mnemonic.xml index 7377452..7df91f6 100644 --- a/app/src/main/res/layout/dialog_bak_mnemonic.xml +++ b/app/src/main/res/layout/dialog_bak_mnemonic.xml @@ -60,11 +60,4 @@ - \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_node_edit.xml b/app/src/main/res/layout/dialog_node_edit.xml index 79cc8dd..658f5ae 100644 --- a/app/src/main/res/layout/dialog_node_edit.xml +++ b/app/src/main/res/layout/dialog_node_edit.xml @@ -15,15 +15,30 @@ android:paddingEnd="30dp" android:paddingBottom="22dp"> - + + + + + + - \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_password.xml b/app/src/main/res/layout/dialog_password.xml index 71644da..4ea6732 100644 --- a/app/src/main/res/layout/dialog_password.xml +++ b/app/src/main/res/layout/dialog_password.xml @@ -15,14 +15,31 @@ android:paddingEnd="30dp" android:paddingBottom="22dp"> - + + + + + + + - + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_recovery_mnemonic.xml b/app/src/main/res/layout/fragment_recovery_mnemonic.xml index 4d0129d..d8a25bf 100644 --- a/app/src/main/res/layout/fragment_recovery_mnemonic.xml +++ b/app/src/main/res/layout/fragment_recovery_mnemonic.xml @@ -1,6 +1,7 @@ @@ -46,36 +47,17 @@ android:layout_marginEnd="30dp" android:text="@string/block_height_prompt" /> - - - - - + android:layout_height="wrap_content" + android:layout_marginTop="20dp"> + + + + + - - - - - + android:layout_height="wrap_content" + android:layout_marginTop="20dp"> + android:layout_marginEnd="25dp"> + + + + + 更多节点地址 >> + 删除钱包 + 删除失败 + 删除成功 + + 未填写区块号或近期交易时间,可能需要同步几个小时 + 坚决不填 + 去填 + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d38e3ae..46c33bb 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -179,4 +179,12 @@ More node addresses >> + Delete Wallet + Failed to delete + Deleted successfully + + If you don\'t set date or height, it may take several hours to sync. + Continue + Back to set + diff --git a/monero/src/main/java/io/wookey/monero/util/RestoreHeight.java b/monero/src/main/java/io/wookey/monero/util/RestoreHeight.java index a0e5b35..4edbff3 100644 --- a/monero/src/main/java/io/wookey/monero/util/RestoreHeight.java +++ b/monero/src/main/java/io/wookey/monero/util/RestoreHeight.java @@ -99,6 +99,7 @@ static public RestoreHeight getInstance() { blockheight.put("2019-02-01", 1761435L); blockheight.put("2019-03-01", 1781681L); blockheight.put("2019-04-01", 1803081L); + blockheight.put("2019-05-01", 1824671L); } public long getHeight(String date) {