diff --git a/app/build.gradle b/app/build.gradle index 5cd5378..c9d588c 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:$kotlinVersion" //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' 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..d77e761 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -31,13 +31,15 @@ + + + + - - @@ -100,6 +102,22 @@ + + + + + + + + + + + + + + + + \ 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..90be602 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; @@ -47,7 +56,7 @@ import cy.agorise.crystalwallet.viewmodels.CryptoNetBalanceListViewModel; * */ -public class BoardActivity extends AppCompatActivity { +public class BoardActivity extends CustomActivity { @BindView(R.id.tabLayout) public TabLayout tabLayout; @@ -64,6 +73,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 +110,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/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..e62f0b0 --- /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.build() + 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() + } + } + + val thread = object : Thread() { + override fun run() { + + /* + * + * Run thread*/ + CryptoNetInfoRequests.getInstance().addRequest(request) + } + } + + thread.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..71d9d99 --- /dev/null +++ b/app/src/main/java/cy/agorise/crystalwallet/activities/CustomActivity.kt @@ -0,0 +1,35 @@ +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/IntroActivity.java b/app/src/main/java/cy/agorise/crystalwallet/activities/IntroActivity.java index 9d4eaf1..6fa59d2 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,23 +20,29 @@ 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; import cy.agorise.crystalwallet.randomdatagenerators.RandomTransactionsGenerator; import cy.agorise.crystalwallet.application.CrystalSecurityMonitor; +import cy.agorise.crystalwallet.util.NetworkUtility; import cy.agorise.crystalwallet.viewmodels.AccountSeedListViewModel; import cy.agorise.crystalwallet.viewmodels.TransactionListViewModel; import cy.agorise.crystalwallet.views.TransactionListView; -public class IntroActivity extends AppCompatActivity { +public class IntroActivity extends CustomActivity { TransactionListViewModel transactionListViewModel; TransactionListView transactionListView; @@ -53,8 +60,14 @@ public class IntroActivity extends AppCompatActivity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_intro); + ButterKnife.bind(this); + /* + * Test connection with server + * */ + NetworkUtility.testServerConnnection(this); + // Appbar animation mSurfaceView.getHolder().addCallback(new SurfaceHolder.Callback() { @Override @@ -137,21 +150,56 @@ public class IntroActivity extends AppCompatActivity { @OnClick(R.id.btnCreateAccount) public void createAccount() { - Intent intent = new Intent(this, CreateSeedActivity.class); - startActivity(intent); + + /* + * Test connection with server, if no conection user can not continue + * + * */ + final GrapheneApiGenerator.OnResponsesWebSocket onResponsesWebSocket = new GrapheneApiGenerator.OnResponsesWebSocket() { + @Override + public void onSuccess() { + + /* + * Open the window to create seed and account + * */ + Intent intent = new Intent(globalActivity, CreateSeedActivity.class); + startActivity(intent); + } + @Override + public void onError(Exception exception) { + //Nothing to implement, internally the user will see a toast + } + }; + NetworkUtility.testServerConnnectionNormalError(this,onResponsesWebSocket); } @OnClick(R.id.btnImportAccount) public void importAccount() { - FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); - Fragment prev = getSupportFragmentManager().findFragmentByTag("importAccountOptions"); - if (prev != null) { - ft.remove(prev); - } - ft.addToBackStack(null); - // Create and show the dialog. - ImportAccountOptionsFragment newFragment = ImportAccountOptionsFragment.newInstance(); - newFragment.show(ft, "importAccountOptions"); + /* + * Test connection with server, if no conection user can not continue + * + * */ + final GrapheneApiGenerator.OnResponsesWebSocket onResponsesWebSocket = new GrapheneApiGenerator.OnResponsesWebSocket() { + @Override + public void onSuccess() { + + FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); + Fragment prev = getSupportFragmentManager().findFragmentByTag("importAccountOptions"); + if (prev != null) { + ft.remove(prev); + } + ft.addToBackStack(null); + + // Create and show the dialog. + ImportAccountOptionsFragment newFragment = ImportAccountOptionsFragment.newInstance(); + newFragment.show(ft, "importAccountOptions"); + } + @Override + public void onError(Exception exception) { + //Nothing to implement, internally the user will see a toast + } + }; + NetworkUtility.testServerConnnectionNormalError(this,onResponsesWebSocket); } } 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..a0bed91 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 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..beb58ba --- /dev/null +++ b/app/src/main/java/cy/agorise/crystalwallet/activities/LoadingActivity.kt @@ -0,0 +1,263 @@ +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/apigenerator/GrapheneApiGenerator.java b/app/src/main/java/cy/agorise/crystalwallet/apigenerator/GrapheneApiGenerator.java index d1bfc00..9d781fd 100644 --- a/app/src/main/java/cy/agorise/crystalwallet/apigenerator/GrapheneApiGenerator.java +++ b/app/src/main/java/cy/agorise/crystalwallet/apigenerator/GrapheneApiGenerator.java @@ -1,5 +1,6 @@ package cy.agorise.crystalwallet.apigenerator; +import android.app.Activity; import android.content.Context; import java.io.Serializable; @@ -81,6 +82,30 @@ public abstract class GrapheneApiGenerator { */ private static HashMap currentBitsharesListener = new HashMap<>(); + /* + * + * To present erros to user + * */ + private static Activity activity; + + + /* + * + * Interface to catch only errors in connection with sockets + * */ + private static OnErrorWebSocket onErrorWebSocket; + + /* + * + * Interface to catch both errors and success in connection with sockets + * */ + private static OnResponsesWebSocket onResponsesWebSocker; + + + + + + /** * Retrieves the data of an account searching by it's id * @@ -221,6 +246,8 @@ public abstract class GrapheneApiGenerator { request.getListener().fail(request.getId()); } }), CryptoNetManager.getURL(CryptoNet.BITSHARES)); + thread.setActivity(activity); //To catch websocket errors to user interface + thread.setOnErrorWebSocker(onErrorWebSocket); //To deliver websocket errors to user interface thread.start(); } @@ -703,4 +730,35 @@ public abstract class GrapheneApiGenerator { } } + + public static void setActivity(Activity activity) { + GrapheneApiGenerator.activity = activity; + } + + + public static void setOnResponsesWebSocker(OnResponsesWebSocket onResponsesWebSocker) { + GrapheneApiGenerator.onResponsesWebSocker = onResponsesWebSocker; + } + + public static void setOnErrorWebSocket(OnErrorWebSocket onErrorWebSocket) { + GrapheneApiGenerator.onErrorWebSocket = onErrorWebSocket; + } + + /* + * + * Interface to catch errors in connection with sockets + * */ + public interface OnErrorWebSocket { + void onError(Exception exception); + } + + /* + * + * Interface to catch succesfully connection with sockets + * */ + public interface OnResponsesWebSocket { + void onSuccess(); + void onError(Exception exception); + } + } 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..d6dad49 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; 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..415beb5 --- /dev/null +++ b/app/src/main/java/cy/agorise/crystalwallet/dialogs/material/CrystalDialog.kt @@ -0,0 +1,31 @@ +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..7082a12 --- /dev/null +++ b/app/src/main/java/cy/agorise/crystalwallet/dialogs/material/DialogMaterial.kt @@ -0,0 +1,154 @@ +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..1c9fc9a --- /dev/null +++ b/app/src/main/java/cy/agorise/crystalwallet/dialogs/material/ToastIt.kt @@ -0,0 +1,31 @@ +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/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..5a89378 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) @@ -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..8084096 100644 --- a/app/src/main/java/cy/agorise/crystalwallet/network/CryptoNetManager.java +++ b/app/src/main/java/cy/agorise/crystalwallet/network/CryptoNetManager.java @@ -1,14 +1,14 @@ package cy.agorise.crystalwallet.network; +import android.app.Activity; 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; +import cy.agorise.crystalwallet.apigenerator.GrapheneApiGenerator; import cy.agorise.crystalwallet.enums.CryptoNet; /** @@ -61,20 +61,63 @@ 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) { + /* + * Add url with listener on error only for sockets + * */ + public static void addCryptoNetURL(CryptoNet crypto, + String[] urls, + final Activity activity, + final GrapheneApiGenerator.OnErrorWebSocket onErrorWebSocker, + final boolean showNormalError){ + addCryptoNetURL(crypto,urls,activity,onErrorWebSocker,null,showNormalError); + } + + + /* + * Add url with listener on error and response for sockets + * */ + public static void addCryptoNetURL(CryptoNet crypto, + String[] urls, + final Activity activity, + final GrapheneApiGenerator.OnResponsesWebSocket onResponsesWebSocket, + final boolean showNormalError){ + addCryptoNetURL(crypto,urls,activity,null,onResponsesWebSocket,showNormalError); + } + + + /* + * Utility for above methods + * + * */ + public static void addCryptoNetURL(CryptoNet crypto, + String[] urls, + final Activity activity, + final GrapheneApiGenerator.OnErrorWebSocket onErrorWebSocker, + final GrapheneApiGenerator.OnResponsesWebSocket onResponsesWebSocket, + final boolean showNormalError) { + + if (!CryptoNetURLs.containsKey(crypto)) { + CryptoNetURLs.put(crypto, new HashSet()); + } + + CryptoNetVerifier verifier = CryptoNetVerifier.getNetworkVerify(crypto); + verifier.getThread().setActivity(activity); //Logical connection to ui + verifier.getThread().setOnErrorWebSocker(onErrorWebSocker); //Connect response web socket error to ui response + verifier.getThread().setOnResponsesWebSocket(onResponsesWebSocket); //Connect response and error web socket error to ui response + verifier.getThread().setShowNormalMessage(showNormalError); //Not show native message error, we handle it + + 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 +188,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..3009f2e 100644 --- a/app/src/main/java/cy/agorise/crystalwallet/network/WebSocketThread.java +++ b/app/src/main/java/cy/agorise/crystalwallet/network/WebSocketThread.java @@ -1,15 +1,19 @@ package cy.agorise.crystalwallet.network; +import android.app.Activity; import android.util.Log; +import android.widget.Toast; import com.neovisionaries.ws.client.WebSocket; -import com.neovisionaries.ws.client.WebSocketException; import com.neovisionaries.ws.client.WebSocketFactory; import com.neovisionaries.ws.client.WebSocketListener; import java.io.IOException; import java.util.HashMap; +import cy.agorise.crystalwallet.activities.LoadingActivity; +import cy.agorise.crystalwallet.apigenerator.GrapheneApiGenerator; + /** * Created by henry on 8/10/2017. */ @@ -33,6 +37,34 @@ public class WebSocketThread extends Thread { // If the parameters of this class can be change private boolean canChange = true; + /* + * + * Interface to catch only errors in connection with sockets + * */ + private GrapheneApiGenerator.OnErrorWebSocket onErrorWebSocker; + + /* + * + * Interface to catch errors and success responses in connection with sockets + * */ + private GrapheneApiGenerator.OnResponsesWebSocket onResponsesWebSocket; + + + /* + * To catch websocket errors + * */ + private Activity activity; + + /* + * To show normal error message or not + * */ + private boolean showNormalMessage = true; + + + /* + * Object needed for socket connection + * */ + private WebSocketFactory factory; /** * Basic constructor, @@ -43,16 +75,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 +153,62 @@ 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) { + + /* + * + * Websocket success response + * */ + if(onResponsesWebSocket!=null){ + onResponsesWebSocket.onSuccess(); + } + + } catch (final Exception e) { Log.e(TAG, "WebSocketException. Msg: "+e.getMessage()); - } catch(NullPointerException e){ - Log.e(TAG, "NullPointerException. Msg: "+e.getMessage()); + + //Deliver error to user + if(activity!=null){ + + /* + * Show error to user if aplies + * */ + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + + if(showNormalMessage){ + Toast.makeText(activity, e.getMessage(), Toast.LENGTH_LONG).show(); + } + } + }); + + /*Deliver response in the listeners*/ + if(onErrorWebSocker!=null){ + onErrorWebSocker.onError(e); + } + else if(onResponsesWebSocket!=null){ + onResponsesWebSocket.onError(e); + } + } + } WebSocketThread.currentThreads.remove(this.getId()); } @@ -127,4 +216,25 @@ public class WebSocketThread extends Thread { public boolean isConnected(){ return mWebSocket.isOpen(); } + + + public void setOnErrorWebSocker(GrapheneApiGenerator.OnErrorWebSocket onErrorWebSocker) { + this.onErrorWebSocker = onErrorWebSocker; + } + + public void setActivity(Activity activity) { + this.activity = activity; + } + + public void setShowNormalMessage(boolean showNormalMessage) { + this.showNormalMessage = showNormalMessage; + } + + public void setmUrl(String mUrl) { + this.mUrl = mUrl; + } + + public void setOnResponsesWebSocket(GrapheneApiGenerator.OnResponsesWebSocket onResponsesWebSocket) { + this.onResponsesWebSocket = onResponsesWebSocket; + } } 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/tests/activities/dialogs/Test_CrystalDialogActivity.kt b/app/src/main/java/cy/agorise/crystalwallet/tests/activities/dialogs/Test_CrystalDialogActivity.kt new file mode 100644 index 0000000..c1d2138 --- /dev/null +++ b/app/src/main/java/cy/agorise/crystalwallet/tests/activities/dialogs/Test_CrystalDialogActivity.kt @@ -0,0 +1,94 @@ +package cy.agorise.crystalwallet.tests.activities.dialogs + +import android.os.Bundle +import android.widget.Toast +import cy.agorise.crystalwallet.R +import cy.agorise.crystalwallet.activities.CustomActivity +import cy.agorise.crystalwallet.dialogs.material.CrystalDialog +import cy.agorise.crystalwallet.dialogs.material.NegativeResponse +import cy.agorise.crystalwallet.dialogs.material.PositiveResponse + +/* +* Class to test CrystalDialog +* */ +class Test_CrystalDialogActivity : CustomActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + setContentView(R.layout.empty_activity) + + //show() + //showOKDialog() + //showOKCancelDialog() + //showProgressIndeterminate() + } + + /* + * + * Show the simplest dialog + * + * */ + fun show(){ + + var crytalDialog:CrystalDialog = CrystalDialog(this) + crytalDialog.setTitle("Title") + crytalDialog.setText("Text") + crytalDialog.show() + //crytalDialog.dismiss() + } + + /* + * + * Show the accept dialog + * + * */ + fun showOKDialog(){ + + var crytalDialog:CrystalDialog = CrystalDialog(this) + crytalDialog.setTitle("Title") + crytalDialog.setText("Text") + crytalDialog.positiveResponse = object:PositiveResponse{ + override fun onPositive() { + Toast.makeText(globalActivity, "CrystalDialog Positive clicked", Toast.LENGTH_LONG).show() + } + } + crytalDialog.show() + } + + /* + * + * Show the accept and cancel dialog + * + * */ + fun showOKCancelDialog(){ + + var crytalDialog:CrystalDialog = CrystalDialog(this) + crytalDialog.setTitle("Title") + crytalDialog.setText("Text") + crytalDialog.positiveResponse = object:PositiveResponse{ + override fun onPositive() { + Toast.makeText(globalActivity, "CrystalDialog Positive clicked", Toast.LENGTH_LONG).show() + } + } + crytalDialog.negativeResponse = object:NegativeResponse{ + override fun onNegative() { + Toast.makeText(globalActivity, "CrystalDialog Negative clicked", Toast.LENGTH_LONG).show() + } + } + crytalDialog.show() + } + + /* + * + * Show the indeterminate dialog with text + * + * */ + fun showProgressIndeterminate(){ + + var crytalDialog:CrystalDialog = CrystalDialog(this) + crytalDialog.setText("Loading...") + crytalDialog.progress() + crytalDialog.show() + } +} \ No newline at end of file diff --git a/app/src/main/java/cy/agorise/crystalwallet/tests/activities/dialogs/Test_DialogMaterialActivity.kt b/app/src/main/java/cy/agorise/crystalwallet/tests/activities/dialogs/Test_DialogMaterialActivity.kt new file mode 100644 index 0000000..89af0bc --- /dev/null +++ b/app/src/main/java/cy/agorise/crystalwallet/tests/activities/dialogs/Test_DialogMaterialActivity.kt @@ -0,0 +1,38 @@ +package cy.agorise.crystalwallet.tests.activities.dialogs + +import android.os.Bundle +import cy.agorise.crystalwallet.R +import cy.agorise.crystalwallet.activities.CustomActivity +import cy.agorise.crystalwallet.dialogs.material.DialogMaterial + + +/* +* Class to test DialogMaterial.kt +* */ +class Test_DialogMaterialActivity : CustomActivity() { + + /* + * Object to be tested + * */ + lateinit var dialogMaterial: DialogMaterial; + + + + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + setContentView(R.layout.empty_activity) + } + + + /* + * This class can not be instantiate, + * this methos is commented to prevent compilation error, if uncommented it should + * throw error compilations and based on this this assertion is complete + * + * */ + fun instiantiation(){ + //dialogMaterial = DialogMaterial(this) + } +} \ No newline at end of file diff --git a/app/src/main/java/cy/agorise/crystalwallet/tests/activities/dialogs/Test_GIFActivity.kt b/app/src/main/java/cy/agorise/crystalwallet/tests/activities/dialogs/Test_GIFActivity.kt new file mode 100644 index 0000000..81c732c --- /dev/null +++ b/app/src/main/java/cy/agorise/crystalwallet/tests/activities/dialogs/Test_GIFActivity.kt @@ -0,0 +1,44 @@ +package cy.agorise.crystalwallet.tests.activities.dialogs + +import android.os.Bundle +import android.view.View +import cy.agorise.crystalwallet.R +import cy.agorise.crystalwallet.activities.CustomActivity +import kotlinx.android.synthetic.main.empty_activity.* + + +/* +* Unit test for class GIFView +* */ +class Test_GIFActivity : CustomActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + setContentView(R.layout.empty_activity) + + /* + * For testings purpouses + * */ + gifView.visibility = View.VISIBLE + + load() + //loadWithContainer() + } + + + /* + * Load normaly the gif + * */ + fun load(){ + gifView.load(R.raw.burbujas) + } + + /* + * Load with fit into container + * */ + fun loadWithContainer(){ + gifView.centerCrop() + gifView.load(R.raw.burbujas) + } +} \ No newline at end of file diff --git a/app/src/main/java/cy/agorise/crystalwallet/tests/activities/dialogs/Test_LoadingActivity.kt b/app/src/main/java/cy/agorise/crystalwallet/tests/activities/dialogs/Test_LoadingActivity.kt new file mode 100644 index 0000000..e3d95c0 --- /dev/null +++ b/app/src/main/java/cy/agorise/crystalwallet/tests/activities/dialogs/Test_LoadingActivity.kt @@ -0,0 +1,78 @@ +package cy.agorise.crystalwallet.tests.activities.dialogs + +import android.os.Bundle +import android.widget.Toast +import cy.agorise.crystalwallet.activities.CustomActivity +import cy.agorise.crystalwallet.activities.LoadingActivity + + +/* +* Class to test LoadingActivity +* */ +class Test_LoadingActivity : CustomActivity(){ + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + show() + //withTimer() + //onLoadingClose() + //onLoadingReady() + //sizeInLoadingIcon() + } + + + /* + * Show the simple loading graphic + * */ + fun show(){ + LoadingActivity.show(globalActivity) + //LoadingActivity.dismiss() //For testing porpouse + } + + + /* + * Loading with timer + * */ + fun withTimer(){ + LoadingActivity.closeOnTime(3) + LoadingActivity.show(globalActivity) + } + + + /* + * Listener when the loading window is closed + * */ + fun onLoadingClose(){ + LoadingActivity.closeOnTime(3) + LoadingActivity.onLoadingClosed(object : LoadingActivity.LoadingClosed{ + override fun onLoadingClosed() { + Toast.makeText(globalActivity, "onLoadingClosed event fired", Toast.LENGTH_LONG).show() + } + + }) + LoadingActivity.show(globalActivity) + } + + + /* + * Listener when the loading window is resume + * */ + fun onLoadingReady(){ + LoadingActivity.onLoadingReady(object : LoadingActivity.LoadingReady{ + override fun onLoadingReady() { + Toast.makeText(globalActivity, "onLoadingReady event fired", Toast.LENGTH_LONG).show() + } + + }) + LoadingActivity.show(globalActivity) + } + + /* + * Set specified size to the loading icon + * */ + fun sizeInLoadingIcon(){ + LoadingActivity.loadingIconSize(30,30) + LoadingActivity.show(globalActivity) + } +} \ No newline at end of file diff --git a/app/src/main/java/cy/agorise/crystalwallet/tests/activities/dialogs/Test_NetworkActivity.kt b/app/src/main/java/cy/agorise/crystalwallet/tests/activities/dialogs/Test_NetworkActivity.kt new file mode 100644 index 0000000..43d7d42 --- /dev/null +++ b/app/src/main/java/cy/agorise/crystalwallet/tests/activities/dialogs/Test_NetworkActivity.kt @@ -0,0 +1,25 @@ +package cy.agorise.crystalwallet.tests.activities.dialogs + +import android.os.Bundle +import cy.agorise.crystalwallet.activities.CustomActivity + + + + +/* +* Class to test all network implementation +* */ +class Test_NetworkActivity:CustomActivity(){ + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + + } + + + fun testServerConnection(){ + + + } +} \ No newline at end of file diff --git a/app/src/main/java/cy/agorise/crystalwallet/tests/activities/dialogs/Test_ToastActivity.kt b/app/src/main/java/cy/agorise/crystalwallet/tests/activities/dialogs/Test_ToastActivity.kt new file mode 100644 index 0000000..b8566e5 --- /dev/null +++ b/app/src/main/java/cy/agorise/crystalwallet/tests/activities/dialogs/Test_ToastActivity.kt @@ -0,0 +1,34 @@ +package cy.agorise.crystalwallet.tests.activities.dialogs + +import android.os.Bundle +import cy.agorise.crystalwallet.R +import cy.agorise.crystalwallet.activities.CustomActivity +import cy.agorise.crystalwallet.dialogs.material.ToastIt + +class Test_ToastActivity : CustomActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + setContentView(R.layout.empty_activity) + + //showShort() + showLongShort() + } + + + /* + * Show simple short toast + * */ + fun showShort(){ + ToastIt.showShortToast(this,"showShortToast") + } + + + /* + * Show simple short toast + * */ + fun showLongShort(){ + ToastIt.showShortToast(this,"showLongShort") + } +} \ No newline at end of file diff --git a/app/src/main/java/cy/agorise/crystalwallet/tests/activities/dialogs/Test_VideoActivity.kt b/app/src/main/java/cy/agorise/crystalwallet/tests/activities/dialogs/Test_VideoActivity.kt new file mode 100644 index 0000000..45d7a59 --- /dev/null +++ b/app/src/main/java/cy/agorise/crystalwallet/tests/activities/dialogs/Test_VideoActivity.kt @@ -0,0 +1,53 @@ +package cy.agorise.crystalwallet.tests.activities.dialogs + +import android.net.Uri +import android.os.Bundle +import android.view.View +import cy.agorise.crystalwallet.R +import cy.agorise.crystalwallet.activities.CustomActivity +import kotlinx.android.synthetic.main.empty_activity.* + + +/* +* Class for test VideoExView +* */ +class Test_VideoActivity : CustomActivity() { + + /* + * cy.agorise.crystalwallet.views.natives.VideoViewEx + * should be used as view to get the new implementations + * */ + + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + setContentView(R.layout.empty_activity) + + /* + * For testing purpouses + * */ + video.visibility = View.VISIBLE + + //play() + playIndeterminate() + } + + + /* + * Just one time play + * */ + fun play(){ + video.setVideoRaw(R.raw.appbar_background) + video.start() + } + + + /* + * Just one time play + * */ + fun playIndeterminate(){ + video.setVideoRaw(R.raw.appbar_background) + video.playContinius() + } +} \ No newline at end of file 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/util/NetworkUtility.kt b/app/src/main/java/cy/agorise/crystalwallet/util/NetworkUtility.kt new file mode 100644 index 0000000..45b0cf1 --- /dev/null +++ b/app/src/main/java/cy/agorise/crystalwallet/util/NetworkUtility.kt @@ -0,0 +1,72 @@ +package cy.agorise.crystalwallet.util + +import android.app.Activity +import android.widget.Toast +import cy.agorise.crystalwallet.R +import cy.agorise.crystalwallet.apigenerator.GrapheneApiGenerator +import cy.agorise.crystalwallet.application.CrystalApplication +import cy.agorise.crystalwallet.enums.CryptoNet +import cy.agorise.crystalwallet.network.CryptoNetManager + +/* +* +* Static methods for network utility +* */ +class NetworkUtility { + + /* + * Satitic methods + * */ + companion object { + + /* + * Test connection with server + * */ + @JvmStatic fun testServerConnnection(activity:Activity){ + + val onErrorWebSocker = GrapheneApiGenerator.OnErrorWebSocket { + /* + * Show message to client + * */ + activity.runOnUiThread(Runnable { Toast.makeText(activity, activity.getResources().getString(R.string.network_err_no_server_connection), Toast.LENGTH_LONG).show() }) + } + CryptoNetManager.addCryptoNetURL(CryptoNet.BITSHARES, CrystalApplication.BITSHARES_TESTNET_URL, activity, onErrorWebSocker, false) + + } + + /* + * Test connection with server and custom implementation callback + * */ + @JvmStatic fun testServerConnnection(activity:Activity, onResponseWebSocket: GrapheneApiGenerator.OnResponsesWebSocket){ + CryptoNetManager.addCryptoNetURL(CryptoNet.BITSHARES, CrystalApplication.BITSHARES_TESTNET_URL, activity, onResponseWebSocket, false) + } + + /* + * Test connection with server and custom implementation callback and with normal error + * */ + @JvmStatic fun testServerConnnectionNormalError(activity:Activity, onResponseWebSocket: GrapheneApiGenerator.OnResponsesWebSocket){ + + /* + * + * Listener to catch the error and show the normal user error message + * + * */ + val onErrorWebSocker = GrapheneApiGenerator.OnErrorWebSocket { + /* + * Show message to client + * */ + activity.runOnUiThread(Runnable { Toast.makeText(activity, activity.getResources().getString(R.string.network_err_no_server_connection), Toast.LENGTH_LONG).show() }) + } + + /* + * Request + * */ + CryptoNetManager.addCryptoNetURL( CryptoNet.BITSHARES, + CrystalApplication.BITSHARES_TESTNET_URL, + activity, + onErrorWebSocker, + onResponseWebSocket, + false) + } + } +} \ 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..5118b71 --- /dev/null +++ b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/AccountSeedViewModel.kt @@ -0,0 +1,39 @@ +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 : AndroidViewModel { + + private var accountSeed: LiveData? = null + private val db: CrystalDatabase + private val app: Application + + + + + constructor(application: Application,db:CrystalDatabase): super(application) { + this.app = application; + this.db = db; + } + + + 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/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..2fc915e --- /dev/null +++ b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/customImpl/interfaces/UIValidator.kt @@ -0,0 +1,10 @@ +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..bd638fe --- /dev/null +++ b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/customImpl/interfaces/UIValidatorListener.kt @@ -0,0 +1,14 @@ +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..c482f87 --- /dev/null +++ b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/customImpl/validationFields/BitsharesAccountNameValidation.kt @@ -0,0 +1,265 @@ +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 + + +/* +* +* 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) + + /* + * Deliver the response + * */ + if (uiValidatorListener != null) { + uiValidatorListener.onValidationFailed(globalCustomValidationField) + } + + /* + * Deliver response to local callback + * */ + if (onAccountExist != null) { + onAccountExist!!.onAccountExists() + } + + } else { + + /* + * Passed all validations + * */ + accountNameField.fieldValidatorModel.setValid() + + /* + * Deliver the response + * */ + if (uiValidatorListener != null) { + uiValidatorListener.onValidationSucceeded(globalCustomValidationField) + } + } + } + + /* + * Listener for websocket error + * */ + GrapheneApiGenerator.setActivity(activity); //Set the activity to catch errors + val onErrorWebSocker = object : GrapheneApiGenerator.OnErrorWebSocket { + override fun onError(exception: java.lang.Exception?) { + + /* + * + * Hide loading dialog + * + * */ + creatingAccountMaterialDialog.dismiss(); + + } + + } + GrapheneApiGenerator.setOnErrorWebSocket(onErrorWebSocker); //Set the activity to catch errors + + + /* + * + * Run thread*/ + 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..e45393c --- /dev/null +++ b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/customImpl/validationFields/CustomValidationField.kt @@ -0,0 +1,46 @@ +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..9fa7493 --- /dev/null +++ b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/customImpl/validationFields/PinDoubleConfirmationValidationField.kt @@ -0,0 +1,135 @@ +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/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..950b4fb --- /dev/null +++ b/app/src/main/java/cy/agorise/crystalwallet/views/natives/CustomTextInputEditText.java @@ -0,0 +1,78 @@ +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..d98c7f9 --- /dev/null +++ b/app/src/main/java/cy/agorise/crystalwallet/views/natives/GIFView.kt @@ -0,0 +1,40 @@ +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..c018d33 --- /dev/null +++ b/app/src/main/java/cy/agorise/crystalwallet/views/natives/VideoExView.kt @@ -0,0 +1,36 @@ +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/backup_seed.xml b/app/src/main/res/layout/backup_seed.xml index 9166b1c..0a50ef8 100644 --- a/app/src/main/res/layout/backup_seed.xml +++ b/app/src/main/res/layout/backup_seed.xml @@ -1,52 +1,91 @@ - - + android:layout_centerInParent="true"> - - - - -