From b400f52263fde51c3e772367b8b71508d2b152c3 Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Thu, 13 Jul 2023 15:17:25 +0700 Subject: [PATCH 1/3] feat: entrypoint & info dialog --- wallet/res/layout/fragment_accept_invite.xml | 2 +- wallet/res/layout/fragment_more.xml | 18 + .../res/layout/fragment_username_requests.xml | 51 + wallet/res/navigation/nav_voting.xml | 52 + wallet/res/values/strings-extra.xml | 1 - wallet/res/values/strings.xml | 6 +- .../18.json | 926 ++++++++++++++++++ .../wallet/ui/invite/AcceptInviteActivity.kt | 2 +- .../wallet/ui/main/MainViewModel.kt | 29 +- .../schildbach/wallet/ui/more/MoreFragment.kt | 4 + .../ui/username/UsernameRequestsFragment.kt | 24 + wallet/staging/res/drawable/ic_voting.xml | 9 + 12 files changed, 1105 insertions(+), 19 deletions(-) create mode 100644 wallet/res/layout/fragment_username_requests.xml create mode 100644 wallet/res/navigation/nav_voting.xml create mode 100644 wallet/schemas/de.schildbach.wallet.database.AppDatabase/18.json create mode 100644 wallet/src/de/schildbach/wallet/ui/username/UsernameRequestsFragment.kt create mode 100644 wallet/staging/res/drawable/ic_voting.xml diff --git a/wallet/res/layout/fragment_accept_invite.xml b/wallet/res/layout/fragment_accept_invite.xml index bad7c6642c..f3cc54a6e6 100644 --- a/wallet/res/layout/fragment_accept_invite.xml +++ b/wallet/res/layout/fragment_accept_invite.xml @@ -40,7 +40,7 @@ android:layout_height="wrap_content" android:gravity="center_horizontal" android:textAlignment="gravity" - android:text="@string/invitation_accept_message_1" /> + android:text="@string/pay_to_usernames" /> diff --git a/wallet/res/layout/fragment_more.xml b/wallet/res/layout/fragment_more.xml index eb5359b749..9f549264fc 100644 --- a/wallet/res/layout/fragment_more.xml +++ b/wallet/res/layout/fragment_more.xml @@ -343,6 +343,24 @@ + + + + + + + diff --git a/wallet/res/layout/fragment_username_requests.xml b/wallet/res/layout/fragment_username_requests.xml new file mode 100644 index 0000000000..b15d46dafb --- /dev/null +++ b/wallet/res/layout/fragment_username_requests.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/wallet/res/navigation/nav_voting.xml b/wallet/res/navigation/nav_voting.xml new file mode 100644 index 0000000000..21c03d3c23 --- /dev/null +++ b/wallet/res/navigation/nav_voting.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/wallet/res/values/strings-extra.xml b/wallet/res/values/strings-extra.xml index b78ab038b1..ec1f8185f3 100644 --- a/wallet/res/values/strings-extra.xml +++ b/wallet/res/values/strings-extra.xml @@ -437,7 +437,6 @@ Onboarding Error Onboarding is in process, please claim your invite after you have created your wallet. Get your Username - Pay to usernames. No more alphanumeric addresses Add your Friends & Family Invite your family, find your friends by searching their usernames Personalize diff --git a/wallet/res/values/strings.xml b/wallet/res/values/strings.xml index 664efec126..8f3cb04368 100644 --- a/wallet/res/values/strings.xml +++ b/wallet/res/values/strings.xml @@ -394,9 +394,9 @@ Basic Intermediate Advanced users can determine your transaction history - 1-2 minutes to create a username + 1–2 minutes to create a username Advanced users who have a very high level of technical expertise can determine your transaction history - 1-2 hours + 1–2 hours Several hours It would be very difficult for advanced users with any level of technical expertise to determine your transaction history Advanced @@ -419,4 +419,6 @@ Voting will not be required forever The Dash network must vote to approve your username before it is created. What is username voting? + Username Voting + As a masternode owner you can vote to approve requested usernames before users will be able to create it. diff --git a/wallet/schemas/de.schildbach.wallet.database.AppDatabase/18.json b/wallet/schemas/de.schildbach.wallet.database.AppDatabase/18.json new file mode 100644 index 0000000000..d99fe8d4c7 --- /dev/null +++ b/wallet/schemas/de.schildbach.wallet.database.AppDatabase/18.json @@ -0,0 +1,926 @@ +{ + "formatVersion": 1, + "database": { + "version": 18, + "identityHash": "7527e563d2b597699544f5cc515d0873", + "entities": [ + { + "tableName": "exchange_rates", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`currencyCode` TEXT NOT NULL, `rate` TEXT, PRIMARY KEY(`currencyCode`))", + "fields": [ + { + "fieldPath": "currencyCode", + "columnName": "currencyCode", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "rate", + "columnName": "rate", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "currencyCode" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "blockchain_state", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`bestChainDate` INTEGER, `bestChainHeight` INTEGER NOT NULL, `replaying` INTEGER NOT NULL, `impediments` TEXT NOT NULL, `chainlockHeight` INTEGER NOT NULL, `mnlistHeight` INTEGER NOT NULL, `percentageSync` INTEGER NOT NULL, `id` INTEGER NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "bestChainDate", + "columnName": "bestChainDate", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "bestChainHeight", + "columnName": "bestChainHeight", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "replaying", + "columnName": "replaying", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "impediments", + "columnName": "impediments", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "chainlockHeight", + "columnName": "chainlockHeight", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "mnlistHeight", + "columnName": "mnlistHeight", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "percentageSync", + "columnName": "percentageSync", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "transaction_metadata", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`txId` BLOB NOT NULL, `timestamp` INTEGER NOT NULL, `value` INTEGER NOT NULL, `type` TEXT NOT NULL, `taxCategory` TEXT, `currencyCode` TEXT, `rate` TEXT, `memo` TEXT NOT NULL, `service` TEXT, `customIconId` BLOB, PRIMARY KEY(`txId`))", + "fields": [ + { + "fieldPath": "txId", + "columnName": "txId", + "affinity": "BLOB", + "notNull": true + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "taxCategory", + "columnName": "taxCategory", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "currencyCode", + "columnName": "currencyCode", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "rate", + "columnName": "rate", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "memo", + "columnName": "memo", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "service", + "columnName": "service", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "customIconId", + "columnName": "customIconId", + "affinity": "BLOB", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "txId" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "address_metadata", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`address` TEXT NOT NULL, `isInput` INTEGER NOT NULL, `taxCategory` TEXT NOT NULL, `service` TEXT NOT NULL, PRIMARY KEY(`address`, `isInput`))", + "fields": [ + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isInput", + "columnName": "isInput", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "taxCategory", + "columnName": "taxCategory", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "service", + "columnName": "service", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "address", + "isInput" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "icon_bitmaps", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` BLOB NOT NULL, `imageData` BLOB NOT NULL, `originalUrl` TEXT NOT NULL, `height` INTEGER NOT NULL, `width` INTEGER NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "BLOB", + "notNull": true + }, + { + "fieldPath": "imageData", + "columnName": "imageData", + "affinity": "BLOB", + "notNull": true + }, + { + "fieldPath": "originalUrl", + "columnName": "originalUrl", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "height", + "columnName": "height", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "width", + "columnName": "width", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "gift_cards", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`txId` BLOB NOT NULL, `merchantName` TEXT NOT NULL, `price` REAL NOT NULL, `number` TEXT, `pin` TEXT, `barcodeValue` TEXT, `barcodeFormat` TEXT, `merchantUrl` TEXT, `note` TEXT, PRIMARY KEY(`txId`))", + "fields": [ + { + "fieldPath": "txId", + "columnName": "txId", + "affinity": "BLOB", + "notNull": true + }, + { + "fieldPath": "merchantName", + "columnName": "merchantName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "price", + "columnName": "price", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pin", + "columnName": "pin", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "barcodeValue", + "columnName": "barcodeValue", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "barcodeFormat", + "columnName": "barcodeFormat", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "merchantUrl", + "columnName": "merchantUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "note", + "columnName": "note", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "txId" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "blockchain_identity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`creationState` INTEGER NOT NULL, `creationStateErrorMessage` TEXT, `username` TEXT, `userId` TEXT, `restoring` INTEGER NOT NULL, `identity` BLOB, `creditFundingTxId` BLOB, `usingInvite` INTEGER NOT NULL, `invite` TEXT, `preorderSalt` BLOB, `registrationStatus` INTEGER, `usernameStatus` INTEGER, `creditBalance` INTEGER, `activeKeyCount` INTEGER, `totalKeyCount` INTEGER, `keysCreated` INTEGER, `currentMainKeyIndex` INTEGER, `currentMainKeyType` INTEGER, `id` INTEGER NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "creationState", + "columnName": "creationState", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "creationStateErrorMessage", + "columnName": "creationStateErrorMessage", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "username", + "columnName": "username", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "restoring", + "columnName": "restoring", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "identity", + "columnName": "identity", + "affinity": "BLOB", + "notNull": false + }, + { + "fieldPath": "creditFundingTxId", + "columnName": "creditFundingTxId", + "affinity": "BLOB", + "notNull": false + }, + { + "fieldPath": "usingInvite", + "columnName": "usingInvite", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "invite", + "columnName": "invite", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "preorderSalt", + "columnName": "preorderSalt", + "affinity": "BLOB", + "notNull": false + }, + { + "fieldPath": "registrationStatus", + "columnName": "registrationStatus", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "usernameStatus", + "columnName": "usernameStatus", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "creditBalance", + "columnName": "creditBalance", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "activeKeyCount", + "columnName": "activeKeyCount", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "totalKeyCount", + "columnName": "totalKeyCount", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "keysCreated", + "columnName": "keysCreated", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "currentMainKeyIndex", + "columnName": "currentMainKeyIndex", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "currentMainKeyType", + "columnName": "currentMainKeyType", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "dashpay_profile", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` TEXT NOT NULL, `username` TEXT NOT NULL, `displayName` TEXT NOT NULL, `publicMessage` TEXT NOT NULL, `avatarUrl` TEXT NOT NULL, `avatarHash` BLOB, `avatarFingerprint` BLOB, `createdAt` INTEGER NOT NULL, `updatedAt` INTEGER NOT NULL, PRIMARY KEY(`userId`))", + "fields": [ + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "username", + "columnName": "username", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "displayName", + "columnName": "displayName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "publicMessage", + "columnName": "publicMessage", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "avatarUrl", + "columnName": "avatarUrl", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "avatarHash", + "columnName": "avatarHash", + "affinity": "BLOB", + "notNull": false + }, + { + "fieldPath": "avatarFingerprint", + "columnName": "avatarFingerprint", + "affinity": "BLOB", + "notNull": false + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "userId" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "dashpay_contact_request", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` TEXT NOT NULL, `toUserId` TEXT NOT NULL, `accountReference` INTEGER NOT NULL, `encryptedPublicKey` BLOB NOT NULL, `senderKeyIndex` INTEGER NOT NULL, `recipientKeyIndex` INTEGER NOT NULL, `timestamp` INTEGER NOT NULL, `encryptedAccountLabel` BLOB, `autoAcceptProof` BLOB, PRIMARY KEY(`userId`, `toUserId`, `accountReference`))", + "fields": [ + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "toUserId", + "columnName": "toUserId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "accountReference", + "columnName": "accountReference", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "encryptedPublicKey", + "columnName": "encryptedPublicKey", + "affinity": "BLOB", + "notNull": true + }, + { + "fieldPath": "senderKeyIndex", + "columnName": "senderKeyIndex", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "recipientKeyIndex", + "columnName": "recipientKeyIndex", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "encryptedAccountLabel", + "columnName": "encryptedAccountLabel", + "affinity": "BLOB", + "notNull": false + }, + { + "fieldPath": "autoAcceptProof", + "columnName": "autoAcceptProof", + "affinity": "BLOB", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "userId", + "toUserId", + "accountReference" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "user_alerts", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`stringResId` INTEGER NOT NULL, `iconResId` INTEGER NOT NULL, `dismissed` INTEGER NOT NULL, `createdAt` INTEGER NOT NULL, PRIMARY KEY(`stringResId`))", + "fields": [ + { + "fieldPath": "stringResId", + "columnName": "stringResId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "iconResId", + "columnName": "iconResId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dismissed", + "columnName": "dismissed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "stringResId" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "invitation_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` TEXT NOT NULL, `txid` BLOB NOT NULL, `createdAt` INTEGER NOT NULL, `memo` TEXT NOT NULL, `sentAt` INTEGER NOT NULL, `acceptedAt` INTEGER NOT NULL, `shortDynamicLink` TEXT, `dynamicLink` TEXT, PRIMARY KEY(`userId`))", + "fields": [ + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "txid", + "columnName": "txid", + "affinity": "BLOB", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "memo", + "columnName": "memo", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "sentAt", + "columnName": "sentAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "acceptedAt", + "columnName": "acceptedAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "shortDynamicLink", + "columnName": "shortDynamicLink", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "dynamicLink", + "columnName": "dynamicLink", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "userId" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "transaction_metadata_cache", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`cacheTimestamp` INTEGER NOT NULL, `txId` BLOB NOT NULL, `sentTimestamp` INTEGER, `taxCategory` TEXT, `currencyCode` TEXT, `rate` TEXT, `memo` TEXT, `service` TEXT, `customIconUrl` TEXT, `giftCardNumber` TEXT, `giftCardPin` TEXT, `merchantName` TEXT, `originalPrice` REAL, `barcodeValue` TEXT, `barcodeFormat` TEXT, `merchantUrl` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "cacheTimestamp", + "columnName": "cacheTimestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "txId", + "columnName": "txId", + "affinity": "BLOB", + "notNull": true + }, + { + "fieldPath": "sentTimestamp", + "columnName": "sentTimestamp", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "taxCategory", + "columnName": "taxCategory", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "currencyCode", + "columnName": "currencyCode", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "rate", + "columnName": "rate", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "memo", + "columnName": "memo", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "service", + "columnName": "service", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "customIconUrl", + "columnName": "customIconUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "giftCardNumber", + "columnName": "giftCardNumber", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "giftCardPin", + "columnName": "giftCardPin", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "merchantName", + "columnName": "merchantName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "originalPrice", + "columnName": "originalPrice", + "affinity": "REAL", + "notNull": false + }, + { + "fieldPath": "barcodeValue", + "columnName": "barcodeValue", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "barcodeFormat", + "columnName": "barcodeFormat", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "merchantUrl", + "columnName": "merchantUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "transaction_metadata_platform", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, `txId` BLOB NOT NULL, `sentTimestamp` INTEGER, `taxCategory` TEXT, `currencyCode` TEXT, `rate` REAL, `memo` TEXT, `service` TEXT, `customIconUrl` TEXT, `giftCardNumber` TEXT, `giftCardPin` TEXT, `merchantName` TEXT, `originalPrice` REAL, `barcodeValue` TEXT, `barcodeFormat` TEXT, `merchantUrl` TEXT, PRIMARY KEY(`id`, `txId`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "txId", + "columnName": "txId", + "affinity": "BLOB", + "notNull": true + }, + { + "fieldPath": "sentTimestamp", + "columnName": "sentTimestamp", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "taxCategory", + "columnName": "taxCategory", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "currencyCode", + "columnName": "currencyCode", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "rate", + "columnName": "rate", + "affinity": "REAL", + "notNull": false + }, + { + "fieldPath": "memo", + "columnName": "memo", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "service", + "columnName": "service", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "customIconUrl", + "columnName": "customIconUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "giftCardNumber", + "columnName": "giftCardNumber", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "giftCardPin", + "columnName": "giftCardPin", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "merchantName", + "columnName": "merchantName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "originalPrice", + "columnName": "originalPrice", + "affinity": "REAL", + "notNull": false + }, + { + "fieldPath": "barcodeValue", + "columnName": "barcodeValue", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "barcodeFormat", + "columnName": "barcodeFormat", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "merchantUrl", + "columnName": "merchantUrl", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id", + "txId" + ], + "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, '7527e563d2b597699544f5cc515d0873')" + ] + } +} \ No newline at end of file diff --git a/wallet/src/de/schildbach/wallet/ui/invite/AcceptInviteActivity.kt b/wallet/src/de/schildbach/wallet/ui/invite/AcceptInviteActivity.kt index c3f11c3486..54c499f9b5 100644 --- a/wallet/src/de/schildbach/wallet/ui/invite/AcceptInviteActivity.kt +++ b/wallet/src/de/schildbach/wallet/ui/invite/AcceptInviteActivity.kt @@ -99,7 +99,7 @@ class AcceptInviteActivity : InteractionAwareActivity() { override fun getItem(position: Int): Fragment { when (position) { - 0 -> return AcceptInviteFragment.newInstance(R.string.invitation_accept_title_1, R.string.invitation_accept_message_1, + 0 -> return AcceptInviteFragment.newInstance(R.string.invitation_accept_title_1, R.string.pay_to_usernames, R.drawable.ic_accept_invite_slide_1) 1 -> return AcceptInviteFragment.newInstance(R.string.invitation_accept_title_2, R.string.invitation_accept_message_2, R.drawable.ic_accept_invite_slide_2) diff --git a/wallet/src/de/schildbach/wallet/ui/main/MainViewModel.kt b/wallet/src/de/schildbach/wallet/ui/main/MainViewModel.kt index 38df54aaed..5f88b4aad2 100644 --- a/wallet/src/de/schildbach/wallet/ui/main/MainViewModel.kt +++ b/wallet/src/de/schildbach/wallet/ui/main/MainViewModel.kt @@ -180,20 +180,21 @@ class MainViewModel @Inject constructor( } } - val isAbleToCreateIdentityLiveData = MediatorLiveData().apply { - addSource(isPlatformAvailableData) { - value = combineLatestData() - } - addSource(_isBlockchainSynced) { - value = combineLatestData() - } - addSource(blockchainIdentity) { - value = combineLatestData() - } - addSource(_balance) { - value = combineLatestData() - } - } + val isAbleToCreateIdentityLiveData = MutableLiveData(true) +// MediatorLiveData().apply { +// addSource(isPlatformAvailableData) { +// value = combineLatestData() +// } +// addSource(_isBlockchainSynced) { +// value = combineLatestData() +// } +// addSource(blockchainIdentity) { +// value = combineLatestData() +// } +// addSource(_balance) { +// value = combineLatestData() +// } +// } val isAbleToCreateIdentity: Boolean get() = isAbleToCreateIdentityLiveData.value ?: false diff --git a/wallet/src/de/schildbach/wallet/ui/more/MoreFragment.kt b/wallet/src/de/schildbach/wallet/ui/more/MoreFragment.kt index a7839177c4..27ffe872ee 100644 --- a/wallet/src/de/schildbach/wallet/ui/more/MoreFragment.kt +++ b/wallet/src/de/schildbach/wallet/ui/more/MoreFragment.kt @@ -152,6 +152,10 @@ class MoreFragment : Fragment(R.layout.fragment_more) { binding.joinDashpayBtn.setOnClickListener { startActivity(Intent(requireContext(), CreateUsernameActivity::class.java)) } + binding.usernameVoting.setOnClickListener { + safeNavigate(MoreFragmentDirections.moreToUsernameVoting()) + } + initViewModel() } diff --git a/wallet/src/de/schildbach/wallet/ui/username/UsernameRequestsFragment.kt b/wallet/src/de/schildbach/wallet/ui/username/UsernameRequestsFragment.kt new file mode 100644 index 0000000000..dcb6addbbd --- /dev/null +++ b/wallet/src/de/schildbach/wallet/ui/username/UsernameRequestsFragment.kt @@ -0,0 +1,24 @@ +/* + * Copyright 2023 Dash Core Group. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package de.schildbach.wallet.ui.username + +import androidx.fragment.app.Fragment +import de.schildbach.wallet_test.R + +class UsernameRequestsFragment : Fragment(R.layout.fragment_username_requests) { +} \ No newline at end of file diff --git a/wallet/staging/res/drawable/ic_voting.xml b/wallet/staging/res/drawable/ic_voting.xml new file mode 100644 index 0000000000..9fafffff04 --- /dev/null +++ b/wallet/staging/res/drawable/ic_voting.xml @@ -0,0 +1,9 @@ + + + From 130854511afe4c02fd9570e501aa60537419182d Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Fri, 14 Jul 2023 14:45:01 +0700 Subject: [PATCH 2/3] feat: voting entrypoint --- .../org/dash/wallet/common/util/FlowExt.kt | 44 +++++++++++++++ .../org/dash/wallet/common/util/TickerFlow.kt | 25 --------- wallet/res/drawable/ic_user_list.xml | 53 ++++++++++++++++++ .../res/layout/fragment_username_requests.xml | 14 ++++- wallet/res/navigation/nav_home.xml | 8 +++ wallet/res/navigation/nav_voting.xml | 32 ++--------- wallet/res/values/strings.xml | 2 + .../wallet/ui/dashpay/utils/DashPayConfig.kt | 7 ++- .../ui/payments/PaymentsReceiveFragment.kt | 1 - .../ui/username/UsernameRequestsFragment.kt | 32 ++++++++++- .../ui/username/UsernameRequestsViewModel.kt | 54 +++++++++++++++++++ 11 files changed, 214 insertions(+), 58 deletions(-) create mode 100644 common/src/main/java/org/dash/wallet/common/util/FlowExt.kt delete mode 100644 common/src/main/java/org/dash/wallet/common/util/TickerFlow.kt create mode 100644 wallet/res/drawable/ic_user_list.xml create mode 100644 wallet/src/de/schildbach/wallet/ui/username/UsernameRequestsViewModel.kt diff --git a/common/src/main/java/org/dash/wallet/common/util/FlowExt.kt b/common/src/main/java/org/dash/wallet/common/util/FlowExt.kt new file mode 100644 index 0000000000..25161508e3 --- /dev/null +++ b/common/src/main/java/org/dash/wallet/common/util/FlowExt.kt @@ -0,0 +1,44 @@ +package org.dash.wallet.common.util + +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle +import kotlinx.coroutines.FlowPreview +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.AbstractFlow +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.FlowCollector +import kotlinx.coroutines.launch +import kotlin.time.Duration +import kotlin.time.ExperimentalTime + +/** + * @author Joffrey Bion + * https://stackoverflow.com/a/54828055/2279177 + */ +@OptIn(FlowPreview::class) +class TickerFlow( + private val period: Duration, + private val initialDelay: Duration = Duration.ZERO +): AbstractFlow() { + override suspend fun collectSafely(collector: FlowCollector) { + delay(initialDelay) + while (true) { + collector.emit(Unit) + delay(period) + } + } +} + +/** + * @author Tobias Preuss @johnjohndoe + * https://github.com/EventFahrplan/EventFahrplan/blob/master/commons/src/main/java/info/metadude/android/eventfahrplan/commons/flow/FlowExtensions.kt + */ +fun Flow.observe(lifecycleOwner: LifecycleOwner, collector: FlowCollector) { + lifecycleOwner.lifecycleScope.launch { + lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { + collect(collector) + } + } +} diff --git a/common/src/main/java/org/dash/wallet/common/util/TickerFlow.kt b/common/src/main/java/org/dash/wallet/common/util/TickerFlow.kt deleted file mode 100644 index 5ea5c63c91..0000000000 --- a/common/src/main/java/org/dash/wallet/common/util/TickerFlow.kt +++ /dev/null @@ -1,25 +0,0 @@ -package org.dash.wallet.common.util - -import kotlinx.coroutines.FlowPreview -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.AbstractFlow -import kotlinx.coroutines.flow.FlowCollector -import kotlin.time.Duration - -/** - * @author Joffrey Bion - * https://stackoverflow.com/a/54828055/2279177 - */ -@OptIn(FlowPreview::class) -class TickerFlow( - private val period: Duration, - private val initialDelay: Duration = Duration.ZERO -): AbstractFlow() { - override suspend fun collectSafely(collector: FlowCollector) { - delay(initialDelay) - while (true) { - collector.emit(Unit) - delay(period) - } - } -} diff --git a/wallet/res/drawable/ic_user_list.xml b/wallet/res/drawable/ic_user_list.xml new file mode 100644 index 0000000000..46ba534638 --- /dev/null +++ b/wallet/res/drawable/ic_user_list.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + diff --git a/wallet/res/layout/fragment_username_requests.xml b/wallet/res/layout/fragment_username_requests.xml index b15d46dafb..5de552dda4 100644 --- a/wallet/res/layout/fragment_username_requests.xml +++ b/wallet/res/layout/fragment_username_requests.xml @@ -14,12 +14,12 @@ ~ You should have received a copy of the GNU General Public License ~ along with this program. If not, see . --> - + android:layout_height="match_parent" + android:background="@color/background_primary"> + + \ No newline at end of file diff --git a/wallet/res/navigation/nav_home.xml b/wallet/res/navigation/nav_home.xml index 0e9e2111c9..4cb51d777b 100644 --- a/wallet/res/navigation/nav_home.xml +++ b/wallet/res/navigation/nav_home.xml @@ -188,6 +188,13 @@ app:exitAnim="@anim/activity_stay" app:popExitAnim="@anim/slide_out_left" /> + + + \ No newline at end of file diff --git a/wallet/res/navigation/nav_voting.xml b/wallet/res/navigation/nav_voting.xml index 21c03d3c23..65da845dcb 100644 --- a/wallet/res/navigation/nav_voting.xml +++ b/wallet/res/navigation/nav_voting.xml @@ -19,34 +19,12 @@ xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/nav_voting" - app:startDestination="@id/paymentsReceiveFragment"> + app:startDestination="@id/usernameRequestsFragment"> - - - - - - + android:id="@+id/usernameRequestsFragment" + android:name="de.schildbach.wallet.ui.username.UsernameRequestsFragment" + android:label="UsernameRequestsFragment" + tools:layout="@layout/fragment_username_requests"> - - \ No newline at end of file diff --git a/wallet/res/values/strings.xml b/wallet/res/values/strings.xml index 8f3cb04368..9def783dc7 100644 --- a/wallet/res/values/strings.xml +++ b/wallet/res/values/strings.xml @@ -421,4 +421,6 @@ What is username voting? Username Voting As a masternode owner you can vote to approve requested usernames before users will be able to create it. + Vote only on duplicates + You can review all requests but you only need to vote on duplicates diff --git a/wallet/src/de/schildbach/wallet/ui/dashpay/utils/DashPayConfig.kt b/wallet/src/de/schildbach/wallet/ui/dashpay/utils/DashPayConfig.kt index 39aae97014..79abf3eb9e 100644 --- a/wallet/src/de/schildbach/wallet/ui/dashpay/utils/DashPayConfig.kt +++ b/wallet/src/de/schildbach/wallet/ui/dashpay/utils/DashPayConfig.kt @@ -27,8 +27,10 @@ import javax.inject.Inject import javax.inject.Singleton @Singleton -open class DashPayConfig @Inject constructor(private val context: Context, walletDataProvider: WalletDataProvider) -:BaseConfig( +open class DashPayConfig @Inject constructor( + context: Context, + walletDataProvider: WalletDataProvider +): BaseConfig( context, PREFERENCES_NAME, walletDataProvider, @@ -49,6 +51,7 @@ open class DashPayConfig @Inject constructor(private val context: Context, walle val LAST_SEEN_NOTIFICATION_TIME = longPreferencesKey("last_seen_notification_time") val LAST_METADATA_PUSH = longPreferencesKey("last_metadata_push") val HAS_DASH_PAY_INFO_SCREEN_BEEN_SHOWN = booleanPreferencesKey("has_dash_pay_info_screen_been_shown") + val VOTING_INFO_SHOWN = booleanPreferencesKey("voting_info_shown") } open suspend fun areNotificationsDisabled(): Boolean { diff --git a/wallet/src/de/schildbach/wallet/ui/payments/PaymentsReceiveFragment.kt b/wallet/src/de/schildbach/wallet/ui/payments/PaymentsReceiveFragment.kt index e42f945313..6beb7c3477 100644 --- a/wallet/src/de/schildbach/wallet/ui/payments/PaymentsReceiveFragment.kt +++ b/wallet/src/de/schildbach/wallet/ui/payments/PaymentsReceiveFragment.kt @@ -21,7 +21,6 @@ import android.os.Bundle import android.view.Gravity.CENTER_VERTICAL import android.view.View import android.widget.FrameLayout -import android.widget.LinearLayout import androidx.core.os.bundleOf import androidx.core.view.isVisible import androidx.core.view.updateLayoutParams diff --git a/wallet/src/de/schildbach/wallet/ui/username/UsernameRequestsFragment.kt b/wallet/src/de/schildbach/wallet/ui/username/UsernameRequestsFragment.kt index dcb6addbbd..99aece9e97 100644 --- a/wallet/src/de/schildbach/wallet/ui/username/UsernameRequestsFragment.kt +++ b/wallet/src/de/schildbach/wallet/ui/username/UsernameRequestsFragment.kt @@ -17,8 +17,38 @@ package de.schildbach.wallet.ui.username +import android.os.Bundle +import android.view.View import androidx.fragment.app.Fragment +import androidx.fragment.app.viewModels +import androidx.lifecycle.lifecycleScope +import dagger.hilt.android.AndroidEntryPoint import de.schildbach.wallet_test.R +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import org.dash.wallet.common.ui.dialogs.AdaptiveDialog +import org.dash.wallet.common.util.observe +@AndroidEntryPoint class UsernameRequestsFragment : Fragment(R.layout.fragment_username_requests) { -} \ No newline at end of file + private val viewModel: UsernameRequestsViewModel by viewModels() + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + viewModel.uiState.observe(viewLifecycleOwner) { state -> + if (state.showFirstTimeInfo) { + lifecycleScope.launch { + delay(200) + AdaptiveDialog.create( + R.drawable.ic_user_list, + getString(R.string.voting_duplicates_only_title), + getString(R.string.voting_duplicates_only_message), + getString(R.string.button_ok) + ).showAsync(requireActivity()) + viewModel.setFirstTimeInfoShown() + } + } + } + } +} diff --git a/wallet/src/de/schildbach/wallet/ui/username/UsernameRequestsViewModel.kt b/wallet/src/de/schildbach/wallet/ui/username/UsernameRequestsViewModel.kt new file mode 100644 index 0000000000..5f0892d0d4 --- /dev/null +++ b/wallet/src/de/schildbach/wallet/ui/username/UsernameRequestsViewModel.kt @@ -0,0 +1,54 @@ +/* + * Copyright 2023 Dash Core Group. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package de.schildbach.wallet.ui.username + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import dagger.hilt.android.lifecycle.HiltViewModel +import de.schildbach.wallet.ui.dashpay.utils.DashPayConfig +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.update +import javax.inject.Inject + +data class UsernameRequestsUIState( +// val isLoading: Boolean, +// val usernameRequests: List, + val showFirstTimeInfo: Boolean = false +) + +@HiltViewModel +class UsernameRequestsViewModel @Inject constructor( + private val dashPayConfig: DashPayConfig +): ViewModel() { + private val _uiState = MutableStateFlow(UsernameRequestsUIState()) + val uiState: StateFlow = _uiState.asStateFlow() + + init { + dashPayConfig.observe(DashPayConfig.VOTING_INFO_SHOWN) + .onEach { isShown -> _uiState.update { it.copy(showFirstTimeInfo = isShown != true) } } + .launchIn(viewModelScope) + } + + suspend fun setFirstTimeInfoShown() { + dashPayConfig.set(DashPayConfig.VOTING_INFO_SHOWN, true) + } +} From dc1a1b3bc603ddfa2e3f05947dda5e4a61307daf Mon Sep 17 00:00:00 2001 From: Andrei Ashikhmin Date: Fri, 14 Jul 2023 18:28:35 +0700 Subject: [PATCH 3/3] chore: cleanup --- .../wallet/ui/main/MainViewModel.kt | 29 +++++++++---------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/wallet/src/de/schildbach/wallet/ui/main/MainViewModel.kt b/wallet/src/de/schildbach/wallet/ui/main/MainViewModel.kt index 5f88b4aad2..38df54aaed 100644 --- a/wallet/src/de/schildbach/wallet/ui/main/MainViewModel.kt +++ b/wallet/src/de/schildbach/wallet/ui/main/MainViewModel.kt @@ -180,21 +180,20 @@ class MainViewModel @Inject constructor( } } - val isAbleToCreateIdentityLiveData = MutableLiveData(true) -// MediatorLiveData().apply { -// addSource(isPlatformAvailableData) { -// value = combineLatestData() -// } -// addSource(_isBlockchainSynced) { -// value = combineLatestData() -// } -// addSource(blockchainIdentity) { -// value = combineLatestData() -// } -// addSource(_balance) { -// value = combineLatestData() -// } -// } + val isAbleToCreateIdentityLiveData = MediatorLiveData().apply { + addSource(isPlatformAvailableData) { + value = combineLatestData() + } + addSource(_isBlockchainSynced) { + value = combineLatestData() + } + addSource(blockchainIdentity) { + value = combineLatestData() + } + addSource(_balance) { + value = combineLatestData() + } + } val isAbleToCreateIdentity: Boolean get() = isAbleToCreateIdentityLiveData.value ?: false