diff --git a/app/build.gradle b/app/build.gradle index 5cd5378..f20eac1 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,5 +1,13 @@ apply plugin: 'com.android.application' apply plugin: 'kotlin-android' +apply plugin: 'kotlin-android-extensions' + +kapt { + generateStubs = true + javacOptions { + option("-Xmaxerrs", 500) + } +} android { compileSdkVersion 27 @@ -40,19 +48,24 @@ android { dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) - //androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { + //androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { androidTestImplementation('com.android.support.test.espresso:espresso-core:3.0.1', { exclude group: 'com.android.support', module: 'support-annotations' }) + implementation 'com.jaredrummler:material-spinner:1.2.5' + implementation "org.jetbrains.kotlin:kotlin-stdlib:1.1.60" //testCompile 'com.android.support.test:runner:1.0.1' + implementation 'com.afollestad.material-dialogs:core:0.9.6.0' //DTVV Thrusday 31 July 2018 implementation 'com.android.support:appcompat-v7:27.1.1' + implementation 'com.github.bumptech.glide:glide:4.7.1' + // Glide v4 uses this new annotation processor -- see https://bumptech.github.io/glide/doc/generatedapi.html + annotationProcessor 'com.github.bumptech.glide:compiler:4.7.1' implementation 'com.android.support:support-v4:27.1.1' implementation 'com.android.support:design:27.1.1' implementation 'com.android.support:cardview-v7:27.1.1' implementation 'com.android.support.constraint:constraint-layout:1.1.2' implementation 'android.arch.lifecycle:runtime:1.1.1' implementation 'android.arch.lifecycle:extensions:1.1.1' - implementation 'android.arch.persistence.room:runtime:1.1.0' implementation 'android.arch.paging:runtime:1.0.0' implementation 'com.idescout.sql:sqlscout-server:2.0' implementation 'com.google.code.gson:gson:2.8.0' @@ -72,8 +85,15 @@ dependencies { //testCompile 'junit:junit: 4.12' testImplementation 'org.mockito:mockito-core:1.10.19' + implementation 'android.arch.persistence.room:runtime:1.1.0' + + kapt 'android.arch.persistence.room:runtime:1.1.0' annotationProcessor 'android.arch.lifecycle:compiler:1.1.1' + kapt 'android.arch.lifecycle:compiler:1.1.1' + annotationProcessor 'android.arch.lifecycle:compiler:1.1.1' + kapt 'android.arch.persistence.room:compiler:1.1.0' annotationProcessor 'android.arch.persistence.room:compiler:1.1.0' + kapt 'com.jakewharton:butterknife-compiler:8.8.1' annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1' implementation 'com.squareup.picasso:picasso:2.5.2' @@ -88,4 +108,6 @@ dependencies { exclude group: 'org.json', module: 'json' } + kapt "android.arch.lifecycle:compiler:1.1.1" + kapt "android.arch.persistence.room:compiler:1.1.0" } diff --git a/app/schemas/cy.agorise.crystalwallet.dao.CrystalDatabase/3.json b/app/schemas/cy.agorise.crystalwallet.dao.CrystalDatabase/3.json new file mode 100644 index 0000000..06d211c --- /dev/null +++ b/app/schemas/cy.agorise.crystalwallet.dao.CrystalDatabase/3.json @@ -0,0 +1,704 @@ +{ + "formatVersion": 1, + "database": { + "version": 3, + "identityHash": "5aa4eae5c7cf7e77a2ebc1d7a9dc7070", + "entities": [ + { + "tableName": "account_seed", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT, `master_seed` TEXT, `type` TEXT)", + "fields": [ + { + "fieldPath": "mId", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "mName", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "mMasterSeed", + "columnName": "master_seed", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "crypto_net_account", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `seed_id` INTEGER NOT NULL, `account_index` INTEGER NOT NULL, `crypto_net` TEXT, `name` TEXT, FOREIGN KEY(`seed_id`) REFERENCES `account_seed`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )", + "fields": [ + { + "fieldPath": "mId", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "mSeedId", + "columnName": "seed_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "mAccountIndex", + "columnName": "account_index", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "mCryptoNet", + "columnName": "crypto_net", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "mName", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_crypto_net_account_id", + "unique": false, + "columnNames": [ + "id" + ], + "createSql": "CREATE INDEX `index_crypto_net_account_id` ON `${TABLE_NAME}` (`id`)" + }, + { + "name": "index_crypto_net_account_seed_id", + "unique": false, + "columnNames": [ + "seed_id" + ], + "createSql": "CREATE INDEX `index_crypto_net_account_seed_id` ON `${TABLE_NAME}` (`seed_id`)" + }, + { + "name": "index_crypto_net_account_seed_id_crypto_net_account_index", + "unique": true, + "columnNames": [ + "seed_id", + "crypto_net", + "account_index" + ], + "createSql": "CREATE UNIQUE INDEX `index_crypto_net_account_seed_id_crypto_net_account_index` ON `${TABLE_NAME}` (`seed_id`, `crypto_net`, `account_index`)" + } + ], + "foreignKeys": [ + { + "table": "account_seed", + "onDelete": "NO ACTION", + "onUpdate": "NO ACTION", + "columns": [ + "seed_id" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "crypto_coin_transaction", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `date` INTEGER, `is_input` INTEGER NOT NULL, `account_id` INTEGER NOT NULL, `amount` INTEGER NOT NULL, `id_currency` INTEGER NOT NULL, `is_confirmed` INTEGER NOT NULL, `from` TEXT, `to` TEXT, FOREIGN KEY(`account_id`) REFERENCES `crypto_net_account`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`id_currency`) REFERENCES `crypto_currency`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isInput", + "columnName": "is_input", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "accountId", + "columnName": "account_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "amount", + "columnName": "amount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "idCurrency", + "columnName": "id_currency", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isConfirmed", + "columnName": "is_confirmed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "from", + "columnName": "from", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "to", + "columnName": "to", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_crypto_coin_transaction_account_id", + "unique": false, + "columnNames": [ + "account_id" + ], + "createSql": "CREATE INDEX `index_crypto_coin_transaction_account_id` ON `${TABLE_NAME}` (`account_id`)" + }, + { + "name": "index_crypto_coin_transaction_id_currency", + "unique": false, + "columnNames": [ + "id_currency" + ], + "createSql": "CREATE INDEX `index_crypto_coin_transaction_id_currency` ON `${TABLE_NAME}` (`id_currency`)" + } + ], + "foreignKeys": [ + { + "table": "crypto_net_account", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "account_id" + ], + "referencedColumns": [ + "id" + ] + }, + { + "table": "crypto_currency", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "id_currency" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "contact", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT, `email` TEXT, `gravatar` TEXT)", + "fields": [ + { + "fieldPath": "mId", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "mName", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "mEmail", + "columnName": "email", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "mGravatar", + "columnName": "gravatar", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_contact_id", + "unique": false, + "columnNames": [ + "id" + ], + "createSql": "CREATE INDEX `index_contact_id` ON `${TABLE_NAME}` (`id`)" + }, + { + "name": "index_contact_name", + "unique": true, + "columnNames": [ + "name" + ], + "createSql": "CREATE UNIQUE INDEX `index_contact_name` ON `${TABLE_NAME}` (`name`)" + }, + { + "name": "index_contact_email", + "unique": false, + "columnNames": [ + "email" + ], + "createSql": "CREATE INDEX `index_contact_email` ON `${TABLE_NAME}` (`email`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "contact_address", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `contact_id` INTEGER NOT NULL, `crypto_net` TEXT NOT NULL, `address` TEXT)", + "fields": [ + { + "fieldPath": "mId", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "mContactId", + "columnName": "contact_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "mCryptoNet", + "columnName": "crypto_net", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "mAddress", + "columnName": "address", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_contact_address_id", + "unique": true, + "columnNames": [ + "id" + ], + "createSql": "CREATE UNIQUE INDEX `index_contact_address_id` ON `${TABLE_NAME}` (`id`)" + }, + { + "name": "index_contact_address_contact_id_crypto_net", + "unique": true, + "columnNames": [ + "contact_id", + "crypto_net" + ], + "createSql": "CREATE UNIQUE INDEX `index_contact_address_contact_id_crypto_net` ON `${TABLE_NAME}` (`contact_id`, `crypto_net`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "crypto_currency", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT, `crypto_net` TEXT, `precision` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "mId", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "mName", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "mCryptoNet", + "columnName": "crypto_net", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "mPrecision", + "columnName": "precision", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_crypto_currency_crypto_net_name", + "unique": true, + "columnNames": [ + "crypto_net", + "name" + ], + "createSql": "CREATE UNIQUE INDEX `index_crypto_currency_crypto_net_name` ON `${TABLE_NAME}` (`crypto_net`, `name`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "crypto_coin_balance", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `account_id` INTEGER NOT NULL, `crypto_currency_id` INTEGER NOT NULL, `balance` INTEGER NOT NULL, FOREIGN KEY(`account_id`) REFERENCES `crypto_net_account`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )", + "fields": [ + { + "fieldPath": "mId", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "mAccountId", + "columnName": "account_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "mCryptoCurrencyId", + "columnName": "crypto_currency_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "mBalance", + "columnName": "balance", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_crypto_coin_balance_id", + "unique": false, + "columnNames": [ + "id" + ], + "createSql": "CREATE INDEX `index_crypto_coin_balance_id` ON `${TABLE_NAME}` (`id`)" + }, + { + "name": "index_crypto_coin_balance_account_id", + "unique": false, + "columnNames": [ + "account_id" + ], + "createSql": "CREATE INDEX `index_crypto_coin_balance_account_id` ON `${TABLE_NAME}` (`account_id`)" + }, + { + "name": "index_crypto_coin_balance_account_id_crypto_currency_id", + "unique": true, + "columnNames": [ + "account_id", + "crypto_currency_id" + ], + "createSql": "CREATE UNIQUE INDEX `index_crypto_coin_balance_account_id_crypto_currency_id` ON `${TABLE_NAME}` (`account_id`, `crypto_currency_id`)" + } + ], + "foreignKeys": [ + { + "table": "crypto_net_account", + "onDelete": "NO ACTION", + "onUpdate": "NO ACTION", + "columns": [ + "account_id" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "graphene_account", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`crypto_net_account_id` INTEGER NOT NULL, `account_name` TEXT, `account_id` TEXT, `upgraded_to_ltm` INTEGER NOT NULL, PRIMARY KEY(`crypto_net_account_id`), FOREIGN KEY(`crypto_net_account_id`) REFERENCES `crypto_net_account`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )", + "fields": [ + { + "fieldPath": "cryptoNetAccountId", + "columnName": "crypto_net_account_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "account_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "accountId", + "columnName": "account_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "upgradedToLtm", + "columnName": "upgraded_to_ltm", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "crypto_net_account_id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [ + { + "table": "crypto_net_account", + "onDelete": "NO ACTION", + "onUpdate": "NO ACTION", + "columns": [ + "crypto_net_account_id" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "bitshares_asset", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`crypto_curreny_id` INTEGER NOT NULL, `bitshares_id` TEXT, `asset_type` TEXT, PRIMARY KEY(`crypto_curreny_id`), FOREIGN KEY(`crypto_curreny_id`) REFERENCES `crypto_currency`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )", + "fields": [ + { + "fieldPath": "cryptoCurrencyId", + "columnName": "crypto_curreny_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "bitsharesId", + "columnName": "bitshares_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "assetType", + "columnName": "asset_type", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "crypto_curreny_id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [ + { + "table": "crypto_currency", + "onDelete": "NO ACTION", + "onUpdate": "NO ACTION", + "columns": [ + "crypto_curreny_id" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "crypto_currency_equivalence", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `from_crypto_currency_id` INTEGER NOT NULL, `to_crypto_currency_id` INTEGER NOT NULL, `value` INTEGER NOT NULL, `last_checked` INTEGER, FOREIGN KEY(`from_crypto_currency_id`) REFERENCES `crypto_currency`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`to_crypto_currency_id`) REFERENCES `crypto_currency`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "fromCurrencyId", + "columnName": "from_crypto_currency_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "toCurrencyId", + "columnName": "to_crypto_currency_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lastChecked", + "columnName": "last_checked", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_crypto_currency_equivalence_from_crypto_currency_id_to_crypto_currency_id", + "unique": true, + "columnNames": [ + "from_crypto_currency_id", + "to_crypto_currency_id" + ], + "createSql": "CREATE UNIQUE INDEX `index_crypto_currency_equivalence_from_crypto_currency_id_to_crypto_currency_id` ON `${TABLE_NAME}` (`from_crypto_currency_id`, `to_crypto_currency_id`)" + }, + { + "name": "index_crypto_currency_equivalence_from_crypto_currency_id", + "unique": false, + "columnNames": [ + "from_crypto_currency_id" + ], + "createSql": "CREATE INDEX `index_crypto_currency_equivalence_from_crypto_currency_id` ON `${TABLE_NAME}` (`from_crypto_currency_id`)" + }, + { + "name": "index_crypto_currency_equivalence_to_crypto_currency_id", + "unique": false, + "columnNames": [ + "to_crypto_currency_id" + ], + "createSql": "CREATE INDEX `index_crypto_currency_equivalence_to_crypto_currency_id` ON `${TABLE_NAME}` (`to_crypto_currency_id`)" + } + ], + "foreignKeys": [ + { + "table": "crypto_currency", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "from_crypto_currency_id" + ], + "referencedColumns": [ + "id" + ] + }, + { + "table": "crypto_currency", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "to_crypto_currency_id" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "general_setting", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT, `value` TEXT)", + "fields": [ + { + "fieldPath": "mId", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "mName", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "mValue", + "columnName": "value", + "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, \"5aa4eae5c7cf7e77a2ebc1d7a9dc7070\")" + ] + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 0563193..0258a50 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -17,7 +17,9 @@ android:supportsRtl="true" android:configChanges="locale" android:theme="@style/AppTheme"> - + @@ -31,13 +33,15 @@ + + + + - - @@ -100,6 +104,7 @@ + \ No newline at end of file diff --git a/app/src/main/java/cy/agorise/crystalwallet/activities/BackupSeedActivity.java b/app/src/main/java/cy/agorise/crystalwallet/activities/BackupSeedActivity.java index 4cd2f7e..15b7d6b 100644 --- a/app/src/main/java/cy/agorise/crystalwallet/activities/BackupSeedActivity.java +++ b/app/src/main/java/cy/agorise/crystalwallet/activities/BackupSeedActivity.java @@ -3,12 +3,16 @@ package cy.agorise.crystalwallet.activities; import android.arch.lifecycle.LiveData; import android.arch.lifecycle.Observer; import android.arch.lifecycle.ViewModelProviders; +import android.content.ClipData; +import android.content.ClipboardManager; +import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v7.app.AppCompatActivity; import android.widget.Button; import android.widget.TextView; +import android.widget.Toast; import butterknife.BindView; import butterknife.ButterKnife; @@ -17,19 +21,28 @@ import cy.agorise.crystalwallet.R; import cy.agorise.crystalwallet.models.AccountSeed; import cy.agorise.crystalwallet.viewmodels.AccountSeedViewModel; +//tvBrainKey public class BackupSeedActivity extends AppCompatActivity { AccountSeedViewModel accountSeedViewModel; - @BindView(R.id.tvMnemonic) - TextView tvMnemonic; + @BindView(R.id.tvBrainKey) + TextView textfieldBrainkey; @BindView(R.id.btnOk) Button btnOk; + @BindView(R.id.btnCopy) + Button btnCopy; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.backup_seed); + + //Note: Test porpouses + /*final TextView textView = findViewById(R.id.tvBrainKey); + textView.setText("sakk902909321o p3k21kldsa0'dsa90'e930eidakdñsakdñlsakdi90i03 2i90idopsasakk902909321op3k21 kldsa0'dsa90'e930eid akdñsakdñlsakdi90i032i90idopsa"); + */ + ButterKnife.bind(this); long seedId = getIntent().getLongExtra("SEED_ID",-1); @@ -41,7 +54,7 @@ public class BackupSeedActivity extends AppCompatActivity { liveDataAccountSeed.observe(this, new Observer() { @Override public void onChanged(@Nullable AccountSeed accountSeed) { - tvMnemonic.setText(accountSeed.getMasterSeed()); + textfieldBrainkey.setText(accountSeed.getMasterSeed()); } }); accountSeedViewModel.loadSeed(seedId); @@ -56,4 +69,23 @@ public class BackupSeedActivity extends AppCompatActivity { Intent intent = new Intent(this, IntroActivity.class); startActivity(intent); } + + /* + * Clic on button copy to clipboard + * */ + @OnClick(R.id.btnCopy) + public void btnCopyClick(){ + + /* + * Save to clipboard the brainkey chain + * */ + ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); + ClipData clip = ClipData.newPlainText(textfieldBrainkey.getText(), textfieldBrainkey.getText().toString()); + clipboard.setPrimaryClip(clip); + + /* + * Success message + * */ + Toast.makeText(this.getBaseContext(),getResources().getString(R.string.window_seed_toast_clipboard), Toast.LENGTH_SHORT).show(); + } } diff --git a/app/src/main/java/cy/agorise/crystalwallet/activities/BoardActivity.java b/app/src/main/java/cy/agorise/crystalwallet/activities/BoardActivity.java index c9f5a16..3d37978 100644 --- a/app/src/main/java/cy/agorise/crystalwallet/activities/BoardActivity.java +++ b/app/src/main/java/cy/agorise/crystalwallet/activities/BoardActivity.java @@ -8,6 +8,8 @@ import android.content.Intent; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; +import android.graphics.Color; +import android.graphics.Typeface; import android.media.MediaPlayer; import android.os.Bundle; import android.support.design.widget.FloatingActionButton; @@ -17,16 +19,22 @@ import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentStatePagerAdapter; import android.support.v4.app.FragmentTransaction; import android.support.v4.view.ViewPager; -import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; +import android.text.Spannable; +import android.text.SpannableString; +import android.text.style.ForegroundColorSpan; import android.util.Pair; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; +import android.view.ViewGroup; import android.view.animation.LinearInterpolator; import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; -import com.sjaramillo10.animatedtablayout.AnimatedTabLayout; +import com.bumptech.glide.Glide; +import com.bumptech.glide.request.RequestOptions; import java.io.File; @@ -39,6 +47,7 @@ import cy.agorise.crystalwallet.fragments.ContactsFragment; import cy.agorise.crystalwallet.fragments.ReceiveTransactionFragment; import cy.agorise.crystalwallet.fragments.SendTransactionFragment; import cy.agorise.crystalwallet.fragments.TransactionsFragment; +import cy.agorise.crystalwallet.views.natives.GIFView; import de.hdodenhof.circleimageview.CircleImageView; import cy.agorise.crystalwallet.viewmodels.CryptoNetBalanceListViewModel; @@ -46,8 +55,7 @@ import cy.agorise.crystalwallet.viewmodels.CryptoNetBalanceListViewModel; * Created by Henry Varona on 7/10/2017. * */ - -public class BoardActivity extends AppCompatActivity { +public class BoardActivity extends CustomActivity { @BindView(R.id.tabLayout) public TabLayout tabLayout; @@ -64,6 +72,9 @@ public class BoardActivity extends AppCompatActivity { @BindView(R.id.fabAddContact) public FloatingActionButton fabAddContact; + @BindView(R.id.imagevieGIF) + public GIFView imagevieGIF; + public BoardPagerAdapter boardAdapter; /* @@ -98,6 +109,61 @@ public class BoardActivity extends AppCompatActivity { Toolbar toolbar = findViewById(R.id.toolbar); setSupportActionBar(toolbar); + /* + * Set the bubbles animation + * */ + //imagevieGIF.centerCrop(); + //imagevieGIF.load(R.raw.burbujas); + + /* + * Listener tabLayout to resalt text when clicked + * */ + final TabLayout tabLayoutFinal = tabLayout; + tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() { + @Override + public void onTabSelected(final TabLayout.Tab tab) { + + globalActivity.runOnUiThread(new Runnable() { + @Override + public void run() { + + LinearLayout tabLayout = (LinearLayout)((ViewGroup) tabLayoutFinal.getChildAt(0)).getChildAt(tab.getPosition()); + tabLayout.setBackgroundColor(Color.TRANSPARENT); + TextView tabTextView = (TextView) tabLayout.getChildAt(1); + //tabTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP ,50); + Spannable WordtoSpan = new SpannableString(tabTextView.getText()); + WordtoSpan.setSpan(new ForegroundColorSpan(Color.WHITE), 0, tabTextView.getText().length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + tabTextView.setText(WordtoSpan); + tabTextView.setTypeface(tabTextView.getTypeface(), Typeface.BOLD); + } + }); + } + + @Override + public void onTabUnselected(final TabLayout.Tab tab) { + + globalActivity.runOnUiThread(new Runnable() { + @Override + public void run() { + + LinearLayout tabLayout = (LinearLayout)((ViewGroup) tabLayoutFinal.getChildAt(0)).getChildAt(tab.getPosition()); + TextView tabTextView = (TextView) tabLayout.getChildAt(1); + //tabTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP ,50); + Spannable WordtoSpan = new SpannableString(tabTextView.getText()); + WordtoSpan.setSpan(new ForegroundColorSpan(globalActivity.getResources().getColor(R.color.lightGrayClear)), 0, tabTextView.getText().length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + tabTextView.setText(WordtoSpan); + tabTextView.setTypeface(tabTextView.getTypeface(), Typeface.NORMAL); + } + }); + } + + @Override + public void onTabReselected(TabLayout.Tab tab) { + + + } + }); + // Appbar animation mSurfaceView.getHolder().addCallback(new SurfaceHolder.Callback() { @Override diff --git a/app/src/main/java/cy/agorise/crystalwallet/activities/CreateContactActivity.java b/app/src/main/java/cy/agorise/crystalwallet/activities/CreateContactActivity.java index 0791d40..373d41c 100644 --- a/app/src/main/java/cy/agorise/crystalwallet/activities/CreateContactActivity.java +++ b/app/src/main/java/cy/agorise/crystalwallet/activities/CreateContactActivity.java @@ -4,15 +4,14 @@ import android.arch.lifecycle.LiveData; import android.arch.lifecycle.Observer; import android.arch.lifecycle.ViewModelProviders; import android.support.annotation.Nullable; +import android.support.design.widget.TextInputEditText; +import android.support.design.widget.TextInputLayout; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; -import android.text.Editable; import android.view.View; import android.widget.Button; -import android.widget.EditText; -import android.widget.TextView; import java.util.ArrayList; import java.util.List; @@ -33,20 +32,14 @@ import cy.agorise.crystalwallet.views.ContactAddressListAdapter; public class CreateContactActivity extends AppCompatActivity implements UIValidatorListener { - @BindView(R.id.etName) - EditText etName; - @BindView(R.id.tvNameError) - TextView tvNameError; - @BindView(R.id.etEmail) - EditText etEmail; - @BindView(R.id.tvEmailError) - TextView tvEmailError; + @BindView(R.id.tilName) + TextInputLayout tilName; + @BindView(R.id.tietName) + TextInputEditText tietName; @BindView(R.id.btnCancel) Button btnCancel; @BindView(R.id.btnCreate) Button btnCreate; - @BindView(R.id.btnModify) - Button btnModify; @BindView(R.id.rvContactAddresses) RecyclerView rvContactAddresses; @BindView(R.id.btnAddAddress) @@ -59,6 +52,8 @@ public class CreateContactActivity extends AppCompatActivity implements UIValida Contact contact; + long contactId; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -71,10 +66,7 @@ public class CreateContactActivity extends AppCompatActivity implements UIValida rvContactAddresses.setLayoutManager(new LinearLayoutManager(getApplicationContext())); rvContactAddresses.setAdapter(listAdapter); - long contactId = this.getIntent().getLongExtra("CONTACT_ID",-1); - - btnCreate.setVisibility(View.GONE); - btnModify.setVisibility(View.GONE); + contactId = getIntent().getLongExtra("CONTACT_ID",-1); if (contactId >= 0){ final ContactViewModel contactViewModel = ViewModelProviders.of(this).get(ContactViewModel.class); @@ -88,8 +80,7 @@ public class CreateContactActivity extends AppCompatActivity implements UIValida public void onChanged(@Nullable Contact contactChanged) { if (contactChanged != null){ contact = contactChanged; - etName.setText(contact.getName()); - etEmail.setText(contact.getEmail()); + tietName.setText(contact.getName()); LiveData> contactAddresses = contactViewModel.getContactAddresses(); @@ -102,9 +93,10 @@ public class CreateContactActivity extends AppCompatActivity implements UIValida } }); - modifyContactValidator = new ModifyContactValidator(thisActivity.getApplicationContext(),contact,etName,etEmail); + modifyContactValidator = new ModifyContactValidator( + thisActivity.getApplicationContext(), contact, tietName); modifyContactValidator.setListener(thisActivity); - btnModify.setVisibility(View.VISIBLE); + btnCreate.setText(R.string.modify); } else { //No contact was found, this will exit the activity finish(); @@ -112,9 +104,9 @@ public class CreateContactActivity extends AppCompatActivity implements UIValida } }); } else { - contactAddressList = new ArrayList(); + contactAddressList = new ArrayList<>(); listAdapter.submitList(contactAddressList); - createContactValidator = new CreateContactValidator(this.getApplicationContext(),etName,etEmail); + createContactValidator = new CreateContactValidator(this.getApplicationContext(),tietName); createContactValidator.setListener(this); btnCreate.setVisibility(View.VISIBLE); @@ -139,19 +131,12 @@ public class CreateContactActivity extends AppCompatActivity implements UIValida return false; } - @OnTextChanged(value = R.id.etName, + @OnTextChanged(value = R.id.tietName, callback = OnTextChanged.Callback.AFTER_TEXT_CHANGED) - void afterContactNameChanged(Editable editable) { + void afterContactNameChanged() { this.validate(); } - @OnTextChanged(value = R.id.etEmail, - callback = OnTextChanged.Callback.AFTER_TEXT_CHANGED) - void afterEmailChanged(Editable editable) { - this.validate(); - } - - @OnClick(R.id.btnAddAddress) public void addAddress(){ ContactAddress newContactAddress = new ContactAddress(); @@ -164,42 +149,43 @@ public class CreateContactActivity extends AppCompatActivity implements UIValida this.finish(); } - @OnClick(R.id.btnModify) - public void modifyContact(){ - if (this.modifyContactValidator.isValid()) { - this.contact.setName(etName.getText().toString()); - this.contact.setEmail(etEmail.getText().toString()); - this.contact.clearAddresses(); - - for (ContactAddress contactAddress : contactAddressList){ - this.contact.addAddress(contactAddress); - } - - ContactViewModel contactViewModel = ViewModelProviders.of(this).get(ContactViewModel.class); - if (contactViewModel.modifyContact(this.contact)){ - this.finish(); - } else { - this.modifyContactValidator.validate(); - } - } - } - @OnClick(R.id.btnCreate) - public void createContact(){ - if (this.createContactValidator.isValid()) { - Contact newContact = new Contact(); - newContact.setName(etName.getText().toString()); - newContact.setEmail(etEmail.getText().toString()); + public void createOrModifyContact(){ + if(contactId >= 0) { + // Modifying existing contact - for (ContactAddress contactAddress : contactAddressList){ - newContact.addAddress(contactAddress); + if (this.modifyContactValidator.isValid()) { + this.contact.setName(tietName.getText().toString()); + this.contact.clearAddresses(); + + for (ContactAddress contactAddress : contactAddressList){ + this.contact.addAddress(contactAddress); + } + + ContactViewModel contactViewModel = ViewModelProviders.of(this).get(ContactViewModel.class); + if (contactViewModel.modifyContact(this.contact)){ + this.finish(); + } else { + this.modifyContactValidator.validate(); + } } + } else { + // Creating a new contact - ContactViewModel contactViewModel = ViewModelProviders.of(this).get(ContactViewModel.class); - if (contactViewModel.addContact(newContact)){ - this.finish(); - } else { - createContactValidator.validate(); + if (this.createContactValidator.isValid()) { + Contact newContact = new Contact(); + newContact.setName(tietName.getText().toString()); + + for (ContactAddress contactAddress : contactAddressList){ + newContact.addAddress(contactAddress); + } + + ContactViewModel contactViewModel = ViewModelProviders.of(this).get(ContactViewModel.class); + if (contactViewModel.addContact(newContact)){ + this.finish(); + } else { + createContactValidator.validate(); + } } } } @@ -211,10 +197,8 @@ public class CreateContactActivity extends AppCompatActivity implements UIValida activity.runOnUiThread(new Runnable() { public void run() { - if (field.getView() == etName) { - tvNameError.setText(""); - } else if (field.getView() == etEmail) { - tvEmailError.setText(""); + if (field.getView() == tietName) { + tilName.setError(""); } if (activity.isValid()){ @@ -232,10 +216,8 @@ public class CreateContactActivity extends AppCompatActivity implements UIValida @Override public void run() { - if (field.getView() == etName) { - tvNameError.setText(field.getMessage()); - } else if (field.getView() == etEmail) { - tvEmailError.setText(field.getMessage()); + if (field.getView() == tietName) { + tilName.setError(field.getMessage()); } } }); diff --git a/app/src/main/java/cy/agorise/crystalwallet/activities/CreateSeedActivity.java b/app/src/main/java/cy/agorise/crystalwallet/activities/CreateSeedActivity.java deleted file mode 100644 index aa6d024..0000000 --- a/app/src/main/java/cy/agorise/crystalwallet/activities/CreateSeedActivity.java +++ /dev/null @@ -1,204 +0,0 @@ -package cy.agorise.crystalwallet.activities; - -import android.arch.lifecycle.ViewModelProviders; -import android.content.Intent; -import android.os.Bundle; -import android.support.design.widget.TextInputEditText; -import android.support.design.widget.TextInputLayout; -import android.support.v7.app.AlertDialog; -import android.support.v7.app.AppCompatActivity; -import android.text.Editable; -import android.view.ViewGroup; -import android.widget.Button; - -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.OnClick; -import butterknife.OnTextChanged; -import cy.agorise.crystalwallet.R; -import cy.agorise.crystalwallet.requestmanagers.CryptoNetInfoRequestListener; -import cy.agorise.crystalwallet.requestmanagers.CryptoNetInfoRequests; -import cy.agorise.crystalwallet.requestmanagers.ValidateCreateBitsharesAccountRequest; -import cy.agorise.crystalwallet.models.GrapheneAccount; -import cy.agorise.crystalwallet.viewmodels.AccountSeedViewModel; -import cy.agorise.crystalwallet.viewmodels.validators.CreateSeedValidator; -import cy.agorise.crystalwallet.viewmodels.validators.UIValidatorListener; -import cy.agorise.crystalwallet.viewmodels.validators.validationfields.ValidationField; - -public class CreateSeedActivity extends AppCompatActivity implements UIValidatorListener { - - AccountSeedViewModel accountSeedViewModel; - CreateSeedValidator createSeedValidator; - - @BindView(R.id.tilPin) - TextInputLayout tilPin; - - @BindView(R.id.tietPin) - TextInputEditText tietPin; - - @BindView(R.id.tilPinConfirmation) - TextInputLayout tilPinConfirmation; - - @BindView(R.id.tietPinConfirmation) - TextInputEditText tietPinConfirmation; - - //@BindView(R.id.tvSeedWords) - //TextView tvSeedWords; - - @BindView(R.id.tilAccountName) - TextInputLayout tilAccountName; - - @BindView (R.id.tietAccountName) - TextInputEditText tietAccountName; - - @BindView(R.id.btnCreate) - Button btnCreate; - - @BindView(R.id.btnCancel) - Button btnCancel; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.create_seed); - ButterKnife.bind(this); - - tilPin.setErrorEnabled(true); - tilPinConfirmation.setErrorEnabled(true); - tilAccountName.setErrorEnabled(true); - - btnCreate.setEnabled(false); - accountSeedViewModel = ViewModelProviders.of(this).get(AccountSeedViewModel.class); - createSeedValidator = new CreateSeedValidator(this.getApplicationContext(),tietPin,tietPinConfirmation,tietAccountName); - createSeedValidator.setListener(this); - } - - @OnTextChanged(value = R.id.tietPin, - callback = OnTextChanged.Callback.AFTER_TEXT_CHANGED) - void afterPinChanged(Editable editable) { - this.createSeedValidator.validate(); - } - - @OnTextChanged(value = R.id.tietPinConfirmation, - callback = OnTextChanged.Callback.AFTER_TEXT_CHANGED) - void afterPinConfirmationChanged(Editable editable) { - this.createSeedValidator.validate(); - } - - /*@OnTextChanged(value = R.id.etSeedWords, - callback = OnTextChanged.Callback.AFTER_TEXT_CHANGED) - void afterSeedWordsChanged(Editable editable) { - this.createSeedValidator.validate(); - } -*/ - - @OnTextChanged(value = R.id.tietAccountName, - callback = OnTextChanged.Callback.AFTER_TEXT_CHANGED) - void afterAccountNameChanged(Editable editable) { - this.createSeedValidator.validate(); - } - - @OnClick(R.id.btnCancel) - public void cancel(){ - this.finish(); - } - - @OnClick(R.id.btnCreate) - public void createSeed(){ - if (this.createSeedValidator.isValid()) { - // Make request to create a bitshare account - final ValidateCreateBitsharesAccountRequest request = - new ValidateCreateBitsharesAccountRequest(tietAccountName.getText().toString(), getApplicationContext()); - - - //Makes dialog to tell the user that the account is been created - AlertDialog.Builder alertBuilder = new AlertDialog.Builder(CreateSeedActivity.this,R.style.AppTheme); - alertBuilder.setView(R.layout.progress_creating_account); - //alertBuilder.setTitle("Processing"); - //alertBuilder.setMessage("Creating Bitshares Account"); - final AlertDialog processDialog = alertBuilder.create(); - CreateSeedActivity.this.runOnUiThread(new Runnable() { - @Override - public void run() { - processDialog.setCancelable(false); - processDialog.show(); - processDialog.getWindow().setLayout(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); - } - }); - - request.setListener(new CryptoNetInfoRequestListener() { - @Override - public void onCarryOut() { - processDialog.dismiss(); - if (request.getStatus().equals(ValidateCreateBitsharesAccountRequest.StatusCode.SUCCEEDED)) { - GrapheneAccount accountSeed = request.getAccount(); - Intent intent = new Intent(getApplicationContext(), BackupSeedActivity.class); - intent.putExtra("SEED_ID", accountSeed.getId()); - startActivity(intent); - } else { - createSeedValidator.validate(); - } - } - }); - - Thread thread = new Thread() { - @Override - public void run() { - CryptoNetInfoRequests.getInstance().addRequest(request); - } - }; - - thread.start(); - - - - //this.finish(); - } - } - - @Override - public void onValidationSucceeded(final ValidationField field) { - final CreateSeedActivity activity = this; - - activity.runOnUiThread(new Runnable() { - public void run() { - - if (field.getView() == tietPin) { - tilPin.setError(""); - } else if (field.getView() == tietPinConfirmation){ - tilPinConfirmation.setError(""); - } else if (field.getView() == tietAccountName){ - tilAccountName.setError(""); - } //else if (field.getView() == etSeedWords){ - // tvSeedWordsError.setText(""); - //} - - if (activity.createSeedValidator.isValid()){ - btnCreate.setEnabled(true); - } else { - btnCreate.setEnabled(false); - } - - } - }); - } - - @Override - public void onValidationFailed(final ValidationField field) { - runOnUiThread(new Runnable() { - - @Override - public void run() { - if (field.getView() == tietPin) { - tilPin.setError(field.getMessage()); - } else if (field.getView() == tietPinConfirmation){ - tilPinConfirmation.setError(field.getMessage()); - } else if (field.getView() == tietAccountName){ - tilAccountName.setError(field.getMessage()); - } //else if (field.getView() == etSeedWords){ - // tvSeedWordsError.setText(field.getMessage()); - //} - } - }); - } -} diff --git a/app/src/main/java/cy/agorise/crystalwallet/activities/CreateSeedActivity.kt b/app/src/main/java/cy/agorise/crystalwallet/activities/CreateSeedActivity.kt new file mode 100644 index 0000000..b1b86ba --- /dev/null +++ b/app/src/main/java/cy/agorise/crystalwallet/activities/CreateSeedActivity.kt @@ -0,0 +1,245 @@ +package cy.agorise.crystalwallet.activities + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import android.text.Editable +import android.view.inputmethod.InputMethodManager +import android.widget.Toast +import butterknife.ButterKnife +import butterknife.OnClick +import butterknife.OnTextChanged +import cy.agorise.crystalwallet.R +import cy.agorise.crystalwallet.dialogs.material.CrystalDialog +import cy.agorise.crystalwallet.requestmanagers.CryptoNetInfoRequests +import cy.agorise.crystalwallet.requestmanagers.ValidateCreateBitsharesAccountRequest +import cy.agorise.crystalwallet.viewmodels.validators.customImpl.interfaces.UIValidatorListener +import cy.agorise.crystalwallet.viewmodels.validators.customImpl.validationFields.BitsharesAccountNameValidation +import cy.agorise.crystalwallet.viewmodels.validators.customImpl.validationFields.BitsharesAccountNameValidation.OnAccountExist +import cy.agorise.crystalwallet.viewmodels.validators.customImpl.validationFields.CustomValidationField +import cy.agorise.crystalwallet.viewmodels.validators.customImpl.validationFields.PinDoubleConfirmationValidationField +import cy.agorise.crystalwallet.views.natives.CustomTextInputEditText +import kotlinx.android.synthetic.main.create_seed.* + + +/* +* This activity creates a new account with some security concerns +* */ +class CreateSeedActivity : CustomActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + /* + * Assign the view to this controller + * */ + setContentView(R.layout.create_seed) + + /* + * Initialice butterknife MVC + * */ + ButterKnife.bind(this) + + /* + * Add the controls to the validator + * */ + this.fieldsValidator.add(tietPin) + this.fieldsValidator.add(tietPinConfirmation) + this.fieldsValidator.add(tietAccountName) + + /* + * Validations listener + * */ + val uiValidatorListener = object : UIValidatorListener { + + override fun onValidationSucceeded(customValidationField: CustomValidationField) { + + try { + + /* + * Remove error + * */ + runOnUiThread { + val customTextInputEditText = customValidationField.currentView as CustomTextInputEditText + customTextInputEditText.error = null + } + + } catch (e: Exception) { + e.printStackTrace() + } + + /* + * Validate if can continue + * */ + validateFieldsToContinue() + } + + override fun onValidationFailed(customValidationField: CustomValidationField) { + + /* + * Set error label + * */ + runOnUiThread { + val customTextInputEditText = customValidationField.currentView as CustomTextInputEditText + customTextInputEditText.error = customTextInputEditText.fieldValidatorModel.message + } + } + } + + /* + * Create the pin double validation + * */ + val pinDoubleConfirmationValidationField = PinDoubleConfirmationValidationField(this, tietPin, tietPinConfirmation, uiValidatorListener) + + /* + * Listener for the validation for success or fail + * */ + tietPin?.setUiValidator(pinDoubleConfirmationValidationField) //Validator for the field + tietPinConfirmation?.setUiValidator(pinDoubleConfirmationValidationField) //Validator for the field + + /* + * Account name validator + * */ + val bitsharesAccountNameValidation = BitsharesAccountNameValidation(this, tietAccountName, uiValidatorListener) + val onAccountExist = object : OnAccountExist { + override fun onAccountExists() { + runOnUiThread { + Toast.makeText(globalActivity, resources.getString(R.string.account_name_already_exist), Toast.LENGTH_LONG).show() + } + } + + } + bitsharesAccountNameValidation.setOnAccountExist(onAccountExist) + tietAccountName?.setUiValidator(bitsharesAccountNameValidation) + + /*This button should not be enabled till all the fields be correctly filled*/ + disableCreate() + + /* + * Set the focus on the fisrt field and show keyboard + * */ + tilPin?.requestFocus() + val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager + imm.showSoftInput(tilPin, InputMethodManager.SHOW_IMPLICIT) + } + + @OnTextChanged(value = R.id.tietPin, callback = OnTextChanged.Callback.AFTER_TEXT_CHANGED) + internal fun afterPinChanged(editable: Editable) { + this.fieldsValidator.validate() + + /* + * Validate continue to create account + * */ + validateFieldsToContinue() + } + + @OnTextChanged(value = R.id.tietPinConfirmation, callback = OnTextChanged.Callback.AFTER_TEXT_CHANGED) + internal fun afterPinConfirmationChanged(editable: Editable) { + this.fieldsValidator.validate() + + /* + * Validate continue to create account + * */ + validateFieldsToContinue() + } + + @OnTextChanged(value = R.id.tietAccountName, callback = OnTextChanged.Callback.AFTER_TEXT_CHANGED) + internal fun afterAccountNameChanged(editable: Editable) { + this.fieldsValidator.validate() + + /* + * Always disable till the server response comes + * */ + disableCreate() + } + + @OnClick(R.id.btnCancel) + fun cancel() { + + /* + * Exit of the activity + * */ + this.finish() + } + + @OnClick(R.id.btnCreate) + fun createSeed() { + + // Make request to create a bitshare account + val request = ValidateCreateBitsharesAccountRequest(tietAccountName?.getText().toString(), applicationContext) + + //DTVV: Friday 27 July 2018 + //Makes dialog to tell the user that the account is been created + val creatingAccountMaterialDialog = CrystalDialog(this) + creatingAccountMaterialDialog.setText(this.resources.getString(R.string.window_create_seed_DialogMessage)) + creatingAccountMaterialDialog.progress() + this@CreateSeedActivity.runOnUiThread { + creatingAccountMaterialDialog.show() + } + request.setListener { + creatingAccountMaterialDialog.dismiss() + if (request.status == ValidateCreateBitsharesAccountRequest.StatusCode.SUCCEEDED) { + val accountSeed = request.account + val intent = Intent(applicationContext, BackupSeedActivity::class.java) + intent.putExtra("SEED_ID", accountSeed.id) + startActivity(intent) + } else { + fieldsValidator.validate() + } + } + + (object : Thread() { + override fun run() { + + /* + * + * Run thread*/ + CryptoNetInfoRequests.getInstance().addRequest(request) + } + }).start() + } + + /* + * Validate that all is complete to continue to create + * */ + private fun validateFieldsToContinue() { + + var result = false //Contains the final result + + val pinValid: Boolean? = this.tietPin?.getFieldValidatorModel()?.isValid + val pinConfirmationValid = this.tietPinConfirmation?.getFieldValidatorModel()?.isValid + val pinAccountNameValid = this.tietAccountName?.getFieldValidatorModel()?.isValid + + if (pinValid!! && + pinConfirmationValid!! && + pinAccountNameValid!!) { + result = true //Validation is correct + } + + + /* + * If the result is true so the user can continue to the creation of the account + * */ + if (result) { + enableCreate() + } else { + disableCreate() + } + } + + /* + * Enable create button + * */ + private fun enableCreate() { + btnCreate?.setEnabled(true) + btnCreate?.setBackgroundColor(resources.getColor(R.color.colorPrimary)) + } + + /* + * Disable create button + * */ + private fun disableCreate() { + btnCreate?.setEnabled(false) + btnCreate?.setBackground(resources.getDrawable(R.drawable.disable_style)) + } +} \ No newline at end of file diff --git a/app/src/main/java/cy/agorise/crystalwallet/activities/CustomActivity.kt b/app/src/main/java/cy/agorise/crystalwallet/activities/CustomActivity.kt new file mode 100644 index 0000000..5d9a8ae --- /dev/null +++ b/app/src/main/java/cy/agorise/crystalwallet/activities/CustomActivity.kt @@ -0,0 +1,31 @@ +package cy.agorise.crystalwallet.activities + +import android.app.Activity +import android.os.Bundle +import android.support.v7.app.AppCompatActivity +import cy.agorise.crystalwallet.util.FieldsValidator + +/* +* Custom implementaion of the activity +* */ +open class CustomActivity : AppCompatActivity() { + + /* + * Contains the validator for general fields + * */ + @JvmField protected var fieldsValidator = FieldsValidator() + + /* + * Contains the global activity + * */ + protected lateinit var globalActivity: Activity + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + /* + * Save the current activity for further reference + * */ + this.globalActivity = this + } +} \ No newline at end of file diff --git a/app/src/main/java/cy/agorise/crystalwallet/activities/ImportSeedActivity.java b/app/src/main/java/cy/agorise/crystalwallet/activities/ImportSeedActivity.java index 26e3541..db7c445 100644 --- a/app/src/main/java/cy/agorise/crystalwallet/activities/ImportSeedActivity.java +++ b/app/src/main/java/cy/agorise/crystalwallet/activities/ImportSeedActivity.java @@ -1,22 +1,31 @@ package cy.agorise.crystalwallet.activities; import android.arch.lifecycle.ViewModelProviders; +import android.content.Intent; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.text.Editable; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; +import android.widget.Toast; import butterknife.BindView; import butterknife.ButterKnife; import butterknife.OnClick; import butterknife.OnTextChanged; import cy.agorise.crystalwallet.R; +import cy.agorise.crystalwallet.enums.CryptoNet; import cy.agorise.crystalwallet.enums.SeedType; +import cy.agorise.crystalwallet.manager.BitsharesAccountManager; import cy.agorise.crystalwallet.models.AccountSeed; import cy.agorise.crystalwallet.models.CryptoNetAccount; +import cy.agorise.crystalwallet.models.GrapheneAccount; import cy.agorise.crystalwallet.models.GrapheneAccountInfo; +import cy.agorise.crystalwallet.requestmanagers.CryptoNetInfoRequestListener; +import cy.agorise.crystalwallet.requestmanagers.CryptoNetInfoRequests; +import cy.agorise.crystalwallet.requestmanagers.ImportBackupRequest; +import cy.agorise.crystalwallet.requestmanagers.ValidateImportBitsharesAccountRequest; import cy.agorise.crystalwallet.viewmodels.AccountSeedViewModel; import cy.agorise.crystalwallet.viewmodels.CryptoNetAccountViewModel; import cy.agorise.crystalwallet.viewmodels.GrapheneAccountInfoViewModel; @@ -100,7 +109,45 @@ public class ImportSeedActivity extends AppCompatActivity implements UIValidator @OnClick(R.id.btnImport) public void importSeed(){ + final ImportSeedActivity thisActivity = this; + if (this.importSeedValidator.isValid()) { + + final ValidateImportBitsharesAccountRequest validatorRequest = + new ValidateImportBitsharesAccountRequest(etAccountName.getText().toString(), etSeedWords.getText().toString()); + + validatorRequest.setListener(new CryptoNetInfoRequestListener() { + @Override + public void onCarryOut() { + if (!validatorRequest.getStatus().equals(ValidateImportBitsharesAccountRequest.StatusCode.SUCCEEDED)) { + String errorText = "An error ocurred attempting to import the account"; + + switch (validatorRequest.getStatus()){ + case PETITION_FAILED: + case NO_INTERNET: + case NO_SERVER_CONNECTION: + errorText = "There was an error with the connection. Try again later"; + break; + case ACCOUNT_DOESNT_EXIST: + errorText = "The account doesn't exists"; + break; + case BAD_SEED: + errorText = "The seed is not valid"; + break; + case NO_ACCOUNT_DATA: + errorText = "The account doesn't have any data"; + break; + } + + Toast.makeText(thisActivity.getApplicationContext(),errorText,Toast.LENGTH_LONG).show(); + } else { + Intent intent = new Intent(thisActivity, IntroActivity.class); + startActivity(intent); + } + } + }); + /*CryptoNetInfoRequests.getInstance().addRequest(validatorRequest); + AccountSeed seed = new AccountSeed(); //TODO verify if words are already in the db @@ -122,7 +169,7 @@ public class ImportSeedActivity extends AppCompatActivity implements UIValidator grapheneAccountInfo.setName(etAccountName.getText().toString()); grapheneAccountInfoViewModel.addGrapheneAccountInfo(grapheneAccountInfo); - this.finish(); + this.finish();*/ } } diff --git a/app/src/main/java/cy/agorise/crystalwallet/activities/IntroActivity.java b/app/src/main/java/cy/agorise/crystalwallet/activities/IntroActivity.java index 9d4eaf1..50968fa 100644 --- a/app/src/main/java/cy/agorise/crystalwallet/activities/IntroActivity.java +++ b/app/src/main/java/cy/agorise/crystalwallet/activities/IntroActivity.java @@ -12,6 +12,7 @@ import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; import android.widget.Button; +import android.widget.Toast; import java.util.List; @@ -19,13 +20,18 @@ import butterknife.BindView; import butterknife.ButterKnife; import butterknife.OnClick; import cy.agorise.crystalwallet.R; +import cy.agorise.crystalwallet.apigenerator.GrapheneApiGenerator; +import cy.agorise.crystalwallet.application.CrystalApplication; import cy.agorise.crystalwallet.dao.CrystalDatabase; +import cy.agorise.crystalwallet.dialogs.material.ToastIt; +import cy.agorise.crystalwallet.enums.CryptoNet; import cy.agorise.crystalwallet.fragments.ImportAccountOptionsFragment; import cy.agorise.crystalwallet.models.AccountSeed; import cy.agorise.crystalwallet.models.CryptoCoinBalance; import cy.agorise.crystalwallet.models.CryptoCoinTransaction; import cy.agorise.crystalwallet.models.CryptoNetAccount; import cy.agorise.crystalwallet.models.GeneralSetting; +import cy.agorise.crystalwallet.network.CryptoNetManager; import cy.agorise.crystalwallet.randomdatagenerators.RandomCryptoCoinBalanceGenerator; import cy.agorise.crystalwallet.randomdatagenerators.RandomCryptoNetAccountGenerator; import cy.agorise.crystalwallet.randomdatagenerators.RandomSeedGenerator; @@ -35,10 +41,7 @@ import cy.agorise.crystalwallet.viewmodels.AccountSeedListViewModel; import cy.agorise.crystalwallet.viewmodels.TransactionListViewModel; import cy.agorise.crystalwallet.views.TransactionListView; -public class IntroActivity extends AppCompatActivity { - - TransactionListViewModel transactionListViewModel; - TransactionListView transactionListView; +public class IntroActivity extends CustomActivity { @BindView(R.id.surface_view) public SurfaceView mSurfaceView; @@ -53,6 +56,7 @@ public class IntroActivity extends AppCompatActivity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_intro); + ButterKnife.bind(this); // Appbar animation @@ -97,6 +101,7 @@ public class IntroActivity extends AppCompatActivity { Intent intent = new Intent(this, BoardActivity.class); //Intent intent = new Intent(this, PocketRequestActivity.class); startActivity(intent); + finish(); } /*CrystalDatabase db = CrystalDatabase.getAppDatabase(getApplicationContext()); diff --git a/app/src/main/java/cy/agorise/crystalwallet/activities/LicenseActivity.java b/app/src/main/java/cy/agorise/crystalwallet/activities/LicenseActivity.java index 8964738..0cf6e82 100644 --- a/app/src/main/java/cy/agorise/crystalwallet/activities/LicenseActivity.java +++ b/app/src/main/java/cy/agorise/crystalwallet/activities/LicenseActivity.java @@ -10,8 +10,11 @@ import butterknife.BindView; import butterknife.ButterKnife; import butterknife.OnClick; import cy.agorise.crystalwallet.R; +import cy.agorise.crystalwallet.application.CrystalApplication; import cy.agorise.crystalwallet.dao.CrystalDatabase; +import cy.agorise.crystalwallet.enums.CryptoNet; import cy.agorise.crystalwallet.models.GeneralSetting; +import cy.agorise.crystalwallet.network.CryptoNetManager; public class LicenseActivity extends AppCompatActivity { @@ -23,6 +26,7 @@ public class LicenseActivity extends AppCompatActivity { protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_license); + ButterKnife.bind(this); // TODO check if license has been agreed @@ -37,6 +41,7 @@ public class LicenseActivity extends AppCompatActivity { if ((generalSettingLastLicenseRead != null) && (Integer.parseInt(generalSettingLastLicenseRead.getValue()) >= licenseVersion)) { Intent intent = new Intent(this, IntroActivity.class); startActivity(intent); + finish(); } } @@ -52,6 +57,7 @@ public class LicenseActivity extends AppCompatActivity { Intent intent = new Intent(this, IntroActivity.class); startActivity(intent); + finish(); } @OnClick(R.id.btnDisAgree) diff --git a/app/src/main/java/cy/agorise/crystalwallet/activities/LoadingActivity.kt b/app/src/main/java/cy/agorise/crystalwallet/activities/LoadingActivity.kt new file mode 100644 index 0000000..9b7c2d7 --- /dev/null +++ b/app/src/main/java/cy/agorise/crystalwallet/activities/LoadingActivity.kt @@ -0,0 +1,256 @@ +package cy.agorise.crystalwallet.activities + +import android.app.Activity +import android.content.Intent +import android.os.Bundle +import android.os.Handler +import butterknife.ButterKnife +import kotlinx.android.synthetic.main.loading_activity.* +import android.view.animation.AnimationUtils +import android.widget.ImageView +import butterknife.BindView +import cy.agorise.crystalwallet.R + + +/* +* This activity is to show a loading window when it is needed +* */ +class LoadingActivity : CustomActivity() { + + @BindView(R.id.imageviewLoading) + lateinit var imageviewLoading:ImageView + + override fun onCreate(savedInstanceState: Bundle?) { + + /* + * Construct the parent + * */ + super.onCreate(savedInstanceState) + + /* + * If the window was closed before be created so finish it + * */ + if(destroyWindow!!){ + finish() + } + + /* + * Assign the view to this controller + * */ + setContentView(cy.agorise.crystalwallet.R.layout.loading_activity) + + /* + * Save the current activity + * */ + currentActivity = this + + /* + * If has to manage timer + * */ + if(LoadingActivity.seconds != -1){ + + /* + * */ + Handler().postDelayed({ + + /* + * Reset flag + * */ + LoadingActivity.seconds = -1 + + finish() //Finish the current window + }, (LoadingActivity.seconds * 1000).toLong()) + } + + /* + * Initialice butterknife MVC + * */ + ButterKnife.bind(this) + + /* + * If has to change the loading sizes so + * */ + if(loadinIconChangeSize){ + imageviewLoading.layoutParams.width = loadingIconWidth + imageviewLoading.layoutParams.height = loadingIconHeigt + } + + /* + * Rotate the image + * */ + val rotation = AnimationUtils.loadAnimation(this, cy.agorise.crystalwallet.R.anim.rotate360) + imageviewLoading.startAnimation(rotation) + + /* + * If listener is set deliver response + * */ + if(onLoadingReady != null){ + onLoadingReady!!.onLoadingReady() + } + } + + /* + * This events hires when the window is destroyed + * */ + override fun onDestroy() { + super.onDestroy() + + /* + * If listener is set deliver response + * */ + if(onLoadingClosed != null){ + onLoadingClosed?.onLoadingClosed() + } + } + + override fun onResume() { + super.onResume() + + /* + * If the window was closed before be created so finish it + * */ + if(destroyWindow!!){ + finish() + } + } + + /* + * Static methods + * */ + companion object { + + /* + * Contains the activity shown + * */ + private var currentActivity: Activity? = null + + /* + * Flag to validate if the window has to finish or not + * */ + private var destroyWindow:Boolean? = false + + /* + * Listener when the loading window is closed + * */ + private var onLoadingClosed:LoadingClosed? = null + + /* + * Listener when the loading window is resume + * */ + private var onLoadingReady:LoadingReady? = null + + /* + * Contains the seconds to finish the window in case of timer + * */ + private var seconds:Int = -1 + + /* + * Contains the icon loading size + * */ + private var loadingIconWidth:Int = -1 + private var loadingIconHeigt:Int = -1 + private var loadinIconChangeSize:Boolean = false + + /* + * Show the loading activity + * */ + @JvmStatic + open fun show(activity: Activity) { + + if(activity!=null){ + + /* + * If it is not visible + * */ + if(currentActivity==null){ + + /* + * Reset flags + * */ + destroyWindow = false + + /* + * Show the loading activity + * */ + val intent = Intent(activity, LoadingActivity::class.java) + activity.startActivity(intent) + } + } + } + + /* + * Dismiss the loading activity + * */ + @JvmStatic + open fun dismiss() { + + if(currentActivity!=null){ + + /* + * Close the activity + * */ + currentActivity?.finish() + + /* + * Reset flags + * */ + loadinIconChangeSize = false + currentActivity = null + destroyWindow = true + } + } + + /* + * Change the loading icon size + * */ + @JvmStatic + open fun loadingIconSize(width:Int,heigth:Int) { + + /* + * The loading icon size wil change + * */ + loadinIconChangeSize = true + + /* + * Save the sizes + * */ + loadingIconWidth = width + loadingIconHeigt = heigth + } + + /* + * When the loading window is closed + * */ + @JvmStatic + open fun onLoadingClosed(onLoadingClose: LoadingClosed) { + this.onLoadingClosed = onLoadingClose + } + + /* + * When the loading window is up and visible + * */ + @JvmStatic + open fun onLoadingReady(onLoadingResume:LoadingReady) { + this.onLoadingReady = onLoadingResume + } + + + /* + * Timer to close the window + * */ + @JvmStatic + open fun closeOnTime(seconds:Int) { + LoadingActivity.seconds = seconds + } + } + + /* + * Interface for all the events + * */ + interface LoadingClosed{ + fun onLoadingClosed() + } + interface LoadingReady{ + fun onLoadingReady() + } +} \ No newline at end of file diff --git a/app/src/main/java/cy/agorise/crystalwallet/activities/PinRequestActivity.java b/app/src/main/java/cy/agorise/crystalwallet/activities/PinRequestActivity.java index a4f4a5a..289d899 100644 --- a/app/src/main/java/cy/agorise/crystalwallet/activities/PinRequestActivity.java +++ b/app/src/main/java/cy/agorise/crystalwallet/activities/PinRequestActivity.java @@ -7,22 +7,18 @@ import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v7.app.AppCompatActivity; import android.text.Editable; -import android.widget.Button; import android.widget.EditText; -import android.widget.TextView; import java.util.List; import butterknife.BindView; import butterknife.ButterKnife; -import butterknife.OnClick; import butterknife.OnTextChanged; import cy.agorise.crystalwallet.R; import cy.agorise.crystalwallet.application.CrystalSecurityMonitor; import cy.agorise.crystalwallet.models.AccountSeed; import cy.agorise.crystalwallet.models.GeneralSetting; import cy.agorise.crystalwallet.util.PasswordManager; -import cy.agorise.crystalwallet.viewmodels.AccountSeedViewModel; import cy.agorise.crystalwallet.viewmodels.GeneralSettingListViewModel; public class PinRequestActivity extends AppCompatActivity { diff --git a/app/src/main/java/cy/agorise/crystalwallet/activities/SendTransactionActivity.java b/app/src/main/java/cy/agorise/crystalwallet/activities/SendTransactionActivity.java index f1783e3..f34db62 100644 --- a/app/src/main/java/cy/agorise/crystalwallet/activities/SendTransactionActivity.java +++ b/app/src/main/java/cy/agorise/crystalwallet/activities/SendTransactionActivity.java @@ -11,6 +11,8 @@ import android.widget.EditText; import android.widget.Spinner; import android.widget.TextView; +import com.jaredrummler.materialspinner.MaterialSpinner; + import java.util.ArrayList; import java.util.List; @@ -35,8 +37,8 @@ public class SendTransactionActivity extends AppCompatActivity implements UIVali SendTransactionValidator sendTransactionValidator; - /*@BindView(R.id.etFrom) - EditText etFrom;*/ + @BindView(R.id.spFrom) + MaterialSpinner spFrom; @BindView(R.id.tvFromError) TextView tvFromError; @BindView(R.id.etTo) @@ -102,7 +104,7 @@ public class SendTransactionActivity extends AppCompatActivity implements UIVali } }); - //sendTransactionValidator = new SendTransactionValidator(this.getApplicationContext(), this.cryptoNetAccount, etFrom, etTo, spAsset, etAmount, etMemo); + sendTransactionValidator = new SendTransactionValidator(this.getApplicationContext(), this.cryptoNetAccount, spFrom, etTo, spAsset, etAmount, etMemo); sendTransactionValidator.setListener(this); } else { this.finish(); diff --git a/app/src/main/java/cy/agorise/crystalwallet/apigenerator/GrapheneApiGenerator.java b/app/src/main/java/cy/agorise/crystalwallet/apigenerator/GrapheneApiGenerator.java index d1bfc00..575a75c 100644 --- a/app/src/main/java/cy/agorise/crystalwallet/apigenerator/GrapheneApiGenerator.java +++ b/app/src/main/java/cy/agorise/crystalwallet/apigenerator/GrapheneApiGenerator.java @@ -8,7 +8,6 @@ import java.util.Date; import java.util.HashMap; import java.util.List; -import cy.agorise.crystalwallet.application.constant.BitsharesConstant; import cy.agorise.crystalwallet.dao.BitsharesAssetDao; import cy.agorise.crystalwallet.dao.CryptoCoinBalanceDao; import cy.agorise.crystalwallet.dao.CryptoCurrencyDao; @@ -81,6 +80,8 @@ public abstract class GrapheneApiGenerator { */ private static HashMap currentBitsharesListener = new HashMap<>(); + + /** * Retrieves the data of an account searching by it's id * @@ -702,5 +703,4 @@ public abstract class GrapheneApiGenerator { } } - } diff --git a/app/src/main/java/cy/agorise/crystalwallet/application/CrystalApplication.java b/app/src/main/java/cy/agorise/crystalwallet/application/CrystalApplication.java index b7776d1..6431e7c 100644 --- a/app/src/main/java/cy/agorise/crystalwallet/application/CrystalApplication.java +++ b/app/src/main/java/cy/agorise/crystalwallet/application/CrystalApplication.java @@ -1,5 +1,6 @@ package cy.agorise.crystalwallet.application; +import android.app.Activity; import android.app.Application; import android.content.Intent; import android.content.res.Configuration; @@ -11,6 +12,7 @@ import com.idescout.sql.SqlScoutServer; import java.util.Locale; import cy.agorise.crystalwallet.R; +import cy.agorise.crystalwallet.apigenerator.GrapheneApiGenerator; import cy.agorise.crystalwallet.dao.CrystalDatabase; import cy.agorise.crystalwallet.enums.CryptoNet; import cy.agorise.crystalwallet.models.BitsharesAsset; @@ -91,7 +93,9 @@ public class CrystalApplication extends Application { CryptoNetEvents.getInstance().addListener(crystalWalletNotifier); //Next line is for use the bitshares main net - CryptoNetManager.addCryptoNetURL(CryptoNet.BITSHARES,BITSHARES_URL); + // TODO fix, the following line accepts one string not an array it needs to accept an arrey + // TODO and hoop over the urls if no connection can be established + CryptoNetManager.addCryptoNetURL(CryptoNet.BITSHARES,BITSHARES_URL[2]); GeneralSetting generalSettingPreferredLanguage = db.generalSettingDao().getSettingByName(GeneralSetting.SETTING_NAME_PREFERRED_LANGUAGE); diff --git a/app/src/main/java/cy/agorise/crystalwallet/dialogs/material/CrystalDialog.kt b/app/src/main/java/cy/agorise/crystalwallet/dialogs/material/CrystalDialog.kt new file mode 100644 index 0000000..e21e0d2 --- /dev/null +++ b/app/src/main/java/cy/agorise/crystalwallet/dialogs/material/CrystalDialog.kt @@ -0,0 +1,30 @@ +package cy.agorise.crystalwallet.dialogs.material + +import android.app.Activity +import cy.agorise.crystalwallet.R +import kotlinx.android.synthetic.main.account_seed_list.view.* + +/* +* Dialog material that shows loading gif and and explicit message +* */ +open class CrystalDialog : DialogMaterial{ + + constructor(activity: Activity) : super(activity) { + + /* + * Prepare the dialog + * */ + this.builder.title("") + this.builder.content("") + } +} + +/* +* Internal interfaces +* */ +interface PositiveResponse{ + fun onPositive() +} +interface NegativeResponse{ + fun onNegative() +} \ No newline at end of file diff --git a/app/src/main/java/cy/agorise/crystalwallet/dialogs/material/DialogMaterial.kt b/app/src/main/java/cy/agorise/crystalwallet/dialogs/material/DialogMaterial.kt new file mode 100644 index 0000000..973081c --- /dev/null +++ b/app/src/main/java/cy/agorise/crystalwallet/dialogs/material/DialogMaterial.kt @@ -0,0 +1,150 @@ +package cy.agorise.crystalwallet.dialogs.material + +import android.app.Activity +import com.afollestad.materialdialogs.MaterialDialog +import cy.agorise.crystalwallet.R + +/* +* +* Controls the custom implementarion for all kind of material dialogs +* Reference in: https://github.com/afollestad/material-dialogs +* +* */ +open abstract class DialogMaterial{ + + protected var builder: MaterialDialog.Builder //Contains the builder + protected lateinit var materialDialog: MaterialDialog //Contains the controller for the dialog + + /* + * Contains the activity + * */ + protected var activity:Activity; + + + /* + * Contains the response for positive button click + * */ + var positiveResponse:PositiveResponse? = null + + /* + * Contains the response for negative button click + * */ + var negativeResponse:NegativeResponse? = null + + + constructor(activity: Activity) { + + /* + * Save the activity + * */ + this.activity = activity + + /* + * Init the builder + * */ + builder = MaterialDialog.Builder(activity) + } + + /* + * Show the dialog + * */ + fun show() { + + /* + * If user wants positive and negative + * */ + if(positiveResponse != null && negativeResponse != null){ + + /* + * Add positve + * */ + builder.positiveText(activity.resources.getString(R.string.ok)) + builder.onPositive { dialog, which -> + + /* + * If response is not null deliver response + * */ + if(positiveResponse != null){ + positiveResponse!!.onPositive() + } + } + + /* + * Add negative + * */ + builder.negativeText(activity.resources.getString(R.string.cancel)) + builder.onNegative { dialog, which -> + + /* + * If response is not null deliver response + * */ + if(negativeResponse != null){ + negativeResponse!!.onNegative() + } + } + } + + /* + * If user wants positive button + * */ + if(positiveResponse != null){ + builder.positiveText(activity.resources.getString(R.string.ok)) + builder.onPositive { dialog, which -> + + /* + * If response is not null deliver response + * */ + if(positiveResponse != null){ + positiveResponse!!.onPositive() + } + } + } + + /* + * Build internal material dialog, this lets to show it + * */ + this.build() + + /* + * Show the dialog + * */ + materialDialog.show() + } + + /* + * Close the dialog + * */ + fun dismiss() { + this.materialDialog.dismiss() + } + + /* + * After the class is completed as needed, we need to call this method to join all together after show the + * childs implementations + * */ + open fun build() { + this.materialDialog = this.builder.build() + } + + /* + * Set indeterminate progress + * + * */ + open fun progress(){ + this.builder.progress(true, 0) + } + + /* + * Setters and getters + * */ + fun setText(message: String) { + this.builder.content(message) + } + + fun setTitle(title: String) { + this.builder.title(title) + } + /* + * End of setters and getters + * */ +} \ No newline at end of file diff --git a/app/src/main/java/cy/agorise/crystalwallet/dialogs/material/ToastIt.kt b/app/src/main/java/cy/agorise/crystalwallet/dialogs/material/ToastIt.kt new file mode 100644 index 0000000..00e1ea5 --- /dev/null +++ b/app/src/main/java/cy/agorise/crystalwallet/dialogs/material/ToastIt.kt @@ -0,0 +1,30 @@ +package cy.agorise.crystalwallet.dialogs.material + +import android.app.Activity +import android.widget.Toast + +/* +* This class is an implementation of toast +* */ +class ToastIt { + + /* + * Satitic methods + * */ + companion object { + + /* + * Show long toast + * */ + @JvmStatic fun showLongToast(activity: Activity, message:String) { + Toast.makeText(activity, message, Toast.LENGTH_LONG).show() + } + + /* + * Show short toast + * */ + @JvmStatic fun showShortToast(activity: Activity, message:String) { + Toast.makeText(activity, message, Toast.LENGTH_SHORT).show() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/cy/agorise/crystalwallet/fragments/ContactsFragment.java b/app/src/main/java/cy/agorise/crystalwallet/fragments/ContactsFragment.java index 6f34284..d17c179 100644 --- a/app/src/main/java/cy/agorise/crystalwallet/fragments/ContactsFragment.java +++ b/app/src/main/java/cy/agorise/crystalwallet/fragments/ContactsFragment.java @@ -5,8 +5,12 @@ import android.arch.lifecycle.Observer; import android.arch.lifecycle.ViewModelProviders; import android.arch.paging.PagedList; import android.os.Bundle; +import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import android.support.design.widget.FloatingActionButton; import android.support.v4.app.Fragment; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -16,12 +20,16 @@ import butterknife.ButterKnife; import cy.agorise.crystalwallet.R; import cy.agorise.crystalwallet.models.Contact; import cy.agorise.crystalwallet.viewmodels.ContactListViewModel; -import cy.agorise.crystalwallet.views.ContactListView; +import cy.agorise.crystalwallet.views.ContactListAdapter; public class ContactsFragment extends Fragment { - @BindView(R.id.vContactListView) - ContactListView contactListView; + @BindView(R.id.rvContacts) + RecyclerView rvContacts; + + ContactListAdapter adapter; + + FloatingActionButton fabAddContact; public ContactsFragment() { // Required empty public constructor @@ -40,23 +48,47 @@ public class ContactsFragment extends Fragment { } @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout for this fragment - View v = inflater.inflate(R.layout.fragment_contacts, container, false); - ButterKnife.bind(this, v); + View view = inflater.inflate(R.layout.fragment_contacts, container, false); + ButterKnife.bind(this, view); - ContactListViewModel contactListViewModel = ViewModelProviders.of(this).get(ContactListViewModel.class); - contactListViewModel.init(); + // Configure RecyclerView and its adapter + rvContacts.setLayoutManager(new LinearLayoutManager(getContext())); + adapter = new ContactListAdapter(); + rvContacts.setAdapter(adapter); + + fabAddContact = getActivity().findViewById(R.id.fabAddContact); + + // Hides the fab when scrolling down + rvContacts.addOnScrollListener(new RecyclerView.OnScrollListener() { + @Override + public void onScrolled(RecyclerView recyclerView, int dx, int dy) { + super.onScrolled(recyclerView, dx, dy); + + // Scroll down + if(dy > 0 && fabAddContact.isShown()) + fabAddContact.hide(); + + // Scroll up + if(dy < 0 && !fabAddContact.isShown()) + fabAddContact.show(); + } + }); + + // Gets contacts LiveData instance from ContactsViewModel + ContactListViewModel contactListViewModel = + ViewModelProviders.of(this).get(ContactListViewModel.class); LiveData> contactsLiveData = contactListViewModel.getContactList(); contactsLiveData.observe(this, new Observer>() { @Override public void onChanged(@Nullable PagedList contacts) { - contactListView.setData(contacts); + adapter.submitList(contacts); } }); - return v; + return view; } } diff --git a/app/src/main/java/cy/agorise/crystalwallet/fragments/ImportAccountOptionsFragment.java b/app/src/main/java/cy/agorise/crystalwallet/fragments/ImportAccountOptionsFragment.java index f4ae3da..ca278d5 100644 --- a/app/src/main/java/cy/agorise/crystalwallet/fragments/ImportAccountOptionsFragment.java +++ b/app/src/main/java/cy/agorise/crystalwallet/fragments/ImportAccountOptionsFragment.java @@ -22,12 +22,15 @@ import butterknife.BindView; import butterknife.ButterKnife; import butterknife.OnClick; import cy.agorise.crystalwallet.R; +import cy.agorise.crystalwallet.activities.ImportSeedActivity; import cy.agorise.crystalwallet.activities.IntroActivity; import cy.agorise.crystalwallet.requestmanagers.FileServiceRequestListener; import cy.agorise.crystalwallet.requestmanagers.FileServiceRequests; import cy.agorise.crystalwallet.requestmanagers.ImportBackupRequest; import cy.agorise.crystalwallet.util.UriTranslator; +import static android.app.Activity.RESULT_OK; + /** * Created by xd on 1/25/18. * Shows a dialog where the user can select how to import his/her existing account @@ -95,11 +98,17 @@ public class ImportAccountOptionsFragment extends DialogFragment { } + @OnClick (R.id.btnImportSeed) + public void importSeed(){ + Intent intent = new Intent(this.getActivity(), ImportSeedActivity.class); + startActivity(intent); + } + @Override public void onActivityResult(int requestCode, int resultCode, final Intent data) { super.onActivityResult(requestCode, resultCode, data); - if (requestCode == FILE_CONTENT_REQUEST_CODE){ + if ((requestCode == FILE_CONTENT_REQUEST_CODE) && (resultCode == RESULT_OK)){ LayoutInflater inflater = getActivity().getLayoutInflater(); View passwordDialogView = inflater.inflate(R.layout.dialog_password_input, null); AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(getContext()); diff --git a/app/src/main/java/cy/agorise/crystalwallet/fragments/PinSecurityFragment.java b/app/src/main/java/cy/agorise/crystalwallet/fragments/PinSecurityFragment.java index edfaea9..c5d298c 100644 --- a/app/src/main/java/cy/agorise/crystalwallet/fragments/PinSecurityFragment.java +++ b/app/src/main/java/cy/agorise/crystalwallet/fragments/PinSecurityFragment.java @@ -1,16 +1,13 @@ package cy.agorise.crystalwallet.fragments; import android.arch.lifecycle.LiveData; -import android.arch.lifecycle.Observer; import android.arch.lifecycle.ViewModelProviders; import android.os.Bundle; -import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.text.Editable; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.ArrayAdapter; import android.widget.EditText; import android.widget.TextView; import android.widget.Toast; @@ -21,9 +18,7 @@ import butterknife.BindView; import butterknife.ButterKnife; import butterknife.OnTextChanged; import cy.agorise.crystalwallet.R; -import cy.agorise.crystalwallet.activities.CreateSeedActivity; import cy.agorise.crystalwallet.application.CrystalSecurityMonitor; -import cy.agorise.crystalwallet.dao.CrystalDatabase; import cy.agorise.crystalwallet.models.GeneralSetting; import cy.agorise.crystalwallet.util.PasswordManager; import cy.agorise.crystalwallet.viewmodels.GeneralSettingListViewModel; diff --git a/app/src/main/java/cy/agorise/crystalwallet/fragments/SendTransactionFragment.java b/app/src/main/java/cy/agorise/crystalwallet/fragments/SendTransactionFragment.java index e6d96e1..05bfcfe 100644 --- a/app/src/main/java/cy/agorise/crystalwallet/fragments/SendTransactionFragment.java +++ b/app/src/main/java/cy/agorise/crystalwallet/fragments/SendTransactionFragment.java @@ -32,6 +32,7 @@ import android.widget.Spinner; import android.widget.TextView; import com.google.zxing.Result; +import com.jaredrummler.materialspinner.MaterialSpinner; import java.io.File; import java.math.RoundingMode; @@ -73,7 +74,7 @@ public class SendTransactionFragment extends DialogFragment implements UIValidat SendTransactionValidator sendTransactionValidator; @BindView(R.id.spFrom) - Spinner spFrom; + MaterialSpinner spFrom; @BindView(R.id.tvFromError) TextView tvFromError; @BindView(R.id.etTo) @@ -152,8 +153,8 @@ public class SendTransactionFragment extends DialogFragment implements UIValidat * this is only for graphene accounts. * **/ - this.grapheneAccount = new GrapheneAccount(this.cryptoNetAccount); - this.grapheneAccount.loadInfo(db.grapheneAccountInfoDao().getByAccountId(this.cryptoNetAccountId)); + //this.grapheneAccount = new GrapheneAccount(this.cryptoNetAccount); + //this.grapheneAccount.loadInfo(db.grapheneAccountInfoDao().getByAccountId(this.cryptoNetAccountId)); final LiveData> balancesList = db.cryptoCoinBalanceDao().getBalancesFromAccount(cryptoNetAccountId); balancesList.observe(this, new Observer>() { @@ -176,8 +177,27 @@ public class SendTransactionFragment extends DialogFragment implements UIValidat CryptoNetAccountListViewModel cryptoNetAccountListViewModel = ViewModelProviders.of(this).get(CryptoNetAccountListViewModel.class); List cryptoNetAccounts = cryptoNetAccountListViewModel.getCryptoNetAccountList(); CryptoNetAccountAdapter fromSpinnerAdapter = new CryptoNetAccountAdapter(this.getContext(), android.R.layout.simple_spinner_item, cryptoNetAccounts); - spFrom.setAdapter(fromSpinnerAdapter); - spFrom.setSelection(0); + + //spFrom.setAdapter(fromSpinnerAdapter); + //spFrom.setSelection(0); + + /* + * Custom material spinner implementation + * */ + spFrom.setItems(cryptoNetAccounts); + //spFrom.setSelectedIndex(0); + spFrom.setOnItemSelectedListener(new MaterialSpinner.OnItemSelectedListener() { + @Override + public void onItemSelected(MaterialSpinner view, int position, long id, CryptoNetAccount item) { + sendTransactionValidator.validate(); + } + }); + spFrom.setOnNothingSelectedListener(new MaterialSpinner.OnNothingSelectedListener() { + + @Override public void onNothingSelected(MaterialSpinner spinner) { + + } + }); // etFrom.setText(this.grapheneAccount.getName()); } @@ -230,10 +250,10 @@ public class SendTransactionFragment extends DialogFragment implements UIValidat } } - @OnItemSelected(R.id.spFrom) + /*@OnItemSelected(R.id.spFrom) public void afterFromSelected(Spinner spinner, int position) { this.sendTransactionValidator.validate(); - } + }*/ @OnTextChanged(value = R.id.etTo, callback = OnTextChanged.Callback.AFTER_TEXT_CHANGED) @@ -311,7 +331,7 @@ public class SendTransactionFragment extends DialogFragment implements UIValidat @OnClick(R.id.btnSend) public void sendTransaction(){ if (this.sendTransactionValidator.isValid()) { - CryptoNetAccount fromAccountSelected = (CryptoNetAccount) spFrom.getSelectedItem(); + CryptoNetAccount fromAccountSelected = (CryptoNetAccount) spFrom.getItems().get(spFrom.getSelectedIndex()); /* diff --git a/app/src/main/java/cy/agorise/crystalwallet/models/FieldValidatorModel.java b/app/src/main/java/cy/agorise/crystalwallet/models/FieldValidatorModel.java new file mode 100644 index 0000000..36409ea --- /dev/null +++ b/app/src/main/java/cy/agorise/crystalwallet/models/FieldValidatorModel.java @@ -0,0 +1,56 @@ +package cy.agorise.crystalwallet.models; + +import android.view.View; + +public class FieldValidatorModel { + + /* + * Determine if the field is valid + * */ + private boolean valid; + + /* + * Contains the message of the error + * */ + private String message; + + + + /* + * Setters and getters + * */ + public boolean isValid() { + return valid; + } + + public void setValid(boolean valid) { + this.valid = valid; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + /* + * End of setters and getters + * */ + + + /* + * Set tha the field is invalid + * */ + final public void setInvalid(){ + this.valid = false; + } + + + /* + * Set tha the field is valid + * */ + final public void setValid(){ + this.valid = true; + } +} diff --git a/app/src/main/java/cy/agorise/crystalwallet/network/BitsharesCryptoNetVerifier.java b/app/src/main/java/cy/agorise/crystalwallet/network/BitsharesCryptoNetVerifier.java index 8a47bce..e90758f 100644 --- a/app/src/main/java/cy/agorise/crystalwallet/network/BitsharesCryptoNetVerifier.java +++ b/app/src/main/java/cy/agorise/crystalwallet/network/BitsharesCryptoNetVerifier.java @@ -1,5 +1,6 @@ package cy.agorise.crystalwallet.network; +import cy.agorise.crystalwallet.apigenerator.GrapheneApiGenerator; import cy.agorise.crystalwallet.enums.CryptoNet; import cy.agorise.graphenej.interfaces.WitnessResponseListener; import cy.agorise.graphenej.models.BaseResponse; @@ -23,15 +24,21 @@ public class BitsharesCryptoNetVerifier extends CryptoNetVerifier { private final String CHAIN_ID = "9cf6f255a208100d2bb275a3c52f4b1589b7ec9c9bfc2cb2a5fe6411295106d8";//testnet //private final String CHAIN_ID = "4018d7844c78f6a6c41c6a552b898022310fc5dec06da467ee7905a8dad512c8";//mainnet - @Override - public void checkURL(final String url) { + + + + + + public BitsharesCryptoNetVerifier(){ + + /**/ final long startTime = System.currentTimeMillis(); - WebSocketThread thread = new WebSocketThread(new GetChainId(new WitnessResponseListener() { + thread = new WebSocketThread(new GetChainId(new WitnessResponseListener() { @Override public void onSuccess(WitnessResponse response) { if(response.result instanceof String) { if(response.result.equals(CHAIN_ID)) { - CryptoNetManager.verifiedCryptoNetURL(cryptoNet, url, System.currentTimeMillis() - startTime); + CryptoNetManager.verifiedCryptoNetURL(cryptoNet, null, System.currentTimeMillis() - startTime); }else{ System.out.println(" BitsharesCryptoNetVerifier Error we are not in the net current chain id " + response.result + " excepted " + CHAIN_ID); //TODO handle error bad chain @@ -43,8 +50,15 @@ public class BitsharesCryptoNetVerifier extends CryptoNetVerifier { public void onError(BaseResponse.Error error) { //TODO handle error } - }),url); - thread.start(); + }),null); + + } + + + @Override + public void checkURL(final String url) { + thread.setmUrl(url); //Set the url + thread.start(); //Run the thread connection } @Override diff --git a/app/src/main/java/cy/agorise/crystalwallet/network/CryptoNetManager.java b/app/src/main/java/cy/agorise/crystalwallet/network/CryptoNetManager.java index d424cc8..5603df8 100644 --- a/app/src/main/java/cy/agorise/crystalwallet/network/CryptoNetManager.java +++ b/app/src/main/java/cy/agorise/crystalwallet/network/CryptoNetManager.java @@ -1,11 +1,9 @@ package cy.agorise.crystalwallet.network; import android.support.annotation.NonNull; -import android.util.Log; import java.util.ArrayList; import java.util.Collections; -import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; @@ -61,20 +59,30 @@ public abstract class CryptoNetManager { } } - public static void addCryptoNetURL(CryptoNet crypto, String[] urls){ - if(!CryptoNetURLs.containsKey(crypto)){ - CryptoNetURLs.put(crypto,new HashSet()); - } - CryptoNetVerifier verifier = CryptoNetVerifier.getNetworkVerify(crypto); - for(String url : urls) { + /* + * Utility for above methods + * + * */ + public static void addCryptoNetURL(CryptoNet crypto, + String[] urls) { + + if (!CryptoNetURLs.containsKey(crypto)) { + CryptoNetURLs.put(crypto, new HashSet()); + } + + CryptoNetVerifier verifier = CryptoNetVerifier.getNetworkVerify(crypto); + for (String url : urls) { CryptoNetURLs.get(crypto).add(url); - if(verifier != null) { + if (verifier != null) { verifier.checkURL(url); } } + } + + public static void removeCryptoNetURL(CryptoNet crypto, String url){ if(CryptoNetURLs.containsKey(crypto)){ CryptoNetURLs.get(crypto).remove(url); @@ -145,5 +153,4 @@ public abstract class CryptoNetManager { return (int) (this.getTime() - testedURL.getTime()); } } - } diff --git a/app/src/main/java/cy/agorise/crystalwallet/network/CryptoNetVerifier.java b/app/src/main/java/cy/agorise/crystalwallet/network/CryptoNetVerifier.java index 7364b79..832d2f0 100644 --- a/app/src/main/java/cy/agorise/crystalwallet/network/CryptoNetVerifier.java +++ b/app/src/main/java/cy/agorise/crystalwallet/network/CryptoNetVerifier.java @@ -1,5 +1,8 @@ package cy.agorise.crystalwallet.network; +import android.app.Activity; + +import cy.agorise.crystalwallet.apigenerator.GrapheneApiGenerator; import cy.agorise.crystalwallet.enums.CryptoNet; /** @@ -12,6 +15,14 @@ import cy.agorise.crystalwallet.enums.CryptoNet; public abstract class CryptoNetVerifier { + /* + * Contains the worker connection thread + */ + protected WebSocketThread thread; + + + + static CryptoNetVerifier getNetworkVerify(CryptoNet cryptoNet){ if(cryptoNet.getLabel().equals(CryptoNet.BITSHARES.getLabel())){ return new BitsharesCryptoNetVerifier(); @@ -22,4 +33,9 @@ public abstract class CryptoNetVerifier { public abstract void checkURL(final String url); public abstract String getChainId(); + + + public WebSocketThread getThread() { + return thread; + } } diff --git a/app/src/main/java/cy/agorise/crystalwallet/network/WebSocketThread.java b/app/src/main/java/cy/agorise/crystalwallet/network/WebSocketThread.java index 8f90654..58f4dad 100644 --- a/app/src/main/java/cy/agorise/crystalwallet/network/WebSocketThread.java +++ b/app/src/main/java/cy/agorise/crystalwallet/network/WebSocketThread.java @@ -3,7 +3,6 @@ package cy.agorise.crystalwallet.network; import android.util.Log; import com.neovisionaries.ws.client.WebSocket; -import com.neovisionaries.ws.client.WebSocketException; import com.neovisionaries.ws.client.WebSocketFactory; import com.neovisionaries.ws.client.WebSocketListener; @@ -34,6 +33,11 @@ public class WebSocketThread extends Thread { private boolean canChange = true; + /* + * Object needed for socket connection + * */ + private WebSocketFactory factory; + /** * Basic constructor, * @@ -43,16 +47,27 @@ public class WebSocketThread extends Thread { * @param url The url to connect */ public WebSocketThread(WebSocketListener webSocketListener, String url) { - try { - WebSocketFactory factory = new WebSocketFactory().setConnectionTimeout(5000); - this.mUrl = url; - this.mWebSocketListener = webSocketListener; - this.mWebSocket = factory.createSocket(this.mUrl); - this.mWebSocket.addListener(this.mWebSocketListener); - } catch (IOException e) { - Log.e(TAG, "IOException. Msg: "+e.getMessage()); - } catch(NullPointerException e){ - Log.e(TAG, "NullPointerException at WebsocketWorkerThreas. Msg: "+e.getMessage()); + + /* + * The listener always can be setted + * */ + this.mWebSocketListener = webSocketListener; + + /* + * + * If at this point the url is not defined, this will be set after + * */ + if(url!=null){ + try { + factory = new WebSocketFactory().setConnectionTimeout(5000); + this.mUrl = url; + this.mWebSocket = factory.createSocket(this.mUrl); + this.mWebSocket.addListener(this.mWebSocketListener); + } catch (IOException e) { + Log.e(TAG, "IOException. Msg: "+e.getMessage()); + } catch(NullPointerException e){ + Log.e(TAG, "NullPointerException at WebsocketWorkerThreas. Msg: "+e.getMessage()); + } } } @@ -110,16 +125,28 @@ public class WebSocketThread extends Thread { @Override public void run() { + canChange = false; + // Moves the current Thread into the background android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND); + try { + + /* + * If the initialization of the socket comes after + * */ + if(factory==null){ + factory = new WebSocketFactory().setConnectionTimeout(5000); + this.mWebSocket = factory.createSocket(this.mUrl); + this.mWebSocket.addListener(this.mWebSocketListener); + } + WebSocketThread.currentThreads.put(this.getId(),this); mWebSocket.connect(); - } catch (WebSocketException e) { + + } catch (final Exception e) { Log.e(TAG, "WebSocketException. Msg: "+e.getMessage()); - } catch(NullPointerException e){ - Log.e(TAG, "NullPointerException. Msg: "+e.getMessage()); } WebSocketThread.currentThreads.remove(this.getId()); } @@ -127,4 +154,9 @@ public class WebSocketThread extends Thread { public boolean isConnected(){ return mWebSocket.isOpen(); } + + + public void setmUrl(String mUrl) { + this.mUrl = mUrl; + } } diff --git a/app/src/main/java/cy/agorise/crystalwallet/requestmanagers/CryptoNetInfoRequest.java b/app/src/main/java/cy/agorise/crystalwallet/requestmanagers/CryptoNetInfoRequest.java index 103411b..bdeecbc 100644 --- a/app/src/main/java/cy/agorise/crystalwallet/requestmanagers/CryptoNetInfoRequest.java +++ b/app/src/main/java/cy/agorise/crystalwallet/requestmanagers/CryptoNetInfoRequest.java @@ -1,5 +1,7 @@ package cy.agorise.crystalwallet.requestmanagers; +import android.app.Activity; + import cy.agorise.crystalwallet.enums.CryptoCoin; /** @@ -19,6 +21,13 @@ public abstract class CryptoNetInfoRequest { */ protected CryptoNetInfoRequestListener listener; + + + + + + + protected CryptoNetInfoRequest(CryptoCoin coin){ this.coin = coin; } @@ -31,4 +40,5 @@ public abstract class CryptoNetInfoRequest { listener.onCarryOut(); CryptoNetInfoRequests.getInstance().removeRequest(this); } + } diff --git a/app/src/main/java/cy/agorise/crystalwallet/util/BounceTouchListener.java b/app/src/main/java/cy/agorise/crystalwallet/util/BounceTouchListener.java new file mode 100644 index 0000000..4053ad9 --- /dev/null +++ b/app/src/main/java/cy/agorise/crystalwallet/util/BounceTouchListener.java @@ -0,0 +1,307 @@ +package cy.agorise.crystalwallet.util; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; +import android.support.annotation.IdRes; +import android.support.annotation.Nullable; +import android.support.v4.view.MotionEventCompat; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.StaggeredGridLayoutManager; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.animation.DecelerateInterpolator; +import android.view.animation.Interpolator; +import android.widget.ListView; +import android.widget.ScrollView; + +/** + * Found this class on the internet and did some changes do adjust it to our + * needs but I still need to figure out some stuff to obtain the exact desired + * animation + */ + +public class BounceTouchListener implements View.OnTouchListener { + private static final long DEFAULT_ANIMATION_TIME = 600L; + + private boolean downCalled = false; + private OnTranslateListener onTranslateListener; + private View mMainView; + private View mContent; + private float mDownY; + private boolean mSwipingDown; + private boolean mSwipingUp; + private Interpolator mInterpolator = new DecelerateInterpolator(3f); + private boolean swipeUpEnabled = true; + private int mActivePointerId = -99; + private float mLastTouchY = -99; + private int mMaxAbsTranslation = -99; + + + private BounceTouchListener(View mainView, int contentResId, @Nullable OnTranslateListener listener) { + mMainView = mainView; + mContent = (contentResId == -1) ? mMainView : mMainView.findViewById(contentResId); + onTranslateListener = listener; + } + + /** + * Creates a new BounceTouchListener + * + * @param mainScrollableView The main view that this touch listener is attached to + * @param onTranslateListener To perform action on translation, can be null if not needed + * @return A new BounceTouchListener attached to the given scrollable view + */ + public static BounceTouchListener create(View mainScrollableView, @Nullable OnTranslateListener onTranslateListener) { + return create(mainScrollableView, -1, onTranslateListener); + } + + /** + * Creates a new BounceTouchListener + * + * @param mainView The main view that this touch listener is attached to + * @param contentResId Resource Id of the scrollable view + * @param onTranslateListener To perform action on translation, can be null if not needed + * @return A new BounceTouchListener attached to the given scrollable view + */ + public static BounceTouchListener create(View mainView, @IdRes int contentResId, + @Nullable OnTranslateListener onTranslateListener) { + return new BounceTouchListener(mainView, contentResId, onTranslateListener); + } + + @Override + public boolean onTouch(View view, MotionEvent motionEvent) { + final int action = MotionEventCompat.getActionMasked(motionEvent); + + switch (action) { + case MotionEvent.ACTION_DOWN: { + onDownMotionEvent(motionEvent); + view.onTouchEvent(motionEvent); + downCalled = true; + if (mContent.getTranslationY() == 0) { + return false; + } + } + case MotionEvent.ACTION_MOVE: { + if (mActivePointerId == -99) { + onDownMotionEvent(motionEvent); + downCalled = true; + } + final int pointerIndex = + MotionEventCompat.findPointerIndex(motionEvent, mActivePointerId); + final float y = MotionEventCompat.getY(motionEvent, pointerIndex); + + if (!hasHitTop() && !hasHitBottom() || !downCalled) { + if (!downCalled) { + downCalled = true; + } + mDownY = y; + view.onTouchEvent(motionEvent); + return false; + } + + float deltaY = y - mDownY; + if (Math.abs(deltaY) > 0 && hasHitTop() && deltaY > 0) { + mSwipingDown = true; + sendCancelEventToView(view, motionEvent); + } + if (swipeUpEnabled) { + if (Math.abs(deltaY) > 0 && hasHitBottom() && deltaY < 0) { + mSwipingUp = true; + sendCancelEventToView(view, motionEvent); + } + } + if (mSwipingDown || mSwipingUp) { + if ((deltaY <= 0 && mSwipingDown) || (deltaY >= 0 && mSwipingUp)) { + mDownY = 0; + mSwipingDown = false; + mSwipingUp = false; + downCalled = false; + MotionEvent downEvent = MotionEvent.obtain(motionEvent); + downEvent.setAction(MotionEvent.ACTION_DOWN | + (MotionEventCompat.getActionIndex(motionEvent) << MotionEventCompat.ACTION_POINTER_INDEX_SHIFT)); + view.onTouchEvent(downEvent); + break; + } + int translation = (int) ((deltaY / Math.abs(deltaY)) * Math.pow(Math.abs(deltaY), .8f)); + if (mMaxAbsTranslation > 0) { + if (translation < 0) { + translation = Math.max(-mMaxAbsTranslation, translation); + } else { + translation = Math.min(mMaxAbsTranslation, translation); + } + } + mContent.setTranslationY(translation); + if (onTranslateListener != null) + onTranslateListener.onTranslate(mContent.getTranslationY()); + + return true; + } + break; + } + + case MotionEvent.ACTION_UP: { + mActivePointerId = -99; + // cancel + mContent.animate() + .setInterpolator(mInterpolator) + .translationY(0) + .setDuration(DEFAULT_ANIMATION_TIME) + .setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animation) { + ((ValueAnimator) animation).addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + if (onTranslateListener != null) { + onTranslateListener.onTranslate(mContent.getTranslationY()); + } + } + }); + super.onAnimationStart(animation); + } + }); + + mDownY = 0; + mSwipingDown = false; + mSwipingUp = false; + downCalled = false; + break; + } + + case MotionEvent.ACTION_CANCEL: { + mActivePointerId = -99; + break; + } + + case MotionEvent.ACTION_POINTER_UP: { + final int pointerIndex = MotionEventCompat.getActionIndex(motionEvent); + final int pointerId = MotionEventCompat.getPointerId(motionEvent, pointerIndex); + + if (pointerId == mActivePointerId) { + final int newPointerIndex = pointerIndex == 0 ? 1 : 0; + mLastTouchY = MotionEventCompat.getY(motionEvent, newPointerIndex); + mActivePointerId = MotionEventCompat.getPointerId(motionEvent, newPointerIndex); + + if (mContent.getTranslationY() > 0) { + mDownY = mLastTouchY - (int) Math.pow(mContent.getTranslationY(), 10f / 8f); + mContent.animate().cancel(); + } else if (mContent.getTranslationY() < 0) { + mDownY = mLastTouchY + (int) Math.pow(-mContent.getTranslationY(), 10f / 8f); + mContent.animate().cancel(); + } + } + break; + } + } + return false; + } + + private void sendCancelEventToView(View view, MotionEvent motionEvent) { + ((ViewGroup) view).requestDisallowInterceptTouchEvent(true); + MotionEvent cancelEvent = MotionEvent.obtain(motionEvent); + cancelEvent.setAction(MotionEvent.ACTION_CANCEL | + (MotionEventCompat.getActionIndex(motionEvent) << MotionEventCompat.ACTION_POINTER_INDEX_SHIFT)); + view.onTouchEvent(cancelEvent); + } + + private void onDownMotionEvent(MotionEvent motionEvent) { + final int pointerIndex = MotionEventCompat.getActionIndex(motionEvent); + mLastTouchY = MotionEventCompat.getY(motionEvent, pointerIndex); + mActivePointerId = MotionEventCompat.getPointerId(motionEvent, 0); + + if (mContent.getTranslationY() > 0) { + mDownY = mLastTouchY - (int) Math.pow(mContent.getTranslationY(), 10f / 8f); + mContent.animate().cancel(); + } else if (mContent.getTranslationY() < 0) { + mDownY = mLastTouchY + (int) Math.pow(-mContent.getTranslationY(), 10f / 8f); + mContent.animate().cancel(); + } else { + mDownY = mLastTouchY; + } + } + + private boolean hasHitBottom() { + if (mMainView instanceof ScrollView) { + ScrollView scrollView = (ScrollView) mMainView; + View view = scrollView.getChildAt(scrollView.getChildCount() - 1); + int diff = (view.getBottom() - (scrollView.getHeight() + scrollView.getScrollY()));// Calculate the scrolldiff + return diff == 0; + } else if (mMainView instanceof ListView) { + ListView listView = (ListView) mMainView; + if (listView.getAdapter() != null) { + if (listView.getAdapter().getCount() > 0) { + return listView.getLastVisiblePosition() == listView.getAdapter().getCount() - 1 && + listView.getChildAt(listView.getChildCount() - 1).getBottom() <= listView.getHeight(); + } + } + } else if (mMainView instanceof RecyclerView) { + RecyclerView recyclerView = (RecyclerView) mMainView; + if (recyclerView.getAdapter() != null && recyclerView.getLayoutManager() != null) { + RecyclerView.Adapter adapter = recyclerView.getAdapter(); + if (adapter.getItemCount() > 0) { + RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager(); + if (layoutManager instanceof LinearLayoutManager) { + LinearLayoutManager linearLayoutManager = (LinearLayoutManager) layoutManager; + return linearLayoutManager.findLastCompletelyVisibleItemPosition() == adapter.getItemCount() - 1; + } else if (layoutManager instanceof StaggeredGridLayoutManager) { + StaggeredGridLayoutManager staggeredGridLayoutManager = (StaggeredGridLayoutManager) layoutManager; + int[] checks = staggeredGridLayoutManager.findLastCompletelyVisibleItemPositions(null); + for (int check : checks) { + if (check == adapter.getItemCount() - 1) + return true; + } + } + } + } + } + return false; + } + + private boolean hasHitTop() { + if (mMainView instanceof ScrollView) { + ScrollView scrollView = (ScrollView) mMainView; + return scrollView.getScrollY() == 0; + } else if (mMainView instanceof ListView) { + ListView listView = (ListView) mMainView; + if (listView.getAdapter() != null) { + if (listView.getAdapter().getCount() > 0) { + return listView.getFirstVisiblePosition() == 0 && + listView.getChildAt(0).getTop() >= 0; + } + } + } else if (mMainView instanceof RecyclerView) { + RecyclerView recyclerView = (RecyclerView) mMainView; + if (recyclerView.getAdapter() != null && recyclerView.getLayoutManager() != null) { + RecyclerView.Adapter adapter = recyclerView.getAdapter(); + if (adapter.getItemCount() > 0) { + RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager(); + if (layoutManager instanceof LinearLayoutManager) { + LinearLayoutManager linearLayoutManager = (LinearLayoutManager) layoutManager; + return linearLayoutManager.findFirstCompletelyVisibleItemPosition() == 0; + } else if (layoutManager instanceof StaggeredGridLayoutManager) { + StaggeredGridLayoutManager staggeredGridLayoutManager = (StaggeredGridLayoutManager) layoutManager; + int[] checks = staggeredGridLayoutManager.findFirstCompletelyVisibleItemPositions(null); + for (int check : checks) { + if (check == 0) + return true; + } + } + } + } + } + + return false; + } + + public void setMaxAbsTranslation(int maxAbsTranslation) { + this.mMaxAbsTranslation = maxAbsTranslation; + } + + public interface OnTranslateListener { + void onTranslate(float translation); + } +} + + diff --git a/app/src/main/java/cy/agorise/crystalwallet/util/FieldsValidator.kt b/app/src/main/java/cy/agorise/crystalwallet/util/FieldsValidator.kt new file mode 100644 index 0000000..c6e76d5 --- /dev/null +++ b/app/src/main/java/cy/agorise/crystalwallet/util/FieldsValidator.kt @@ -0,0 +1,38 @@ +package cy.agorise.crystalwallet.util + +import cy.agorise.crystalwallet.viewmodels.validators.customImpl.interfaces.UIValidator +import java.util.ArrayList + +class FieldsValidator { + + /* + * Contains the fields to validate + * */ + private var fields: MutableList = ArrayList() + + /* + * Setters and getters + * */ + fun setFields(fields: List) { + this.fields = fields as MutableList + } + /* + * Endo of setters and getters + * */ + + /* + * Validate all the fields + * */ + fun validate() { + for (uiValidator in fields) { + uiValidator.validate() + } + } + + /* + * Add component to the list + * */ + fun add(uiValidator: UIValidator) { + this.fields.add(uiValidator) + } +} \ No newline at end of file diff --git a/app/src/main/java/cy/agorise/crystalwallet/viewmodels/AccountSeedViewModel.java b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/AccountSeedViewModel.java deleted file mode 100644 index bb546a4..0000000 --- a/app/src/main/java/cy/agorise/crystalwallet/viewmodels/AccountSeedViewModel.java +++ /dev/null @@ -1,39 +0,0 @@ -package cy.agorise.crystalwallet.viewmodels; - -import android.app.Application; -import android.arch.lifecycle.AndroidViewModel; -import android.arch.lifecycle.LiveData; - -import cy.agorise.crystalwallet.dao.CrystalDatabase; -import cy.agorise.crystalwallet.models.AccountSeed; - -/** - * Created by Henry Varona on 27/9/2017. - */ - -public class AccountSeedViewModel extends AndroidViewModel { - - private LiveData accountSeed; - private CrystalDatabase db; - private Application app; - - public AccountSeedViewModel(Application application) { - super(application); - this.db = CrystalDatabase.getAppDatabase(application.getApplicationContext()); - this.app = application; - } - - public void loadSeed(long seedId){ - this.accountSeed = this.db.accountSeedDao().findByIdLiveData(seedId); - } - - public void addSeed(AccountSeed seed){ - long newId = this.db.accountSeedDao().insertAccountSeed(seed); - seed.setId(newId); - } - - public LiveData getAccountSeed(){ - return this.accountSeed; - } - -} diff --git a/app/src/main/java/cy/agorise/crystalwallet/viewmodels/AccountSeedViewModel.kt b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/AccountSeedViewModel.kt new file mode 100644 index 0000000..aa9d3e9 --- /dev/null +++ b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/AccountSeedViewModel.kt @@ -0,0 +1,31 @@ +package cy.agorise.crystalwallet.viewmodels + +import android.app.Application +import android.arch.lifecycle.AndroidViewModel +import android.arch.lifecycle.LiveData +import cy.agorise.crystalwallet.dao.CrystalDatabase +import cy.agorise.crystalwallet.models.AccountSeed + +class AccountSeedViewModel(application: Application) : AndroidViewModel(application) { + + private var accountSeed: LiveData? = null + private val db: CrystalDatabase + private val app: Application = application + + init{ + this.db = CrystalDatabase.getAppDatabase(this.app.getApplicationContext()); + } + + fun loadSeed(seedId: Long) { + this.accountSeed = this.db.accountSeedDao().findByIdLiveData(seedId) + } + + fun addSeed(seed: AccountSeed) { + val newId = this.db.accountSeedDao().insertAccountSeed(seed) + seed.id = newId + } + + fun getAccountSeed(): LiveData? { + return this.accountSeed + } +} \ No newline at end of file diff --git a/app/src/main/java/cy/agorise/crystalwallet/viewmodels/ContactListViewModel.java b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/ContactListViewModel.java index 51c3836..5a1a512 100644 --- a/app/src/main/java/cy/agorise/crystalwallet/viewmodels/ContactListViewModel.java +++ b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/ContactListViewModel.java @@ -32,16 +32,6 @@ public class ContactListViewModel extends AndroidViewModel { ).build(); } - public void init(){ - contactList = new LivePagedListBuilder(this.db.contactDao().contactsByName(), - new PagedList.Config.Builder() - .setEnablePlaceholders(true) - .setPageSize(10) - .setPrefetchDistance(10) - .build() - ).build(); - } - public void init(CryptoNet cryptoNet){ contactList = new LivePagedListBuilder(this.db.contactDao().contactsByNameAndCryptoNet(cryptoNet.name()), new PagedList.Config.Builder() diff --git a/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/CreateContactValidator.java b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/CreateContactValidator.java index 1b84e43..2c91b83 100644 --- a/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/CreateContactValidator.java +++ b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/CreateContactValidator.java @@ -15,9 +15,8 @@ import cy.agorise.crystalwallet.viewmodels.validators.validationfields.PinValida public class CreateContactValidator extends UIValidator { - public CreateContactValidator(Context context, EditText nameEdit, EditText emailEdit){ + public CreateContactValidator(Context context, EditText nameEdit){ super(context); this.addField(new ContactNameValidationField(nameEdit)); - this.addField(new EmailValidationField(emailEdit)); } } diff --git a/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/ModifyContactValidator.java b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/ModifyContactValidator.java index ec640fd..b629434 100644 --- a/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/ModifyContactValidator.java +++ b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/ModifyContactValidator.java @@ -13,9 +13,8 @@ import cy.agorise.crystalwallet.viewmodels.validators.validationfields.EmailVali public class ModifyContactValidator extends UIValidator { - public ModifyContactValidator(Context context, Contact contact, EditText nameEdit, EditText emailEdit){ + public ModifyContactValidator(Context context, Contact contact, EditText nameEdit){ super(context); this.addField(new ContactNameValidationField(nameEdit, contact)); - this.addField(new EmailValidationField(emailEdit)); } } diff --git a/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/SendTransactionValidator.java b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/SendTransactionValidator.java index 66565a7..3cd67d9 100644 --- a/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/SendTransactionValidator.java +++ b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/SendTransactionValidator.java @@ -4,6 +4,8 @@ import android.content.Context; import android.widget.EditText; import android.widget.Spinner; +import com.jaredrummler.materialspinner.MaterialSpinner; + import cy.agorise.crystalwallet.models.CryptoNetAccount; import cy.agorise.crystalwallet.viewmodels.validators.validationfields.AmountValidationField; import cy.agorise.crystalwallet.viewmodels.validators.validationfields.AssetValidationField; @@ -20,7 +22,7 @@ public class SendTransactionValidator extends UIValidator { private CryptoNetAccount account; - public SendTransactionValidator(Context context, CryptoNetAccount account, Spinner fromEdit, EditText toEdit, Spinner assetSpinner, EditText amountEdit, EditText memoEdit){ + public SendTransactionValidator(Context context, CryptoNetAccount account, MaterialSpinner fromEdit, EditText toEdit, Spinner assetSpinner, EditText amountEdit, EditText memoEdit){ super(context); this.account = account; this.addField(new FromValidationField(fromEdit)); diff --git a/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/customImpl/interfaces/UIValidator.kt b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/customImpl/interfaces/UIValidator.kt new file mode 100644 index 0000000..68b99cb --- /dev/null +++ b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/customImpl/interfaces/UIValidator.kt @@ -0,0 +1,9 @@ +package cy.agorise.crystalwallet.viewmodels.validators.customImpl.interfaces + +/* +* Interface that all the views that need validation can use +* */ +interface UIValidator { + + fun validate() +} \ No newline at end of file diff --git a/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/customImpl/interfaces/UIValidatorListener.kt b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/customImpl/interfaces/UIValidatorListener.kt new file mode 100644 index 0000000..13c33f4 --- /dev/null +++ b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/customImpl/interfaces/UIValidatorListener.kt @@ -0,0 +1,12 @@ +package cy.agorise.crystalwallet.viewmodels.validators.customImpl.interfaces + +import cy.agorise.crystalwallet.viewmodels.validators.customImpl.validationFields.CustomValidationField + +/* +* Listener to deliver response to controls from inner validations +* */ +interface UIValidatorListener { + + fun onValidationFailed(customValidationField: CustomValidationField) + fun onValidationSucceeded(customValidationField: CustomValidationField) +} \ No newline at end of file diff --git a/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/customImpl/validationFields/BitsharesAccountNameValidation.kt b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/customImpl/validationFields/BitsharesAccountNameValidation.kt new file mode 100644 index 0000000..9c56bfe --- /dev/null +++ b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/customImpl/validationFields/BitsharesAccountNameValidation.kt @@ -0,0 +1,227 @@ +package cy.agorise.crystalwallet.viewmodels.validators.customImpl.validationFields + +import android.app.Activity +import cy.agorise.crystalwallet.R +import cy.agorise.crystalwallet.apigenerator.GrapheneApiGenerator +import cy.agorise.crystalwallet.dialogs.material.CrystalDialog +import cy.agorise.crystalwallet.requestmanagers.CryptoNetInfoRequests +import cy.agorise.crystalwallet.requestmanagers.ValidateExistBitsharesAccountRequest +import cy.agorise.crystalwallet.viewmodels.validators.customImpl.interfaces.UIValidator +import cy.agorise.crystalwallet.viewmodels.validators.customImpl.interfaces.UIValidatorListener +import cy.agorise.crystalwallet.views.natives.CustomTextInputEditText +import cy.agorise.crystalwallet.requestmanagers.CryptoNetInfoRequestListener + + + +/* +* +* Validation layer for Account Name +* */ +class BitsharesAccountNameValidation : CustomValidationField, UIValidator { + + /* + * Contains the field to validate + * */ + private val accountNameField: CustomTextInputEditText + + /* + * Interface to validate when an account exist an take over control it + * */ + private var onAccountExist: OnAccountExist? = null + + + constructor ( activity: Activity, + accountNameField: CustomTextInputEditText, + uiValidatorListener: UIValidatorListener) : super(activity) { + + this.accountNameField = accountNameField + this.uiValidatorListener = uiValidatorListener + + /* + * The current view for errors will be this + * */ + this.currentView = this.accountNameField + } + + override fun validate() { + + val newValue = accountNameField.text.toString() + + /* + Contains the validation result + */ + var result = true + + /* + * Validate empty field + * */ + if (newValue == "") { + + /* + * Validation not passed + * */ + result = false + accountNameField.fieldValidatorModel.setInvalid() + accountNameField.fieldValidatorModel.message = this.accountNameField.resources.getString(R.string.create_account_window_err_account_empty) + } else { + + /* + * Remove error + * */ + accountNameField.error = null + + /* + Validate at least min length + */ + if (newValue.length < 10) { + + /* + * Validation not passed + * */ + result = false + accountNameField.fieldValidatorModel.setInvalid() + accountNameField.fieldValidatorModel.message = this.accountNameField.resources.getString(R.string.create_account_window_err_min_account_name_len) + } else { + + /* + * Remove error + * */ + accountNameField.error = null + + /* + Validate at least one character + */ + if (!newValue.matches(".*[a-zA-Z]+.*".toRegex())) { + + /* + * Validation not passed + * */ + result = false + accountNameField.fieldValidatorModel.setInvalid() + accountNameField.fieldValidatorModel.message = this.accountNameField.resources.getString(R.string.create_account_window_err_at_least_one_character) + } else { + + /* + * Remove error + * */ + accountNameField.error = null + + /* + Validate at least one number for the account string + */ + if (!newValue.matches(".*\\d+.*".toRegex())) { + + /* + * Validation not passed + * */ + result = false + accountNameField.fieldValidatorModel.setInvalid() + accountNameField.fieldValidatorModel.message = this.accountNameField.resources.getString(R.string.create_account_window_err_at_least_one_number) + } else { + + /* + * Remove error + * */ + accountNameField.error = null + + + /* + Validate at least one middle script + */ + if (!newValue.contains("-")) { + + /* + * Validation not passed + * */ + result = false + accountNameField.fieldValidatorModel.setInvalid() + accountNameField.fieldValidatorModel.message = this.accountNameField.resources.getString(R.string.create_account_window_err_at_least_one_script) + } else { + + /* + * Remove error + * */ + accountNameField.error = null + } + } + } + } + } + + /* + * If passed first validations + * */ + if (!result) { + + /* + * Deliver result + * */ + if (uiValidatorListener != null) { + uiValidatorListener.onValidationFailed(this) + } + } else { + + /* + * Show the dialog for connection with the server + * */ + val creatingAccountMaterialDialog = CrystalDialog(activity) + creatingAccountMaterialDialog.setText(activity.resources.getString(R.string.window_create_seed_Server_validation)) + creatingAccountMaterialDialog.build() + creatingAccountMaterialDialog.show() + + val request = ValidateExistBitsharesAccountRequest(newValue) + request.setListener { + + /* + * Dismiss the dialog of loading + * */ + creatingAccountMaterialDialog.dismiss() + + if (request.accountExists) { + + /* + * The account exists and is not valid + * */ + accountNameField.fieldValidatorModel.setInvalid() + accountNameField.fieldValidatorModel.message = accountNameField.resources.getString(R.string.account_name_already_exist) + + } else { + + /* + * Passed all validations + * */ + accountNameField.fieldValidatorModel.setValid() + + /* + * Deliver the response + * */ + if (uiValidatorListener != null) { + uiValidatorListener.onValidationSucceeded(globalCustomValidationField) + } + } + } + CryptoNetInfoRequests.getInstance().addRequest(request) + + } + /* + * Passed initial validations, next final validations + * */ + } + + /* + * Setters and getters + * */ + fun setOnAccountExist(onAccountExist: OnAccountExist) { + this.onAccountExist = onAccountExist + } + /* + * End of setters and getters + * */ + + /* + * Interface to validate when an account exist an take over control it + * */ + open interface OnAccountExist { + fun onAccountExists() + } +} \ No newline at end of file diff --git a/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/customImpl/validationFields/CustomValidationField.kt b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/customImpl/validationFields/CustomValidationField.kt new file mode 100644 index 0000000..3cb55b0 --- /dev/null +++ b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/customImpl/validationFields/CustomValidationField.kt @@ -0,0 +1,43 @@ +package cy.agorise.crystalwallet.viewmodels.validators.customImpl.validationFields + +import android.app.Activity +import android.view.View +import cy.agorise.crystalwallet.viewmodels.validators.customImpl.interfaces.UIValidatorListener + +open class CustomValidationField { + + /* + * Listener to deliver response to controller + * */ + protected lateinit var uiValidatorListener: UIValidatorListener + + /* + * Contains the field to validate + * */ + @JvmField var currentView : View? = null + + + /* + * Contains a handler to my self + * */ + @JvmField protected var globalCustomValidationField: CustomValidationField + + /* + * Contains the acivity for utility + * */ + @JvmField protected var activity: Activity + + + constructor(activity:Activity){ + + /* + * Save the activity + * */ + this.activity = activity + + /* + * Init the custom field for later references + * */ + this.globalCustomValidationField = this + } +} \ No newline at end of file diff --git a/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/customImpl/validationFields/PinDoubleConfirmationValidationField.kt b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/customImpl/validationFields/PinDoubleConfirmationValidationField.kt new file mode 100644 index 0000000..c2c70b9 --- /dev/null +++ b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/customImpl/validationFields/PinDoubleConfirmationValidationField.kt @@ -0,0 +1,131 @@ +package cy.agorise.crystalwallet.viewmodels.validators.customImpl.validationFields + +import android.app.Activity +import cy.agorise.crystalwallet.R +import cy.agorise.crystalwallet.viewmodels.validators.customImpl.interfaces.UIValidator +import cy.agorise.crystalwallet.viewmodels.validators.customImpl.interfaces.UIValidatorListener +import cy.agorise.crystalwallet.views.natives.CustomTextInputEditText + +/* +* Validate PIN and PIN confirmation +* */ +class PinDoubleConfirmationValidationField : CustomValidationField, UIValidator { + + /* + * Contain the fields to validate + * */ + private val pinField: CustomTextInputEditText + private val pinConfirmationField: CustomTextInputEditText + + + constructor(activity: Activity, + pinField: CustomTextInputEditText, + pinConfirmationField: CustomTextInputEditText, + pinDoubleConfirmationInterface: UIValidatorListener) : super(activity) { + + this.pinField = pinField + this.pinConfirmationField = pinConfirmationField + this.uiValidatorListener = pinDoubleConfirmationInterface + } + + override fun validate() { + + val pin = pinField.text.toString().trim { it <= ' ' } + val pinConfirmation = pinConfirmationField.text.toString().trim { it <= ' ' } + + /* + * Contains the result for the validations + * */ + var result = true + + /* + Check if the two fields are equals theme selfs + * */ + if (pin.length < 5) { + + /* + * + * False validation + * */ + pinField.fieldValidatorModel.message = this.pinField.context.resources.getString(R.string.create_account_window_err_at_least_pin_characters) + result = false + + /* + * The current view for error + * */ + this.currentView = pinField + } else { + + /*Remove the error*/ + pinField.error = null + + /* + * Same validation for PIN Confirmation + * */ + if (pinConfirmation.length < 5) { + + /* + * + * False validation + * */ + pinConfirmationField.fieldValidatorModel.message = this.pinField.context.resources.getString(R.string.create_account_window_err_at_least_pin_characters) + result = false + + /* + * The current view for error + * */ + this.currentView = pinConfirmationField + } else { + + /*Remove the error*/ + pinField.error = null + + /* + * Final validation, check if the PINs are equals + * + * */ + if (!pin.isEmpty() && !pinConfirmation.isEmpty() && pinConfirmation.compareTo(pin) != 0) { + + /* + * + * False validation + * */ + pinField.fieldValidatorModel.message = this.pinField.context.resources.getString(R.string.mismatch_pin) + pinConfirmationField.fieldValidatorModel.message = this.pinField.context.resources.getString(R.string.mismatch_pin) + result = false + + /* + * The current view for error + * */ + this.currentView = pinConfirmationField + } + } + } + + /* + * Passed validations + * */ + if (result) { + + pinField.fieldValidatorModel.setValid() + pinConfirmationField.fieldValidatorModel.setValid() + + /* + * Connect response to controller + * */ + if (uiValidatorListener != null) { + uiValidatorListener.onValidationSucceeded(this) + } + } else { + + /* + * Connect response to controller + * */ + if (uiValidatorListener != null) { + uiValidatorListener.onValidationFailed(this) + } + }/* + * Not passed validations + * */ + } +} \ No newline at end of file diff --git a/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/validationfields/BitsharesAccountNameDoesntExistsValidationField.java b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/validationfields/BitsharesAccountNameDoesntExistsValidationField.java index e7a4d87..4b79eb1 100644 --- a/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/validationfields/BitsharesAccountNameDoesntExistsValidationField.java +++ b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/validationfields/BitsharesAccountNameDoesntExistsValidationField.java @@ -21,15 +21,47 @@ public class BitsharesAccountNameDoesntExistsValidationField extends ValidationF } public void validate(){ + final String newValue = accountNameField.getText().toString(); this.setLastValue(newValue); this.startValidating(); + /* + * Validate empty field + * */ if (newValue.equals("")){ setValidForValue("", false); setMessageForValue("",""); validator.validationFailed(this); - } else { + } + /* + Validate at least min length + */ + else if(newValue.length()<10){ + setValidForValue("", false); + setMessageForValue(validator.getContext().getResources().getString(R.string.create_account_window_err_min_account_name_len),""); + validator.validationFailed(this); + } + /* + Validate at least one number for the account string + */ + else if(!newValue.matches(".*\\\\d+.*")){ + setValidForValue("", false); + setMessageForValue(validator.getContext().getResources().getString(R.string.create_account_window_err_at_least_one_number),""); + validator.validationFailed(this); + } + /* + Validate at least one middle script + */ + else if(!newValue.contains("-")){ + setValidForValue("", false); + setMessageForValue(accountNameField.getContext().getResources().getString(R.string.create_account_window_err_at_least_one_number),""); + validator.validationFailed(this); + } + /* + * Passed all primary validations + * */ + else { final ValidationField field = this; @@ -37,11 +69,17 @@ public class BitsharesAccountNameDoesntExistsValidationField extends ValidationF request.setListener(new CryptoNetInfoRequestListener() { @Override public void onCarryOut() { + if (request.getAccountExists()) { + + /* + * The account exists and is not valid + * */ setMessageForValue(newValue,validator.getContext().getResources().getString(R.string.account_name_already_exist,"'"+newValue+"'")); setValidForValue(newValue, false); + } else { - setValidForValue(newValue, true); + setValidForValue(newValue, true); } } }); diff --git a/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/validationfields/FromValidationField.java b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/validationfields/FromValidationField.java index a693ba9..4f18033 100644 --- a/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/validationfields/FromValidationField.java +++ b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/validationfields/FromValidationField.java @@ -2,7 +2,10 @@ package cy.agorise.crystalwallet.viewmodels.validators.validationfields; import android.widget.Spinner; +import com.jaredrummler.materialspinner.MaterialSpinner; + import cy.agorise.crystalwallet.R; +import cy.agorise.crystalwallet.models.CryptoNetAccount; import cy.agorise.crystalwallet.requestmanagers.CryptoNetInfoRequestListener; import cy.agorise.crystalwallet.requestmanagers.CryptoNetInfoRequests; import cy.agorise.crystalwallet.requestmanagers.ValidateExistBitsharesAccountRequest; @@ -14,9 +17,9 @@ import cy.agorise.crystalwallet.requestmanagers.ValidateExistBitsharesAccountReq public class FromValidationField extends ValidationField { //private EditText fromField; - private Spinner fromField; + private MaterialSpinner fromField; - public FromValidationField(Spinner fromField){ + public FromValidationField(MaterialSpinner fromField){ super(fromField); this.fromField = fromField; } @@ -24,8 +27,9 @@ public class FromValidationField extends ValidationField { public void validate(){ final String newValue; - if (fromField.getSelectedItem() != null) { - newValue = fromField.getSelectedItem().toString(); + if (fromField.getSelectedIndex() != -1) { + final CryptoNetAccount cryptoNetAccount = (CryptoNetAccount) fromField.getItems().get(fromField.getSelectedIndex()); + newValue = cryptoNetAccount.getName(); } else { newValue = ""; } diff --git a/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/validationfields/PinValidationField_.java b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/validationfields/PinValidationField_.java new file mode 100644 index 0000000..cb2c211 --- /dev/null +++ b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/validationfields/PinValidationField_.java @@ -0,0 +1,11 @@ +package cy.agorise.crystalwallet.viewmodels.validators.validationfields; + +import cy.agorise.crystalwallet.viewmodels.validators.customImpl.interfaces.UIValidator; + +public class PinValidationField_ implements UIValidator { + + @Override + public void validate() { + + } +} diff --git a/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/validationfields/ToValidationField.java b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/validationfields/ToValidationField.java index f2e8702..9c06402 100644 --- a/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/validationfields/ToValidationField.java +++ b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/validationfields/ToValidationField.java @@ -3,7 +3,10 @@ package cy.agorise.crystalwallet.viewmodels.validators.validationfields; import android.widget.EditText; import android.widget.Spinner; +import com.jaredrummler.materialspinner.MaterialSpinner; + import cy.agorise.crystalwallet.R; +import cy.agorise.crystalwallet.models.CryptoNetAccount; import cy.agorise.crystalwallet.requestmanagers.CryptoNetInfoRequestListener; import cy.agorise.crystalwallet.requestmanagers.CryptoNetInfoRequests; import cy.agorise.crystalwallet.requestmanagers.ValidateExistBitsharesAccountRequest; @@ -14,10 +17,10 @@ import cy.agorise.crystalwallet.requestmanagers.ValidateExistBitsharesAccountReq public class ToValidationField extends ValidationField { - private Spinner fromField; + private MaterialSpinner fromField; private EditText toField; - public ToValidationField(Spinner fromField, EditText toField){ + public ToValidationField(MaterialSpinner fromField, EditText toField){ super(toField); this.fromField = fromField; this.toField = toField; @@ -25,8 +28,9 @@ public class ToValidationField extends ValidationField { public void validate(){ final String fromNewValue; - if (fromField.getSelectedItem() != null) { - fromNewValue = fromField.getSelectedItem().toString(); + if (fromField.getSelectedIndex() != -1) { + final CryptoNetAccount cryptoNetAccount = (CryptoNetAccount) fromField.getItems().get(fromField.getSelectedIndex()); + fromNewValue = cryptoNetAccount.getName(); } else { fromNewValue = ""; } diff --git a/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/validationfields/ValidationField.java b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/validationfields/ValidationField.java index 0884c19..dff7085 100644 --- a/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/validationfields/ValidationField.java +++ b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/validationfields/ValidationField.java @@ -18,6 +18,10 @@ public abstract class ValidationField { protected UIValidator validator; protected View view; + + + + public ValidationField(View view){ this.lastValue = ""; this.message = ""; diff --git a/app/src/main/java/cy/agorise/crystalwallet/views/ContactListView.java b/app/src/main/java/cy/agorise/crystalwallet/views/ContactListView.java deleted file mode 100644 index 37d5cfa..0000000 --- a/app/src/main/java/cy/agorise/crystalwallet/views/ContactListView.java +++ /dev/null @@ -1,111 +0,0 @@ -package cy.agorise.crystalwallet.views; - -import android.arch.paging.PagedList; -import android.content.Context; -import android.support.v4.app.Fragment; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; -import android.util.AttributeSet; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.RelativeLayout; - -import cy.agorise.crystalwallet.R; -import cy.agorise.crystalwallet.models.Contact; -import cy.agorise.crystalwallet.viewmodels.ContactListViewModel; - -/** - * Created by Henry Varona on 1/15/2018. - * - * A list view showing the user contacts - */ - -public class ContactListView extends RelativeLayout { - - LayoutInflater mInflater; - - /* - * The root view of this view - */ - View rootView; - /* - * The list view that holds every user contact item - */ - RecyclerView listView; - /* - * The adapter for the previous list view - */ - ContactListAdapter listAdapter; - - ContactListViewModel contactListViewModel; - - /* - * how much contacts will remain to show before the list loads more - */ - private int visibleThreshold = 5; - /* - * if true, the contact list will be loading new data - */ - private boolean loading = true; - - /* - * One of three constructors needed to be inflated from a layout - */ - public ContactListView(Context context){ - super(context); - this.mInflater = LayoutInflater.from(context); - init(); - } - - /* - * One of three constructors needed to be inflated from a layout - */ - public ContactListView(Context context, AttributeSet attrs) { - super(context, attrs); - this.mInflater = LayoutInflater.from(context); - init(); - } - - /* - * One of three constructors needed to be inflated from a layout - */ - public ContactListView(Context context, AttributeSet attrs, int defStyle){ - super(context, attrs, defStyle); - this.mInflater = LayoutInflater.from(context); - init(); - } - - /* - * Initializes this view - */ - public void init(){ - rootView = mInflater.inflate(R.layout.contact_list, this, true); - this.listView = rootView.findViewById(R.id.contactListView); - - final LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this.getContext()); - this.listView.setLayoutManager(linearLayoutManager); - //Prevents the list to start again when scrolling to the end - //this.listView.setNestedScrollingEnabled(false); - - } - - /* - * Sets the elements data of this view - * - * @param data the contacts that will be showed to the user - */ - public void setData(PagedList data){ - //Initializes the adapter of the contact list - if (this.listAdapter == null) { - this.listAdapter = new ContactListAdapter(); - this.listView.setAdapter(this.listAdapter); - } - - //Sets the data of the transaction list - if (data != null) { - this.listAdapter.submitList(data); - } - } - - -} diff --git a/app/src/main/java/cy/agorise/crystalwallet/views/ContactViewHolder.java b/app/src/main/java/cy/agorise/crystalwallet/views/ContactViewHolder.java index 376ce11..b37ce0a 100644 --- a/app/src/main/java/cy/agorise/crystalwallet/views/ContactViewHolder.java +++ b/app/src/main/java/cy/agorise/crystalwallet/views/ContactViewHolder.java @@ -78,7 +78,8 @@ public class ContactViewHolder extends RecyclerView.ViewHolder { Picasso.with(this.context) .load(gravatarUrl) - .transform(new CircleTransformation()) + .placeholder(R.drawable.avatar_placeholder) + //.transform(new CircleTransformation()) .into(ivThumbnail); } diff --git a/app/src/main/java/cy/agorise/crystalwallet/views/CryptoNetBalanceViewHolder.java b/app/src/main/java/cy/agorise/crystalwallet/views/CryptoNetBalanceViewHolder.java index bf1d66e..4a2043c 100644 --- a/app/src/main/java/cy/agorise/crystalwallet/views/CryptoNetBalanceViewHolder.java +++ b/app/src/main/java/cy/agorise/crystalwallet/views/CryptoNetBalanceViewHolder.java @@ -192,7 +192,14 @@ public class CryptoNetBalanceViewHolder extends RecyclerView.ViewHolder { } else { final CryptoNetBalanceViewHolder thisViewHolder = this; this.cryptoNetAccountId = balance.getAccountId(); - cryptoNetName.setText(balance.getCryptoNet().getLabel()); + + /* + * The first letter should be in mayus + * */ + final String crypto = balance.getCryptoNet().getLabel().toString().toLowerCase(); + final String upperString = crypto.substring(0,1).toUpperCase() + crypto.substring(1); + + cryptoNetName.setText(upperString); //Loads the crypto coin balance list of this account using a ViewModel and retrieving a LiveData List CryptoCoinBalanceListViewModel cryptoCoinBalanceListViewModel = ViewModelProviders.of(this.fragment).get(CryptoCoinBalanceListViewModel.class); diff --git a/app/src/main/java/cy/agorise/crystalwallet/views/natives/CustomTextInputEditText.java b/app/src/main/java/cy/agorise/crystalwallet/views/natives/CustomTextInputEditText.java new file mode 100644 index 0000000..219444b --- /dev/null +++ b/app/src/main/java/cy/agorise/crystalwallet/views/natives/CustomTextInputEditText.java @@ -0,0 +1,70 @@ +package cy.agorise.crystalwallet.views.natives; + +import android.content.Context; +import android.support.design.widget.TextInputEditText; +import android.util.AttributeSet; +import android.view.View; + +import cy.agorise.crystalwallet.models.FieldValidatorModel; +import cy.agorise.crystalwallet.viewmodels.validators.customImpl.interfaces.UIValidator; + +/* +* Custom implementation of the native control to get more over control it +* */ +public class CustomTextInputEditText extends TextInputEditText implements UIValidator { + + /* + * Contains the field validator, this aid to validate the field + * */ + private FieldValidatorModel fieldValidatorModel = new FieldValidatorModel(); + + /* + * Interface to validate the field + * */ + private UIValidator uiValidator; + + /* + * Contains the last input value + * */ + private String lastValue; + + + public CustomTextInputEditText(Context context, AttributeSet attrs) { + super(context, attrs); + + /* + * Set listener to get the last value of the control + * */ + this.setOnFocusChangeListener(new OnFocusChangeListener() { + @Override + public void onFocusChange(View v, boolean hasFocus) { + + if(!hasFocus){ + lastValue = getText().toString(); + } + } + }); + } + + public void setFieldValidatorModel(FieldValidatorModel fieldValidatorModel) { + this.fieldValidatorModel = fieldValidatorModel; + } + public void setUiValidator(UIValidator uiValidator) { + this.uiValidator = uiValidator; + } + public FieldValidatorModel getFieldValidatorModel() { + return fieldValidatorModel; + } + + public String getLastValue() { + return lastValue; + } + /* + * End of setters and getters + * */ + + @Override + public void validate() { + uiValidator.validate(); + } +} diff --git a/app/src/main/java/cy/agorise/crystalwallet/views/natives/GIFView.kt b/app/src/main/java/cy/agorise/crystalwallet/views/natives/GIFView.kt new file mode 100644 index 0000000..338e36f --- /dev/null +++ b/app/src/main/java/cy/agorise/crystalwallet/views/natives/GIFView.kt @@ -0,0 +1,37 @@ +package cy.agorise.crystalwallet.views.natives + +import android.content.Context +import android.util.AttributeSet +import android.widget.ImageView +import com.bumptech.glide.Glide +import com.bumptech.glide.request.RequestOptions +import cy.agorise.crystalwallet.R + +/* +* GIF implementation based on existing imageview class +* */ +class GIFView : ImageView { + + /* + * Contains aditional options for the gif + * */ + private var options: RequestOptions? = RequestOptions() + + + constructor(context:Context,attrs: AttributeSet?) : super(context,attrs) { + } + + /* + * Load the gif + * */ + fun load(rawID:Int){ + Glide.with(this).asGif().load(rawID).apply(options!!).into(this) + } + + /* + * Option to fit the gif in the container + * */ + fun centerCrop(){ + options!!.centerCrop() + } +} \ No newline at end of file diff --git a/app/src/main/java/cy/agorise/crystalwallet/views/natives/VideoExView.kt b/app/src/main/java/cy/agorise/crystalwallet/views/natives/VideoExView.kt new file mode 100644 index 0000000..b8fb54a --- /dev/null +++ b/app/src/main/java/cy/agorise/crystalwallet/views/natives/VideoExView.kt @@ -0,0 +1,34 @@ +package cy.agorise.crystalwallet.views.natives + +import android.content.Context +import android.net.Uri +import android.util.AttributeSet +import android.widget.VideoView +import cy.agorise.crystalwallet.R +import kotlinx.android.synthetic.main.empty_activity.* + +/* +* Extensión for videoview +* */ +class VideoExView(context: Context?, attrs: AttributeSet?) : VideoView(context, attrs) { + + /* + * Set the path based on raw, this should be called first to set the video path + * */ + fun setVideoRaw(rawID:Int){ + val uriPath = "android.resource://" + context.packageName + "/" + R.raw.appbar_background + val uri = Uri.parse(uriPath) + this.setVideoURI(uri) + } + + /* + * With this method the video play continues + * */ + fun playContinius(){ + + start() + this.setOnCompletionListener { + start() + } + } +} \ No newline at end of file diff --git a/app/src/main/res/anim/rotate360.xml b/app/src/main/res/anim/rotate360.xml index 0efcc73..c582275 100644 --- a/app/src/main/res/anim/rotate360.xml +++ b/app/src/main/res/anim/rotate360.xml @@ -1,13 +1,17 @@ + + \ No newline at end of file diff --git a/app/src/main/res/drawable/disable_style.xml b/app/src/main/res/drawable/disable_style.xml new file mode 100644 index 0000000..f780d52 --- /dev/null +++ b/app/src/main/res/drawable/disable_style.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/loading.png b/app/src/main/res/drawable/loading.png new file mode 100644 index 0000000..f98e21c Binary files /dev/null and b/app/src/main/res/drawable/loading.png differ diff --git a/app/src/main/res/drawable/ok.png b/app/src/main/res/drawable/ok.png new file mode 100644 index 0000000..5fa675e Binary files /dev/null and b/app/src/main/res/drawable/ok.png differ diff --git a/app/src/main/res/drawable/warning.png b/app/src/main/res/drawable/warning.png new file mode 100644 index 0000000..b73afa1 Binary files /dev/null and b/app/src/main/res/drawable/warning.png differ diff --git a/app/src/main/res/layout/activity_account.xml b/app/src/main/res/layout/activity_account.xml deleted file mode 100644 index 285ab12..0000000 --- a/app/src/main/res/layout/activity_account.xml +++ /dev/null @@ -1,146 +0,0 @@ - - - - - - - - - - - - - - - - -