diff --git a/app/build.gradle b/app/build.gradle
index c79b2db..44b82fd 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -63,4 +63,6 @@ dependencies {
annotationProcessor 'android.arch.lifecycle:compiler:1.0.0'
annotationProcessor 'android.arch.persistence.room:compiler:1.0.0'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'
+
+ compile 'com.squareup.picasso:picasso:2.5.2'
}
diff --git a/app/schemas/cy.agorise.crystalwallet.dao.CrystalDatabase/2.json b/app/schemas/cy.agorise.crystalwallet.dao.CrystalDatabase/2.json
index 2f9409f..22f45c4 100644
--- a/app/schemas/cy.agorise.crystalwallet.dao.CrystalDatabase/2.json
+++ b/app/schemas/cy.agorise.crystalwallet.dao.CrystalDatabase/2.json
@@ -2,7 +2,7 @@
"formatVersion": 1,
"database": {
"version": 2,
- "identityHash": "a44ccb96c8213951403ed2a283fb3367",
+ "identityHash": "22cb2a56b28a9f7088ec98d6a72f9f67",
"entities": [
{
"tableName": "account_seed",
@@ -235,7 +235,7 @@
},
{
"tableName": "contact",
- "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT, `gravatar` TEXT)",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT, `email` TEXT, `gravatar` TEXT)",
"fields": [
{
"fieldPath": "mId",
@@ -249,6 +249,12 @@
"affinity": "TEXT",
"notNull": false
},
+ {
+ "fieldPath": "mEmail",
+ "columnName": "email",
+ "affinity": "TEXT",
+ "notNull": false
+ },
{
"fieldPath": "mGravatar",
"columnName": "gravatar",
@@ -278,6 +284,70 @@
"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": []
@@ -622,7 +692,7 @@
],
"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, \"a44ccb96c8213951403ed2a283fb3367\")"
+ "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"22cb2a56b28a9f7088ec98d6a72f9f67\")"
]
}
}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index b3ebbef..1eabefc 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,11 +1,12 @@
+
+ android:theme="@style/AppTheme.NoActionBar" >
@@ -37,20 +38,27 @@
-
+
-
+
+
+
-
+
+
+
\ No newline at end of file
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 9ef1161..95c7fb4 100644
--- a/app/src/main/java/cy/agorise/crystalwallet/activities/BoardActivity.java
+++ b/app/src/main/java/cy/agorise/crystalwallet/activities/BoardActivity.java
@@ -204,6 +204,12 @@ public class BoardActivity extends AppCompatActivity {
}
}
+ @OnClick(R.id.fabAddContact)
+ public void beginCreateContact(){
+ Intent intent = new Intent(this, CreateContactActivity.class);
+ startActivity(intent);
+ }
+
/*
* dispatch the user to the receive fragment using this account
*/
@@ -215,8 +221,16 @@ public class BoardActivity extends AppCompatActivity {
}
ft.addToBackStack(null);
+ long receiveCryptoNetAccountId = -1;
+ if (this.cryptoNetAccountId != -1){
+ receiveCryptoNetAccountId = this.cryptoNetAccountId;
+ } else {
+ CryptoNetBalanceListViewModel cryptoNetBalanceListViewModel = ViewModelProviders.of(this).get(CryptoNetBalanceListViewModel.class);
+ receiveCryptoNetAccountId = cryptoNetBalanceListViewModel.getFirstBitsharesAccountId();
+ }
+
// Create and show the dialog.
- ReceiveTransactionFragment newFragment = ReceiveTransactionFragment.newInstance(this.cryptoNetAccountId);
+ ReceiveTransactionFragment newFragment = ReceiveTransactionFragment.newInstance(receiveCryptoNetAccountId);
newFragment.show(ft, "ReceiveDialog");
}
diff --git a/app/src/main/java/cy/agorise/crystalwallet/activities/CreateContactActivity.java b/app/src/main/java/cy/agorise/crystalwallet/activities/CreateContactActivity.java
new file mode 100644
index 0000000..fcb1469
--- /dev/null
+++ b/app/src/main/java/cy/agorise/crystalwallet/activities/CreateContactActivity.java
@@ -0,0 +1,254 @@
+package cy.agorise.crystalwallet.activities;
+
+import android.arch.lifecycle.LiveData;
+import android.arch.lifecycle.Observer;
+import android.arch.lifecycle.ViewModelProviders;
+import android.content.Intent;
+import android.support.annotation.Nullable;
+import android.support.v4.app.FragmentActivity;
+import android.support.v7.app.AlertDialog;
+import android.support.v7.app.AppCompatActivity;
+import android.os.Bundle;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.text.Editable;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+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.cryptonetinforequests.CryptoNetInfoRequestListener;
+import cy.agorise.crystalwallet.cryptonetinforequests.CryptoNetInfoRequests;
+import cy.agorise.crystalwallet.cryptonetinforequests.ValidateCreateBitsharesAccountRequest;
+import cy.agorise.crystalwallet.models.Contact;
+import cy.agorise.crystalwallet.models.ContactAddress;
+import cy.agorise.crystalwallet.models.GrapheneAccount;
+import cy.agorise.crystalwallet.viewmodels.ContactListViewModel;
+import cy.agorise.crystalwallet.viewmodels.ContactViewModel;
+import cy.agorise.crystalwallet.viewmodels.validators.CreateContactValidator;
+import cy.agorise.crystalwallet.viewmodels.validators.CreateSeedValidator;
+import cy.agorise.crystalwallet.viewmodels.validators.ModifyContactValidator;
+import cy.agorise.crystalwallet.viewmodels.validators.UIValidatorListener;
+import cy.agorise.crystalwallet.viewmodels.validators.validationfields.ValidationField;
+import cy.agorise.crystalwallet.views.ContactAddressListAdapter;
+import cy.agorise.crystalwallet.views.ContactViewHolder;
+
+public class CreateContactActivity extends AppCompatActivity implements UIValidatorListener {
+
+ @BindView(R.id.etName)
+ EditText etName;
+ @BindView(R.id.tvNameError)
+ TextView tvNameError;
+ @BindView(R.id.etEmail)
+ EditText etEmail;
+ @BindView(R.id.tvEmailError)
+ TextView tvEmailError;
+ @BindView(R.id.btnCancel)
+ Button btnCancel;
+ @BindView(R.id.btnCreate)
+ Button btnCreate;
+ @BindView(R.id.btnModify)
+ Button btnModify;
+ @BindView(R.id.rvContactAddresses)
+ RecyclerView rvContactAddresses;
+ @BindView(R.id.btnAddAddress)
+ Button btnAddAddress;
+ List contactAddressList;
+ ContactAddressListAdapter listAdapter;
+
+ CreateContactValidator createContactValidator;
+ ModifyContactValidator modifyContactValidator;
+
+ Contact contact;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_create_contact);
+ ButterKnife.bind(this);
+
+ btnCreate.setEnabled(false);
+
+ listAdapter = new ContactAddressListAdapter();
+ rvContactAddresses.setLayoutManager(new LinearLayoutManager(getApplicationContext()));
+ rvContactAddresses.setAdapter(listAdapter);
+
+ long contactId = this.getIntent().getLongExtra("CONTACT_ID",-1);
+
+ btnCreate.setVisibility(View.GONE);
+ btnModify.setVisibility(View.GONE);
+
+ if (contactId >= 0){
+ final ContactViewModel contactViewModel = ViewModelProviders.of(this).get(ContactViewModel.class);
+
+ contactViewModel.init(contactId);
+ LiveData contactLiveData = contactViewModel.getContact();
+ final CreateContactActivity thisActivity = this;
+
+ contactLiveData.observe(this, new Observer() {
+ @Override
+ public void onChanged(@Nullable Contact contactChanged) {
+ if (contactChanged != null){
+ contact = contactChanged;
+ etName.setText(contact.getName());
+ etEmail.setText(contact.getEmail());
+
+ LiveData> contactAddresses = contactViewModel.getContactAddresses();
+
+ contactAddresses.observe(thisActivity, new Observer>() {
+ @Override
+ public void onChanged(@Nullable List contactAddresses) {
+ contactAddressList = contactAddresses;
+ listAdapter.setList(contactAddressList);
+ listAdapter.notifyDataSetChanged();
+ }
+ });
+
+ modifyContactValidator = new ModifyContactValidator(thisActivity.getApplicationContext(),contact,etName,etEmail);
+ modifyContactValidator.setListener(thisActivity);
+ btnModify.setVisibility(View.VISIBLE);
+ } else {
+ //No contact was found, this will exit the activity
+ finish();
+ }
+ }
+ });
+ } else {
+ contactAddressList = new ArrayList();
+ listAdapter.setList(contactAddressList);
+ createContactValidator = new CreateContactValidator(this.getApplicationContext(),etName,etEmail);
+ createContactValidator.setListener(this);
+
+ btnCreate.setVisibility(View.VISIBLE);
+ }
+ }
+
+ public void validate(){
+ if (this.createContactValidator != null){
+ this.createContactValidator.validate();
+ } else if (this.modifyContactValidator != null){
+ this.modifyContactValidator.validate();
+ }
+ }
+
+ public boolean isValid(){
+ if (this.createContactValidator != null){
+ return this.createContactValidator.isValid();
+ } else if (this.modifyContactValidator != null){
+ return this.modifyContactValidator.isValid();
+ }
+
+ return false;
+ }
+
+ @OnTextChanged(value = R.id.etName,
+ callback = OnTextChanged.Callback.AFTER_TEXT_CHANGED)
+ void afterContactNameChanged(Editable editable) {
+ this.validate();
+ }
+
+ @OnTextChanged(value = R.id.etEmail,
+ callback = OnTextChanged.Callback.AFTER_TEXT_CHANGED)
+ void afterEmailChanged(Editable editable) {
+ this.validate();
+ }
+
+
+ @OnClick(R.id.btnAddAddress)
+ public void addAddress(){
+ ContactAddress newContactAddress = new ContactAddress();
+ this.contactAddressList.add(newContactAddress);
+ this.listAdapter.notifyDataSetChanged();
+ }
+
+ @OnClick(R.id.btnCancel)
+ public void cancel(){
+ this.finish();
+ }
+
+ @OnClick(R.id.btnModify)
+ public void modifyContact(){
+ if (this.modifyContactValidator.isValid()) {
+ this.contact.setName(etName.getText().toString());
+ this.contact.setEmail(etEmail.getText().toString());
+ this.contact.clearAddresses();
+
+ for (ContactAddress contactAddress : contactAddressList){
+ this.contact.addAddress(contactAddress);
+ }
+
+ ContactViewModel contactViewModel = ViewModelProviders.of(this).get(ContactViewModel.class);
+ if (contactViewModel.modifyContact(this.contact)){
+ this.finish();
+ } else {
+ this.modifyContactValidator.validate();
+ }
+ }
+ }
+
+ @OnClick(R.id.btnCreate)
+ public void createContact(){
+ if (this.createContactValidator.isValid()) {
+ Contact newContact = new Contact();
+ newContact.setName(etName.getText().toString());
+ newContact.setEmail(etEmail.getText().toString());
+
+ for (ContactAddress contactAddress : contactAddressList){
+ newContact.addAddress(contactAddress);
+ }
+
+ ContactViewModel contactViewModel = ViewModelProviders.of(this).get(ContactViewModel.class);
+ if (contactViewModel.addContact(newContact)){
+ this.finish();
+ } else {
+ createContactValidator.validate();
+ }
+ }
+ }
+
+ @Override
+ public void onValidationSucceeded(final ValidationField field) {
+ final CreateContactActivity activity = this;
+
+ activity.runOnUiThread(new Runnable() {
+ public void run() {
+
+ if (field.getView() == etName) {
+ tvNameError.setText("");
+ } else if (field.getView() == etEmail) {
+ tvEmailError.setText("");
+ }
+
+ if (activity.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() == etName) {
+ tvNameError.setText(field.getMessage());
+ } else if (field.getView() == etEmail) {
+ tvEmailError.setText(field.getMessage());
+ }
+ }
+ });
+ }
+}
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 fca4558..eafa3e2 100644
--- a/app/src/main/java/cy/agorise/crystalwallet/activities/IntroActivity.java
+++ b/app/src/main/java/cy/agorise/crystalwallet/activities/IntroActivity.java
@@ -1,11 +1,6 @@
package cy.agorise.crystalwallet.activities;
-import android.arch.lifecycle.LifecycleActivity;
-import android.arch.lifecycle.LiveData;
-import android.arch.lifecycle.Observer;
-import android.arch.lifecycle.ViewModelProvider;
import android.arch.lifecycle.ViewModelProviders;
-import android.arch.paging.PagedList;
import android.content.Intent;
import android.media.MediaPlayer;
import android.os.Bundle;
@@ -34,12 +29,11 @@ import cy.agorise.crystalwallet.randomdatagenerators.RandomCryptoCoinBalanceGene
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.viewmodels.AccountSeedListViewModel;
import cy.agorise.crystalwallet.viewmodels.TransactionListViewModel;
import cy.agorise.crystalwallet.views.TransactionListView;
-import static cy.agorise.crystalwallet.R.string.transactions;
-
public class IntroActivity extends AppCompatActivity {
TransactionListViewModel transactionListViewModel;
@@ -82,6 +76,9 @@ public class IntroActivity extends AppCompatActivity {
}
});
+ this.getApplication().registerActivityLifecycleCallbacks(new CrystalSecurityMonitor(this));
+
+
//Checks if the user has any seed created
/*AccountSeedListViewModel accountSeedListViewModel = ViewModelProviders.of(this).get(AccountSeedListViewModel.class);
diff --git a/app/src/main/java/cy/agorise/crystalwallet/activities/PinRequestActivity.java b/app/src/main/java/cy/agorise/crystalwallet/activities/PinRequestActivity.java
new file mode 100644
index 0000000..a95210a
--- /dev/null
+++ b/app/src/main/java/cy/agorise/crystalwallet/activities/PinRequestActivity.java
@@ -0,0 +1,74 @@
+package cy.agorise.crystalwallet.activities;
+
+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.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.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 {
+ private String passwordEncrypted;
+
+ @Override
+ public void onBackPressed() {
+ //Do nothing to prevent the user to use the back button
+ }
+
+ @BindView(R.id.etPassword)
+ EditText etPassword;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_pin_request);
+ ButterKnife.bind(this);
+
+ GeneralSettingListViewModel generalSettingListViewModel = ViewModelProviders.of(this).get(GeneralSettingListViewModel.class);
+ LiveData> generalSettingsLiveData = generalSettingListViewModel.getGeneralSettingList();
+ generalSettingsLiveData.observe(this, new Observer>() {
+ @Override
+ public void onChanged(@Nullable List generalSettings) {
+ passwordEncrypted = "";
+
+ if (generalSettings != null){
+ for (GeneralSetting generalSetting:generalSettings) {
+ if (generalSetting.getName().equals(GeneralSetting.SETTING_PASSWORD)){
+ if (!generalSetting.getValue().isEmpty()){
+ passwordEncrypted = generalSetting.getValue();
+ }
+ break;
+ }
+ }
+ }
+ }
+ });
+ }
+
+ @OnTextChanged(value = R.id.etPassword,
+ callback = OnTextChanged.Callback.AFTER_TEXT_CHANGED)
+ void afterPasswordChanged(Editable editable) {
+ if (PasswordManager.checkPassword(passwordEncrypted, etPassword.getText().toString())) {
+ this.finish();
+ }
+ }
+}
+
+
diff --git a/app/src/main/java/cy/agorise/crystalwallet/apigenerator/BitsharesFaucetApiGenerator.java b/app/src/main/java/cy/agorise/crystalwallet/apigenerator/BitsharesFaucetApiGenerator.java
index ec99d7c..8aa4b56 100644
--- a/app/src/main/java/cy/agorise/crystalwallet/apigenerator/BitsharesFaucetApiGenerator.java
+++ b/app/src/main/java/cy/agorise/crystalwallet/apigenerator/BitsharesFaucetApiGenerator.java
@@ -37,8 +37,9 @@ public abstract class BitsharesFaucetApiGenerator {
* @param url The url of the faucet
* @return The bitshares id of the registered account, or null
*/
- public static boolean registerBitsharesAccount(String accountName, String ownerKey,
- String activeKey, String memoKey, String url){
+ public static void registerBitsharesAccount(String accountName, String ownerKey,
+ String activeKey, String memoKey, String url,
+ final ApiRequest request){
CreateAccountPetition petition = new CreateAccountPetition();
final Account account = new Account();
account.name=accountName;
@@ -62,8 +63,7 @@ public abstract class BitsharesFaucetApiGenerator {
HashMap hashMap = new HashMap<>();
hashMap.put("account", hm);
- final boolean[] answer = {false};
- final Object SYNC = new Object();
+
try {
ServiceGenerator sg = new ServiceGenerator(url);
IWebService service = sg.getService(IWebService.class);
@@ -78,56 +78,36 @@ public abstract class BitsharesFaucetApiGenerator {
if (resp.account != null) {
try {
if(resp.account.name.equals(account.name)) {
- synchronized (SYNC){
- answer[0] = true;
- SYNC.notifyAll();
- }
+ request.getListener().success(true,request.getId());
}else{
- System.out.println("ERROR account name different" + resp.account.name);
- //ERROR
- synchronized (SYNC) {
- SYNC.notifyAll();
- }
+ request.getListener().fail(request.getId());
}
} catch (Exception e) {
e.printStackTrace();
- synchronized (SYNC) {
- SYNC.notifyAll();
- }
+ request.getListener().fail(request.getId());
}
}else{
System.out.println("ERROR response doesn't have account " + response.message());
- //ERROR
- synchronized (SYNC) {
- SYNC.notifyAll();
- }
+ request.getListener().fail(request.getId());
}
}else{
System.out.println("ERROR fetching info");
- //ERROR
- synchronized (SYNC) {
- SYNC.notifyAll();
- }
+ request.getListener().fail(request.getId());
}
}
@Override
public void onFailure(Call call, Throwable t) {
t.printStackTrace();
- synchronized (SYNC) {
- SYNC.notifyAll();
- }
+ request.getListener().fail(request.getId());
}
});
- synchronized (SYNC) {
- SYNC.wait(60000);
- }
} catch (Exception e) {
e.printStackTrace();
- }
+ request.getListener().fail(request.getId());
- return answer[0];
+ }
}
/**
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 15ea05f..0522a9c 100644
--- a/app/src/main/java/cy/agorise/crystalwallet/apigenerator/GrapheneApiGenerator.java
+++ b/app/src/main/java/cy/agorise/crystalwallet/apigenerator/GrapheneApiGenerator.java
@@ -12,14 +12,14 @@ import cy.agorise.crystalwallet.dao.BitsharesAssetDao;
import cy.agorise.crystalwallet.dao.CryptoCoinBalanceDao;
import cy.agorise.crystalwallet.dao.CryptoCurrencyDao;
import cy.agorise.crystalwallet.dao.CrystalDatabase;
-import cy.agorise.crystalwallet.dao.TransactionDao;
-import cy.agorise.crystalwallet.manager.BitsharesAccountManager;
+import cy.agorise.crystalwallet.enums.CryptoNet;
import cy.agorise.crystalwallet.models.BitsharesAsset;
import cy.agorise.crystalwallet.models.BitsharesAssetInfo;
import cy.agorise.crystalwallet.models.CryptoCoinBalance;
import cy.agorise.crystalwallet.models.CryptoCoinTransaction;
import cy.agorise.crystalwallet.models.CryptoCurrency;
import cy.agorise.crystalwallet.models.CryptoCurrencyEquivalence;
+import cy.agorise.crystalwallet.network.CryptoNetManager;
import cy.agorise.crystalwallet.network.WebSocketThread;
import cy.agorise.graphenej.Address;
import cy.agorise.graphenej.Asset;
@@ -43,11 +43,9 @@ import cy.agorise.graphenej.api.TransactionBroadcastSequence;
import cy.agorise.graphenej.interfaces.NodeErrorListener;
import cy.agorise.graphenej.interfaces.SubscriptionListener;
import cy.agorise.graphenej.interfaces.WitnessResponseListener;
-import cy.agorise.graphenej.models.AccountBalanceUpdate;
import cy.agorise.graphenej.models.AccountProperties;
import cy.agorise.graphenej.models.BaseResponse;
import cy.agorise.graphenej.models.BroadcastedTransaction;
-import cy.agorise.graphenej.models.HistoricalTransfer;
import cy.agorise.graphenej.models.SubscriptionResponse;
import cy.agorise.graphenej.models.WitnessResponse;
import cy.agorise.graphenej.operations.TransferOperation;
@@ -62,12 +60,8 @@ public abstract class GrapheneApiGenerator {
//TODO network connections
//TODO make to work with all Graphene type, not only bitshares
- public static String url = "http://185.208.208.147:11012";
public static String faucetUrl = "http://185.208.208.147:5010";
- private static String equivalentUrl = "http://185.208.208.147:8090";
- //public static String url = "wss://bitshares.openledger.info/ws";
- //private static Str ing equivalentUrl = "wss://bitshares.openledger.info/ws";
-
+ private static String equivalentUrl = "wss://bitshares.openledger.info/ws";
// The message broker for bitshares
private static SubscriptionMessagesHub bitsharesSubscriptionHub = new SubscriptionMessagesHub("", "", true, new NodeErrorListener() {
@@ -81,7 +75,7 @@ public abstract class GrapheneApiGenerator {
/**
* The subscription thread for the real time updates
*/
- private static WebSocketThread subscriptionThread = new WebSocketThread(bitsharesSubscriptionHub,url);
+ private static WebSocketThread subscriptionThread = new WebSocketThread(bitsharesSubscriptionHub, CryptoNetManager.getURL(CryptoNet.BITSHARES));
/**
* This is used for manager each listener in the subscription thread
*/
@@ -114,7 +108,7 @@ public abstract class GrapheneApiGenerator {
public void onError(BaseResponse.Error error) {
request.getListener().fail(request.getId());
}
- }),url);
+ }),CryptoNetManager.getURL(CryptoNet.BITSHARES));
thread.start();
}
@@ -142,7 +136,7 @@ public abstract class GrapheneApiGenerator {
public void onError(BaseResponse.Error error) {
request.getListener().fail(request.getId());
}
- }),url);
+ }),CryptoNetManager.getURL(CryptoNet.BITSHARES));
thread.start();
}
@@ -169,7 +163,7 @@ public abstract class GrapheneApiGenerator {
public void onError(BaseResponse.Error error) {
request.getListener().fail(request.getId());
}
- }),url);
+ }),CryptoNetManager.getURL(CryptoNet.BITSHARES));
thread.start();
}
@@ -196,7 +190,7 @@ public abstract class GrapheneApiGenerator {
public void onError(BaseResponse.Error error) {
request.getListener().fail(request.getId());
}
- }),url);
+ }),CryptoNetManager.getURL(CryptoNet.BITSHARES));
thread.start();
}
@@ -223,7 +217,7 @@ public abstract class GrapheneApiGenerator {
public void onError(BaseResponse.Error error) {
request.getListener().fail(request.getId());
}
- }),url);
+ }),CryptoNetManager.getURL(CryptoNet.BITSHARES));
thread.start();
}
@@ -247,7 +241,7 @@ public abstract class GrapheneApiGenerator {
public void onError(BaseResponse.Error error) {
request.getListener().fail(request.getId());
}
- }),url);
+ }),CryptoNetManager.getURL(CryptoNet.BITSHARES));
thread.start();
}
@@ -292,7 +286,7 @@ public abstract class GrapheneApiGenerator {
public void onError(BaseResponse.Error error) {
request.getListener().fail(request.getId());
}
- }),url);
+ }),CryptoNetManager.getURL(CryptoNet.BITSHARES));
thread.start();
}
@@ -340,7 +334,7 @@ public abstract class GrapheneApiGenerator {
public void onError(BaseResponse.Error error) {
request.getListener().fail(request.getId());
}
- }),url);
+ }),CryptoNetManager.getURL(CryptoNet.BITSHARES));
thread.start();
}
@@ -353,12 +347,10 @@ public abstract class GrapheneApiGenerator {
*/
public static void subscribeBitsharesAccount(final long accountId, final String accountBitsharesId,
final Context context){
- System.out.println("GrapheneAPI subscribe to account balance update");
if(!currentBitsharesListener.containsKey(accountId)){
CrystalDatabase db = CrystalDatabase.getAppDatabase(context);
final BitsharesAssetDao bitsharesAssetDao = db.bitsharesAssetDao();
final CryptoCurrencyDao cryptoCurrencyDao = db.cryptoCurrencyDao();
- final TransactionDao transactionDao = db.transactionDao();
SubscriptionListener balanceListener = new SubscriptionListener() {
@Override
public ObjectType getInterestObjectType() {
@@ -374,16 +366,15 @@ public abstract class GrapheneApiGenerator {
BroadcastedTransaction transactionUpdate = (BroadcastedTransaction) update;
for(BaseOperation operation : transactionUpdate.getTransaction().getOperations()){
if(operation instanceof TransferOperation){
- TransferOperation tOperation = (TransferOperation) operation;
+ final TransferOperation tOperation = (TransferOperation) operation;
if(tOperation.getFrom().getObjectId().equals(accountBitsharesId) || tOperation.getTo().getObjectId().equals(accountBitsharesId)){
GrapheneApiGenerator.getAccountBalance(accountId,accountBitsharesId,context);
- CryptoCoinTransaction transaction = new CryptoCoinTransaction();
+ final CryptoCoinTransaction transaction = new CryptoCoinTransaction();
transaction.setAccountId(accountId);
transaction.setAmount(tOperation.getAssetAmount().getAmount().longValue());
BitsharesAssetInfo info = bitsharesAssetDao.getBitsharesAssetInfoById(tOperation.getAssetAmount().getAsset().getObjectId());
if (info == null) {
//The cryptoCurrency is not in the database, queringfor its data
- final Object SYNC = new Object(); //Object to syn the answer
ApiRequest assetRequest = new ApiRequest(0, new ApiRequestListener() {
@Override
public void success(Object answer, int idPetition) {
@@ -394,40 +385,21 @@ public abstract class GrapheneApiGenerator {
info.setCryptoCurrencyId(idCryptoCurrency);
asset.setId((int)idCryptoCurrency);
bitsharesAssetDao.insertBitsharesAssetInfo(info);
- }
- synchronized (SYNC){
- SYNC.notifyAll();
+ saveTransaction(transaction,(int)info.getCryptoCurrencyId(),accountBitsharesId,tOperation,context);
}
}
@Override
public void fail(int idPetition) {
- synchronized (SYNC){
- SYNC.notifyAll();
- }
+ //TODO error retrieving asset
}
});
ArrayList assets = new ArrayList<>();
assets.add(tOperation.getAssetAmount().getAsset().getObjectId());
GrapheneApiGenerator.getAssetById(assets,assetRequest);
-
- synchronized (SYNC){
- try {SYNC.wait(60000);} catch (InterruptedException ignore) {}
- }
- info = bitsharesAssetDao.getBitsharesAssetInfoById(tOperation.getAssetAmount().getAsset().getObjectId());
+ }else{
+ saveTransaction(transaction,(int)info.getCryptoCurrencyId(),accountBitsharesId,tOperation,context);
}
- if( info == null){
- //We couldn't retrieve the cryptocurrency
- return;
- }
- transaction.setIdCurrency((int)info.getCryptoCurrencyId());
- transaction.setConfirmed(true); //graphene transaction are always confirmed
- transaction.setFrom(tOperation.getFrom().getObjectId());
- transaction.setInput(!tOperation.getFrom().getObjectId().equals(accountBitsharesId));
- transaction.setTo(tOperation.getTo().getObjectId());
- transaction.setDate(new Date());
- transactionDao.insertTransaction(transaction);
- //GrapheneApiGenerator.getBlockHeaderTime(, new ApiRequest(0, new BitsharesAccountManager.GetTransactionDate(transaction, db.transactionDao())));
}
}
}
@@ -449,6 +421,26 @@ public abstract class GrapheneApiGenerator {
}
}
+ /**
+ * Fucniton to save a transaction retrieved from the update
+ * @param transaction The transaction db object
+ * @param currencyId The id of the currency on the database
+ * @param accountBitsharesId The id of the account in the bitshares network
+ * @param tOperation The transfer operation fetched from the update
+ * @param context The context of this app
+ */
+ private static void saveTransaction(CryptoCoinTransaction transaction, int currencyId,
+ String accountBitsharesId, TransferOperation tOperation ,
+ Context context){
+ transaction.setIdCurrency(currencyId);
+ transaction.setConfirmed(true); //graphene transaction are always confirmed
+ transaction.setFrom(tOperation.getFrom().getObjectId());
+ transaction.setInput(!tOperation.getFrom().getObjectId().equals(accountBitsharesId));
+ transaction.setTo(tOperation.getTo().getObjectId());
+ transaction.setDate(new Date());
+ CrystalDatabase.getAppDatabase(context).transactionDao().insertTransaction(transaction);
+ }
+
/**
* Cancels all bitshares account subscriptions
*/
@@ -515,7 +507,7 @@ public abstract class GrapheneApiGenerator {
public void onError(BaseResponse.Error error) {
}
- }),url);
+ }),CryptoNetManager.getURL(CryptoNet.BITSHARES));
thread.start();
@@ -542,7 +534,7 @@ public abstract class GrapheneApiGenerator {
public void onError(BaseResponse.Error error) {
request.getListener().fail(request.getId());
}
- }),url);
+ }),CryptoNetManager.getURL(CryptoNet.BITSHARES));
thread.start();
}
@@ -674,7 +666,7 @@ public abstract class GrapheneApiGenerator {
Converter converter = new Converter();
order.getSellPrice().base.getAsset().setPrecision(baseAsset.getPrecision());
order.getSellPrice().quote.getAsset().setPrecision(quoteAsset.getPrecision());
- double equiValue = converter.getConversionRate(order.getSellPrice(), Converter.BASE_TO_QUOTE);
+ double equiValue = converter.getConversionRate(order.getSellPrice(), Converter.QUOTE_TO_BASE);
CryptoCurrencyEquivalence equivalence = new CryptoCurrencyEquivalence(baseAsset.getId(), quoteAsset.getId(), (int) (Math.pow(10, baseAsset.getPrecision()) * equiValue), new Date());
CrystalDatabase.getAppDatabase(context).cryptoCurrencyEquivalenceDao().insertCryptoCurrencyEquivalence(equivalence);
break;
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 fc5f6c7..8a1e16c 100644
--- a/app/src/main/java/cy/agorise/crystalwallet/application/CrystalApplication.java
+++ b/app/src/main/java/cy/agorise/crystalwallet/application/CrystalApplication.java
@@ -6,6 +6,11 @@ import android.content.Intent;
import com.idescout.sql.SqlScoutServer;
import cy.agorise.crystalwallet.dao.CrystalDatabase;
+import cy.agorise.crystalwallet.enums.CryptoNet;
+import cy.agorise.crystalwallet.models.BitsharesAsset;
+import cy.agorise.crystalwallet.models.BitsharesAssetInfo;
+import cy.agorise.crystalwallet.models.CryptoCurrencyEquivalence;
+import cy.agorise.crystalwallet.network.CryptoNetManager;
import cy.agorise.crystalwallet.service.CrystalWalletService;
/**
@@ -15,6 +20,25 @@ import cy.agorise.crystalwallet.service.CrystalWalletService;
*/
public class CrystalApplication extends Application {
+ public static String BITSHARES_URL[] =
+ {
+ "wss://de.palmpay.io/ws", // Custom node
+ "wss://bitshares.nu/ws",
+ "wss://dexnode.net/ws", // Dallas, USA
+ "wss://bitshares.crypto.fans/ws", // Munich, Germany
+ "wss://bitshares.openledger.info/ws", // Openledger node
+ "ws://185.208.208.147:8090" // Custom node
+ };
+
+ public static String BITSHARES_TESTNET_URL[] =
+ {
+ "http://185.208.208.147:11012", // Openledger node
+ };
+
+ //This is for testing the equivalent values on the testnet TODO remove
+ public static BitsharesAsset bitUSDAsset = new BitsharesAsset("USD",4,"1.3.121",BitsharesAsset.Type.SMART_COIN);
+ //This is for testing the equivalent values on the testnet TODO remove
+ public static BitsharesAsset bitEURAsset = new BitsharesAsset("EUR",4,"1.3.120",BitsharesAsset.Type.SMART_COIN);
@Override
public void onCreate() {
@@ -24,6 +48,36 @@ public class CrystalApplication extends Application {
CrystalDatabase db = CrystalDatabase.getAppDatabase(this.getApplicationContext());
SqlScoutServer.create(this, getPackageName());
+ //Using Bitshares Agorise Testnet
+ CryptoNetManager.addCryptoNetURL(CryptoNet.BITSHARES,BITSHARES_TESTNET_URL);
+
+ //This is for testing the equivalent values on the testnet TODO remove
+ if(db.bitsharesAssetDao().getBitsharesAssetInfoById(bitEURAsset.getBitsharesId())== null){
+ if(db.cryptoCurrencyDao().getByName(bitEURAsset.getName())== null){
+ db.cryptoCurrencyDao().insertCryptoCurrency(bitEURAsset);
+ }
+ long idCurrency = db.cryptoCurrencyDao().getByName(bitEURAsset.getName()).getId();
+ BitsharesAssetInfo info = new BitsharesAssetInfo(bitEURAsset);
+ info.setCryptoCurrencyId(idCurrency);
+ db.bitsharesAssetDao().insertBitsharesAssetInfo(info);
+
+ }
+
+ //This is for testing the equivalent values on the testnet TODO remove
+ if(db.bitsharesAssetDao().getBitsharesAssetInfoById(bitUSDAsset.getBitsharesId())== null){
+ if(db.cryptoCurrencyDao().getByName(bitUSDAsset.getName())== null){
+ db.cryptoCurrencyDao().insertCryptoCurrency(bitUSDAsset);
+ }
+ long idCurrency = db.cryptoCurrencyDao().getByName(bitUSDAsset.getName()).getId();
+ BitsharesAssetInfo info = new BitsharesAssetInfo(bitUSDAsset);
+ info.setCryptoCurrencyId(idCurrency);
+ db.bitsharesAssetDao().insertBitsharesAssetInfo(info);
+
+ }
+
+ //Next line is for use the bitshares main net
+ //CryptoNetManager.addCryptoNetURL(CryptoNet.BITSHARES,BITSHARES_URL);
+
Intent intent = new Intent(getApplicationContext(), CrystalWalletService.class);
startService(intent);
}
diff --git a/app/src/main/java/cy/agorise/crystalwallet/application/CrystalSecurityMonitor.java b/app/src/main/java/cy/agorise/crystalwallet/application/CrystalSecurityMonitor.java
new file mode 100644
index 0000000..852daa9
--- /dev/null
+++ b/app/src/main/java/cy/agorise/crystalwallet/application/CrystalSecurityMonitor.java
@@ -0,0 +1,109 @@
+package cy.agorise.crystalwallet.application;
+
+import android.app.Activity;
+
+import android.app.Application;
+import android.arch.lifecycle.LiveData;
+import android.arch.lifecycle.Observer;
+import android.arch.lifecycle.ViewModelProviders;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v4.app.FragmentActivity;
+
+import java.util.List;
+
+import cy.agorise.crystalwallet.activities.PinRequestActivity;
+import cy.agorise.crystalwallet.models.GeneralSetting;
+import cy.agorise.crystalwallet.viewmodels.GeneralSettingListViewModel;
+
+/**
+ * Created by Henry Varona on 27/1/2018.
+ */
+
+public class CrystalSecurityMonitor implements Application.ActivityLifecycleCallbacks {
+ private int numStarted = 0;
+ private String passwordEncrypted;
+
+ public CrystalSecurityMonitor(final FragmentActivity fragmentActivity){
+ GeneralSettingListViewModel generalSettingListViewModel = ViewModelProviders.of(fragmentActivity).get(GeneralSettingListViewModel.class);
+ LiveData> generalSettingsLiveData = generalSettingListViewModel.getGeneralSettingList();
+
+ generalSettingsLiveData.observe(fragmentActivity, new Observer>() {
+ @Override
+ public void onChanged(@Nullable List generalSettings) {
+ boolean founded = false;
+ passwordEncrypted = "";
+
+ if (generalSettings != null){
+ for (GeneralSetting generalSetting:generalSettings) {
+ if (generalSetting.getName().equals(GeneralSetting.SETTING_PASSWORD)){
+ founded = true;
+ if (!generalSetting.getValue().isEmpty()){
+ passwordEncrypted = generalSetting.getValue();
+ callPasswordRequest(fragmentActivity);
+ }
+ break;
+ }
+ }
+ }
+ }
+ });
+ }
+
+ @Override
+ public void onActivityStarted(Activity activity) {
+ if (numStarted == 0) {
+ if ((this.passwordEncrypted != null) && (!this.passwordEncrypted.equals(""))) {
+ callPasswordRequest(activity);
+ }
+ }
+ numStarted++;
+ }
+
+ @Override
+ public void onActivityStopped(Activity activity) {
+ numStarted--;
+ if (numStarted == 0) {
+ if ((this.passwordEncrypted != null) && (!this.passwordEncrypted.equals(""))) {
+ callPasswordRequest(activity);
+ }
+ }
+ }
+
+ public void callPasswordRequest(Activity activity){
+ if ((!activity.getIntent().hasExtra("ACTIVITY_TYPE")) || (!activity.getIntent().getStringExtra("ACTIVITY_TYPE").equals("PASSWORD_REQUEST"))) {
+ Intent intent = new Intent(activity, PinRequestActivity.class);
+ intent.putExtra("ACTIVITY_TYPE", "PASSWORD_REQUEST");
+ activity.startActivity(intent);
+ }
+ }
+
+ @Override
+ public void onActivityCreated(Activity activity, Bundle bundle) {
+ //
+ }
+
+ @Override
+ public void onActivityResumed(Activity activity) {
+ //
+ }
+
+ @Override
+ public void onActivityPaused(Activity activity) {
+ //
+ }
+
+ @Override
+ public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
+ //
+ }
+
+ @Override
+ public void onActivityDestroyed(Activity activity) {
+ //
+ }
+
+
+
+}
diff --git a/app/src/main/java/cy/agorise/crystalwallet/dao/ContactDao.java b/app/src/main/java/cy/agorise/crystalwallet/dao/ContactDao.java
index 77dd3fd..97d1e51 100644
--- a/app/src/main/java/cy/agorise/crystalwallet/dao/ContactDao.java
+++ b/app/src/main/java/cy/agorise/crystalwallet/dao/ContactDao.java
@@ -3,13 +3,16 @@ package cy.agorise.crystalwallet.dao;
import android.arch.lifecycle.LiveData;
import android.arch.paging.LivePagedListProvider;
import android.arch.persistence.room.Dao;
+import android.arch.persistence.room.Delete;
import android.arch.persistence.room.Insert;
import android.arch.persistence.room.OnConflictStrategy;
import android.arch.persistence.room.Query;
+import android.arch.persistence.room.Update;
import java.util.List;
import cy.agorise.crystalwallet.models.Contact;
+import cy.agorise.crystalwallet.models.ContactAddress;
import cy.agorise.crystalwallet.models.CryptoCoinTransaction;
/**
@@ -24,6 +27,33 @@ public interface ContactDao {
@Query("SELECT * FROM contact ORDER BY name ASC")
LivePagedListProvider contactsByName();
+ @Query("SELECT c.* FROM contact c WHERE c.id IN (SELECT DISTINCT(ca.contact_id) FROM contact_address ca WHERE ca.crypto_net == :cryptoNet) ORDER BY name ASC, email ASC")
+ LivePagedListProvider contactsByNameAndCryptoNet(String cryptoNet);
+
@Query("SELECT * FROM contact WHERE id = :id")
LiveData getById(long id);
+
+ @Query("SELECT count(*) FROM contact WHERE name = :name")
+ boolean existsByName(String name);
+
+ @Query("SELECT * FROM contact_address WHERE contact_id = :contactId")
+ LiveData> getContactAddresses(long contactId);
+
+ @Update(onConflict = OnConflictStrategy.ABORT)
+ public void update(Contact... contacts);
+
+ @Insert(onConflict = OnConflictStrategy.ABORT)
+ public long[] add(Contact... contacts);
+
+ @Insert(onConflict = OnConflictStrategy.ABORT)
+ public void addAddresses(ContactAddress... contactAddresses);
+
+ @Update(onConflict = OnConflictStrategy.REPLACE)
+ public void updateAddresses(ContactAddress... contactAddresses);
+
+ @Update(onConflict = OnConflictStrategy.REPLACE)
+ public void updateAddressesFields(ContactAddress... contactAddresses);
+
+ @Delete
+ public void deleteContacts(Contact... contacts);
}
diff --git a/app/src/main/java/cy/agorise/crystalwallet/dao/CryptoCurrencyDao.java b/app/src/main/java/cy/agorise/crystalwallet/dao/CryptoCurrencyDao.java
index 6826c4f..f821a35 100644
--- a/app/src/main/java/cy/agorise/crystalwallet/dao/CryptoCurrencyDao.java
+++ b/app/src/main/java/cy/agorise/crystalwallet/dao/CryptoCurrencyDao.java
@@ -1,5 +1,6 @@
package cy.agorise.crystalwallet.dao;
+import android.arch.lifecycle.LiveData;
import android.arch.persistence.room.Dao;
import android.arch.persistence.room.Insert;
import android.arch.persistence.room.OnConflictStrategy;
@@ -28,6 +29,9 @@ public interface CryptoCurrencyDao {
@Query("SELECT * FROM crypto_currency WHERE id IN (:ids)")
List getByIds(List ids);
+ @Query("SELECT * FROM crypto_currency WHERE name = :name")
+ LiveData getLiveDataByName(String name);
+
@Query("SELECT * FROM crypto_currency WHERE name = :name")
CryptoCurrency getByName(String name);
diff --git a/app/src/main/java/cy/agorise/crystalwallet/dao/CrystalDatabase.java b/app/src/main/java/cy/agorise/crystalwallet/dao/CrystalDatabase.java
index dc8b951..d0c79ba 100644
--- a/app/src/main/java/cy/agorise/crystalwallet/dao/CrystalDatabase.java
+++ b/app/src/main/java/cy/agorise/crystalwallet/dao/CrystalDatabase.java
@@ -10,6 +10,7 @@ import cy.agorise.crystalwallet.dao.converters.Converters;
import cy.agorise.crystalwallet.models.AccountSeed;
import cy.agorise.crystalwallet.models.BitsharesAssetInfo;
import cy.agorise.crystalwallet.models.Contact;
+import cy.agorise.crystalwallet.models.ContactAddress;
import cy.agorise.crystalwallet.models.CryptoCoinBalance;
import cy.agorise.crystalwallet.models.CryptoCoinTransaction;
import cy.agorise.crystalwallet.models.CryptoCurrency;
@@ -28,6 +29,7 @@ import cy.agorise.crystalwallet.models.GrapheneAccountInfo;
CryptoNetAccount.class,
CryptoCoinTransaction.class,
Contact.class,
+ ContactAddress.class,
CryptoCurrency.class,
CryptoCoinBalance.class,
GrapheneAccountInfo.class,
diff --git a/app/src/main/java/cy/agorise/crystalwallet/dao/TransactionDao.java b/app/src/main/java/cy/agorise/crystalwallet/dao/TransactionDao.java
index 5b6673a..151b9f9 100644
--- a/app/src/main/java/cy/agorise/crystalwallet/dao/TransactionDao.java
+++ b/app/src/main/java/cy/agorise/crystalwallet/dao/TransactionDao.java
@@ -7,6 +7,7 @@ import android.arch.persistence.room.Insert;
import android.arch.persistence.room.OnConflictStrategy;
import android.arch.persistence.room.Query;
+import java.util.Date;
import java.util.List;
import cy.agorise.crystalwallet.models.CryptoCoinTransaction;
@@ -32,6 +33,9 @@ public interface TransactionDao {
@Query("SELECT * FROM crypto_coin_transaction WHERE id = :id")
LiveData getById(long id);
+ @Query("SELECT * FROM crypto_coin_transaction WHERE date = :date and 'from' = :from and 'to' = :to and amount = :amount ")
+ CryptoCoinTransaction getByTransaction(Date date, String from, String to, long amount);
+
@Insert(onConflict = OnConflictStrategy.REPLACE)
public long[] insertTransaction(CryptoCoinTransaction... transactions);
diff --git a/app/src/main/java/cy/agorise/crystalwallet/fragments/ContactSelectionFragment.java b/app/src/main/java/cy/agorise/crystalwallet/fragments/ContactSelectionFragment.java
new file mode 100644
index 0000000..46576d5
--- /dev/null
+++ b/app/src/main/java/cy/agorise/crystalwallet/fragments/ContactSelectionFragment.java
@@ -0,0 +1,129 @@
+package cy.agorise.crystalwallet.fragments;
+
+import android.app.Dialog;
+import android.arch.lifecycle.LiveData;
+import android.arch.lifecycle.Observer;
+import android.arch.lifecycle.ViewModelProviders;
+import android.arch.paging.PagedList;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.design.widget.FloatingActionButton;
+import android.support.v4.app.DialogFragment;
+import android.support.v7.app.AlertDialog;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.text.Editable;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.ImageView;
+import android.widget.Spinner;
+import android.widget.TextView;
+
+import com.google.zxing.Result;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import butterknife.BindView;
+import butterknife.ButterKnife;
+import butterknife.OnClick;
+import butterknife.OnItemSelected;
+import butterknife.OnTextChanged;
+import cy.agorise.crystalwallet.R;
+import cy.agorise.crystalwallet.cryptonetinforequests.CryptoNetInfoRequestListener;
+import cy.agorise.crystalwallet.cryptonetinforequests.CryptoNetInfoRequests;
+import cy.agorise.crystalwallet.cryptonetinforequests.ValidateBitsharesSendRequest;
+import cy.agorise.crystalwallet.dao.CrystalDatabase;
+import cy.agorise.crystalwallet.enums.CryptoNet;
+import cy.agorise.crystalwallet.models.Contact;
+import cy.agorise.crystalwallet.models.CryptoCoinBalance;
+import cy.agorise.crystalwallet.models.CryptoCurrency;
+import cy.agorise.crystalwallet.models.CryptoNetAccount;
+import cy.agorise.crystalwallet.models.GrapheneAccount;
+import cy.agorise.crystalwallet.viewmodels.ContactListViewModel;
+import cy.agorise.crystalwallet.viewmodels.CryptoNetAccountListViewModel;
+import cy.agorise.crystalwallet.viewmodels.validators.SendTransactionValidator;
+import cy.agorise.crystalwallet.viewmodels.validators.UIValidatorListener;
+import cy.agorise.crystalwallet.viewmodels.validators.validationfields.ValidationField;
+import cy.agorise.crystalwallet.views.ContactSelectionListAdapter;
+import cy.agorise.crystalwallet.views.CryptoCurrencyAdapter;
+import cy.agorise.crystalwallet.views.CryptoNetAccountAdapter;
+import me.dm7.barcodescanner.zxing.ZXingScannerView;
+
+public class ContactSelectionFragment extends DialogFragment implements ContactSelectionListAdapter.ContactSelectionListAdapterListener{
+
+ private CryptoNet cryptoNet;
+ private CrystalDatabase db;
+ private AlertDialog.Builder builder;
+
+ @BindView(R.id.contactListView)
+ RecyclerView contactSelectionListView;
+
+ public static ContactSelectionFragment newInstance(CryptoNet cryptoNet) {
+ ContactSelectionFragment f = new ContactSelectionFragment();
+
+ // Supply num input as an argument.
+ Bundle args = new Bundle();
+ args.putString("CRYPTO_NET", cryptoNet.name());
+ f.setArguments(args);
+
+ return f;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ }
+
+ @NonNull
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ //AlertDialog.Builder
+ builder = new AlertDialog.Builder(getActivity(), R.style.SendTransactionTheme);
+
+ LayoutInflater inflater = getActivity().getLayoutInflater();
+ View view = inflater.inflate(R.layout.contact_list, null);
+ ButterKnife.bind(this, view);
+
+ final LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this.getContext());
+ this.contactSelectionListView.setLayoutManager(linearLayoutManager);
+ //Prevents the list to start again when scrolling to the end
+ this.contactSelectionListView.setNestedScrollingEnabled(false);
+ final ContactSelectionListAdapter contactSelectionListAdapter = new ContactSelectionListAdapter();
+ contactSelectionListAdapter.setListener(this);
+ contactSelectionListView.setAdapter(contactSelectionListAdapter);
+
+ this.cryptoNet = CryptoNet.valueOf(getArguments().getString("CRYPTO_NET"));
+ if (this.cryptoNet != null) {
+ ContactListViewModel contactListViewModel = ViewModelProviders.of(this).get(ContactListViewModel.class);
+ contactListViewModel.init(this.cryptoNet);
+ LiveData> contactsLiveData = contactListViewModel.getContactList();
+
+ contactsLiveData.observe(this, new Observer>() {
+ @Override
+ public void onChanged(@Nullable PagedList contacts) {
+ contactSelectionListAdapter.setList(contacts);
+ }
+ });
+ }
+
+ return builder.setView(view).create();
+ }
+
+ @Override
+ public void onContactSelected(Contact contact) {
+ Intent result = new Intent();
+ result.putExtra("CONTACT_ID", contact.getId());
+ getTargetFragment().onActivityResult(getTargetRequestCode(), 1, result);
+ this.dismiss();
+ }
+}
diff --git a/app/src/main/java/cy/agorise/crystalwallet/fragments/ContactsFragment.java b/app/src/main/java/cy/agorise/crystalwallet/fragments/ContactsFragment.java
index 558d81b..6f34284 100644
--- a/app/src/main/java/cy/agorise/crystalwallet/fragments/ContactsFragment.java
+++ b/app/src/main/java/cy/agorise/crystalwallet/fragments/ContactsFragment.java
@@ -1,14 +1,28 @@
package cy.agorise.crystalwallet.fragments;
+import android.arch.lifecycle.LiveData;
+import android.arch.lifecycle.Observer;
+import android.arch.lifecycle.ViewModelProviders;
+import android.arch.paging.PagedList;
import android.os.Bundle;
+import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import butterknife.BindView;
+import butterknife.ButterKnife;
import cy.agorise.crystalwallet.R;
+import cy.agorise.crystalwallet.models.Contact;
+import cy.agorise.crystalwallet.viewmodels.ContactListViewModel;
+import cy.agorise.crystalwallet.views.ContactListView;
public class ContactsFragment extends Fragment {
+
+ @BindView(R.id.vContactListView)
+ ContactListView contactListView;
+
public ContactsFragment() {
// Required empty public constructor
}
@@ -29,6 +43,20 @@ public class ContactsFragment extends Fragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
- return inflater.inflate(R.layout.fragment_contacts, container, false);
+ View v = inflater.inflate(R.layout.fragment_contacts, container, false);
+ ButterKnife.bind(this, v);
+
+ ContactListViewModel contactListViewModel = ViewModelProviders.of(this).get(ContactListViewModel.class);
+ contactListViewModel.init();
+ LiveData> contactsLiveData = contactListViewModel.getContactList();
+
+ contactsLiveData.observe(this, new Observer>() {
+ @Override
+ public void onChanged(@Nullable PagedList contacts) {
+ contactListView.setData(contacts);
+ }
+ });
+
+ return v;
}
}
diff --git a/app/src/main/java/cy/agorise/crystalwallet/fragments/GeneralSettingsFragment.java b/app/src/main/java/cy/agorise/crystalwallet/fragments/GeneralSettingsFragment.java
index 7be6e28..e91e848 100644
--- a/app/src/main/java/cy/agorise/crystalwallet/fragments/GeneralSettingsFragment.java
+++ b/app/src/main/java/cy/agorise/crystalwallet/fragments/GeneralSettingsFragment.java
@@ -1,19 +1,44 @@
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.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.Spinner;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Currency;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+
+import butterknife.BindView;
import butterknife.ButterKnife;
+import butterknife.OnItemSelected;
import cy.agorise.crystalwallet.R;
+import cy.agorise.crystalwallet.models.GeneralSetting;
+import cy.agorise.crystalwallet.viewmodels.GeneralSettingListViewModel;
/**
* Created by xd on 12/28/17.
*/
public class GeneralSettingsFragment extends Fragment {
+
+ private HashMap countriesMap;
+ private GeneralSettingListViewModel generalSettingListViewModel;
+ private LiveData> generalSettingListLiveData;
+
+ @BindView (R.id.spTaxableCountry)
+ Spinner spTaxableCountry;
+
public GeneralSettingsFragment() {
// Required empty public constructor
}
@@ -37,6 +62,83 @@ public class GeneralSettingsFragment extends Fragment {
View v = inflater.inflate(R.layout.fragment_general_settings, container, false);
ButterKnife.bind(this, v);
+ generalSettingListViewModel = ViewModelProviders.of(this).get(GeneralSettingListViewModel.class);
+ generalSettingListLiveData = generalSettingListViewModel.getGeneralSettingList();
+
+ // Initializes the countries spinner
+ countriesMap = new HashMap();
+ String[] countryCodeList = Locale.getISOCountries();
+ ArrayList countryAndCurrencyList = new ArrayList();
+ String countryAndCurrencyLabel = "";
+ for (String countryCode : countryCodeList) {
+ Locale locale = new Locale("", countryCode);
+ try {
+ Currency currency = Currency.getInstance(locale);
+ countryAndCurrencyLabel = locale.getDisplayCountry() + " (" + currency.getCurrencyCode() + ")";
+ countryAndCurrencyList.add(countryAndCurrencyLabel);
+ countriesMap.put(countryCode, countryAndCurrencyLabel);
+ countriesMap.put(countryAndCurrencyLabel, countryCode);
+ } catch (Exception e) {
+
+ }
+ }
+ Collections.sort(countryAndCurrencyList);
+ countryAndCurrencyList.add(0,"SELECT COUNTRY");
+ ArrayAdapter countryAdapter = new ArrayAdapter(this.getContext(), android.R.layout.simple_spinner_item, countryAndCurrencyList);
+ spTaxableCountry.setAdapter(countryAdapter);
+
+ //Observes the general settings data
+ generalSettingListLiveData.observe(this, new Observer>() {
+ @Override
+ public void onChanged(@Nullable List generalSettings) {
+ loadSettings(generalSettings);
+ }
+ });
+
return v;
}
+
+ public GeneralSetting getSetting(String name){
+ for (GeneralSetting generalSetting:this.generalSettingListLiveData.getValue()) {
+ if (generalSetting.getName().equals(name)) {
+ return generalSetting;
+ }
+ }
+
+ return null;
+ }
+
+ @OnItemSelected(R.id.spTaxableCountry)
+ void onItemSelected(int position) {
+ if (position != 0) {
+ GeneralSetting generalSettingCountryCode = this.getSetting(GeneralSetting.SETTING_NAME_PREFERED_COUNTRY);
+ GeneralSetting generalSettingCurrency = this.getSetting(GeneralSetting.SETTING_NAME_PREFERED_CURRENCY);
+
+ if (generalSettingCountryCode == null){
+ generalSettingCountryCode = new GeneralSetting();
+ generalSettingCountryCode.setName(GeneralSetting.SETTING_NAME_PREFERED_COUNTRY);
+ }
+ if (generalSettingCurrency == null){
+ generalSettingCurrency = new GeneralSetting();
+ generalSettingCurrency.setName(GeneralSetting.SETTING_NAME_PREFERED_CURRENCY);
+ }
+
+ String countryCode = countriesMap.get((String) spTaxableCountry.getSelectedItem());
+ Locale locale = new Locale("", countryCode);
+ Currency currency = Currency.getInstance(locale);
+
+ generalSettingCountryCode.setValue(countryCode);
+ generalSettingCurrency.setValue(currency.getCurrencyCode());
+ this.generalSettingListViewModel.saveGeneralSettings(generalSettingCountryCode, generalSettingCurrency);
+ }
+ }
+
+ public void loadSettings(List generalSettings){
+ for (GeneralSetting generalSetting:generalSettings) {
+ if (generalSetting.getName().equals(GeneralSetting.SETTING_NAME_PREFERED_COUNTRY)){
+ String preferedCountryCode = generalSetting.getValue();
+ spTaxableCountry.setSelection(((ArrayAdapter)spTaxableCountry.getAdapter()).getPosition(countriesMap.get(preferedCountryCode)));
+ }
+ }
+ }
}
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 52e2c55..6c75211 100644
--- a/app/src/main/java/cy/agorise/crystalwallet/fragments/PinSecurityFragment.java
+++ b/app/src/main/java/cy/agorise/crystalwallet/fragments/PinSecurityFragment.java
@@ -1,19 +1,60 @@
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;
+import java.util.List;
+
+import butterknife.BindView;
import butterknife.ButterKnife;
+import butterknife.OnTextChanged;
import cy.agorise.crystalwallet.R;
+import cy.agorise.crystalwallet.activities.CreateSeedActivity;
+import cy.agorise.crystalwallet.dao.CrystalDatabase;
+import cy.agorise.crystalwallet.models.GeneralSetting;
+import cy.agorise.crystalwallet.util.PasswordManager;
+import cy.agorise.crystalwallet.viewmodels.GeneralSettingListViewModel;
+import cy.agorise.crystalwallet.viewmodels.validators.PinSecurityValidator;
+import cy.agorise.crystalwallet.viewmodels.validators.UIValidatorListener;
+import cy.agorise.crystalwallet.viewmodels.validators.validationfields.ValidationField;
/**
* Created by xd on 1/18/18.
*/
-public class PinSecurityFragment extends Fragment {
+public class PinSecurityFragment extends Fragment implements UIValidatorListener {
+
+ @BindView(R.id.tvCurrentPin)
+ TextView tvCurrentPin;
+ @BindView(R.id.etCurrentPin)
+ EditText etCurrentPin;
+ @BindView(R.id.etNewPin)
+ EditText etNewPin;
+ @BindView(R.id.etConfirmPin)
+ EditText etConfirmPin;
+
+ @BindView(R.id.tvCurrentPinError)
+ TextView tvCurrentPinError;
+ @BindView(R.id.tvNewPinError)
+ TextView tvNewPinError;
+ @BindView(R.id.tvConfirmPinError)
+ TextView tvConfirmPinError;
+
+ GeneralSettingListViewModel generalSettingListViewModel;
+ GeneralSetting passwordGeneralSetting;
+ PinSecurityValidator pinSecurityValidator;
public PinSecurityFragment() {
// Required empty public constructor
@@ -33,6 +74,140 @@ public class PinSecurityFragment extends Fragment {
View v = inflater.inflate(R.layout.fragment_pin_security, container, false);
ButterKnife.bind(this, v);
+ generalSettingListViewModel = ViewModelProviders.of(this).get(GeneralSettingListViewModel.class);
+ LiveData> generalSettingsLiveData = generalSettingListViewModel.getGeneralSettingList();
+
+ pinSecurityValidator = new PinSecurityValidator(this.getContext(), etCurrentPin, etNewPin, etConfirmPin);
+ pinSecurityValidator.setListener(this);
+
+ generalSettingsLiveData.observe(this, new Observer>() {
+ @Override
+ public void onChanged(@Nullable List generalSettings) {
+ boolean founded = false;
+
+ if (generalSettings != null){
+ for (GeneralSetting generalSetting:generalSettings) {
+ if (generalSetting.getName().equals(GeneralSetting.SETTING_PASSWORD)){
+ founded = true;
+ if (!generalSetting.getValue().isEmpty()){
+ passwordGeneralSetting = generalSetting;
+ showCurrentPinUI(true);
+ } else {
+ showCurrentPinUI(false);
+ }
+ break;
+ }
+ }
+ if (!founded){
+ showCurrentPinUI(false);
+ }
+ } else {
+ showCurrentPinUI(false);
+ }
+ }
+ });
+
return v;
}
+
+ public void showCurrentPinUI(Boolean visible){
+ if (visible){
+ tvCurrentPin.setVisibility(View.VISIBLE);
+ etCurrentPin.setVisibility(View.VISIBLE);
+ } else {
+ tvCurrentPin.setVisibility(View.GONE);
+ etCurrentPin.setVisibility(View.GONE);
+ }
+ }
+
+ @OnTextChanged(value = R.id.etCurrentPin,
+ callback = OnTextChanged.Callback.AFTER_TEXT_CHANGED)
+ void afterCurrentPinChanged(Editable editable) {
+ this.pinSecurityValidator.validate();
+ }
+
+ @OnTextChanged(value = R.id.etNewPin,
+ callback = OnTextChanged.Callback.AFTER_TEXT_CHANGED)
+ void afterNewPinChanged(Editable editable) {
+ this.pinSecurityValidator.validate();
+ }
+
+ @OnTextChanged(value = R.id.etConfirmPin,
+ callback = OnTextChanged.Callback.AFTER_TEXT_CHANGED)
+ void afterConfirmPinChanged(Editable editable) {
+ this.pinSecurityValidator.validate();
+ }
+
+ public void clearFields(){
+ if (!this.etCurrentPin.getText().toString().equals("")) {
+ this.etCurrentPin.setText("");
+ }
+ if (!this.etNewPin.getText().toString().equals("")) {
+ this.etNewPin.setText("");
+ }
+ if (!this.etConfirmPin.getText().toString().equals("")) {
+ this.etConfirmPin.setText("");
+ }
+ }
+
+ @Override
+ public void onValidationSucceeded(final ValidationField field) {
+ final PinSecurityFragment fragment = this;
+
+ this.getActivity().runOnUiThread(new Runnable() {
+ public void run() {
+
+ if (field.getView() == etCurrentPin) {
+ tvCurrentPinError.setText("");
+ } else if (field.getView() == etNewPin){
+ tvNewPinError.setText("");
+ } else if (field.getView() == etConfirmPin){
+ tvConfirmPinError.setText("");
+ }
+
+ if (pinSecurityValidator.isValid()){
+ CharSequence text = "Your password has been sucessfully changed!";
+ int duration = Toast.LENGTH_SHORT;
+
+ Toast toast = Toast.makeText(getContext(), text, duration);
+ toast.show();
+ //showCurrentPinUI(true);
+
+ savePassword(etNewPin.getText().toString());
+
+
+ clearFields();
+ }
+ }
+ });
+ }
+
+ public void savePassword(String password) {
+ String passwordEncripted = PasswordManager.encriptPassword(password);
+
+ if (passwordGeneralSetting == null) {
+ passwordGeneralSetting = new GeneralSetting();
+ passwordGeneralSetting.setName(GeneralSetting.SETTING_PASSWORD);
+ }
+
+ passwordGeneralSetting.setValue(passwordEncripted);
+ generalSettingListViewModel.saveGeneralSetting(passwordGeneralSetting);
+ }
+
+ @Override
+ public void onValidationFailed(final ValidationField field) {
+ this.getActivity().runOnUiThread(new Runnable() {
+
+ @Override
+ public void run() {
+ if (field.getView() == etCurrentPin) {
+ tvCurrentPinError.setText(field.getMessage());
+ } else if (field.getView() == etNewPin){
+ tvNewPinError.setText(field.getMessage());
+ } else if (field.getView() == etConfirmPin){
+ tvConfirmPinError.setText(field.getMessage());
+ }
+ }
+ });
+ }
}
diff --git a/app/src/main/java/cy/agorise/crystalwallet/fragments/ReceiveTransactionFragment.java b/app/src/main/java/cy/agorise/crystalwallet/fragments/ReceiveTransactionFragment.java
index 3e26231..db94f95 100644
--- a/app/src/main/java/cy/agorise/crystalwallet/fragments/ReceiveTransactionFragment.java
+++ b/app/src/main/java/cy/agorise/crystalwallet/fragments/ReceiveTransactionFragment.java
@@ -4,6 +4,7 @@ import android.app.Dialog;
import android.app.LauncherActivity;
import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.Observer;
+import android.arch.lifecycle.ViewModelProviders;
import android.content.DialogInterface;
import android.graphics.Bitmap;
import android.os.Bundle;
@@ -31,6 +32,8 @@ import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import butterknife.OnClick;
+import cy.agorise.crystalwallet.viewmodels.CryptoNetAccountListViewModel;
+import cy.agorise.crystalwallet.views.CryptoNetAccountAdapter;
import cy.agorise.graphenej.Invoice;
import java.util.ArrayList;
import java.util.List;
@@ -57,6 +60,8 @@ public class ReceiveTransactionFragment extends DialogFragment implements UIVali
ReceiveTransactionValidator receiveTransactionValidator;
+ @BindView(R.id.spTo)
+ Spinner spTo;
@BindView(R.id.etAmount)
EditText etAmount;
@BindView(R.id.tvAmountError)
@@ -145,6 +150,12 @@ public class ReceiveTransactionFragment extends DialogFragment implements UIVali
receiveTransactionValidator = new ReceiveTransactionValidator(this.getContext(), this.cryptoNetAccount, spAsset, etAmount);
receiveTransactionValidator.setListener(this);
+
+ CryptoNetAccountListViewModel cryptoNetAccountListViewModel = ViewModelProviders.of(this).get(CryptoNetAccountListViewModel.class);
+ List cryptoNetAccounts = cryptoNetAccountListViewModel.getCryptoNetAccountList();
+ CryptoNetAccountAdapter toSpinnerAdapter = new CryptoNetAccountAdapter(this.getContext(), android.R.layout.simple_spinner_item, cryptoNetAccounts);
+ spTo.setAdapter(toSpinnerAdapter);
+ spTo.setSelection(0);
}
builder.setView(view);
@@ -199,6 +210,11 @@ public class ReceiveTransactionFragment extends DialogFragment implements UIVali
}, 400);
}
+ @OnItemSelected(R.id.spTo)
+ public void afterToSelected(Spinner spinner, int position) {
+ this.receiveTransactionValidator.validate();
+ }
+
@OnTextChanged(value = R.id.etAmount,
callback = OnTextChanged.Callback.AFTER_TEXT_CHANGED)
void afterAmountChanged(Editable editable) {
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 619d688..c73b340 100644
--- a/app/src/main/java/cy/agorise/crystalwallet/fragments/SendTransactionFragment.java
+++ b/app/src/main/java/cy/agorise/crystalwallet/fragments/SendTransactionFragment.java
@@ -5,6 +5,7 @@ import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.Observer;
import android.arch.lifecycle.ViewModelProviders;
import android.content.DialogInterface;
+import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.support.annotation.NonNull;
@@ -12,6 +13,7 @@ import android.support.annotation.Nullable;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.text.Editable;
@@ -23,13 +25,19 @@ import android.view.Window;
import android.view.animation.LinearInterpolator;
import android.widget.Button;
import android.widget.EditText;
+import android.widget.ImageView;
import android.widget.Spinner;
import android.widget.TextView;
+import android.widget.Toast;
import com.google.zxing.Result;
+import java.math.RoundingMode;
+import java.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
import java.util.ArrayList;
import java.util.List;
+import java.util.Locale;
import butterknife.BindView;
import butterknife.ButterKnife;
@@ -41,10 +49,13 @@ import cy.agorise.crystalwallet.cryptonetinforequests.CryptoNetInfoRequestListen
import cy.agorise.crystalwallet.cryptonetinforequests.CryptoNetInfoRequests;
import cy.agorise.crystalwallet.cryptonetinforequests.ValidateBitsharesSendRequest;
import cy.agorise.crystalwallet.dao.CrystalDatabase;
+import cy.agorise.crystalwallet.models.Contact;
+import cy.agorise.crystalwallet.models.ContactAddress;
import cy.agorise.crystalwallet.models.CryptoCoinBalance;
import cy.agorise.crystalwallet.models.CryptoCurrency;
import cy.agorise.crystalwallet.models.CryptoNetAccount;
import cy.agorise.crystalwallet.models.GrapheneAccount;
+import cy.agorise.crystalwallet.viewmodels.ContactViewModel;
import cy.agorise.crystalwallet.viewmodels.CryptoNetAccountListViewModel;
import cy.agorise.crystalwallet.viewmodels.CryptoNetAccountViewModel;
import cy.agorise.crystalwallet.viewmodels.validators.SendTransactionValidator;
@@ -52,6 +63,8 @@ import cy.agorise.crystalwallet.viewmodels.validators.UIValidatorListener;
import cy.agorise.crystalwallet.viewmodels.validators.validationfields.ValidationField;
import cy.agorise.crystalwallet.views.CryptoCurrencyAdapter;
import cy.agorise.crystalwallet.views.CryptoNetAccountAdapter;
+import cy.agorise.graphenej.Invoice;
+import cy.agorise.graphenej.LineItem;
import me.dm7.barcodescanner.zxing.ZXingScannerView;
public class SendTransactionFragment extends DialogFragment implements UIValidatorListener, ZXingScannerView.ResultHandler {
@@ -82,6 +95,9 @@ public class SendTransactionFragment extends DialogFragment implements UIValidat
FloatingActionButton btnSend;
@BindView(R.id.btnCancel)
TextView btnCancel;
+ @BindView(R.id.ivPeople)
+ ImageView ivPeople;
+ CryptoCurrencyAdapter assetAdapter;
Button btnScanQrCode;
@@ -145,7 +161,7 @@ public class SendTransactionFragment extends DialogFragment implements UIValidat
}
List cryptoCurrencyList = db.cryptoCurrencyDao().getByIds(assetIds);
- CryptoCurrencyAdapter assetAdapter = new CryptoCurrencyAdapter(getContext(), android.R.layout.simple_spinner_item, cryptoCurrencyList);
+ assetAdapter = new CryptoCurrencyAdapter(getContext(), android.R.layout.simple_spinner_item, cryptoCurrencyList);
spAsset.setAdapter(assetAdapter);
}
});
@@ -222,6 +238,50 @@ public class SendTransactionFragment extends DialogFragment implements UIValidat
this.sendTransactionValidator.validate();
}
+ @OnClick(R.id.ivPeople)
+ public void searchContact(){
+ FragmentTransaction ft = this.getActivity().getSupportFragmentManager().beginTransaction();
+ Fragment prev = this.getActivity().getSupportFragmentManager().findFragmentByTag("ContactSelectionDialog");
+ if (prev != null) {
+ ft.remove(prev);
+ }
+ ft.addToBackStack(null);
+
+ // Show a contact selection list
+ ContactSelectionFragment contactSelectionFragment = ContactSelectionFragment.newInstance(this.cryptoNetAccount.getCryptoNet());
+ contactSelectionFragment.setTargetFragment(this, 1);
+ contactSelectionFragment.show(ft, "ContactSelectionDialog");
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode,resultCode,data);
+
+ if(requestCode == 1) {
+ if(resultCode == 1) {
+ long contactId = data.getLongExtra("CONTACT_ID",-1);
+ if (contactId > -1){
+ ContactViewModel contactViewModel = ViewModelProviders.of(this).get(ContactViewModel.class);
+ contactViewModel.init(contactId);
+ LiveData> contactAddressesLiveData = contactViewModel.getContactAddresses();
+
+ contactAddressesLiveData.observe(this, new Observer>() {
+ @Override
+ public void onChanged(@Nullable List contactAddresses) {
+ if (contactAddresses != null) {
+ for (ContactAddress contactAddress : contactAddresses) {
+ if (contactAddress.getCryptoNet() == cryptoNetAccount.getCryptoNet()) {
+ etTo.setText(contactAddress.getAddress());
+ }
+ }
+ }
+ }
+ });
+ }
+ }
+ }
+ }
+
@OnClick(R.id.btnCancel)
public void cancel(){
this.dismiss();
@@ -317,6 +377,27 @@ public class SendTransactionFragment extends DialogFragment implements UIValidat
@Override
public void handleResult(Result result) {
+ Invoice invoice = Invoice.fromQrCode(result.getText());
+
+ etTo.setText(invoice.getTo());
+
+ for (int i=0;i operationList = new ArrayList<>();
- operationList.add(builder.build());
+ }
- ECKey privateKey = sendRequest.getSourceAccount().getActiveKey(sendRequest.getContext());
+ private void validateSendRequest(final ValidateBitsharesSendRequest sendRequest , final String idAsset){
+ final Asset feeAsset = new Asset(idAsset);
+ final UserAccount fromUserAccount =new UserAccount(sendRequest.getSourceAccount().getAccountId());
- Transaction transaction = new Transaction(privateKey, null, operationList);
- transaction.setChainId(BITSHARES_TESTNET_CHAIN_ID);
+ //TODO cached to accounts
+ this.getAccountInfoByName(sendRequest.getToAccount(), new ManagerRequest() {
- ApiRequest transactionRequest = new ApiRequest(0, new ApiRequestListener() {
@Override
- public void success(Object answer, int idPetition) {
- sendRequest.setSend(true);
+ public void success(Object answer) {
+ GrapheneAccount toUserGrapheneAccount = (GrapheneAccount) answer;
+ UserAccount toUserAccount = new UserAccount(toUserGrapheneAccount.getAccountId());
+ TransferOperationBuilder builder = new TransferOperationBuilder()
+ .setSource(fromUserAccount)
+ .setDestination(toUserAccount)
+ .setTransferAmount(new AssetAmount(UnsignedLong.valueOf(sendRequest.getAmount()), new Asset(idAsset)))
+ .setFee(new AssetAmount(UnsignedLong.valueOf(0), feeAsset));
+ if(sendRequest.getMemo() != null) {
+ //builder.setMemo(new Memo(fromUserAccount,toUserAccount,0,sendRequest.getMemo().getBytes()));
+ //TODO memo
+ System.out.println("transaction has memo");
+ }
+ ArrayList operationList = new ArrayList<>();
+ operationList.add(builder.build());
+
+ ECKey privateKey = sendRequest.getSourceAccount().getActiveKey(sendRequest.getContext());
+
+ Transaction transaction = new Transaction(privateKey, null, operationList);
+ transaction.setChainId(CryptoNetManager.getChaindId(CryptoNet.BITSHARES));
+
+ ApiRequest transactionRequest = new ApiRequest(0, new ApiRequestListener() {
+ @Override
+ public void success(Object answer, int idPetition) {
+ sendRequest.setSend(true);
+ }
+
+ @Override
+ public void fail(int idPetition) {
+ sendRequest.setSend(false);
+ }
+ });
+
+ GrapheneApiGenerator.broadcastTransaction(transaction,feeAsset, transactionRequest);
}
@Override
- public void fail(int idPetition) {
- sendRequest.setSend(false);
+ public void fail() {
+ //TODO bad user to user account
}
});
- GrapheneApiGenerator.broadcastTransaction(transaction,feeAsset, transactionRequest);
}
/**
* Returns the account info from a graphene id
* @param grapheneId The graphene id of the account
*/
- private GrapheneAccount getAccountInfoById(String grapheneId){
- final Object SYNC = new Object();
- long timeout = 60000;
+ private void getAccountInfoById(String grapheneId, ManagerRequest request){
- AccountIdOrNameListener listener = new AccountIdOrNameListener(SYNC);
+ AccountIdOrNameListener listener = new AccountIdOrNameListener(request);
- ApiRequest request = new ApiRequest(0, listener);
- GrapheneApiGenerator.getAccountById(grapheneId,request);
-
- long cTime = System.currentTimeMillis();
-
- while(!listener.ready && (System.currentTimeMillis()-cTime) < timeout){
- synchronized (SYNC){
- try {
- SYNC.wait(100);
- } catch (InterruptedException ignore) {}
- }
- }
-
- return listener.account;
+ ApiRequest accountRequest = new ApiRequest(0, listener);
+ GrapheneApiGenerator.getAccountById(grapheneId,accountRequest);
}
/**
* Gets account info by its name
* @param grapheneName The name of the account to retrieve
*/
- private GrapheneAccount getAccountInfoByName(String grapheneName){
- final Object SYNC = new Object();
- long timeout = 60000;
+ private void getAccountInfoByName(String grapheneName, ManagerRequest request){
- AccountIdOrNameListener listener = new AccountIdOrNameListener(SYNC);
+ AccountIdOrNameListener listener = new AccountIdOrNameListener(request);
- ApiRequest request = new ApiRequest(0, listener);
- GrapheneApiGenerator.getAccountByName(grapheneName,request);
+ ApiRequest accountRequest = new ApiRequest(0, listener);
+ GrapheneApiGenerator.getAccountByName(grapheneName,accountRequest);
- long cTime = System.currentTimeMillis();
-
- while(!listener.ready && (System.currentTimeMillis()-cTime) < timeout){
- synchronized (SYNC){
- try {
- SYNC.wait(100);
- } catch (InterruptedException ignore) {}
- }
- }
-
- return listener.account;
}
//TODO expand function to be more generic
- private String getAssetInfoByName(String assetName){
- final Object SYNC = new Object();
- long timeout = 60000;
+ private void getAssetInfoByName(String assetName, ManagerRequest request){
- AssetIdOrNameListener nameListener = new AssetIdOrNameListener(SYNC);
- ApiRequest request = new ApiRequest(0, nameListener);
+ AssetIdOrNameListener nameListener = new AssetIdOrNameListener(request);
+ ApiRequest assetRequest = new ApiRequest(0, nameListener);
ArrayList assetNames = new ArrayList<>();
assetNames.add(assetName);
- GrapheneApiGenerator.getAssetByName(assetNames, request);
-
- long cTime = System.currentTimeMillis();
-
- while(!nameListener.ready && (System.currentTimeMillis()-cTime) < timeout){
- synchronized (SYNC){
- try {
- SYNC.wait(100);
- } catch (InterruptedException ignore) {}
- }
- }
-
- return nameListener.asset.getBitsharesId();
+ GrapheneApiGenerator.getAssetByName(assetNames, assetRequest);
}
@@ -393,7 +462,7 @@ public class BitsharesAccountManager implements CryptoAccountManager, CryptoNetI
* @param idAccount database id of the account
* @param context The android context of this application
*/
- public static void refreshAccountTransactions(long idAccount, Context context){
+ private static void refreshAccountTransactions(long idAccount, Context context){
CrystalDatabase db = CrystalDatabase.getAppDatabase(context);
List transactions = db.transactionDao().getByIdAccount(idAccount);
CryptoNetAccount account = db.cryptoNetAccountDao().getById(idAccount);
@@ -449,23 +518,22 @@ public class BitsharesAccountManager implements CryptoAccountManager, CryptoNetI
}
/**
- * Handles the succes request of the transaction, if the amount of transaction is equal to the limit, ask for more transaction
+ * Handles the success request of the transaction, if the amount of transaction is equal to the limit, ask for more transaction
* @param answer The answer, this object depends on the kind of request is made to the api
* @param idPetition the id of the ApiRequest petition
*/
@Override
public void success(Object answer, int idPetition) {
List transfers = (List) answer ;
- for(HistoricalTransfer transfer : transfers) {
+ for(final HistoricalTransfer transfer : transfers) {
if (transfer.getOperation() != null){
- CryptoCoinTransaction transaction = new CryptoCoinTransaction();
+ final CryptoCoinTransaction transaction = new CryptoCoinTransaction();
transaction.setAccountId(account.getId());
transaction.setAmount(transfer.getOperation().getAssetAmount().getAmount().longValue());
BitsharesAssetInfo info = db.bitsharesAssetDao().getBitsharesAssetInfoById(transfer.getOperation().getAssetAmount().getAsset().getObjectId());
if (info == null) {
//The cryptoCurrency is not in the database, queringfor its data
- final Object SYNC = new Object(); //Object to syn the answer
ApiRequest assetRequest = new ApiRequest(0, new ApiRequestListener() {
@Override
public void success(Object answer, int idPetition) {
@@ -476,39 +544,24 @@ public class BitsharesAccountManager implements CryptoAccountManager, CryptoNetI
info.setCryptoCurrencyId(idCryptoCurrency);
asset.setId((int)idCryptoCurrency);
db.bitsharesAssetDao().insertBitsharesAssetInfo(info);
+ saveTransaction(transaction,info,transfer);
}
- synchronized (SYNC){
- SYNC.notifyAll();
- }
+
}
@Override
public void fail(int idPetition) {
- synchronized (SYNC){
- SYNC.notifyAll();
- }
+ //TODO Error
}
});
ArrayList assets = new ArrayList<>();
assets.add(transfer.getOperation().getAssetAmount().getAsset().getObjectId());
GrapheneApiGenerator.getAssetById(assets,assetRequest);
- synchronized (SYNC){
- try {SYNC.wait(60000);} catch (InterruptedException ignore) {}
- }
- info = db.bitsharesAssetDao().getBitsharesAssetInfoById(transfer.getOperation().getAssetAmount().getAsset().getObjectId());
+ }else{
+ saveTransaction(transaction,info,transfer);
}
- if( info == null){
- //We couldn't retrieve the cryptocurrency
- return;
- }
- transaction.setIdCurrency((int)info.getCryptoCurrencyId());
- transaction.setConfirmed(true); //graphene transaction are always confirmed
- transaction.setFrom(transfer.getOperation().getFrom().getObjectId());
- transaction.setInput(!transfer.getOperation().getFrom().getObjectId().equals(account.getAccountId()));
- transaction.setTo(transfer.getOperation().getTo().getObjectId());
- GrapheneApiGenerator.getBlockHeaderTime(transfer.getBlockNum(), new ApiRequest(0, new GetTransactionDate(transaction, db.transactionDao())));
}
}
if(transfers.size()>= limit){
@@ -524,6 +577,16 @@ public class BitsharesAccountManager implements CryptoAccountManager, CryptoNetI
public void fail(int idPetition) {
}
+
+ private void saveTransaction(CryptoCoinTransaction transaction, BitsharesAssetInfo info, HistoricalTransfer transfer){
+ transaction.setIdCurrency((int)info.getCryptoCurrencyId());
+ transaction.setConfirmed(true); //graphene transaction are always confirmed
+ transaction.setFrom(transfer.getOperation().getFrom().getObjectId());
+ transaction.setInput(!transfer.getOperation().getFrom().getObjectId().equals(account.getAccountId()));
+ transaction.setTo(transfer.getOperation().getTo().getObjectId());
+
+ GrapheneApiGenerator.getBlockHeaderTime(transfer.getBlockNum(), new ApiRequest(0, new GetTransactionDate(transaction, db.transactionDao())));
+ }
}
/**
@@ -547,6 +610,7 @@ public class BitsharesAccountManager implements CryptoAccountManager, CryptoNetI
GrapheneApiGenerator.getEquivalentValue(fromAsset.getBitsharesId(),toAsset.getBitsharesId(), getEquivalentRequest);
}else{
//TODO error
+ System.out.println("Equivalen Value error ");
}
}
@@ -554,13 +618,12 @@ public class BitsharesAccountManager implements CryptoAccountManager, CryptoNetI
* Class to retrieve the account id or the account name, if one of those is missing
*/
private class AccountIdOrNameListener implements ApiRequestListener{
- final Object SYNC;
- boolean ready = false;
+ final ManagerRequest request;
GrapheneAccount account;
- AccountIdOrNameListener(Object SYNC) {
- this.SYNC = SYNC;
+ AccountIdOrNameListener(ManagerRequest request) {
+ this.request = request;
}
@Override
@@ -572,18 +635,12 @@ public class BitsharesAccountManager implements CryptoAccountManager, CryptoNetI
account.setName(props.name);
}
- synchronized (SYNC){
- ready = true;
- SYNC.notifyAll();
- }
+ request.success(account);
}
@Override
public void fail(int idPetition) {
- synchronized (SYNC){
- ready = true;
- SYNC.notifyAll();
- }
+ request.fail();
}
}
@@ -591,36 +648,27 @@ public class BitsharesAccountManager implements CryptoAccountManager, CryptoNetI
* Class to retrieve the asset id or the asset name, if one of those is missing
*/
private class AssetIdOrNameListener implements ApiRequestListener{
- final Object SYNC;
- boolean ready = false;
+ final ManagerRequest request;
BitsharesAsset asset;
- AssetIdOrNameListener(Object SYNC) {
- this.SYNC = SYNC;
+ AssetIdOrNameListener(ManagerRequest request) {
+ this.request = request;
}
@Override
public void success(Object answer, int idPetition) {
if(answer instanceof ArrayList) {
-
if (((ArrayList) answer).get(0) instanceof BitsharesAsset) {
asset = (BitsharesAsset) ((ArrayList) answer).get(0);
+ request.success(asset);
}
}
-
- synchronized (SYNC){
- ready = true;
- SYNC.notifyAll();
- }
}
@Override
public void fail(int idPetition) {
- synchronized (SYNC){
- ready = true;
- SYNC.notifyAll();
- }
+ //TODO fail asset retrieve
}
}
@@ -637,7 +685,7 @@ public class BitsharesAccountManager implements CryptoAccountManager, CryptoNetI
*/
TransactionDao transactionDao;
- public GetTransactionDate(CryptoCoinTransaction transaction, TransactionDao transactionDao) {
+ GetTransactionDate(CryptoCoinTransaction transaction, TransactionDao transactionDao) {
this.transaction = transaction;
this.transactionDao = transactionDao;
}
@@ -645,11 +693,13 @@ public class BitsharesAccountManager implements CryptoAccountManager, CryptoNetI
@Override
public void success(Object answer, int idPetition) {
if(answer instanceof BlockHeader){
- SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
- dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
+ @SuppressLint("SimpleDateFormat") SimpleDateFormat dateFormat = new SimpleDateFormat(SIMPLE_DATE_FORMAT);
+ dateFormat.setTimeZone(TimeZone.getTimeZone(DEFAULT_TIME_ZONE));
try {
transaction.setDate(dateFormat.parse(((BlockHeader) answer).timestamp));
- transactionDao.insertTransaction(transaction);
+ if (transactionDao.getByTransaction(transaction.getDate(),transaction.getFrom(),transaction.getTo(),transaction.getAmount()) == null) {
+ transactionDao.insertTransaction(transaction);
+ }
} catch (ParseException e) {
e.printStackTrace();
}
diff --git a/app/src/main/java/cy/agorise/crystalwallet/manager/CryptoAccountManager.java b/app/src/main/java/cy/agorise/crystalwallet/manager/CryptoAccountManager.java
index e1e3b6d..9941e8e 100644
--- a/app/src/main/java/cy/agorise/crystalwallet/manager/CryptoAccountManager.java
+++ b/app/src/main/java/cy/agorise/crystalwallet/manager/CryptoAccountManager.java
@@ -18,14 +18,14 @@ public interface CryptoAccountManager {
* @param account The values to be created,
* @returnThe CruptoNetAccount created, or null if it couldn't be created
*/
- public CryptoNetAccount createAccountFromSeed(CryptoNetAccount account, Context context);
+ public void createAccountFromSeed(CryptoNetAccount account, ManagerRequest request, Context context);
/**
* Imports a CryptoCoin account from a seed
* @param account A CryptoNetAccount with the parameters to be imported
* @returnThe CruptoNetAccount imported
*/
- public CryptoNetAccount importAccountFromSeed(CryptoNetAccount account, Context context);
+ public void importAccountFromSeed(CryptoNetAccount account, Context context);
/**
* Loads account data from the database
diff --git a/app/src/main/java/cy/agorise/crystalwallet/manager/ManagerRequest.java b/app/src/main/java/cy/agorise/crystalwallet/manager/ManagerRequest.java
new file mode 100644
index 0000000..cfd6464
--- /dev/null
+++ b/app/src/main/java/cy/agorise/crystalwallet/manager/ManagerRequest.java
@@ -0,0 +1,12 @@
+package cy.agorise.crystalwallet.manager;
+
+/**
+ * Created by henry on 28/1/2018.
+ */
+
+public interface ManagerRequest {
+
+ public void success(Object answer);
+
+ public void fail();
+}
diff --git a/app/src/main/java/cy/agorise/crystalwallet/models/Contact.java b/app/src/main/java/cy/agorise/crystalwallet/models/Contact.java
index 1cc3d6e..be43fb9 100644
--- a/app/src/main/java/cy/agorise/crystalwallet/models/Contact.java
+++ b/app/src/main/java/cy/agorise/crystalwallet/models/Contact.java
@@ -10,8 +10,11 @@ import android.arch.persistence.room.PrimaryKey;
import android.support.annotation.NonNull;
import android.support.v7.recyclerview.extensions.DiffCallback;
+import java.util.ArrayList;
import java.util.List;
+import cy.agorise.crystalwallet.enums.CryptoNet;
+
/**
* Represents a user contact
*
@@ -19,7 +22,7 @@ import java.util.List;
*/
@Entity(tableName="contact",
- indices = {@Index("id"),@Index(value = {"name"}, unique=true)})
+ indices = {@Index("id"),@Index(value = {"name"}, unique=true),@Index("email")})
public class Contact {
/**
@@ -32,6 +35,9 @@ public class Contact {
@ColumnInfo(name="name")
private String mName;
+ @ColumnInfo(name="email")
+ private String mEmail;
+
@ColumnInfo(name = "gravatar")
private String mGravatar;
@@ -62,6 +68,14 @@ public class Contact {
this.mGravatar = gravatar;
}
+ public String getEmail() {
+ return this.mEmail;
+ }
+
+ public void setEmail(String email) {
+ this.mEmail = email;
+ }
+
public int addressesCount(){
return this.mAddresses.size();
}
@@ -70,8 +84,30 @@ public class Contact {
return this.mAddresses.get(index);
}
+ public void clearAddresses(){
+ if (this.mAddresses != null) {
+ this.mAddresses.clear();
+ }
+ }
+
public void addAddress(ContactAddress address){
+ if (this.mAddresses == null) {
+ this.mAddresses = new ArrayList();
+ }
this.mAddresses.add(address);
+ address.setContactId(this.getId());
+ }
+
+ public ContactAddress getCryptoNetAddress(CryptoNet cryptoNet){
+ if (this.mAddresses != null) {
+ for (ContactAddress address : this.mAddresses) {
+ if (address.getCryptoNet() == cryptoNet) {
+ return address;
+ }
+ }
+ }
+
+ return null;
}
public static final DiffCallback DIFF_CALLBACK = new DiffCallback() {
diff --git a/app/src/main/java/cy/agorise/crystalwallet/models/ContactAddress.java b/app/src/main/java/cy/agorise/crystalwallet/models/ContactAddress.java
index ae247dd..85d9930 100644
--- a/app/src/main/java/cy/agorise/crystalwallet/models/ContactAddress.java
+++ b/app/src/main/java/cy/agorise/crystalwallet/models/ContactAddress.java
@@ -4,9 +4,12 @@ package cy.agorise.crystalwallet.models;
import android.arch.persistence.room.ColumnInfo;
import android.arch.persistence.room.Entity;
import android.arch.persistence.room.Index;
+import android.arch.persistence.room.PrimaryKey;
import android.support.annotation.NonNull;
import android.support.v7.recyclerview.extensions.DiffCallback;
+import cy.agorise.crystalwallet.enums.CryptoNet;
+
/**
* Represents a user contact address
*
@@ -14,19 +17,37 @@ import android.support.v7.recyclerview.extensions.DiffCallback;
*/
@Entity(tableName="contact_address",
- primaryKeys = {"contact_id","crypto_currency_id"},
- indices = {@Index(value = {"contact_id","crypto_currency_id"}, unique=true)})
+ indices = {@Index(value = {"id"}, unique=true),@Index(value = {"contact_id","crypto_net"}, unique=true)})
public class ContactAddress {
+ /**
+ * The id on the database
+ */
+ @PrimaryKey(autoGenerate = true)
+ @ColumnInfo(name = "id")
+ private long mId;
+
@ColumnInfo(name = "contact_id")
private long mContactId;
- @ColumnInfo(name = "crypto_currency_id")
- private long mCryptoCurrencyId;
+ /**
+ * The crypto net of the address
+ */
+ @NonNull
+ @ColumnInfo(name = "crypto_net")
+ private CryptoNet mCryptoNet;
@ColumnInfo(name="address")
private String mAddress;
+ public long getId() {
+ return mId;
+ }
+
+ public void setId(long id) {
+ this.mId = id;
+ }
+
public long getContactId() {
return mContactId;
}
@@ -35,12 +56,12 @@ public class ContactAddress {
this.mContactId = contactId;
}
- public long getCryptoCurrencyId() {
- return mCryptoCurrencyId;
+ public CryptoNet getCryptoNet() {
+ return mCryptoNet;
}
- public void setCryptoCurrencyId(long cryptoCurrencyId) {
- this.mCryptoCurrencyId = cryptoCurrencyId;
+ public void setCryptoNet(CryptoNet cryptoNet) {
+ this.mCryptoNet = cryptoNet;
}
public String getAddress() {
@@ -56,7 +77,7 @@ public class ContactAddress {
public boolean areItemsTheSame(
@NonNull ContactAddress oldContactAddress, @NonNull ContactAddress newContactAddress) {
return (oldContactAddress.getContactId() == newContactAddress.getContactId())
- && (oldContactAddress.getCryptoCurrencyId() == newContactAddress.getCryptoCurrencyId());
+ && (oldContactAddress.getCryptoNet() == newContactAddress.getCryptoNet());
}
@Override
public boolean areContentsTheSame(
@@ -73,7 +94,7 @@ public class ContactAddress {
ContactAddress that = (ContactAddress) o;
if (mContactId != that.mContactId) return false;
- if (mCryptoCurrencyId != that.mCryptoCurrencyId) return false;
+ if (mCryptoNet != that.mCryptoNet) return false;
return mAddress.equals(that.mAddress);
}
}
diff --git a/app/src/main/java/cy/agorise/crystalwallet/models/GeneralSetting.java b/app/src/main/java/cy/agorise/crystalwallet/models/GeneralSetting.java
index b5b322d..822a10e 100644
--- a/app/src/main/java/cy/agorise/crystalwallet/models/GeneralSetting.java
+++ b/app/src/main/java/cy/agorise/crystalwallet/models/GeneralSetting.java
@@ -17,6 +17,7 @@ public class GeneralSetting {
public final static String SETTING_NAME_PREFERED_COUNTRY = "PREFERED_COUNTRY";
public final static String SETTING_NAME_PREFERED_CURRENCY = "PREFERED_CURRENCY";
+ public final static String SETTING_PASSWORD = "PASSWORD";
/**
* The id on the database
diff --git a/app/src/main/java/cy/agorise/crystalwallet/network/BitsharesCryptoNetVerifier.java b/app/src/main/java/cy/agorise/crystalwallet/network/BitsharesCryptoNetVerifier.java
new file mode 100644
index 0000000..8a47bce
--- /dev/null
+++ b/app/src/main/java/cy/agorise/crystalwallet/network/BitsharesCryptoNetVerifier.java
@@ -0,0 +1,54 @@
+package cy.agorise.crystalwallet.network;
+
+import cy.agorise.crystalwallet.enums.CryptoNet;
+import cy.agorise.graphenej.interfaces.WitnessResponseListener;
+import cy.agorise.graphenej.models.BaseResponse;
+import cy.agorise.graphenej.models.WitnessResponse;
+
+/**
+ *
+ * Created by henry on 28/2/2018.
+ */
+
+public class BitsharesCryptoNetVerifier extends CryptoNetVerifier {
+
+
+ /**
+ * TODO We need to change this to a type of subCryptoNet
+ */
+ private final CryptoNet cryptoNet = CryptoNet.BITSHARES;
+ /**
+ * Todo info need to be on the SubCryptoNet
+ */
+ private final String CHAIN_ID = "9cf6f255a208100d2bb275a3c52f4b1589b7ec9c9bfc2cb2a5fe6411295106d8";//testnet
+ //private final String CHAIN_ID = "4018d7844c78f6a6c41c6a552b898022310fc5dec06da467ee7905a8dad512c8";//mainnet
+
+ @Override
+ public void checkURL(final String url) {
+ final long startTime = System.currentTimeMillis();
+ WebSocketThread 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);
+ }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
+ }
+ }
+ }
+
+ @Override
+ public void onError(BaseResponse.Error error) {
+ //TODO handle error
+ }
+ }),url);
+ thread.start();
+ }
+
+ @Override
+ public String getChainId() {
+ return CHAIN_ID;
+ }
+}
diff --git a/app/src/main/java/cy/agorise/crystalwallet/network/CryptoNetManager.java b/app/src/main/java/cy/agorise/crystalwallet/network/CryptoNetManager.java
new file mode 100644
index 0000000..d424cc8
--- /dev/null
+++ b/app/src/main/java/cy/agorise/crystalwallet/network/CryptoNetManager.java
@@ -0,0 +1,149 @@
+package cy.agorise.crystalwallet.network;
+
+import android.support.annotation.NonNull;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+
+import cy.agorise.crystalwallet.enums.CryptoNet;
+
+/**
+ * Created by henry on 6/3/2018.
+ */
+
+public abstract class CryptoNetManager {
+ /**
+ * This map contains the list of the urls to be tested
+ */
+ private static HashMap> CryptoNetURLs = new HashMap<>();
+
+ /**
+ * This map contains the list of urls been tested and ordered by the fastests
+ */
+ private static HashMap> TestedURLs = new HashMap<>();
+
+ public static String getURL(CryptoNet crypto){
+ return CryptoNetManager.getURL(crypto,0);
+ }
+
+
+ public static String getURL(CryptoNet crypto, int index){
+ if(TestedURLs.containsKey(crypto) && TestedURLs.get(crypto).size()>index){
+ return TestedURLs.get(crypto).get(index).getUrl();
+ }
+
+ if(CryptoNetURLs.containsKey(crypto) && !CryptoNetURLs.get(crypto).isEmpty()){
+ return CryptoNetURLs.get(crypto).iterator().next();
+ }
+ return null;
+ }
+
+ public static int getURLSize(CryptoNet crypto){
+ if(TestedURLs.containsKey(crypto)){
+ return TestedURLs.get(crypto).size();
+ }
+ return 0;
+ }
+
+ public static void addCryptoNetURL(CryptoNet crypto, String url){
+ if(!CryptoNetURLs.containsKey(crypto)){
+ CryptoNetURLs.put(crypto,new HashSet());
+ }
+
+ CryptoNetURLs.get(crypto).add(url);
+ CryptoNetVerifier verifier = CryptoNetVerifier.getNetworkVerify(crypto);
+ if(verifier != null) {
+ verifier.checkURL(url);
+ }
+ }
+
+ public static void addCryptoNetURL(CryptoNet crypto, String[] urls){
+ if(!CryptoNetURLs.containsKey(crypto)){
+ CryptoNetURLs.put(crypto,new HashSet());
+ }
+ CryptoNetVerifier verifier = CryptoNetVerifier.getNetworkVerify(crypto);
+
+ for(String url : urls) {
+ CryptoNetURLs.get(crypto).add(url);
+ if(verifier != null) {
+ verifier.checkURL(url);
+ }
+ }
+ }
+
+ public static void removeCryptoNetURL(CryptoNet crypto, String url){
+ if(CryptoNetURLs.containsKey(crypto)){
+ CryptoNetURLs.get(crypto).remove(url);
+ }
+ }
+
+ public static void verifiedCryptoNetURL(CryptoNet crypto, String url, long time){
+ if(CryptoNetURLs.containsKey(crypto) && CryptoNetURLs.get(crypto).contains(url)){
+ if(!TestedURLs.containsKey(crypto)){
+ TestedURLs.put(crypto,new ArrayList());
+ }
+ TestedURL testedUrl = new TestedURL(time,url);
+ if(!TestedURLs.get(crypto).contains(testedUrl)){
+ TestedURLs.get(crypto).add(testedUrl);
+ Collections.sort(TestedURLs.get(crypto));
+ }
+ }else{
+ //TODO add error handler
+ }
+ }
+
+ public static String getChaindId(CryptoNet crypto){
+ CryptoNetVerifier verifier = CryptoNetVerifier.getNetworkVerify(crypto);
+ if(verifier != null) {
+ return verifier.getChainId();
+ }
+ return null;
+ }
+
+ private static class TestedURL implements Comparable{
+ private long time;
+ private String url;
+
+ public TestedURL(long time, String url) {
+ this.time = time;
+ this.url = url;
+ }
+
+ public long getTime() {
+ return time;
+ }
+
+ String getUrl() {
+ return url;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof TestedURL)) return false;
+
+ TestedURL testedURL = (TestedURL) o;
+
+ return getUrl().equals(testedURL.getUrl());
+ }
+
+ @Override
+ public int hashCode() {
+ return getUrl().hashCode();
+ }
+
+ @Override
+ public int compareTo(@NonNull Object o) {
+ if (this == o) return 0;
+ if (!(o instanceof TestedURL)) return 0;
+
+ TestedURL testedURL = (TestedURL) o;
+ 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
new file mode 100644
index 0000000..7364b79
--- /dev/null
+++ b/app/src/main/java/cy/agorise/crystalwallet/network/CryptoNetVerifier.java
@@ -0,0 +1,25 @@
+package cy.agorise.crystalwallet.network;
+
+import cy.agorise.crystalwallet.enums.CryptoNet;
+
+/**
+ * This is used to check if the connection is stable and fast.
+ *
+ * Also verifies if the connection with the server is valid.
+ *
+ * Created by henry on 28/2/2018.
+ */
+
+public abstract class CryptoNetVerifier {
+
+ static CryptoNetVerifier getNetworkVerify(CryptoNet cryptoNet){
+ if(cryptoNet.getLabel().equals(CryptoNet.BITSHARES.getLabel())){
+ return new BitsharesCryptoNetVerifier();
+ }
+ return null;
+ }
+
+ public abstract void checkURL(final String url);
+
+ public abstract String getChainId();
+}
diff --git a/app/src/main/java/cy/agorise/crystalwallet/network/GetChainId.java b/app/src/main/java/cy/agorise/crystalwallet/network/GetChainId.java
new file mode 100644
index 0000000..2863514
--- /dev/null
+++ b/app/src/main/java/cy/agorise/crystalwallet/network/GetChainId.java
@@ -0,0 +1,61 @@
+package cy.agorise.crystalwallet.network;
+
+import com.google.gson.GsonBuilder;
+import com.google.gson.reflect.TypeToken;
+import com.neovisionaries.ws.client.WebSocket;
+import com.neovisionaries.ws.client.WebSocketFrame;
+
+import java.io.Serializable;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import cy.agorise.graphenej.RPC;
+import cy.agorise.graphenej.api.BaseGrapheneHandler;
+import cy.agorise.graphenej.interfaces.WitnessResponseListener;
+import cy.agorise.graphenej.models.ApiCall;
+import cy.agorise.graphenej.models.WitnessResponse;
+
+/**
+ * Created by henry on 28/2/2018.
+ */
+
+public class GetChainId extends BaseGrapheneHandler {
+
+ private final WitnessResponseListener mListener;
+
+ public GetChainId(WitnessResponseListener listener) {
+ super(listener);
+ this.mListener = listener;
+ }
+
+ @Override
+ public void onConnected(WebSocket websocket, Map> headers) throws Exception {
+ ApiCall getAccountByName = new ApiCall(0, "get_chain_id", new ArrayList(), RPC.VERSION, 1);
+ websocket.sendText(getAccountByName.toJsonString());
+ }
+
+ @Override
+ public void onTextFrame(WebSocket websocket, WebSocketFrame frame) throws Exception {
+ System.out.println("<<< "+frame.getPayloadText());
+ String response = frame.getPayloadText();
+
+ Type GetChainIdResponse = new TypeToken>(){}.getType();
+ GsonBuilder builder = new GsonBuilder();
+ WitnessResponse> witnessResponse = builder.create().fromJson(response, GetChainIdResponse);
+ if(witnessResponse.error != null){
+ this.mListener.onError(witnessResponse.error);
+ }else{
+ this.mListener.onSuccess(witnessResponse);
+ }
+
+ websocket.disconnect();
+ }
+
+ @Override
+ public void onFrameSent(WebSocket websocket, WebSocketFrame frame) throws Exception {
+ if(frame.isTextFrame())
+ System.out.println(">>> "+frame.getPayloadText());
+ }
+}
diff --git a/app/src/main/java/cy/agorise/crystalwallet/service/CrystalWalletService.java b/app/src/main/java/cy/agorise/crystalwallet/service/CrystalWalletService.java
index 5cecb10..93b89a5 100644
--- a/app/src/main/java/cy/agorise/crystalwallet/service/CrystalWalletService.java
+++ b/app/src/main/java/cy/agorise/crystalwallet/service/CrystalWalletService.java
@@ -109,8 +109,7 @@ public class CrystalWalletService extends LifecycleService {
if (LoadEquivalencesThread != null) {
LoadEquivalencesThread.stopLoadingEquivalences();
- }
- ;
+ };
LoadEquivalencesThread = new EquivalencesThread(service, generalSetting.getValue(), bitsharesAssets);
LoadEquivalencesThread.start();
}
@@ -184,14 +183,14 @@ public class CrystalWalletService extends LifecycleService {
}
//if (LoadEquivalencesThread == null) {
- // LoadEquivalencesThread = new Thread() {
+ // LoadEquivalencesThread = new EquivalencesThread() {
// @Override
// public void run() {
loadEquivalentsValues();
// }
// };
// LoadEquivalencesThread.start();
- //}
+ // }
// If we get killed, after returning from here, restart
return START_STICKY;
diff --git a/app/src/main/java/cy/agorise/crystalwallet/util/CircleTransformation.java b/app/src/main/java/cy/agorise/crystalwallet/util/CircleTransformation.java
new file mode 100644
index 0000000..e31ce57
--- /dev/null
+++ b/app/src/main/java/cy/agorise/crystalwallet/util/CircleTransformation.java
@@ -0,0 +1,47 @@
+package cy.agorise.crystalwallet.util;
+
+/**
+ * Created by Henry Varona on 25/2/2018.
+ */
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapShader;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+
+import com.squareup.picasso.Transformation;
+
+public class CircleTransformation implements Transformation {
+ @Override
+ public Bitmap transform(Bitmap source) {
+ int size = Math.min(source.getWidth(), source.getHeight());
+
+ int x = (source.getWidth() - size) / 2;
+ int y = (source.getHeight() - size) / 2;
+
+ Bitmap squaredBitmap = Bitmap.createBitmap(source, x, y, size, size);
+ if (squaredBitmap != source) {
+ source.recycle();
+ }
+
+ Bitmap bitmap = Bitmap.createBitmap(size, size, source.getConfig());
+
+ Canvas canvas = new Canvas(bitmap);
+ Paint paint = new Paint();
+ BitmapShader shader = new BitmapShader(squaredBitmap,
+ BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP);
+ paint.setShader(shader);
+ paint.setAntiAlias(true);
+
+ float r = size / 2f;
+ canvas.drawCircle(r, r, r, paint);
+
+ squaredBitmap.recycle();
+ return bitmap;
+ }
+
+ @Override
+ public String key() {
+ return "CircleTransformation";
+ }
+}
diff --git a/app/src/main/java/cy/agorise/crystalwallet/util/MD5Hash.java b/app/src/main/java/cy/agorise/crystalwallet/util/MD5Hash.java
new file mode 100644
index 0000000..38e6534
--- /dev/null
+++ b/app/src/main/java/cy/agorise/crystalwallet/util/MD5Hash.java
@@ -0,0 +1,22 @@
+package cy.agorise.crystalwallet.util;
+
+/**
+ * Created by Henry Varona on 24/2/2018.
+ */
+
+public class MD5Hash {
+
+ static public String hash(String md5) {
+ try {
+ java.security.MessageDigest md = java.security.MessageDigest.getInstance("MD5");
+ byte[] array = md.digest(md5.getBytes());
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < array.length; ++i) {
+ sb.append(Integer.toHexString((array[i] & 0xFF) | 0x100).substring(1,3));
+ }
+ return sb.toString();
+ } catch (java.security.NoSuchAlgorithmException e) {
+ }
+ return null;
+ }
+}
diff --git a/app/src/main/java/cy/agorise/crystalwallet/util/PasswordManager.java b/app/src/main/java/cy/agorise/crystalwallet/util/PasswordManager.java
new file mode 100644
index 0000000..7f52970
--- /dev/null
+++ b/app/src/main/java/cy/agorise/crystalwallet/util/PasswordManager.java
@@ -0,0 +1,22 @@
+package cy.agorise.crystalwallet.util;
+
+/**
+ * Created by Henry Varona on 29/1/2018.
+ */
+
+public class PasswordManager {
+
+ //TODO implement password checking using the encryption implemented in encriptPassword
+ public static boolean checkPassword(String encriptedPassword, String passwordToCheck){
+ if (encriptedPassword.equals(passwordToCheck)){
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ //TODO implement password encryption
+ public static String encriptPassword(String password){
+ return password;
+ }
+}
diff --git a/app/src/main/java/cy/agorise/crystalwallet/viewmodels/ContactListViewModel.java b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/ContactListViewModel.java
index ec076d9..8a3258b 100644
--- a/app/src/main/java/cy/agorise/crystalwallet/viewmodels/ContactListViewModel.java
+++ b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/ContactListViewModel.java
@@ -6,6 +6,7 @@ import android.arch.lifecycle.LiveData;
import android.arch.paging.PagedList;
import cy.agorise.crystalwallet.dao.CrystalDatabase;
+import cy.agorise.crystalwallet.enums.CryptoNet;
import cy.agorise.crystalwallet.models.Contact;
import cy.agorise.crystalwallet.models.CryptoCoinTransaction;
@@ -30,7 +31,31 @@ public class ContactListViewModel extends AndroidViewModel {
);
}
+ public void init(){
+ contactList = this.db.contactDao().contactsByName().create(0,
+ new PagedList.Config.Builder()
+ .setEnablePlaceholders(true)
+ .setPageSize(10)
+ .setPrefetchDistance(10)
+ .build()
+ );
+ }
+
+ public void init(CryptoNet cryptoNet){
+ contactList = this.db.contactDao().contactsByNameAndCryptoNet(cryptoNet.name()).create(0,
+ new PagedList.Config.Builder()
+ .setEnablePlaceholders(true)
+ .setPageSize(10)
+ .setPrefetchDistance(10)
+ .build()
+ );
+ }
+
public LiveData> getContactList(){
return this.contactList;
}
+
+ public boolean contactExists(String name){
+ return this.db.contactDao().existsByName(name);
+ }
}
diff --git a/app/src/main/java/cy/agorise/crystalwallet/viewmodels/ContactViewModel.java b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/ContactViewModel.java
new file mode 100644
index 0000000..1951215
--- /dev/null
+++ b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/ContactViewModel.java
@@ -0,0 +1,70 @@
+package cy.agorise.crystalwallet.viewmodels;
+
+import android.app.Application;
+import android.arch.lifecycle.AndroidViewModel;
+import android.arch.lifecycle.LiveData;
+import android.arch.paging.PagedList;
+
+import java.util.List;
+
+import cy.agorise.crystalwallet.dao.CrystalDatabase;
+import cy.agorise.crystalwallet.models.Contact;
+import cy.agorise.crystalwallet.models.ContactAddress;
+
+/**
+ * Created by Henry Varona on 2/4/2018.
+ */
+
+public class ContactViewModel extends AndroidViewModel {
+
+ private CrystalDatabase db;
+ private LiveData contact;
+ private LiveData> contactAddresses;
+
+ public ContactViewModel(Application application) {
+ super(application);
+ this.db = CrystalDatabase.getAppDatabase(application.getApplicationContext());
+ }
+
+ public void init(long contactId){
+ this.contact = this.db.contactDao().getById(contactId);
+ this.contactAddresses = this.db.contactDao().getContactAddresses(contactId);
+ }
+
+ public LiveData getContact(){
+ return this.contact;
+ }
+
+ public LiveData> getContactAddresses(){
+ return this.contactAddresses;
+ }
+
+ public boolean modifyContact(Contact contact){
+ this.db.contactDao().update(contact);
+
+ for (int i=0;i 0){
+ this.db.contactDao().updateAddresses(nextAddress);
+ } else {
+ nextAddress.setContactId(contact.getId());
+ this.db.contactDao().addAddresses(nextAddress);
+ }
+ }
+
+ return true;
+ }
+
+ public boolean addContact(Contact contact){
+ long newContactId = this.db.contactDao().add(contact)[0];
+ boolean contactWasAdded = newContactId >= 0;
+
+ for (int i=0;i getCryptoNetAccount(){
return this.cryptoNetAccount;
}
-
}
diff --git a/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/CreateContactValidator.java b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/CreateContactValidator.java
new file mode 100644
index 0000000..1b84e43
--- /dev/null
+++ b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/CreateContactValidator.java
@@ -0,0 +1,23 @@
+package cy.agorise.crystalwallet.viewmodels.validators;
+
+import android.content.Context;
+import android.widget.EditText;
+
+import cy.agorise.crystalwallet.viewmodels.validators.validationfields.BitsharesAccountNameDoesntExistsValidationField;
+import cy.agorise.crystalwallet.viewmodels.validators.validationfields.ContactNameValidationField;
+import cy.agorise.crystalwallet.viewmodels.validators.validationfields.EmailValidationField;
+import cy.agorise.crystalwallet.viewmodels.validators.validationfields.PinConfirmationValidationField;
+import cy.agorise.crystalwallet.viewmodels.validators.validationfields.PinValidationField;
+
+/**
+ * Created by Henry Varona on 2/2/2018.
+ */
+
+public class CreateContactValidator extends UIValidator {
+
+ public CreateContactValidator(Context context, EditText nameEdit, EditText emailEdit){
+ super(context);
+ this.addField(new ContactNameValidationField(nameEdit));
+ this.addField(new EmailValidationField(emailEdit));
+ }
+}
diff --git a/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/ModifyContactValidator.java b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/ModifyContactValidator.java
new file mode 100644
index 0000000..ec640fd
--- /dev/null
+++ b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/ModifyContactValidator.java
@@ -0,0 +1,21 @@
+package cy.agorise.crystalwallet.viewmodels.validators;
+
+import android.content.Context;
+import android.widget.EditText;
+
+import cy.agorise.crystalwallet.models.Contact;
+import cy.agorise.crystalwallet.viewmodels.validators.validationfields.ContactNameValidationField;
+import cy.agorise.crystalwallet.viewmodels.validators.validationfields.EmailValidationField;
+
+/**
+ * Created by Henry Varona on 2/11/2018.
+ */
+
+public class ModifyContactValidator extends UIValidator {
+
+ public ModifyContactValidator(Context context, Contact contact, EditText nameEdit, EditText emailEdit){
+ super(context);
+ this.addField(new ContactNameValidationField(nameEdit, contact));
+ this.addField(new EmailValidationField(emailEdit));
+ }
+}
diff --git a/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/PinSecurityValidator.java b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/PinSecurityValidator.java
new file mode 100644
index 0000000..b99f155
--- /dev/null
+++ b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/PinSecurityValidator.java
@@ -0,0 +1,23 @@
+package cy.agorise.crystalwallet.viewmodels.validators;
+
+import android.content.Context;
+import android.widget.EditText;
+
+import cy.agorise.crystalwallet.viewmodels.validators.validationfields.BitsharesAccountNameDoesntExistsValidationField;
+import cy.agorise.crystalwallet.viewmodels.validators.validationfields.CurrentPinValidationField;
+import cy.agorise.crystalwallet.viewmodels.validators.validationfields.PinConfirmationValidationField;
+import cy.agorise.crystalwallet.viewmodels.validators.validationfields.PinValidationField;
+
+/**
+ * Created by Henry Varona on 1/28/2018.
+ */
+
+public class PinSecurityValidator extends UIValidator {
+
+ public PinSecurityValidator(Context context, EditText currentPinEdit, EditText newPinEdit, EditText newPinConfirmationEdit){
+ super(context);
+ this.addField(new CurrentPinValidationField(currentPinEdit));
+ this.addField(new PinValidationField(newPinEdit));
+ this.addField(new PinConfirmationValidationField(newPinEdit,newPinConfirmationEdit));
+ }
+}
diff --git a/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/validationfields/ContactNameValidationField.java b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/validationfields/ContactNameValidationField.java
new file mode 100644
index 0000000..275c550
--- /dev/null
+++ b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/validationfields/ContactNameValidationField.java
@@ -0,0 +1,76 @@
+package cy.agorise.crystalwallet.viewmodels.validators.validationfields;
+
+import android.arch.lifecycle.LifecycleOwner;
+import android.arch.lifecycle.LiveData;
+import android.arch.lifecycle.Observer;
+import android.arch.lifecycle.ViewModelProviders;
+import android.support.annotation.Nullable;
+import android.support.v4.app.FragmentActivity;
+import android.widget.EditText;
+import android.widget.Spinner;
+
+import java.util.List;
+
+import cy.agorise.crystalwallet.R;
+import cy.agorise.crystalwallet.cryptonetinforequests.CryptoNetInfoRequestListener;
+import cy.agorise.crystalwallet.cryptonetinforequests.CryptoNetInfoRequests;
+import cy.agorise.crystalwallet.cryptonetinforequests.ValidateExistBitsharesAccountRequest;
+import cy.agorise.crystalwallet.models.Contact;
+import cy.agorise.crystalwallet.models.GeneralSetting;
+import cy.agorise.crystalwallet.viewmodels.ContactListViewModel;
+import cy.agorise.crystalwallet.viewmodels.GeneralSettingListViewModel;
+
+/**
+ * Created by Henry Varona on 2/03/2017.
+ */
+
+public class ContactNameValidationField extends ValidationField {
+
+ private EditText nameField;
+ private Contact contact;
+
+ public ContactNameValidationField(EditText nameField){
+ super(nameField);
+ this.nameField = nameField;
+ this.contact = null;
+ }
+
+ public ContactNameValidationField(EditText nameField, Contact contact){
+ super(nameField);
+ this.nameField = nameField;
+ this.contact = contact;
+ }
+
+ public void validate(){
+ final String newValue = this.nameField.getText().toString();
+
+ if (this.contact != null){
+ if (this.contact.getName().equals(newValue)){
+ this.setLastValue(newValue);
+ this.startValidating();
+ this.setValidForValue(newValue, true);
+ return;
+ }
+ }
+
+ if (!newValue.equals("")) {
+ if (!newValue.equals(this.getLastValue())) {
+ this.setLastValue(newValue);
+ this.startValidating();
+
+ ContactListViewModel contactListViewModel = ViewModelProviders.of((FragmentActivity) view.getContext()).get(ContactListViewModel.class);
+ if (contactListViewModel.contactExists(newValue)) {
+ this.setMessageForValue(newValue, "This name is already used by another contact.");
+ this.setValidForValue(newValue, false);
+ } else {
+ this.setValidForValue(newValue, true);
+ }
+ }
+ } else {
+ this.setLastValue("");
+ this.startValidating();
+ this.setMessageForValue("", "");
+ this.setValidForValue("", false);
+ }
+ }
+}
diff --git a/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/validationfields/CurrentPinValidationField.java b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/validationfields/CurrentPinValidationField.java
new file mode 100644
index 0000000..7877184
--- /dev/null
+++ b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/validationfields/CurrentPinValidationField.java
@@ -0,0 +1,69 @@
+package cy.agorise.crystalwallet.viewmodels.validators.validationfields;
+
+import android.arch.lifecycle.LifecycleOwner;
+import android.arch.lifecycle.LiveData;
+import android.arch.lifecycle.Observer;
+import android.arch.lifecycle.ViewModelProviders;
+import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentActivity;
+import android.widget.EditText;
+
+import java.util.List;
+
+import cy.agorise.crystalwallet.models.GeneralSetting;
+import cy.agorise.crystalwallet.util.PasswordManager;
+import cy.agorise.crystalwallet.viewmodels.GeneralSettingListViewModel;
+
+/**
+ * Created by Henry Varona on 1/28/2018.
+ */
+
+public class CurrentPinValidationField extends ValidationField {
+
+ private EditText currentPinField;
+ String currentPassword = "";
+
+ public CurrentPinValidationField(EditText currentPinField){
+ super(currentPinField);
+ this.currentPinField = currentPinField;
+ GeneralSettingListViewModel generalSettingListViewModel = ViewModelProviders.of((FragmentActivity)view.getContext()).get(GeneralSettingListViewModel.class);
+ LiveData> generalSettingsLiveData = generalSettingListViewModel.getGeneralSettingList();
+ generalSettingsLiveData.observe((LifecycleOwner) this.view.getContext(), new Observer>() {
+ @Override
+ public void onChanged(@Nullable List generalSettings) {
+ for (GeneralSetting generalSetting:generalSettings) {
+ if (generalSetting.getName().equals(GeneralSetting.SETTING_PASSWORD)){
+ currentPassword = generalSetting.getValue();
+ break;
+ }
+ }
+ }
+ });
+ }
+
+ public void validate(){
+ final String newValue = currentPinField.getText().toString();
+
+ if (this.currentPassword.equals("")) {
+ this.setLastValue("");
+ this.startValidating();
+ setValidForValue("",true);
+ } else if (!newValue.equals(this.getLastValue())) {
+ this.setLastValue(newValue);
+ this.startValidating();
+ if (newValue.equals("")){
+ setMessageForValue(lastValue, "");
+ setValidForValue(lastValue, false);
+ } else {
+
+ if (PasswordManager.checkPassword(this.currentPassword, newValue)) {
+ setValidForValue(lastValue, true);
+ } else {
+ setMessageForValue(lastValue, "Password is invalid.");
+ setValidForValue(lastValue, false);
+ }
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/validationfields/EmailValidationField.java b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/validationfields/EmailValidationField.java
new file mode 100644
index 0000000..05c4e51
--- /dev/null
+++ b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/validationfields/EmailValidationField.java
@@ -0,0 +1,49 @@
+package cy.agorise.crystalwallet.viewmodels.validators.validationfields;
+
+import android.widget.EditText;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import cy.agorise.crystalwallet.R;
+
+/**
+ * Created by Henry Varona on 2/21/2017.
+ */
+
+public class EmailValidationField extends ValidationField {
+
+ private EditText emailField;
+
+ public EmailValidationField(EditText emailField){
+ super(emailField);
+ this.emailField = emailField;
+ }
+
+ public void validate(){
+ String newValue = emailField.getText().toString();
+ if (!newValue.equals("")) {
+
+ if (!newValue.equals(this.getLastValue())) {
+ this.setLastValue(newValue);
+ this.startValidating();
+
+ String expression = "^[\\w\\.-]+@([\\w\\-]+\\.)+[A-Z]{2,4}$";
+ Pattern pattern = Pattern.compile(expression, Pattern.CASE_INSENSITIVE);
+ Matcher matcher = pattern.matcher(newValue);
+
+ if (!matcher.matches()) {
+ this.setMessageForValue(newValue, "The email is not valid");
+ this.setValidForValue(newValue, false);
+ } else {
+ this.setValidForValue(newValue, true);
+ }
+ }
+ } else {
+ this.setLastValue("");
+ this.startValidating();
+ this.setMessageForValue("", "");
+ this.setValidForValue("", false);
+ }
+ }
+}
diff --git a/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/validationfields/PinConfirmationValidationField.java b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/validationfields/PinConfirmationValidationField.java
index 00f951e..77b7043 100644
--- a/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/validationfields/PinConfirmationValidationField.java
+++ b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/validators/validationfields/PinConfirmationValidationField.java
@@ -22,18 +22,24 @@ public class PinConfirmationValidationField extends ValidationField {
public void validate(){
String newConfirmationValue = pinConfirmationField.getText().toString();
String newValue = pinField.getText().toString();
- String mixedValue = newValue+"_"+newConfirmationValue;
- if (mixedValue != this.getLastValue()) {
- this.setLastValue(mixedValue);
- this.startValidating();
+ String mixedValue = newValue + "_" + newConfirmationValue;
-
- if (!newConfirmationValue.equals(newValue)){
- this.setMessageForValue(mixedValue,this.validator.getContext().getResources().getString(R.string.mismatch_pin));
- this.setValidForValue(mixedValue,false);
- } else {
- this.setValidForValue(mixedValue, true);
+ if (!newConfirmationValue.equals("")) {
+ if (!mixedValue.equals(this.getLastValue())) {
+ this.setLastValue(mixedValue);
+ this.startValidating();
+ if (!newConfirmationValue.equals(newValue)) {
+ this.setMessageForValue(mixedValue, this.validator.getContext().getResources().getString(R.string.mismatch_pin));
+ this.setValidForValue(mixedValue, false);
+ } else {
+ this.setValidForValue(mixedValue, true);
+ }
}
+ } else {
+ this.setLastValue("");
+ this.startValidating();
+ this.setMessageForValue("", "");
+ this.setValidForValue("", false);
}
}
}
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
index 809b8c6..6228641 100644
--- 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
@@ -20,16 +20,23 @@ public class PinValidationField extends ValidationField {
public void validate(){
String newValue = pinField.getText().toString();
- if (newValue != this.getLastValue()) {
- this.setLastValue(newValue);
- this.startValidating();
+ if (!newValue.equals("")) {
+ if (!newValue.equals(this.getLastValue())) {
+ this.setLastValue(newValue);
+ this.startValidating();
- if (newValue.length() < 6) {
- this.setMessageForValue(newValue, this.validator.getContext().getResources().getString(R.string.pin_number_warning));
- this.setValidForValue(newValue, false);
- } else {
- this.setValidForValue(newValue, true);
+ if (newValue.length() < 6) {
+ this.setMessageForValue(newValue, this.validator.getContext().getResources().getString(R.string.pin_number_warning));
+ this.setValidForValue(newValue, false);
+ } else {
+ this.setValidForValue(newValue, true);
+ }
}
+ } else {
+ this.setLastValue("");
+ this.startValidating();
+ this.setMessageForValue("", "");
+ this.setValidForValue("", false);
}
}
}
diff --git a/app/src/main/java/cy/agorise/crystalwallet/views/ContactAddressListAdapter.java b/app/src/main/java/cy/agorise/crystalwallet/views/ContactAddressListAdapter.java
new file mode 100644
index 0000000..1be39eb
--- /dev/null
+++ b/app/src/main/java/cy/agorise/crystalwallet/views/ContactAddressListAdapter.java
@@ -0,0 +1,41 @@
+package cy.agorise.crystalwallet.views;
+
+
+import android.support.v7.recyclerview.extensions.ListAdapter;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import cy.agorise.crystalwallet.R;
+import cy.agorise.crystalwallet.models.ContactAddress;
+
+/**
+ * Created by Henry Varona on 2/5/2018.
+ *
+ * An adapter to show the elements of a list of contacts addresses
+ */
+
+
+public class ContactAddressListAdapter extends ListAdapter {
+
+ public ContactAddressListAdapter() {
+ super(ContactAddress.DIFF_CALLBACK);
+ }
+
+ @Override
+ public ContactAddressViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.contact_address_list_item,parent,false);
+
+ return new ContactAddressViewHolder(v);
+ }
+
+ @Override
+ public void onBindViewHolder(ContactAddressViewHolder holder, int position) {
+ ContactAddress contactAddress = getItem(position);
+ if (contactAddress != null) {
+ holder.bindTo(contactAddress);
+ } else {
+ holder.clear();
+ }
+ }
+}
diff --git a/app/src/main/java/cy/agorise/crystalwallet/views/ContactAddressViewHolder.java b/app/src/main/java/cy/agorise/crystalwallet/views/ContactAddressViewHolder.java
new file mode 100644
index 0000000..711c9ab
--- /dev/null
+++ b/app/src/main/java/cy/agorise/crystalwallet/views/ContactAddressViewHolder.java
@@ -0,0 +1,111 @@
+package cy.agorise.crystalwallet.views;
+
+import android.content.Context;
+import android.support.v7.widget.RecyclerView;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.EditText;
+import android.widget.ImageView;
+import android.widget.Spinner;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import cy.agorise.crystalwallet.R;
+import cy.agorise.crystalwallet.enums.CryptoNet;
+import cy.agorise.crystalwallet.models.Contact;
+import cy.agorise.crystalwallet.models.ContactAddress;
+
+/**
+ * Created by Henry Varona on 2/5/2017.
+ *
+ * Represents an element view from the Contact Address List
+ */
+
+public class ContactAddressViewHolder extends RecyclerView.ViewHolder {
+ private Spinner spCryptoNet;
+ private EditText etAddress;
+ private Context context;
+ private CryptoNet[] cryptoNetArray;
+ private ArrayAdapter cryptoNetSpinnerAdapter;
+
+ public ContactAddressViewHolder(View itemView) {
+ super(itemView);
+ //TODO: use ButterKnife to load this
+ spCryptoNet = (Spinner) itemView.findViewById(R.id.spCryptoNet);
+ etAddress = (EditText) itemView.findViewById(R.id.etAddress);
+ this.context = itemView.getContext();
+
+
+ //load spinners values
+ cryptoNetArray = CryptoNet.values();
+ cryptoNetSpinnerAdapter = new ArrayAdapter(
+ this.context,
+ android.R.layout.simple_list_item_1,
+ cryptoNetArray
+ );
+ spCryptoNet.setAdapter(cryptoNetSpinnerAdapter);
+ }
+
+ /*
+ * Clears the information in this element view
+ */
+ public void clear(){
+ spCryptoNet.setSelection(0);
+ etAddress.setText("");
+ }
+
+ /*
+ * Binds this view with the data of an element of the list
+ */
+ public void bindTo(final ContactAddress contactAddress) {
+ if (contactAddress == null){
+ this.clear();
+ } else {
+ etAddress.setText(contactAddress.getAddress());
+
+ CryptoNet nextCryptoNet;
+ for (int i=0;i adapterView, View view, int i, long l) {
+ contactAddress.setCryptoNet(((CryptoNet)spCryptoNet.getSelectedItem()));
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView> adapterView) {
+ //
+ }
+ });
+ etAddress.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
+ //
+ }
+
+ @Override
+ public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
+ //
+ }
+
+ @Override
+ public void afterTextChanged(Editable editable) {
+ contactAddress.setAddress(editable.toString());
+ }
+ });
+ //etAddress.setText(contactAddress.getAddress());
+ }
+ }
+}
diff --git a/app/src/main/java/cy/agorise/crystalwallet/views/ContactListView.java b/app/src/main/java/cy/agorise/crystalwallet/views/ContactListView.java
index e9efce9..854be6b 100644
--- a/app/src/main/java/cy/agorise/crystalwallet/views/ContactListView.java
+++ b/app/src/main/java/cy/agorise/crystalwallet/views/ContactListView.java
@@ -12,9 +12,7 @@ import android.widget.RelativeLayout;
import cy.agorise.crystalwallet.R;
import cy.agorise.crystalwallet.models.Contact;
-import cy.agorise.crystalwallet.models.CryptoCoinTransaction;
import cy.agorise.crystalwallet.viewmodels.ContactListViewModel;
-import cy.agorise.crystalwallet.viewmodels.TransactionListViewModel;
/**
* Created by Henry Varona on 1/15/2018.
diff --git a/app/src/main/java/cy/agorise/crystalwallet/views/ContactSelectionListAdapter.java b/app/src/main/java/cy/agorise/crystalwallet/views/ContactSelectionListAdapter.java
new file mode 100644
index 0000000..58f1350
--- /dev/null
+++ b/app/src/main/java/cy/agorise/crystalwallet/views/ContactSelectionListAdapter.java
@@ -0,0 +1,57 @@
+package cy.agorise.crystalwallet.views;
+
+
+import android.support.v7.recyclerview.extensions.ListAdapter;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import cy.agorise.crystalwallet.R;
+import cy.agorise.crystalwallet.models.Contact;
+
+/**
+ * Created by Henry Varona on 2/16/2018.
+ *
+ * An adapter to show the elements of a list of contacts to be selected by the user
+ */
+
+public class ContactSelectionListAdapter extends ListAdapter implements ContactSelectionViewHolder.ContactSelectionViewHolderListener {
+
+ private ContactSelectionListAdapterListener listener;
+
+ public ContactSelectionListAdapter() {
+ super(Contact.DIFF_CALLBACK);
+ }
+
+ @Override
+ public ContactSelectionViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.contact_selection_list_item,parent,false);
+ return new ContactSelectionViewHolder(v);
+ }
+
+ @Override
+ public void onBindViewHolder(ContactSelectionViewHolder holder, int position) {
+ Contact contact = getItem(position);
+ if (contact != null) {
+ holder.bindTo(contact);
+ holder.setListener(this);
+ } else {
+ holder.clear();
+ }
+ }
+
+ @Override
+ public void onContactSelected(ContactSelectionViewHolder contactSelectionViewHolder, Contact contact) {
+ if (this.listener != null){
+ this.listener.onContactSelected(contact);
+ }
+ }
+
+ public void setListener(ContactSelectionListAdapterListener listener){
+ this.listener = listener;
+ }
+
+ public interface ContactSelectionListAdapterListener{
+ public void onContactSelected(Contact contact);
+ }
+}
diff --git a/app/src/main/java/cy/agorise/crystalwallet/views/ContactSelectionViewHolder.java b/app/src/main/java/cy/agorise/crystalwallet/views/ContactSelectionViewHolder.java
new file mode 100644
index 0000000..797ae02
--- /dev/null
+++ b/app/src/main/java/cy/agorise/crystalwallet/views/ContactSelectionViewHolder.java
@@ -0,0 +1,93 @@
+package cy.agorise.crystalwallet.views;
+
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.support.v7.app.AlertDialog;
+import android.support.v7.widget.RecyclerView;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.squareup.picasso.Picasso;
+
+import cy.agorise.crystalwallet.R;
+import cy.agorise.crystalwallet.activities.CreateContactActivity;
+import cy.agorise.crystalwallet.dao.CrystalDatabase;
+import cy.agorise.crystalwallet.models.Contact;
+import cy.agorise.crystalwallet.util.CircleTransformation;
+import cy.agorise.crystalwallet.util.MD5Hash;
+
+/**
+ * Created by Henry Varona on 2/16/2018.
+ *
+ * Represents an element view from the Contact Selection List
+ */
+
+public class ContactSelectionViewHolder extends RecyclerView.ViewHolder {
+ private TextView tvName;
+ private ImageView ivThumbnail;
+ private TextView tvLastPaid;
+ private Context context;
+ private ContactSelectionViewHolderListener listener;
+
+ public ContactSelectionViewHolder(View itemView) {
+ super(itemView);
+ //TODO: use ButterKnife to load this
+ tvName = (TextView) itemView.findViewById(R.id.tvContactName);
+ ivThumbnail = (ImageView) itemView.findViewById(R.id.ivContactThumbnail);
+ tvLastPaid = (TextView) itemView.findViewById(R.id.tvLastPaid);
+ this.context = itemView.getContext();
+
+ }
+
+ public void setListener(ContactSelectionViewHolderListener listener){
+ this.listener = listener;
+ }
+
+ /*
+ * Clears the information in this element view
+ */
+ public void clear(){
+ tvName.setText("");
+ ivThumbnail.setImageResource(android.R.color.transparent);
+ tvLastPaid.setText("");
+ }
+
+ /*
+ * Binds this view with the data of an element of the list
+ */
+ public void bindTo(final Contact contact) {
+ if (contact == null){
+ this.clear();
+ } else {
+ final ContactSelectionViewHolder thisViewHolder = this;
+
+ tvName.setText(contact.getName());
+ tvLastPaid.setText("Paid: 1 Jan, 2001 01:01");
+
+ if (contact.getEmail() != null){
+ String emailHash = MD5Hash.hash(contact.getEmail());
+ String gravatarUrl = "http://www.gravatar.com/avatar/" + emailHash + "?s=204&d=404";
+
+ Picasso.with(this.context)
+ .load(gravatarUrl)
+ .transform(new CircleTransformation())
+ .into(ivThumbnail);
+ }
+
+ this.itemView.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ if (listener != null){
+ listener.onContactSelected(thisViewHolder, contact);
+ }
+ }
+ });
+ }
+ }
+
+ public interface ContactSelectionViewHolderListener {
+ public void onContactSelected(ContactSelectionViewHolder contactSelectionViewHolder, Contact contact);
+ }
+}
diff --git a/app/src/main/java/cy/agorise/crystalwallet/views/ContactViewHolder.java b/app/src/main/java/cy/agorise/crystalwallet/views/ContactViewHolder.java
index 429acf0..376ce11 100644
--- a/app/src/main/java/cy/agorise/crystalwallet/views/ContactViewHolder.java
+++ b/app/src/main/java/cy/agorise/crystalwallet/views/ContactViewHolder.java
@@ -1,13 +1,24 @@
package cy.agorise.crystalwallet.views;
import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.provider.ContactsContract;
+import android.support.v7.app.AlertDialog;
import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.ThemedSpinnerAdapter;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
+import com.squareup.picasso.Picasso;
+
import cy.agorise.crystalwallet.R;
+import cy.agorise.crystalwallet.activities.CreateContactActivity;
+import cy.agorise.crystalwallet.dao.CrystalDatabase;
import cy.agorise.crystalwallet.models.Contact;
+import cy.agorise.crystalwallet.util.CircleTransformation;
+import cy.agorise.crystalwallet.util.MD5Hash;
/**
* Created by Henry Varona on 1/17/2017.
@@ -19,6 +30,7 @@ public class ContactViewHolder extends RecyclerView.ViewHolder {
private TextView tvName;
private ImageView ivThumbnail;
private TextView tvLastPaid;
+ private ImageView ivDeleteContact;
private Context context;
public ContactViewHolder(View itemView) {
@@ -27,6 +39,7 @@ public class ContactViewHolder extends RecyclerView.ViewHolder {
tvName = (TextView) itemView.findViewById(R.id.tvContactName);
ivThumbnail = (ImageView) itemView.findViewById(R.id.ivContactThumbnail);
tvLastPaid = (TextView) itemView.findViewById(R.id.tvLastPaid);
+ ivDeleteContact = (ImageView) itemView.findViewById(R.id.ivDeleteContact);
this.context = itemView.getContext();
}
@@ -49,6 +62,47 @@ public class ContactViewHolder extends RecyclerView.ViewHolder {
} else {
tvName.setText(contact.getName());
tvLastPaid.setText("Paid: 1 Jan, 2001 01:01");
+
+ this.itemView.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ Intent intent = new Intent(itemView.getContext(), CreateContactActivity.class);
+ intent.putExtra("CONTACT_ID", contact.getId());
+ itemView.getContext().startActivity(intent);
+ }
+ });
+
+ if (contact.getEmail() != null){
+ String emailHash = MD5Hash.hash(contact.getEmail());
+ String gravatarUrl = "http://www.gravatar.com/avatar/" + emailHash + "?s=204&d=404";
+
+ Picasso.with(this.context)
+ .load(gravatarUrl)
+ .transform(new CircleTransformation())
+ .into(ivThumbnail);
+ }
+
+ this.ivDeleteContact.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ //delete the contact
+ new AlertDialog.Builder(context)
+ .setIcon(android.R.drawable.ic_dialog_alert)
+ .setMessage("Are you sure you want to delete this contact?")
+ .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ CrystalDatabase.getAppDatabase(context).contactDao().deleteContacts(contact);
+ }
+
+ })
+ .setNegativeButton("No", null)
+ .show();
+
+
+ }
+ });
}
}
}
diff --git a/app/src/main/java/cy/agorise/crystalwallet/views/CryptoCoinBalanceListAdapter.java b/app/src/main/java/cy/agorise/crystalwallet/views/CryptoCoinBalanceListAdapter.java
index 7554a5d..8d43915 100644
--- a/app/src/main/java/cy/agorise/crystalwallet/views/CryptoCoinBalanceListAdapter.java
+++ b/app/src/main/java/cy/agorise/crystalwallet/views/CryptoCoinBalanceListAdapter.java
@@ -17,16 +17,22 @@ import cy.agorise.crystalwallet.models.CryptoCoinBalance;
public class CryptoCoinBalanceListAdapter extends ListAdapter {
+ CryptoNetBalanceViewHolder cryptoNetBalanceViewHolder;
+
public CryptoCoinBalanceListAdapter() {
super(CryptoCoinBalance.DIFF_CALLBACK);
}
+ public void setCryptoNetBalanceViewHolder(CryptoNetBalanceViewHolder cryptoNetBalanceViewHolder){
+ this.cryptoNetBalanceViewHolder = cryptoNetBalanceViewHolder;
+ }
+
@Override
public CryptoCoinBalanceViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.crypto_coin_balance_list_item,parent,false);
- return new CryptoCoinBalanceViewHolder(v);
+ return new CryptoCoinBalanceViewHolder(v, cryptoNetBalanceViewHolder);
}
@Override
diff --git a/app/src/main/java/cy/agorise/crystalwallet/views/CryptoCoinBalanceListView.java b/app/src/main/java/cy/agorise/crystalwallet/views/CryptoCoinBalanceListView.java
index 0c3e5ed..df4958f 100644
--- a/app/src/main/java/cy/agorise/crystalwallet/views/CryptoCoinBalanceListView.java
+++ b/app/src/main/java/cy/agorise/crystalwallet/views/CryptoCoinBalanceListView.java
@@ -83,7 +83,7 @@ public class CryptoCoinBalanceListView extends RelativeLayout {
*
* @param data the list of crypto coin balances that will be show to the user
*/
- public void setData(List data){
+ public void setData(List data, CryptoNetBalanceViewHolder cryptoNetBalanceViewHolder){
//initializes the list adapter
if (this.listAdapter == null) {
this.listAdapter = new CryptoCoinBalanceListAdapter();
@@ -92,6 +92,7 @@ public class CryptoCoinBalanceListView extends RelativeLayout {
//sets the data of the list adapter
if (data != null) {
+ this.listAdapter.setCryptoNetBalanceViewHolder(cryptoNetBalanceViewHolder);
this.listAdapter.setList(data);
}
}
diff --git a/app/src/main/java/cy/agorise/crystalwallet/views/CryptoCoinBalanceViewHolder.java b/app/src/main/java/cy/agorise/crystalwallet/views/CryptoCoinBalanceViewHolder.java
index a17cb05..aee2bdc 100644
--- a/app/src/main/java/cy/agorise/crystalwallet/views/CryptoCoinBalanceViewHolder.java
+++ b/app/src/main/java/cy/agorise/crystalwallet/views/CryptoCoinBalanceViewHolder.java
@@ -39,12 +39,15 @@ public class CryptoCoinBalanceViewHolder extends RecyclerView.ViewHolder {
private Context context;
- public CryptoCoinBalanceViewHolder(View itemView) {
+ private CryptoNetBalanceViewHolder cryptoNetBalanceViewHolder;
+
+ public CryptoCoinBalanceViewHolder(View itemView, CryptoNetBalanceViewHolder cryptoNetBalanceViewHolder) {
super(itemView);
//TODO: use ButterKnife to load this
cryptoCoinName = (TextView) itemView.findViewById(R.id.tvCryptoCoinName);
cryptoCoinBalance = (TextView) itemView.findViewById(R.id.tvCryptoCoinBalanceAmount);
cryptoCoinBalanceEquivalence = (TextView) itemView.findViewById(R.id.tvCryptoCoinBalanceEquivalence);
+ this.cryptoNetBalanceViewHolder = cryptoNetBalanceViewHolder;
this.context = itemView.getContext();
}
@@ -80,30 +83,41 @@ public class CryptoCoinBalanceViewHolder extends RecyclerView.ViewHolder {
public void onChanged(@Nullable GeneralSetting generalSetting) {
if (generalSetting != null) {
//Gets the currency object of the preferred currency
- CryptoCurrency currencyTo = CrystalDatabase.getAppDatabase(context).cryptoCurrencyDao().getByName(generalSetting.getValue());
+ LiveData currencyToLiveData = CrystalDatabase.getAppDatabase(context).cryptoCurrencyDao().getLiveDataByName(generalSetting.getValue());
- //Retrieves the equivalent value of this balance using the "From" currency and the "To" currency
- LiveData currencyEquivalenceLiveData = CrystalDatabase.getAppDatabase(context)
- .cryptoCurrencyEquivalenceDao().getByFromTo(
- currencyTo.getId(),
- currencyFrom.getId()
-
- );
-
- //Observes the equivalent value. If the equivalent value changes, this will keep the value showed correct
- currencyEquivalenceLiveData.observe((LifecycleOwner) context, new Observer() {
+ currencyToLiveData.observe((LifecycleOwner) context, new Observer() {
@Override
- public void onChanged(@Nullable CryptoCurrencyEquivalence cryptoCurrencyEquivalence) {
- if (cryptoCurrencyEquivalence != null) {
- CryptoCurrency toCurrency = CrystalDatabase.getAppDatabase(context).cryptoCurrencyDao().getById(cryptoCurrencyEquivalence.getFromCurrencyId());
- String equivalenceString = String.format(
- "%.2f",
- (balance.getBalance()/Math.pow(10,currencyFrom.getPrecision()))/
- (cryptoCurrencyEquivalence.getValue()/Math.pow(10,toCurrency.getPrecision()))
- );
+ public void onChanged(@Nullable CryptoCurrency cryptoCurrency) {
+ if (cryptoCurrency != null) {
+ CryptoCurrency currencyTo = cryptoCurrency;
- cryptoCoinBalanceEquivalence.setText(
- equivalenceString + " " + toCurrency.getName());
+ //Retrieves the equivalent value of this balance using the "From" currency and the "To" currency
+ LiveData currencyEquivalenceLiveData = CrystalDatabase.getAppDatabase(context)
+ .cryptoCurrencyEquivalenceDao().getByFromTo(
+ currencyTo.getId(),
+ currencyFrom.getId()
+
+ );
+
+ //Observes the equivalent value. If the equivalent value changes, this will keep the value showed correct
+ currencyEquivalenceLiveData.observe((LifecycleOwner) context, new Observer() {
+ @Override
+ public void onChanged(@Nullable CryptoCurrencyEquivalence cryptoCurrencyEquivalence) {
+ if (cryptoCurrencyEquivalence != null) {
+ CryptoCurrency toCurrency = CrystalDatabase.getAppDatabase(context).cryptoCurrencyDao().getById(cryptoCurrencyEquivalence.getFromCurrencyId());
+ double equivalentValue = (balance.getBalance() / Math.pow(10, currencyFrom.getPrecision())) /
+ (cryptoCurrencyEquivalence.getValue() / Math.pow(10, toCurrency.getPrecision()));
+ String equivalenceString = String.format(
+ "%.2f",
+ equivalentValue
+ );
+
+ cryptoNetBalanceViewHolder.setEquivalentBalance(balance,equivalentValue);
+ cryptoCoinBalanceEquivalence.setText(
+ equivalenceString + " " + toCurrency.getName());
+ }
+ }
+ });
}
}
});
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 5fe8fc2..1753a02 100644
--- a/app/src/main/java/cy/agorise/crystalwallet/views/CryptoNetBalanceViewHolder.java
+++ b/app/src/main/java/cy/agorise/crystalwallet/views/CryptoNetBalanceViewHolder.java
@@ -16,6 +16,7 @@ import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
+import java.util.HashMap;
import java.util.List;
import butterknife.BindView;
@@ -48,11 +49,18 @@ public class CryptoNetBalanceViewHolder extends RecyclerView.ViewHolder {
*/
TextView cryptoNetName;
+ /*
+ * the view holding the equivalent total of the crypto net user account
+ */
+ TextView cryptoNetEquivalentTotal;
+
/*
* The list view of the crypto coins balances of this crypto net balance
*/
CryptoCoinBalanceListView cryptoCoinBalanceListView;
+ HashMap equivalentTotalHashMap;
+
/*
* The button for sending transactions from this crypto net balance account
*/
@@ -86,6 +94,7 @@ public class CryptoNetBalanceViewHolder extends RecyclerView.ViewHolder {
//TODO: use ButterKnife to load the views
cryptoNetIcon = (ImageView) itemView.findViewById(R.id.ivCryptoNetIcon);
cryptoNetName = (TextView) itemView.findViewById(R.id.tvCryptoNetName);
+ cryptoNetEquivalentTotal = (TextView) itemView.findViewById(R.id.tvCryptoNetEquivalentTotal);
cryptoCoinBalanceListView = (CryptoCoinBalanceListView) itemView.findViewById(R.id.cryptoCoinBalancesListView);
btnSendFromThisAccount = (Button) itemView.findViewById(R.id.btnSendFromThisAccount);
btnReceiveToThisAccount = (Button) itemView.findViewById(R.id.btnReceiveWithThisAccount);
@@ -157,6 +166,23 @@ public class CryptoNetBalanceViewHolder extends RecyclerView.ViewHolder {
newFragment.show(ft, "ReceiveDialog");
}
+ public void setEquivalentBalance(CryptoCoinBalance cryptoCoinBalance, double equivalentValue){
+ if (this.equivalentTotalHashMap == null){
+ this.equivalentTotalHashMap = new HashMap();
+ }
+
+ if (cryptoCoinBalance != null) {
+ this.equivalentTotalHashMap.put(cryptoCoinBalance, equivalentValue);
+ float totalEquivalent = 0;
+
+ for (Double nextEquivalent : this.equivalentTotalHashMap.values()){
+ totalEquivalent += nextEquivalent;
+ }
+
+ this.cryptoNetEquivalentTotal.setText(""+totalEquivalent);
+ }
+ }
+
/*
* Binds this view with the data of an element of the list
*/
@@ -164,6 +190,7 @@ public class CryptoNetBalanceViewHolder extends RecyclerView.ViewHolder {
if (balance == null){
cryptoNetName.setText("loading...");
} else {
+ final CryptoNetBalanceViewHolder thisViewHolder = this;
this.cryptoNetAccountId = balance.getAccountId();
cryptoNetName.setText(balance.getCryptoNet().getLabel());
@@ -172,13 +199,15 @@ public class CryptoNetBalanceViewHolder extends RecyclerView.ViewHolder {
cryptoCoinBalanceListViewModel.init(balance.getAccountId());
LiveData> cryptoCoinBalanceData = cryptoCoinBalanceListViewModel.getCryptoCoinBalanceList();
- cryptoCoinBalanceListView.setData(null);
+ cryptoCoinBalanceListView.setData(null, this);
//Observes the livedata, so any of its changes on the database will be reloaded here
cryptoCoinBalanceData.observe((LifecycleOwner)this.itemView.getContext(), new Observer>() {
@Override
public void onChanged(List cryptoCoinBalances) {
- cryptoCoinBalanceListView.setData(cryptoCoinBalances);
+ cryptoCoinBalanceListView.setData(cryptoCoinBalances, thisViewHolder);
+
+
}
});
}
diff --git a/app/src/main/java/cy/agorise/crystalwallet/views/TransactionViewHolder.java b/app/src/main/java/cy/agorise/crystalwallet/views/TransactionViewHolder.java
index 364a581..ded083f 100644
--- a/app/src/main/java/cy/agorise/crystalwallet/views/TransactionViewHolder.java
+++ b/app/src/main/java/cy/agorise/crystalwallet/views/TransactionViewHolder.java
@@ -1,20 +1,30 @@
package cy.agorise.crystalwallet.views;
import android.arch.lifecycle.LifecycleOwner;
+import android.arch.lifecycle.LiveData;
+import android.arch.lifecycle.Observer;
import android.arch.lifecycle.ViewModelProviders;
import android.content.Context;
import android.content.Intent;
+import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.RelativeLayout;
import android.widget.TextView;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.time.LocalDateTime;
+import java.util.TimeZone;
+
import cy.agorise.crystalwallet.R;
import cy.agorise.crystalwallet.activities.CryptoCoinTransactionReceiptActivity;
import cy.agorise.crystalwallet.models.CryptoCoinTransaction;
import cy.agorise.crystalwallet.models.CryptoCurrency;
+import cy.agorise.crystalwallet.models.CryptoNetAccount;
import cy.agorise.crystalwallet.viewmodels.CryptoCurrencyViewModel;
+import cy.agorise.crystalwallet.viewmodels.CryptoNetAccountViewModel;
/**
* Created by Henry Varona on 17/9/2017.
@@ -26,16 +36,18 @@ public class TransactionViewHolder extends RecyclerView.ViewHolder {
/*
* The view holding the transaction "from"
*/
- private TextView transactionFrom;
+ private TextView tvFrom;
/*
* The view holding the transaction "to"
*/
- private TextView transactionTo;
+ private TextView tvTo;
/*
* The view holding the transaction amount
*/
- private TextView transactionAmount;
+ private TextView tvAmount;
+ private TextView tvEquivalent;
private TextView tvTransactionDate;
+ private TextView tvTransactionHour;
private View rootView;
private Fragment fragment;
@@ -48,10 +60,12 @@ public class TransactionViewHolder extends RecyclerView.ViewHolder {
this.cryptoCoinTransactionId = -1;
rootView = itemView.findViewById(R.id.rlTransactionItem);
- transactionFrom = (TextView) itemView.findViewById(R.id.fromText);
- transactionTo = (TextView) itemView.findViewById(R.id.toText);
- transactionAmount = (TextView) itemView.findViewById(R.id.amountText);
+ tvFrom = (TextView) itemView.findViewById(R.id.fromText);
+ tvTo = (TextView) itemView.findViewById(R.id.toText);
+ tvAmount = (TextView) itemView.findViewById(R.id.tvAmount);
+ tvEquivalent = (TextView) itemView.findViewById(R.id.tvEquivalent);
tvTransactionDate = (TextView) itemView.findViewById(R.id.tvTransactionDate);
+ tvTransactionHour = (TextView) itemView.findViewById(R.id.tvTransactionHour);
this.fragment = fragment;
rootView.setOnClickListener(new View.OnClickListener() {
@@ -85,9 +99,12 @@ public class TransactionViewHolder extends RecyclerView.ViewHolder {
* Clears all info in this element view
*/
public void clear(){
- transactionFrom.setText("loading...");
- transactionTo.setText("");
- transactionAmount.setText("");
+ tvFrom.setText("loading...");
+ tvTo.setText("");
+ tvAmount.setText("");
+ tvEquivalent.setText("");
+ tvTransactionDate.setText("");
+ tvTransactionHour.setText("");
}
/*
@@ -95,28 +112,53 @@ public class TransactionViewHolder extends RecyclerView.ViewHolder {
*/
public void bindTo(final CryptoCoinTransaction transaction/*, final OnTransactionClickListener listener*/) {
if (transaction == null){
- transactionFrom.setText("loading...");
- transactionTo.setText("");
- transactionAmount.setText("");
+ clear();
} else {
this.cryptoCoinTransactionId = transaction.getId();
CryptoCurrencyViewModel cryptoCurrencyViewModel = ViewModelProviders.of(this.fragment).get(CryptoCurrencyViewModel.class);
CryptoCurrency cryptoCurrency = cryptoCurrencyViewModel.getCryptoCurrencyById(transaction.getIdCurrency());
+ CryptoNetAccountViewModel cryptoNetAccountViewModel = ViewModelProviders.of(this.fragment).get(CryptoNetAccountViewModel.class);
+ cryptoNetAccountViewModel.loadCryptoNetAccount(transaction.getAccountId());
+
String amountString = String.format("%.2f",transaction.getAmount()/Math.pow(10,cryptoCurrency.getPrecision()));
- tvTransactionDate.setText(transaction.getDate().toString());
- transactionFrom.setText(transaction.getFrom());
- transactionTo.setText(transaction.getTo());
+ DateFormat dateFormat = new SimpleDateFormat("dd MMM");
+ dateFormat.setTimeZone(TimeZone.getTimeZone("cet"));
+ DateFormat hourFormat = new SimpleDateFormat("HH:mm:ss");
+ hourFormat.setTimeZone(TimeZone.getTimeZone("cet"));
+ tvTransactionDate.setText(dateFormat.format(transaction.getDate()));
+ tvTransactionHour.setText(hourFormat.format(transaction.getDate())+" CET");
+
+ tvFrom.setText(transaction.getFrom());
+ tvTo.setText(transaction.getTo());
+
+ LiveData cryptoNetAccountLiveData = cryptoNetAccountViewModel.getCryptoNetAccount();
+
+ cryptoNetAccountLiveData.observe(this.fragment, new Observer() {
+ @Override
+ public void onChanged(@Nullable CryptoNetAccount cryptoNetAccount) {
+ if (transaction.getInput()){
+ tvTo.setText(cryptoNetAccount.getName());
+ } else {
+ tvFrom.setText(cryptoNetAccount.getName());
+ }
+ }
+ });
+
+ String finalAmountText = "";
if (transaction.getInput()) {
- transactionAmount.setTextColor(itemView.getContext().getResources().getColor(R.color.green));
+ tvAmount.setTextColor(itemView.getContext().getResources().getColor(R.color.green));
+ finalAmountText = "+ "+amountString
+ + " "
+ + cryptoCurrency.getName();
} else {
- transactionAmount.setTextColor(itemView.getContext().getResources().getColor(R.color.red));
+ tvAmount.setTextColor(itemView.getContext().getResources().getColor(R.color.red));
+ finalAmountText = amountString
+ + " "
+ + cryptoCurrency.getName();
}
- transactionAmount.setText(
- amountString
- + " "
- + cryptoCurrency.getName());
+ tvAmount.setText(finalAmountText);
//This will load the transaction receipt when the user clicks this view
/*itemView.setOnClickListener(new View.OnClickListener() {
@Override
diff --git a/app/src/main/res/drawable/transaction_list_item_background.xml b/app/src/main/res/drawable/transaction_list_item_background.xml
new file mode 100644
index 0000000..2a4b404
--- /dev/null
+++ b/app/src/main/res/drawable/transaction_list_item_background.xml
@@ -0,0 +1,13 @@
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_create_contact.xml b/app/src/main/res/layout/activity_create_contact.xml
new file mode 100644
index 0000000..44df5aa
--- /dev/null
+++ b/app/src/main/res/layout/activity_create_contact.xml
@@ -0,0 +1,174 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_pin_request.xml b/app/src/main/res/layout/activity_pin_request.xml
new file mode 100644
index 0000000..d05df7d
--- /dev/null
+++ b/app/src/main/res/layout/activity_pin_request.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/balance_list_item.xml b/app/src/main/res/layout/balance_list_item.xml
index b2f51fc..f0f5e08 100644
--- a/app/src/main/res/layout/balance_list_item.xml
+++ b/app/src/main/res/layout/balance_list_item.xml
@@ -14,7 +14,7 @@
android:layout_alignParentStart="true"
android:layout_alignParentTop="true">
-
+
+
+ android:text="send"
+ android:visibility="gone" />
-
+ android:text="receive"
+ android:visibility="gone" />
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/contact_list_item.xml b/app/src/main/res/layout/contact_list_item.xml
index 121112f..2beaec2 100644
--- a/app/src/main/res/layout/contact_list_item.xml
+++ b/app/src/main/res/layout/contact_list_item.xml
@@ -34,6 +34,14 @@
android:layout_below="@+id/tvContactName"
android:layout_toRightOf="@+id/ivContactThumbnail"
android:text="Paid: Jan 1, 2001, 01:01"
- android:textColor="@android:color/darker_gray" />
+ android:textColor="@android:color/darker_gray"
+ android:visibility="gone" />
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/contact_selection_list_item.xml b/app/src/main/res/layout/contact_selection_list_item.xml
new file mode 100644
index 0000000..121112f
--- /dev/null
+++ b/app/src/main/res/layout/contact_selection_list_item.xml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_general_settings.xml b/app/src/main/res/layout/fragment_general_settings.xml
index 6364bb0..257d0f2 100644
--- a/app/src/main/res/layout/fragment_general_settings.xml
+++ b/app/src/main/res/layout/fragment_general_settings.xml
@@ -56,9 +56,9 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
- app:layout_constraintTop_toBottomOf="@+id/tvTaxableCountry"
+ app:layout_constraintEnd_toEndOf="@id/spBackupAsset"
app:layout_constraintStart_toStartOf="@id/spBackupAsset"
- app:layout_constraintEnd_toEndOf="@id/spBackupAsset"/>
+ app:layout_constraintTop_toBottomOf="@+id/tvTaxableCountry" />
+
+
+ app:layout_constraintTop_toBottomOf="@+id/tvCurrentPinError" />
+
-
+ app:layout_constraintTop_toBottomOf="@+id/tvNewPinError" />
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_transactions.xml b/app/src/main/res/layout/fragment_transactions.xml
index c982fd9..881c8da 100644
--- a/app/src/main/res/layout/fragment_transactions.xml
+++ b/app/src/main/res/layout/fragment_transactions.xml
@@ -4,9 +4,22 @@
android:layout_height="match_parent"
tools:context="cy.agorise.crystalwallet.fragments.TransactionsFragment">
-
+ android:layout_height="wrap_content">
+
+
+
diff --git a/app/src/main/res/layout/transaction_list_item.xml b/app/src/main/res/layout/transaction_list_item.xml
index c71db3f..67aec33 100644
--- a/app/src/main/res/layout/transaction_list_item.xml
+++ b/app/src/main/res/layout/transaction_list_item.xml
@@ -1,59 +1,111 @@
+ android:paddingTop="5dp">
-
-
+ android:layout_alignParentTop="true"
+ android:background="@drawable/transaction_list_item_background">
+
+
+
+
+
+
+
+
+ android:text="+ 1 BTS"
+ android:textAlignment="textEnd"
+ android:textSize="14sp"
+ android:textStyle="bold" />
+
+
+
+
+
\ No newline at end of file