Merge branch 'master' into master

This commit is contained in:
Severiano Jaramillo Quintanar 2018-03-13 15:57:47 -06:00 committed by GitHub
commit fcc20711dd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
68 changed files with 3261 additions and 438 deletions

View file

@ -63,4 +63,6 @@ dependencies {
annotationProcessor 'android.arch.lifecycle:compiler:1.0.0' annotationProcessor 'android.arch.lifecycle:compiler:1.0.0'
annotationProcessor 'android.arch.persistence.room:compiler:1.0.0' annotationProcessor 'android.arch.persistence.room:compiler:1.0.0'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1' annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'
compile 'com.squareup.picasso:picasso:2.5.2'
} }

View file

@ -2,7 +2,7 @@
"formatVersion": 1, "formatVersion": 1,
"database": { "database": {
"version": 2, "version": 2,
"identityHash": "a44ccb96c8213951403ed2a283fb3367", "identityHash": "22cb2a56b28a9f7088ec98d6a72f9f67",
"entities": [ "entities": [
{ {
"tableName": "account_seed", "tableName": "account_seed",
@ -235,7 +235,7 @@
}, },
{ {
"tableName": "contact", "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": [ "fields": [
{ {
"fieldPath": "mId", "fieldPath": "mId",
@ -249,6 +249,12 @@
"affinity": "TEXT", "affinity": "TEXT",
"notNull": false "notNull": false
}, },
{
"fieldPath": "mEmail",
"columnName": "email",
"affinity": "TEXT",
"notNull": false
},
{ {
"fieldPath": "mGravatar", "fieldPath": "mGravatar",
"columnName": "gravatar", "columnName": "gravatar",
@ -278,6 +284,70 @@
"name" "name"
], ],
"createSql": "CREATE UNIQUE INDEX `index_contact_name` ON `${TABLE_NAME}` (`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": [] "foreignKeys": []
@ -622,7 +692,7 @@
], ],
"setupQueries": [ "setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", "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\")"
] ]
} }
} }

View file

@ -1,11 +1,12 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="cy.agorise.crystalwallet"> package="cy.agorise.crystalwallet">
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application <application
android:name="cy.agorise.crystalwallet.application.CrystalApplication" android:name=".application.CrystalApplication"
android:allowBackup="true" android:allowBackup="true"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:label="Crystal" android:label="Crystal"
@ -21,7 +22,7 @@
</intent-filter> </intent-filter>
</activity> </activity>
<activity android:name=".activities.BoardActivity" <activity android:name=".activities.BoardActivity"
android:theme="@style/AppTheme.NoActionBar" > <!-- Dirty trick to avoid toolbar error on balance --> android:theme="@style/AppTheme.NoActionBar" >
</activity> </activity>
<activity android:name=".activities.AccountSeedsManagementActivity" > <activity android:name=".activities.AccountSeedsManagementActivity" >
</activity> </activity>
@ -37,20 +38,27 @@
</activity> </activity>
<activity android:name=".activities.BackupSeedActivity" > <activity android:name=".activities.BackupSeedActivity" >
</activity> </activity>
<activity android:name=".activities.SettingsActivity" <activity android:name=".activities.PinRequestActivity">
</activity>
<activity
android:name=".activities.SettingsActivity"
android:theme="@style/AppTheme.NoActionBar" android:theme="@style/AppTheme.NoActionBar"
android:windowSoftInputMode="adjustPan"> android:windowSoftInputMode="adjustPan">
</activity> </activity>
<activity android:name=".activities.AccountsActivity" <activity
android:theme="@style/ActivityDialog" android:name=".activities.AccountsActivity"
android:parentActivityName=".activities.BoardActivity" > android:parentActivityName=".activities.BoardActivity"
android:theme="@style/ActivityDialog">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value=".activities.BoardActivity" /> android:value=".activities.BoardActivity" />
</activity> </activity>
<service android:name=".service.CrystalWalletService" <service
android:name=".service.CrystalWalletService"
android:exported="false" /> android:exported="false" />
<activity android:name=".activities.CreateContactActivity"></activity>
</application> </application>
</manifest> </manifest>

View file

@ -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 * dispatch the user to the receive fragment using this account
*/ */
@ -215,8 +221,16 @@ public class BoardActivity extends AppCompatActivity {
} }
ft.addToBackStack(null); 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. // Create and show the dialog.
ReceiveTransactionFragment newFragment = ReceiveTransactionFragment.newInstance(this.cryptoNetAccountId); ReceiveTransactionFragment newFragment = ReceiveTransactionFragment.newInstance(receiveCryptoNetAccountId);
newFragment.show(ft, "ReceiveDialog"); newFragment.show(ft, "ReceiveDialog");
} }

View file

@ -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<ContactAddress> 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<Contact> contactLiveData = contactViewModel.getContact();
final CreateContactActivity thisActivity = this;
contactLiveData.observe(this, new Observer<Contact>() {
@Override
public void onChanged(@Nullable Contact contactChanged) {
if (contactChanged != null){
contact = contactChanged;
etName.setText(contact.getName());
etEmail.setText(contact.getEmail());
LiveData<List<ContactAddress>> contactAddresses = contactViewModel.getContactAddresses();
contactAddresses.observe(thisActivity, new Observer<List<ContactAddress>>() {
@Override
public void onChanged(@Nullable List<ContactAddress> 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<ContactAddress>();
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());
}
}
});
}
}

View file

@ -1,11 +1,6 @@
package cy.agorise.crystalwallet.activities; 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.lifecycle.ViewModelProviders;
import android.arch.paging.PagedList;
import android.content.Intent; import android.content.Intent;
import android.media.MediaPlayer; import android.media.MediaPlayer;
import android.os.Bundle; 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.RandomCryptoNetAccountGenerator;
import cy.agorise.crystalwallet.randomdatagenerators.RandomSeedGenerator; import cy.agorise.crystalwallet.randomdatagenerators.RandomSeedGenerator;
import cy.agorise.crystalwallet.randomdatagenerators.RandomTransactionsGenerator; import cy.agorise.crystalwallet.randomdatagenerators.RandomTransactionsGenerator;
import cy.agorise.crystalwallet.application.CrystalSecurityMonitor;
import cy.agorise.crystalwallet.viewmodels.AccountSeedListViewModel; import cy.agorise.crystalwallet.viewmodels.AccountSeedListViewModel;
import cy.agorise.crystalwallet.viewmodels.TransactionListViewModel; import cy.agorise.crystalwallet.viewmodels.TransactionListViewModel;
import cy.agorise.crystalwallet.views.TransactionListView; import cy.agorise.crystalwallet.views.TransactionListView;
import static cy.agorise.crystalwallet.R.string.transactions;
public class IntroActivity extends AppCompatActivity { public class IntroActivity extends AppCompatActivity {
TransactionListViewModel transactionListViewModel; 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 //Checks if the user has any seed created
/*AccountSeedListViewModel accountSeedListViewModel = ViewModelProviders.of(this).get(AccountSeedListViewModel.class); /*AccountSeedListViewModel accountSeedListViewModel = ViewModelProviders.of(this).get(AccountSeedListViewModel.class);

View file

@ -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<List<GeneralSetting>> generalSettingsLiveData = generalSettingListViewModel.getGeneralSettingList();
generalSettingsLiveData.observe(this, new Observer<List<GeneralSetting>>() {
@Override
public void onChanged(@Nullable List<GeneralSetting> 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();
}
}
}

View file

@ -37,8 +37,9 @@ public abstract class BitsharesFaucetApiGenerator {
* @param url The url of the faucet * @param url The url of the faucet
* @return The bitshares id of the registered account, or null * @return The bitshares id of the registered account, or null
*/ */
public static boolean registerBitsharesAccount(String accountName, String ownerKey, public static void registerBitsharesAccount(String accountName, String ownerKey,
String activeKey, String memoKey, String url){ String activeKey, String memoKey, String url,
final ApiRequest request){
CreateAccountPetition petition = new CreateAccountPetition(); CreateAccountPetition petition = new CreateAccountPetition();
final Account account = new Account(); final Account account = new Account();
account.name=accountName; account.name=accountName;
@ -62,8 +63,7 @@ public abstract class BitsharesFaucetApiGenerator {
HashMap<String, HashMap> hashMap = new HashMap<>(); HashMap<String, HashMap> hashMap = new HashMap<>();
hashMap.put("account", hm); hashMap.put("account", hm);
final boolean[] answer = {false};
final Object SYNC = new Object();
try { try {
ServiceGenerator sg = new ServiceGenerator(url); ServiceGenerator sg = new ServiceGenerator(url);
IWebService service = sg.getService(IWebService.class); IWebService service = sg.getService(IWebService.class);
@ -78,56 +78,36 @@ public abstract class BitsharesFaucetApiGenerator {
if (resp.account != null) { if (resp.account != null) {
try { try {
if(resp.account.name.equals(account.name)) { if(resp.account.name.equals(account.name)) {
synchronized (SYNC){ request.getListener().success(true,request.getId());
answer[0] = true;
SYNC.notifyAll();
}
}else{ }else{
System.out.println("ERROR account name different" + resp.account.name); request.getListener().fail(request.getId());
//ERROR
synchronized (SYNC) {
SYNC.notifyAll();
}
} }
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
synchronized (SYNC) { request.getListener().fail(request.getId());
SYNC.notifyAll();
}
} }
}else{ }else{
System.out.println("ERROR response doesn't have account " + response.message()); System.out.println("ERROR response doesn't have account " + response.message());
//ERROR request.getListener().fail(request.getId());
synchronized (SYNC) {
SYNC.notifyAll();
}
} }
}else{ }else{
System.out.println("ERROR fetching info"); System.out.println("ERROR fetching info");
//ERROR request.getListener().fail(request.getId());
synchronized (SYNC) {
SYNC.notifyAll();
}
} }
} }
@Override @Override
public void onFailure(Call<RegisterAccountResponse> call, Throwable t) { public void onFailure(Call<RegisterAccountResponse> call, Throwable t) {
t.printStackTrace(); t.printStackTrace();
synchronized (SYNC) { request.getListener().fail(request.getId());
SYNC.notifyAll();
}
} }
}); });
synchronized (SYNC) {
SYNC.wait(60000);
}
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} request.getListener().fail(request.getId());
return answer[0]; }
} }
/** /**

View file

@ -12,14 +12,14 @@ import cy.agorise.crystalwallet.dao.BitsharesAssetDao;
import cy.agorise.crystalwallet.dao.CryptoCoinBalanceDao; import cy.agorise.crystalwallet.dao.CryptoCoinBalanceDao;
import cy.agorise.crystalwallet.dao.CryptoCurrencyDao; import cy.agorise.crystalwallet.dao.CryptoCurrencyDao;
import cy.agorise.crystalwallet.dao.CrystalDatabase; import cy.agorise.crystalwallet.dao.CrystalDatabase;
import cy.agorise.crystalwallet.dao.TransactionDao; import cy.agorise.crystalwallet.enums.CryptoNet;
import cy.agorise.crystalwallet.manager.BitsharesAccountManager;
import cy.agorise.crystalwallet.models.BitsharesAsset; import cy.agorise.crystalwallet.models.BitsharesAsset;
import cy.agorise.crystalwallet.models.BitsharesAssetInfo; import cy.agorise.crystalwallet.models.BitsharesAssetInfo;
import cy.agorise.crystalwallet.models.CryptoCoinBalance; import cy.agorise.crystalwallet.models.CryptoCoinBalance;
import cy.agorise.crystalwallet.models.CryptoCoinTransaction; import cy.agorise.crystalwallet.models.CryptoCoinTransaction;
import cy.agorise.crystalwallet.models.CryptoCurrency; import cy.agorise.crystalwallet.models.CryptoCurrency;
import cy.agorise.crystalwallet.models.CryptoCurrencyEquivalence; import cy.agorise.crystalwallet.models.CryptoCurrencyEquivalence;
import cy.agorise.crystalwallet.network.CryptoNetManager;
import cy.agorise.crystalwallet.network.WebSocketThread; import cy.agorise.crystalwallet.network.WebSocketThread;
import cy.agorise.graphenej.Address; import cy.agorise.graphenej.Address;
import cy.agorise.graphenej.Asset; 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.NodeErrorListener;
import cy.agorise.graphenej.interfaces.SubscriptionListener; import cy.agorise.graphenej.interfaces.SubscriptionListener;
import cy.agorise.graphenej.interfaces.WitnessResponseListener; import cy.agorise.graphenej.interfaces.WitnessResponseListener;
import cy.agorise.graphenej.models.AccountBalanceUpdate;
import cy.agorise.graphenej.models.AccountProperties; import cy.agorise.graphenej.models.AccountProperties;
import cy.agorise.graphenej.models.BaseResponse; import cy.agorise.graphenej.models.BaseResponse;
import cy.agorise.graphenej.models.BroadcastedTransaction; import cy.agorise.graphenej.models.BroadcastedTransaction;
import cy.agorise.graphenej.models.HistoricalTransfer;
import cy.agorise.graphenej.models.SubscriptionResponse; import cy.agorise.graphenej.models.SubscriptionResponse;
import cy.agorise.graphenej.models.WitnessResponse; import cy.agorise.graphenej.models.WitnessResponse;
import cy.agorise.graphenej.operations.TransferOperation; import cy.agorise.graphenej.operations.TransferOperation;
@ -62,12 +60,8 @@ public abstract class GrapheneApiGenerator {
//TODO network connections //TODO network connections
//TODO make to work with all Graphene type, not only bitshares //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"; public static String faucetUrl = "http://185.208.208.147:5010";
private static String equivalentUrl = "http://185.208.208.147:8090"; private static String equivalentUrl = "wss://bitshares.openledger.info/ws";
//public static String url = "wss://bitshares.openledger.info/ws";
//private static Str ing equivalentUrl = "wss://bitshares.openledger.info/ws";
// The message broker for bitshares // The message broker for bitshares
private static SubscriptionMessagesHub bitsharesSubscriptionHub = new SubscriptionMessagesHub("", "", true, new NodeErrorListener() { 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 * 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 * 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) { public void onError(BaseResponse.Error error) {
request.getListener().fail(request.getId()); request.getListener().fail(request.getId());
} }
}),url); }),CryptoNetManager.getURL(CryptoNet.BITSHARES));
thread.start(); thread.start();
} }
@ -142,7 +136,7 @@ public abstract class GrapheneApiGenerator {
public void onError(BaseResponse.Error error) { public void onError(BaseResponse.Error error) {
request.getListener().fail(request.getId()); request.getListener().fail(request.getId());
} }
}),url); }),CryptoNetManager.getURL(CryptoNet.BITSHARES));
thread.start(); thread.start();
} }
@ -169,7 +163,7 @@ public abstract class GrapheneApiGenerator {
public void onError(BaseResponse.Error error) { public void onError(BaseResponse.Error error) {
request.getListener().fail(request.getId()); request.getListener().fail(request.getId());
} }
}),url); }),CryptoNetManager.getURL(CryptoNet.BITSHARES));
thread.start(); thread.start();
} }
@ -196,7 +190,7 @@ public abstract class GrapheneApiGenerator {
public void onError(BaseResponse.Error error) { public void onError(BaseResponse.Error error) {
request.getListener().fail(request.getId()); request.getListener().fail(request.getId());
} }
}),url); }),CryptoNetManager.getURL(CryptoNet.BITSHARES));
thread.start(); thread.start();
} }
@ -223,7 +217,7 @@ public abstract class GrapheneApiGenerator {
public void onError(BaseResponse.Error error) { public void onError(BaseResponse.Error error) {
request.getListener().fail(request.getId()); request.getListener().fail(request.getId());
} }
}),url); }),CryptoNetManager.getURL(CryptoNet.BITSHARES));
thread.start(); thread.start();
} }
@ -247,7 +241,7 @@ public abstract class GrapheneApiGenerator {
public void onError(BaseResponse.Error error) { public void onError(BaseResponse.Error error) {
request.getListener().fail(request.getId()); request.getListener().fail(request.getId());
} }
}),url); }),CryptoNetManager.getURL(CryptoNet.BITSHARES));
thread.start(); thread.start();
} }
@ -292,7 +286,7 @@ public abstract class GrapheneApiGenerator {
public void onError(BaseResponse.Error error) { public void onError(BaseResponse.Error error) {
request.getListener().fail(request.getId()); request.getListener().fail(request.getId());
} }
}),url); }),CryptoNetManager.getURL(CryptoNet.BITSHARES));
thread.start(); thread.start();
} }
@ -340,7 +334,7 @@ public abstract class GrapheneApiGenerator {
public void onError(BaseResponse.Error error) { public void onError(BaseResponse.Error error) {
request.getListener().fail(request.getId()); request.getListener().fail(request.getId());
} }
}),url); }),CryptoNetManager.getURL(CryptoNet.BITSHARES));
thread.start(); thread.start();
} }
@ -353,12 +347,10 @@ public abstract class GrapheneApiGenerator {
*/ */
public static void subscribeBitsharesAccount(final long accountId, final String accountBitsharesId, public static void subscribeBitsharesAccount(final long accountId, final String accountBitsharesId,
final Context context){ final Context context){
System.out.println("GrapheneAPI subscribe to account balance update");
if(!currentBitsharesListener.containsKey(accountId)){ if(!currentBitsharesListener.containsKey(accountId)){
CrystalDatabase db = CrystalDatabase.getAppDatabase(context); CrystalDatabase db = CrystalDatabase.getAppDatabase(context);
final BitsharesAssetDao bitsharesAssetDao = db.bitsharesAssetDao(); final BitsharesAssetDao bitsharesAssetDao = db.bitsharesAssetDao();
final CryptoCurrencyDao cryptoCurrencyDao = db.cryptoCurrencyDao(); final CryptoCurrencyDao cryptoCurrencyDao = db.cryptoCurrencyDao();
final TransactionDao transactionDao = db.transactionDao();
SubscriptionListener balanceListener = new SubscriptionListener() { SubscriptionListener balanceListener = new SubscriptionListener() {
@Override @Override
public ObjectType getInterestObjectType() { public ObjectType getInterestObjectType() {
@ -374,16 +366,15 @@ public abstract class GrapheneApiGenerator {
BroadcastedTransaction transactionUpdate = (BroadcastedTransaction) update; BroadcastedTransaction transactionUpdate = (BroadcastedTransaction) update;
for(BaseOperation operation : transactionUpdate.getTransaction().getOperations()){ for(BaseOperation operation : transactionUpdate.getTransaction().getOperations()){
if(operation instanceof TransferOperation){ if(operation instanceof TransferOperation){
TransferOperation tOperation = (TransferOperation) operation; final TransferOperation tOperation = (TransferOperation) operation;
if(tOperation.getFrom().getObjectId().equals(accountBitsharesId) || tOperation.getTo().getObjectId().equals(accountBitsharesId)){ if(tOperation.getFrom().getObjectId().equals(accountBitsharesId) || tOperation.getTo().getObjectId().equals(accountBitsharesId)){
GrapheneApiGenerator.getAccountBalance(accountId,accountBitsharesId,context); GrapheneApiGenerator.getAccountBalance(accountId,accountBitsharesId,context);
CryptoCoinTransaction transaction = new CryptoCoinTransaction(); final CryptoCoinTransaction transaction = new CryptoCoinTransaction();
transaction.setAccountId(accountId); transaction.setAccountId(accountId);
transaction.setAmount(tOperation.getAssetAmount().getAmount().longValue()); transaction.setAmount(tOperation.getAssetAmount().getAmount().longValue());
BitsharesAssetInfo info = bitsharesAssetDao.getBitsharesAssetInfoById(tOperation.getAssetAmount().getAsset().getObjectId()); BitsharesAssetInfo info = bitsharesAssetDao.getBitsharesAssetInfoById(tOperation.getAssetAmount().getAsset().getObjectId());
if (info == null) { if (info == null) {
//The cryptoCurrency is not in the database, queringfor its data //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() { ApiRequest assetRequest = new ApiRequest(0, new ApiRequestListener() {
@Override @Override
public void success(Object answer, int idPetition) { public void success(Object answer, int idPetition) {
@ -394,40 +385,21 @@ public abstract class GrapheneApiGenerator {
info.setCryptoCurrencyId(idCryptoCurrency); info.setCryptoCurrencyId(idCryptoCurrency);
asset.setId((int)idCryptoCurrency); asset.setId((int)idCryptoCurrency);
bitsharesAssetDao.insertBitsharesAssetInfo(info); bitsharesAssetDao.insertBitsharesAssetInfo(info);
} saveTransaction(transaction,(int)info.getCryptoCurrencyId(),accountBitsharesId,tOperation,context);
synchronized (SYNC){
SYNC.notifyAll();
} }
} }
@Override @Override
public void fail(int idPetition) { public void fail(int idPetition) {
synchronized (SYNC){ //TODO error retrieving asset
SYNC.notifyAll();
}
} }
}); });
ArrayList<String> assets = new ArrayList<>(); ArrayList<String> assets = new ArrayList<>();
assets.add(tOperation.getAssetAmount().getAsset().getObjectId()); assets.add(tOperation.getAssetAmount().getAsset().getObjectId());
GrapheneApiGenerator.getAssetById(assets,assetRequest); GrapheneApiGenerator.getAssetById(assets,assetRequest);
}else{
synchronized (SYNC){ saveTransaction(transaction,(int)info.getCryptoCurrencyId(),accountBitsharesId,tOperation,context);
try {SYNC.wait(60000);} catch (InterruptedException ignore) {}
} }
info = bitsharesAssetDao.getBitsharesAssetInfoById(tOperation.getAssetAmount().getAsset().getObjectId());
}
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 * Cancels all bitshares account subscriptions
*/ */
@ -515,7 +507,7 @@ public abstract class GrapheneApiGenerator {
public void onError(BaseResponse.Error error) { public void onError(BaseResponse.Error error) {
} }
}),url); }),CryptoNetManager.getURL(CryptoNet.BITSHARES));
thread.start(); thread.start();
@ -542,7 +534,7 @@ public abstract class GrapheneApiGenerator {
public void onError(BaseResponse.Error error) { public void onError(BaseResponse.Error error) {
request.getListener().fail(request.getId()); request.getListener().fail(request.getId());
} }
}),url); }),CryptoNetManager.getURL(CryptoNet.BITSHARES));
thread.start(); thread.start();
} }
@ -674,7 +666,7 @@ public abstract class GrapheneApiGenerator {
Converter converter = new Converter(); Converter converter = new Converter();
order.getSellPrice().base.getAsset().setPrecision(baseAsset.getPrecision()); order.getSellPrice().base.getAsset().setPrecision(baseAsset.getPrecision());
order.getSellPrice().quote.getAsset().setPrecision(quoteAsset.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()); CryptoCurrencyEquivalence equivalence = new CryptoCurrencyEquivalence(baseAsset.getId(), quoteAsset.getId(), (int) (Math.pow(10, baseAsset.getPrecision()) * equiValue), new Date());
CrystalDatabase.getAppDatabase(context).cryptoCurrencyEquivalenceDao().insertCryptoCurrencyEquivalence(equivalence); CrystalDatabase.getAppDatabase(context).cryptoCurrencyEquivalenceDao().insertCryptoCurrencyEquivalence(equivalence);
break; break;

View file

@ -6,6 +6,11 @@ import android.content.Intent;
import com.idescout.sql.SqlScoutServer; import com.idescout.sql.SqlScoutServer;
import cy.agorise.crystalwallet.dao.CrystalDatabase; 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; import cy.agorise.crystalwallet.service.CrystalWalletService;
/** /**
@ -15,6 +20,25 @@ import cy.agorise.crystalwallet.service.CrystalWalletService;
*/ */
public class CrystalApplication extends Application { 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 @Override
public void onCreate() { public void onCreate() {
@ -24,6 +48,36 @@ public class CrystalApplication extends Application {
CrystalDatabase db = CrystalDatabase.getAppDatabase(this.getApplicationContext()); CrystalDatabase db = CrystalDatabase.getAppDatabase(this.getApplicationContext());
SqlScoutServer.create(this, getPackageName()); 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); Intent intent = new Intent(getApplicationContext(), CrystalWalletService.class);
startService(intent); startService(intent);
} }

View file

@ -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<List<GeneralSetting>> generalSettingsLiveData = generalSettingListViewModel.getGeneralSettingList();
generalSettingsLiveData.observe(fragmentActivity, new Observer<List<GeneralSetting>>() {
@Override
public void onChanged(@Nullable List<GeneralSetting> 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) {
//
}
}

View file

@ -3,13 +3,16 @@ package cy.agorise.crystalwallet.dao;
import android.arch.lifecycle.LiveData; import android.arch.lifecycle.LiveData;
import android.arch.paging.LivePagedListProvider; import android.arch.paging.LivePagedListProvider;
import android.arch.persistence.room.Dao; import android.arch.persistence.room.Dao;
import android.arch.persistence.room.Delete;
import android.arch.persistence.room.Insert; import android.arch.persistence.room.Insert;
import android.arch.persistence.room.OnConflictStrategy; import android.arch.persistence.room.OnConflictStrategy;
import android.arch.persistence.room.Query; import android.arch.persistence.room.Query;
import android.arch.persistence.room.Update;
import java.util.List; import java.util.List;
import cy.agorise.crystalwallet.models.Contact; import cy.agorise.crystalwallet.models.Contact;
import cy.agorise.crystalwallet.models.ContactAddress;
import cy.agorise.crystalwallet.models.CryptoCoinTransaction; import cy.agorise.crystalwallet.models.CryptoCoinTransaction;
/** /**
@ -24,6 +27,33 @@ public interface ContactDao {
@Query("SELECT * FROM contact ORDER BY name ASC") @Query("SELECT * FROM contact ORDER BY name ASC")
LivePagedListProvider<Integer, Contact> contactsByName(); LivePagedListProvider<Integer, Contact> 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<Integer, Contact> contactsByNameAndCryptoNet(String cryptoNet);
@Query("SELECT * FROM contact WHERE id = :id") @Query("SELECT * FROM contact WHERE id = :id")
LiveData<Contact> getById(long id); LiveData<Contact> 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<List<ContactAddress>> 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);
} }

View file

@ -1,5 +1,6 @@
package cy.agorise.crystalwallet.dao; package cy.agorise.crystalwallet.dao;
import android.arch.lifecycle.LiveData;
import android.arch.persistence.room.Dao; import android.arch.persistence.room.Dao;
import android.arch.persistence.room.Insert; import android.arch.persistence.room.Insert;
import android.arch.persistence.room.OnConflictStrategy; import android.arch.persistence.room.OnConflictStrategy;
@ -28,6 +29,9 @@ public interface CryptoCurrencyDao {
@Query("SELECT * FROM crypto_currency WHERE id IN (:ids)") @Query("SELECT * FROM crypto_currency WHERE id IN (:ids)")
List<CryptoCurrency> getByIds(List<Long> ids); List<CryptoCurrency> getByIds(List<Long> ids);
@Query("SELECT * FROM crypto_currency WHERE name = :name")
LiveData<CryptoCurrency> getLiveDataByName(String name);
@Query("SELECT * FROM crypto_currency WHERE name = :name") @Query("SELECT * FROM crypto_currency WHERE name = :name")
CryptoCurrency getByName(String name); CryptoCurrency getByName(String name);

View file

@ -10,6 +10,7 @@ import cy.agorise.crystalwallet.dao.converters.Converters;
import cy.agorise.crystalwallet.models.AccountSeed; import cy.agorise.crystalwallet.models.AccountSeed;
import cy.agorise.crystalwallet.models.BitsharesAssetInfo; import cy.agorise.crystalwallet.models.BitsharesAssetInfo;
import cy.agorise.crystalwallet.models.Contact; import cy.agorise.crystalwallet.models.Contact;
import cy.agorise.crystalwallet.models.ContactAddress;
import cy.agorise.crystalwallet.models.CryptoCoinBalance; import cy.agorise.crystalwallet.models.CryptoCoinBalance;
import cy.agorise.crystalwallet.models.CryptoCoinTransaction; import cy.agorise.crystalwallet.models.CryptoCoinTransaction;
import cy.agorise.crystalwallet.models.CryptoCurrency; import cy.agorise.crystalwallet.models.CryptoCurrency;
@ -28,6 +29,7 @@ import cy.agorise.crystalwallet.models.GrapheneAccountInfo;
CryptoNetAccount.class, CryptoNetAccount.class,
CryptoCoinTransaction.class, CryptoCoinTransaction.class,
Contact.class, Contact.class,
ContactAddress.class,
CryptoCurrency.class, CryptoCurrency.class,
CryptoCoinBalance.class, CryptoCoinBalance.class,
GrapheneAccountInfo.class, GrapheneAccountInfo.class,

View file

@ -7,6 +7,7 @@ import android.arch.persistence.room.Insert;
import android.arch.persistence.room.OnConflictStrategy; import android.arch.persistence.room.OnConflictStrategy;
import android.arch.persistence.room.Query; import android.arch.persistence.room.Query;
import java.util.Date;
import java.util.List; import java.util.List;
import cy.agorise.crystalwallet.models.CryptoCoinTransaction; import cy.agorise.crystalwallet.models.CryptoCoinTransaction;
@ -32,6 +33,9 @@ public interface TransactionDao {
@Query("SELECT * FROM crypto_coin_transaction WHERE id = :id") @Query("SELECT * FROM crypto_coin_transaction WHERE id = :id")
LiveData<CryptoCoinTransaction> getById(long id); LiveData<CryptoCoinTransaction> 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) @Insert(onConflict = OnConflictStrategy.REPLACE)
public long[] insertTransaction(CryptoCoinTransaction... transactions); public long[] insertTransaction(CryptoCoinTransaction... transactions);

View file

@ -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<PagedList<Contact>> contactsLiveData = contactListViewModel.getContactList();
contactsLiveData.observe(this, new Observer<PagedList<Contact>>() {
@Override
public void onChanged(@Nullable PagedList<Contact> 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();
}
}

View file

@ -1,14 +1,28 @@
package cy.agorise.crystalwallet.fragments; 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.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment; import android.support.v4.app.Fragment;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import butterknife.BindView;
import butterknife.ButterKnife;
import cy.agorise.crystalwallet.R; 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 { public class ContactsFragment extends Fragment {
@BindView(R.id.vContactListView)
ContactListView contactListView;
public ContactsFragment() { public ContactsFragment() {
// Required empty public constructor // Required empty public constructor
} }
@ -29,6 +43,20 @@ public class ContactsFragment extends Fragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container, public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) { Bundle savedInstanceState) {
// Inflate the layout for this fragment // 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<PagedList<Contact>> contactsLiveData = contactListViewModel.getContactList();
contactsLiveData.observe(this, new Observer<PagedList<Contact>>() {
@Override
public void onChanged(@Nullable PagedList<Contact> contacts) {
contactListView.setData(contacts);
}
});
return v;
} }
} }

View file

@ -1,19 +1,44 @@
package cy.agorise.crystalwallet.fragments; 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.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment; import android.support.v4.app.Fragment;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; 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.ButterKnife;
import butterknife.OnItemSelected;
import cy.agorise.crystalwallet.R; import cy.agorise.crystalwallet.R;
import cy.agorise.crystalwallet.models.GeneralSetting;
import cy.agorise.crystalwallet.viewmodels.GeneralSettingListViewModel;
/** /**
* Created by xd on 12/28/17. * Created by xd on 12/28/17.
*/ */
public class GeneralSettingsFragment extends Fragment { public class GeneralSettingsFragment extends Fragment {
private HashMap<String,String> countriesMap;
private GeneralSettingListViewModel generalSettingListViewModel;
private LiveData<List<GeneralSetting>> generalSettingListLiveData;
@BindView (R.id.spTaxableCountry)
Spinner spTaxableCountry;
public GeneralSettingsFragment() { public GeneralSettingsFragment() {
// Required empty public constructor // Required empty public constructor
} }
@ -37,6 +62,83 @@ public class GeneralSettingsFragment extends Fragment {
View v = inflater.inflate(R.layout.fragment_general_settings, container, false); View v = inflater.inflate(R.layout.fragment_general_settings, container, false);
ButterKnife.bind(this, v); ButterKnife.bind(this, v);
generalSettingListViewModel = ViewModelProviders.of(this).get(GeneralSettingListViewModel.class);
generalSettingListLiveData = generalSettingListViewModel.getGeneralSettingList();
// Initializes the countries spinner
countriesMap = new HashMap<String, String>();
String[] countryCodeList = Locale.getISOCountries();
ArrayList<String> countryAndCurrencyList = new ArrayList<String>();
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<String> countryAdapter = new ArrayAdapter<String>(this.getContext(), android.R.layout.simple_spinner_item, countryAndCurrencyList);
spTaxableCountry.setAdapter(countryAdapter);
//Observes the general settings data
generalSettingListLiveData.observe(this, new Observer<List<GeneralSetting>>() {
@Override
public void onChanged(@Nullable List<GeneralSetting> generalSettings) {
loadSettings(generalSettings);
}
});
return v; 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<GeneralSetting> generalSettings){
for (GeneralSetting generalSetting:generalSettings) {
if (generalSetting.getName().equals(GeneralSetting.SETTING_NAME_PREFERED_COUNTRY)){
String preferedCountryCode = generalSetting.getValue();
spTaxableCountry.setSelection(((ArrayAdapter<String>)spTaxableCountry.getAdapter()).getPosition(countriesMap.get(preferedCountryCode)));
}
}
}
} }

View file

@ -1,19 +1,60 @@
package cy.agorise.crystalwallet.fragments; 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.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment; import android.support.v4.app.Fragment;
import android.text.Editable;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; 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.ButterKnife;
import butterknife.OnTextChanged;
import cy.agorise.crystalwallet.R; 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. * 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() { public PinSecurityFragment() {
// Required empty public constructor // Required empty public constructor
@ -33,6 +74,140 @@ public class PinSecurityFragment extends Fragment {
View v = inflater.inflate(R.layout.fragment_pin_security, container, false); View v = inflater.inflate(R.layout.fragment_pin_security, container, false);
ButterKnife.bind(this, v); ButterKnife.bind(this, v);
generalSettingListViewModel = ViewModelProviders.of(this).get(GeneralSettingListViewModel.class);
LiveData<List<GeneralSetting>> generalSettingsLiveData = generalSettingListViewModel.getGeneralSettingList();
pinSecurityValidator = new PinSecurityValidator(this.getContext(), etCurrentPin, etNewPin, etConfirmPin);
pinSecurityValidator.setListener(this);
generalSettingsLiveData.observe(this, new Observer<List<GeneralSetting>>() {
@Override
public void onChanged(@Nullable List<GeneralSetting> 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; 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());
}
}
});
}
} }

View file

@ -4,6 +4,7 @@ import android.app.Dialog;
import android.app.LauncherActivity; import android.app.LauncherActivity;
import android.arch.lifecycle.LiveData; import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.Observer; import android.arch.lifecycle.Observer;
import android.arch.lifecycle.ViewModelProviders;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.os.Bundle; import android.os.Bundle;
@ -31,6 +32,8 @@ import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix; import com.google.zxing.common.BitMatrix;
import butterknife.OnClick; import butterknife.OnClick;
import cy.agorise.crystalwallet.viewmodels.CryptoNetAccountListViewModel;
import cy.agorise.crystalwallet.views.CryptoNetAccountAdapter;
import cy.agorise.graphenej.Invoice; import cy.agorise.graphenej.Invoice;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -57,6 +60,8 @@ public class ReceiveTransactionFragment extends DialogFragment implements UIVali
ReceiveTransactionValidator receiveTransactionValidator; ReceiveTransactionValidator receiveTransactionValidator;
@BindView(R.id.spTo)
Spinner spTo;
@BindView(R.id.etAmount) @BindView(R.id.etAmount)
EditText etAmount; EditText etAmount;
@BindView(R.id.tvAmountError) @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 = new ReceiveTransactionValidator(this.getContext(), this.cryptoNetAccount, spAsset, etAmount);
receiveTransactionValidator.setListener(this); receiveTransactionValidator.setListener(this);
CryptoNetAccountListViewModel cryptoNetAccountListViewModel = ViewModelProviders.of(this).get(CryptoNetAccountListViewModel.class);
List<CryptoNetAccount> 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); builder.setView(view);
@ -199,6 +210,11 @@ public class ReceiveTransactionFragment extends DialogFragment implements UIVali
}, 400); }, 400);
} }
@OnItemSelected(R.id.spTo)
public void afterToSelected(Spinner spinner, int position) {
this.receiveTransactionValidator.validate();
}
@OnTextChanged(value = R.id.etAmount, @OnTextChanged(value = R.id.etAmount,
callback = OnTextChanged.Callback.AFTER_TEXT_CHANGED) callback = OnTextChanged.Callback.AFTER_TEXT_CHANGED)
void afterAmountChanged(Editable editable) { void afterAmountChanged(Editable editable) {

View file

@ -5,6 +5,7 @@ import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.Observer; import android.arch.lifecycle.Observer;
import android.arch.lifecycle.ViewModelProviders; import android.arch.lifecycle.ViewModelProviders;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
@ -12,6 +13,7 @@ import android.support.annotation.Nullable;
import android.support.design.widget.FloatingActionButton; import android.support.design.widget.FloatingActionButton;
import android.support.v4.app.DialogFragment; import android.support.v4.app.DialogFragment;
import android.support.v4.app.Fragment; import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.AlertDialog; import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity; import android.support.v7.app.AppCompatActivity;
import android.text.Editable; import android.text.Editable;
@ -23,13 +25,19 @@ import android.view.Window;
import android.view.animation.LinearInterpolator; import android.view.animation.LinearInterpolator;
import android.widget.Button; import android.widget.Button;
import android.widget.EditText; import android.widget.EditText;
import android.widget.ImageView;
import android.widget.Spinner; import android.widget.Spinner;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast;
import com.google.zxing.Result; import com.google.zxing.Result;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Locale;
import butterknife.BindView; import butterknife.BindView;
import butterknife.ButterKnife; import butterknife.ButterKnife;
@ -41,10 +49,13 @@ import cy.agorise.crystalwallet.cryptonetinforequests.CryptoNetInfoRequestListen
import cy.agorise.crystalwallet.cryptonetinforequests.CryptoNetInfoRequests; import cy.agorise.crystalwallet.cryptonetinforequests.CryptoNetInfoRequests;
import cy.agorise.crystalwallet.cryptonetinforequests.ValidateBitsharesSendRequest; import cy.agorise.crystalwallet.cryptonetinforequests.ValidateBitsharesSendRequest;
import cy.agorise.crystalwallet.dao.CrystalDatabase; 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.CryptoCoinBalance;
import cy.agorise.crystalwallet.models.CryptoCurrency; import cy.agorise.crystalwallet.models.CryptoCurrency;
import cy.agorise.crystalwallet.models.CryptoNetAccount; import cy.agorise.crystalwallet.models.CryptoNetAccount;
import cy.agorise.crystalwallet.models.GrapheneAccount; import cy.agorise.crystalwallet.models.GrapheneAccount;
import cy.agorise.crystalwallet.viewmodels.ContactViewModel;
import cy.agorise.crystalwallet.viewmodels.CryptoNetAccountListViewModel; import cy.agorise.crystalwallet.viewmodels.CryptoNetAccountListViewModel;
import cy.agorise.crystalwallet.viewmodels.CryptoNetAccountViewModel; import cy.agorise.crystalwallet.viewmodels.CryptoNetAccountViewModel;
import cy.agorise.crystalwallet.viewmodels.validators.SendTransactionValidator; 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.viewmodels.validators.validationfields.ValidationField;
import cy.agorise.crystalwallet.views.CryptoCurrencyAdapter; import cy.agorise.crystalwallet.views.CryptoCurrencyAdapter;
import cy.agorise.crystalwallet.views.CryptoNetAccountAdapter; import cy.agorise.crystalwallet.views.CryptoNetAccountAdapter;
import cy.agorise.graphenej.Invoice;
import cy.agorise.graphenej.LineItem;
import me.dm7.barcodescanner.zxing.ZXingScannerView; import me.dm7.barcodescanner.zxing.ZXingScannerView;
public class SendTransactionFragment extends DialogFragment implements UIValidatorListener, ZXingScannerView.ResultHandler { public class SendTransactionFragment extends DialogFragment implements UIValidatorListener, ZXingScannerView.ResultHandler {
@ -82,6 +95,9 @@ public class SendTransactionFragment extends DialogFragment implements UIValidat
FloatingActionButton btnSend; FloatingActionButton btnSend;
@BindView(R.id.btnCancel) @BindView(R.id.btnCancel)
TextView btnCancel; TextView btnCancel;
@BindView(R.id.ivPeople)
ImageView ivPeople;
CryptoCurrencyAdapter assetAdapter;
Button btnScanQrCode; Button btnScanQrCode;
@ -145,7 +161,7 @@ public class SendTransactionFragment extends DialogFragment implements UIValidat
} }
List<CryptoCurrency> cryptoCurrencyList = db.cryptoCurrencyDao().getByIds(assetIds); List<CryptoCurrency> 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); spAsset.setAdapter(assetAdapter);
} }
}); });
@ -222,6 +238,50 @@ public class SendTransactionFragment extends DialogFragment implements UIValidat
this.sendTransactionValidator.validate(); 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<List<ContactAddress>> contactAddressesLiveData = contactViewModel.getContactAddresses();
contactAddressesLiveData.observe(this, new Observer<List<ContactAddress>>() {
@Override
public void onChanged(@Nullable List<ContactAddress> contactAddresses) {
if (contactAddresses != null) {
for (ContactAddress contactAddress : contactAddresses) {
if (contactAddress.getCryptoNet() == cryptoNetAccount.getCryptoNet()) {
etTo.setText(contactAddress.getAddress());
}
}
}
}
});
}
}
}
}
@OnClick(R.id.btnCancel) @OnClick(R.id.btnCancel)
public void cancel(){ public void cancel(){
this.dismiss(); this.dismiss();
@ -317,6 +377,27 @@ public class SendTransactionFragment extends DialogFragment implements UIValidat
@Override @Override
public void handleResult(Result result) { public void handleResult(Result result) {
Invoice invoice = Invoice.fromQrCode(result.getText());
etTo.setText(invoice.getTo());
for (int i=0;i<assetAdapter.getCount();i++) {
if (assetAdapter.getItem(i).getName().equals(invoice.getCurrency())) {
spAsset.setSelection(i);
break;
}
}
etMemo.setText(invoice.getMemo());
double amount = 0.0;
for (LineItem nextItem : invoice.getLineItems()) {
amount += nextItem.getQuantity() * nextItem.getPrice();
}
DecimalFormat df = new DecimalFormat("####.####");
df.setRoundingMode(RoundingMode.CEILING);
df.setDecimalFormatSymbols(new DecimalFormatSymbols(Locale.ENGLISH));
etAmount.setText(df.format(amount));
Log.i("SendFragment",result.getText()); Log.i("SendFragment",result.getText());
} }
} }

View file

@ -1,5 +1,6 @@
package cy.agorise.crystalwallet.manager; package cy.agorise.crystalwallet.manager;
import android.annotation.SuppressLint;
import android.content.Context; import android.content.Context;
import com.google.common.primitives.UnsignedLong; import com.google.common.primitives.UnsignedLong;
@ -31,9 +32,11 @@ import cy.agorise.crystalwallet.models.AccountSeed;
import cy.agorise.crystalwallet.models.BitsharesAsset; import cy.agorise.crystalwallet.models.BitsharesAsset;
import cy.agorise.crystalwallet.models.BitsharesAssetInfo; import cy.agorise.crystalwallet.models.BitsharesAssetInfo;
import cy.agorise.crystalwallet.models.CryptoCoinTransaction; import cy.agorise.crystalwallet.models.CryptoCoinTransaction;
import cy.agorise.crystalwallet.models.CryptoCurrency;
import cy.agorise.crystalwallet.models.CryptoNetAccount; import cy.agorise.crystalwallet.models.CryptoNetAccount;
import cy.agorise.crystalwallet.models.GrapheneAccount; import cy.agorise.crystalwallet.models.GrapheneAccount;
import cy.agorise.crystalwallet.models.GrapheneAccountInfo; import cy.agorise.crystalwallet.models.GrapheneAccountInfo;
import cy.agorise.crystalwallet.network.CryptoNetManager;
import cy.agorise.graphenej.Address; import cy.agorise.graphenej.Address;
import cy.agorise.graphenej.Asset; import cy.agorise.graphenej.Asset;
import cy.agorise.graphenej.AssetAmount; import cy.agorise.graphenej.AssetAmount;
@ -54,20 +57,23 @@ import cy.agorise.graphenej.operations.TransferOperationBuilder;
*/ */
public class BitsharesAccountManager implements CryptoAccountManager, CryptoNetInfoRequestsListener { public class BitsharesAccountManager implements CryptoAccountManager, CryptoNetInfoRequestsListener {
private final static String BITSHARES_TESTNET_CHAIN_ID= "9cf6f255a208100d2bb275a3c52f4b1589b7ec9c9bfc2cb2a5fe6411295106d8"; //private final static String BITSHARES_TESTNET_CHAIN_ID= "9cf6f255a208100d2bb275a3c52f4b1589b7ec9c9bfc2cb2a5fe6411295106d8";
private final static String SIMPLE_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss";
private final static String DEFAULT_TIME_ZONE = "GMT";
@Override @Override
public CryptoNetAccount createAccountFromSeed(CryptoNetAccount account, Context context) { public void createAccountFromSeed(CryptoNetAccount account, final ManagerRequest request, final Context context) {
if(account instanceof GrapheneAccount) { if(account instanceof GrapheneAccount) {
GrapheneAccount grapheneAccount = (GrapheneAccount) account; final GrapheneAccount grapheneAccount = (GrapheneAccount) account;
boolean created = BitsharesFaucetApiGenerator.registerBitsharesAccount(grapheneAccount.getName(), ApiRequest creationRequest = new ApiRequest(1, new ApiRequestListener() {
new Address(ECKey.fromPublicOnly(grapheneAccount.getOwnerKey(context).getPubKey())).toString(), @Override
new Address(ECKey.fromPublicOnly(grapheneAccount.getActiveKey(context).getPubKey())).toString(), public void success(Object answer, int idPetition) {
new Address(ECKey.fromPublicOnly(grapheneAccount.getMemoKey(context).getPubKey())).toString(),GrapheneApiGenerator.faucetUrl); getAccountInfoByName(grapheneAccount.getName(), new ManagerRequest() {
@Override
if(created) { public void success(Object answer) {
GrapheneAccount fetch = this.getAccountInfoByName(grapheneAccount.getName()); GrapheneAccount fetch = (GrapheneAccount) answer;
fetch.setSeedId(grapheneAccount.getSeedId()); fetch.setSeedId(grapheneAccount.getSeedId());
fetch.setCryptoNet(grapheneAccount.getCryptoNet()); fetch.setCryptoNet(grapheneAccount.getCryptoNet());
fetch.setAccountIndex(grapheneAccount.getAccountIndex()); fetch.setAccountIndex(grapheneAccount.getAccountIndex());
@ -76,82 +82,130 @@ public class BitsharesAccountManager implements CryptoAccountManager, CryptoNetI
long idAccount = db.cryptoNetAccountDao().insertCryptoNetAccount(fetch)[0]; long idAccount = db.cryptoNetAccountDao().insertCryptoNetAccount(fetch)[0];
fetch.setId(idAccount); fetch.setId(idAccount);
db.grapheneAccountInfoDao().insertGrapheneAccountInfo(new GrapheneAccountInfo(fetch)); db.grapheneAccountInfoDao().insertGrapheneAccountInfo(new GrapheneAccountInfo(fetch));
subscribeBitsharesAccount(fetch.getId(),fetch.getAccountId(),context);
GrapheneApiGenerator.subscribeBitsharesAccount(fetch.getId(), fetch.getAccountId(), context); request.success(fetch);
BitsharesAccountManager.refreshAccountTransactions(fetch.getId(), context);
GrapheneApiGenerator.getAccountBalance(fetch.getId(), fetch.getAccountId(), context);
return fetch;
}
}
return null;
} }
@Override @Override
public CryptoNetAccount importAccountFromSeed(CryptoNetAccount account, Context context) { public void fail() {
//TODO get account data fail
}
});
}
@Override
public void fail(int idPetition) {
request.fail();
}
});
BitsharesFaucetApiGenerator.registerBitsharesAccount(grapheneAccount.getName(),
new Address(ECKey.fromPublicOnly(grapheneAccount.getOwnerKey(context).getPubKey())).toString(),
new Address(ECKey.fromPublicOnly(grapheneAccount.getActiveKey(context).getPubKey())).toString(),
new Address(ECKey.fromPublicOnly(grapheneAccount.getMemoKey(context).getPubKey())).toString(),
GrapheneApiGenerator.faucetUrl, creationRequest);
}
}
@Override
public void importAccountFromSeed(CryptoNetAccount account, final Context context) {
if(account instanceof GrapheneAccount) { if(account instanceof GrapheneAccount) {
GrapheneAccount grapheneAccount = (GrapheneAccount) account; final GrapheneAccount grapheneAccount = (GrapheneAccount) account;
if(grapheneAccount.getAccountId() == null){ if(grapheneAccount.getAccountId() == null){
GrapheneAccount fetch = this.getAccountInfoByName(grapheneAccount.getName()); this.getAccountInfoByName(grapheneAccount.getName(), new ManagerRequest() {
if(fetch == null) { @Override
//TODO grapaheneAccount null, error fetching public void success(Object answer) {
return null; GrapheneAccount fetch = (GrapheneAccount) answer;
}
grapheneAccount.setAccountId(fetch.getAccountId()); grapheneAccount.setAccountId(fetch.getAccountId());
}else if(grapheneAccount.getName() == null){
GrapheneAccount fetch = this.getAccountInfoById(grapheneAccount.getAccountId());
if(fetch == null) {
//TODO grapaheneAccount null, error fetching
return null;
}
grapheneAccount.setName(fetch.getName());
}
CrystalDatabase db = CrystalDatabase.getAppDatabase(context); CrystalDatabase db = CrystalDatabase.getAppDatabase(context);
db.cryptoNetAccountDao().insertCryptoNetAccount(grapheneAccount); db.cryptoNetAccountDao().insertCryptoNetAccount(grapheneAccount);
db.grapheneAccountInfoDao().insertGrapheneAccountInfo(new GrapheneAccountInfo(grapheneAccount)); db.grapheneAccountInfoDao().insertGrapheneAccountInfo(new GrapheneAccountInfo(grapheneAccount));
subscribeBitsharesAccount(grapheneAccount.getId(),grapheneAccount.getAccountId(),context);
GrapheneApiGenerator.subscribeBitsharesAccount(grapheneAccount.getId(), grapheneAccount.getAccountId(), context);
BitsharesAccountManager.refreshAccountTransactions(account.getId(), context);
GrapheneApiGenerator.getAccountBalance(grapheneAccount.getId(), grapheneAccount.getAccountId(), context);
return grapheneAccount;
}
return null;
} }
@Override @Override
public void loadAccountFromDB(CryptoNetAccount account, Context context) { public void fail() {
if(account instanceof GrapheneAccount){ //TODO get account data fail
GrapheneAccount grapheneAccount = (GrapheneAccount) account; }
});
}else if(grapheneAccount.getName() == null){
this.getAccountInfoById(grapheneAccount.getAccountId(), new ManagerRequest() {
@Override
public void success(Object answer) {
GrapheneAccount fetch = (GrapheneAccount) answer;
grapheneAccount.setName(fetch.getName());
CrystalDatabase db = CrystalDatabase.getAppDatabase(context); CrystalDatabase db = CrystalDatabase.getAppDatabase(context);
GrapheneAccountInfo info = db.grapheneAccountInfoDao().getByAccountId(account.getId()); db.cryptoNetAccountDao().insertCryptoNetAccount(grapheneAccount);
db.grapheneAccountInfoDao().insertGrapheneAccountInfo(new GrapheneAccountInfo(grapheneAccount));
subscribeBitsharesAccount(grapheneAccount.getId(),grapheneAccount.getAccountId(),context);
}
@Override
public void fail() {
//TODO get account data fail
}
});
}else {
CrystalDatabase db = CrystalDatabase.getAppDatabase(context);
db.cryptoNetAccountDao().insertCryptoNetAccount(grapheneAccount);
db.grapheneAccountInfoDao().insertGrapheneAccountInfo(new GrapheneAccountInfo(grapheneAccount));
subscribeBitsharesAccount(grapheneAccount.getId(), grapheneAccount.getAccountId(), context);
}
}
}
@Override
public void loadAccountFromDB(CryptoNetAccount account, final Context context) {
if(account instanceof GrapheneAccount){
final GrapheneAccount grapheneAccount = (GrapheneAccount) account;
final CrystalDatabase db = CrystalDatabase.getAppDatabase(context);
final GrapheneAccountInfo info = db.grapheneAccountInfoDao().getByAccountId(account.getId());
grapheneAccount.loadInfo(info); grapheneAccount.loadInfo(info);
if(grapheneAccount.getAccountId() == null){ if(grapheneAccount.getAccountId() == null){
GrapheneAccount fetch = this.getAccountInfoByName(grapheneAccount.getName()); this.getAccountInfoByName(grapheneAccount.getName(), new ManagerRequest() {
if(fetch != null){ @Override
public void success(Object answer) {
GrapheneAccount fetch = (GrapheneAccount) answer;
info.setAccountId(fetch.getAccountId()); info.setAccountId(fetch.getAccountId());
grapheneAccount.setAccountId(fetch.getAccountId()); grapheneAccount.setAccountId(fetch.getAccountId());
db.grapheneAccountInfoDao().insertGrapheneAccountInfo(info); db.grapheneAccountInfoDao().insertGrapheneAccountInfo(info);
subscribeBitsharesAccount(grapheneAccount.getId(),grapheneAccount.getAccountId(),context);
} }
@Override
public void fail() {
//TODO account data retrieve failed
}
});
}else if(grapheneAccount.getName() == null){ }else if(grapheneAccount.getName() == null){
GrapheneAccount fetch = this.getAccountInfoById(grapheneAccount.getAccountId()); this.getAccountInfoById(grapheneAccount.getAccountId(), new ManagerRequest() {
if(fetch != null) { @Override
public void success(Object answer) {
GrapheneAccount fetch = (GrapheneAccount) answer;
info.setName(fetch.getName()); info.setName(fetch.getName());
grapheneAccount.setName(fetch.getName()); grapheneAccount.setName(fetch.getName());
db.grapheneAccountInfoDao().insertGrapheneAccountInfo(info); db.grapheneAccountInfoDao().insertGrapheneAccountInfo(info);
subscribeBitsharesAccount(grapheneAccount.getId(),grapheneAccount.getAccountId(),context);
}
@Override
public void fail() {
//TODO account data retrieve failed
}
});
}else{
subscribeBitsharesAccount(grapheneAccount.getId(),grapheneAccount.getAccountId(),context);
}
} }
} }
if(grapheneAccount.getName() == null || grapheneAccount.getAccountId() == null) { private void subscribeBitsharesAccount(long accountId, String accountBitsharesID, Context context){
//TODO grapaheneAccount null, error fetching GrapheneApiGenerator.subscribeBitsharesAccount(accountId,accountBitsharesID,context);
return; BitsharesAccountManager.refreshAccountTransactions(accountId,context);
} GrapheneApiGenerator.getAccountBalance(accountId,accountBitsharesID,context);
GrapheneApiGenerator.subscribeBitsharesAccount(grapheneAccount.getId(),grapheneAccount.getAccountId(),context);
BitsharesAccountManager.refreshAccountTransactions(account.getId(),context);
GrapheneApiGenerator.getAccountBalance(grapheneAccount.getId(),grapheneAccount.getAccountId(),context);
}
} }
/** /**
@ -172,6 +226,7 @@ public class BitsharesAccountManager implements CryptoAccountManager, CryptoNetI
this.validateCreateAccount((ValidateCreateBitsharesAccountRequest) request); this.validateCreateAccount((ValidateCreateBitsharesAccountRequest) request);
}else{ }else{
//TODO not implemented //TODO not implemented
System.out.println("Error request not implemented " + request.getClass().getName());
} }
} }
@ -189,7 +244,6 @@ public class BitsharesAccountManager implements CryptoAccountManager, CryptoNetI
if(answer != null && answer instanceof AccountProperties) { if(answer != null && answer instanceof AccountProperties) {
AccountProperties prop = (AccountProperties) answer; AccountProperties prop = (AccountProperties) answer;
//TODO change the way to compare keys //TODO change the way to compare keys
BrainKey bk = new BrainKey(importRequest.getMnemonic(), 0); BrainKey bk = new BrainKey(importRequest.getMnemonic(), 0);
System.out.println(bk.getPublicAddress("BTS").toString()); System.out.println(bk.getPublicAddress("BTS").toString());
for(PublicKey activeKey : prop.owner.getKeyAuthList()){ for(PublicKey activeKey : prop.owner.getKeyAuthList()){
@ -220,12 +274,13 @@ public class BitsharesAccountManager implements CryptoAccountManager, CryptoNetI
GrapheneApiGenerator.getAccountIdByName(importRequest.getAccountName(),checkAccountName); GrapheneApiGenerator.getAccountIdByName(importRequest.getAccountName(),checkAccountName);
} }
private void validateCreateAccount(ValidateCreateBitsharesAccountRequest createRequest){ private void validateCreateAccount(final ValidateCreateBitsharesAccountRequest createRequest){
// Generate seed or find key // Generate seed or find key
Context context = createRequest.getContext(); Context context = createRequest.getContext();
AccountSeed seed = AccountSeed.getAccountSeed(SeedType.BIP39, context); AccountSeed seed = AccountSeed.getAccountSeed(SeedType.BIP39, context);
CrystalDatabase db = CrystalDatabase.getAppDatabase(context); CrystalDatabase db = CrystalDatabase.getAppDatabase(context);
long idSeed = db.accountSeedDao().insertAccountSeed(seed); long idSeed = db.accountSeedDao().insertAccountSeed(seed);
assert seed != null;
seed.setId(idSeed); seed.setId(idSeed);
seed.setName(createRequest.getAccountName()); seed.setName(createRequest.getAccountName());
GrapheneAccount account = new GrapheneAccount(); GrapheneAccount account = new GrapheneAccount();
@ -233,12 +288,20 @@ public class BitsharesAccountManager implements CryptoAccountManager, CryptoNetI
account.setSeedId(idSeed); account.setSeedId(idSeed);
account.setAccountIndex(0); account.setAccountIndex(0);
account.setCryptoNet(CryptoNet.BITSHARES); account.setCryptoNet(CryptoNet.BITSHARES);
GrapheneAccount answer =(GrapheneAccount) this.createAccountFromSeed(account,context); this.createAccountFromSeed(account, new ManagerRequest() {
if (answer != null){
@Override
public void success(Object answer) {
createRequest.setAccountExists(false); createRequest.setAccountExists(false);
createRequest.setAccount(answer);; createRequest.setAccount((GrapheneAccount) answer);
} }
createRequest.setAccountExists(false);
@Override
public void fail() {
createRequest.setAccountExists(true);
}
}, context);
} }
@ -272,12 +335,50 @@ public class BitsharesAccountManager implements CryptoAccountManager, CryptoNetI
*/ */
private void validateSendRequest(final ValidateBitsharesSendRequest sendRequest) { private void validateSendRequest(final ValidateBitsharesSendRequest sendRequest) {
//TODO feeAsset //TODO feeAsset
String idAsset = getAssetInfoByName(sendRequest.getAsset()); CrystalDatabase db = CrystalDatabase.getAppDatabase(sendRequest.getContext());
Asset feeAsset = new Asset(idAsset); CryptoCurrency currency = db.cryptoCurrencyDao().getByNameAndCryptoNet(sendRequest.getAsset(), CryptoNet.BITSHARES.name());
UserAccount fromUserAccount =new UserAccount(sendRequest.getSourceAccount().getAccountId()); if (currency == null){
getAssetInfoByName(sendRequest.getAsset(), new ManagerRequest() {
@Override
public void success(Object answer) {
validateSendRequest(sendRequest, ((BitsharesAsset) answer).getBitsharesId());
}
GrapheneAccount toUserGrapheneAccount = this.getAccountInfoByName(sendRequest.getToAccount()); @Override
//TODO bad user to user account public void fail() {
}
});
}else{
BitsharesAssetInfo info = db.bitsharesAssetDao().getBitsharesAssetInfo(currency.getId());
if (info == null || info.getBitsharesId() == null || info.getBitsharesId().isEmpty()){
getAssetInfoByName(sendRequest.getAsset(), new ManagerRequest() {
@Override
public void success(Object answer) {
validateSendRequest(sendRequest, ((BitsharesAsset) answer).getBitsharesId());
}
@Override
public void fail() {
}
});
}else {
this.validateSendRequest(sendRequest, info.getBitsharesId());
}
}
}
private void validateSendRequest(final ValidateBitsharesSendRequest sendRequest , final String idAsset){
final Asset feeAsset = new Asset(idAsset);
final UserAccount fromUserAccount =new UserAccount(sendRequest.getSourceAccount().getAccountId());
//TODO cached to accounts
this.getAccountInfoByName(sendRequest.getToAccount(), new ManagerRequest() {
@Override
public void success(Object answer) {
GrapheneAccount toUserGrapheneAccount = (GrapheneAccount) answer;
UserAccount toUserAccount = new UserAccount(toUserGrapheneAccount.getAccountId()); UserAccount toUserAccount = new UserAccount(toUserGrapheneAccount.getAccountId());
TransferOperationBuilder builder = new TransferOperationBuilder() TransferOperationBuilder builder = new TransferOperationBuilder()
.setSource(fromUserAccount) .setSource(fromUserAccount)
@ -287,6 +388,7 @@ public class BitsharesAccountManager implements CryptoAccountManager, CryptoNetI
if(sendRequest.getMemo() != null) { if(sendRequest.getMemo() != null) {
//builder.setMemo(new Memo(fromUserAccount,toUserAccount,0,sendRequest.getMemo().getBytes())); //builder.setMemo(new Memo(fromUserAccount,toUserAccount,0,sendRequest.getMemo().getBytes()));
//TODO memo //TODO memo
System.out.println("transaction has memo");
} }
ArrayList<BaseOperation> operationList = new ArrayList<>(); ArrayList<BaseOperation> operationList = new ArrayList<>();
operationList.add(builder.build()); operationList.add(builder.build());
@ -294,7 +396,7 @@ public class BitsharesAccountManager implements CryptoAccountManager, CryptoNetI
ECKey privateKey = sendRequest.getSourceAccount().getActiveKey(sendRequest.getContext()); ECKey privateKey = sendRequest.getSourceAccount().getActiveKey(sendRequest.getContext());
Transaction transaction = new Transaction(privateKey, null, operationList); Transaction transaction = new Transaction(privateKey, null, operationList);
transaction.setChainId(BITSHARES_TESTNET_CHAIN_ID); transaction.setChainId(CryptoNetManager.getChaindId(CryptoNet.BITSHARES));
ApiRequest transactionRequest = new ApiRequest(0, new ApiRequestListener() { ApiRequest transactionRequest = new ApiRequest(0, new ApiRequestListener() {
@Override @Override
@ -311,80 +413,47 @@ public class BitsharesAccountManager implements CryptoAccountManager, CryptoNetI
GrapheneApiGenerator.broadcastTransaction(transaction,feeAsset, transactionRequest); GrapheneApiGenerator.broadcastTransaction(transaction,feeAsset, transactionRequest);
} }
@Override
public void fail() {
//TODO bad user to user account
}
});
}
/** /**
* Returns the account info from a graphene id * Returns the account info from a graphene id
* @param grapheneId The graphene id of the account * @param grapheneId The graphene id of the account
*/ */
private GrapheneAccount getAccountInfoById(String grapheneId){ private void getAccountInfoById(String grapheneId, ManagerRequest request){
final Object SYNC = new Object();
long timeout = 60000;
AccountIdOrNameListener listener = new AccountIdOrNameListener(SYNC); AccountIdOrNameListener listener = new AccountIdOrNameListener(request);
ApiRequest request = new ApiRequest(0, listener); ApiRequest accountRequest = new ApiRequest(0, listener);
GrapheneApiGenerator.getAccountById(grapheneId,request); GrapheneApiGenerator.getAccountById(grapheneId,accountRequest);
long cTime = System.currentTimeMillis();
while(!listener.ready && (System.currentTimeMillis()-cTime) < timeout){
synchronized (SYNC){
try {
SYNC.wait(100);
} catch (InterruptedException ignore) {}
}
}
return listener.account;
} }
/** /**
* Gets account info by its name * Gets account info by its name
* @param grapheneName The name of the account to retrieve * @param grapheneName The name of the account to retrieve
*/ */
private GrapheneAccount getAccountInfoByName(String grapheneName){ private void getAccountInfoByName(String grapheneName, ManagerRequest request){
final Object SYNC = new Object();
long timeout = 60000;
AccountIdOrNameListener listener = new AccountIdOrNameListener(SYNC); AccountIdOrNameListener listener = new AccountIdOrNameListener(request);
ApiRequest request = new ApiRequest(0, listener); ApiRequest accountRequest = new ApiRequest(0, listener);
GrapheneApiGenerator.getAccountByName(grapheneName,request); 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 //TODO expand function to be more generic
private String getAssetInfoByName(String assetName){ private void getAssetInfoByName(String assetName, ManagerRequest request){
final Object SYNC = new Object();
long timeout = 60000;
AssetIdOrNameListener nameListener = new AssetIdOrNameListener(SYNC); AssetIdOrNameListener nameListener = new AssetIdOrNameListener(request);
ApiRequest request = new ApiRequest(0, nameListener); ApiRequest assetRequest = new ApiRequest(0, nameListener);
ArrayList<String> assetNames = new ArrayList<>(); ArrayList<String> assetNames = new ArrayList<>();
assetNames.add(assetName); assetNames.add(assetName);
GrapheneApiGenerator.getAssetByName(assetNames, request); GrapheneApiGenerator.getAssetByName(assetNames, assetRequest);
long cTime = System.currentTimeMillis();
while(!nameListener.ready && (System.currentTimeMillis()-cTime) < timeout){
synchronized (SYNC){
try {
SYNC.wait(100);
} catch (InterruptedException ignore) {}
}
}
return nameListener.asset.getBitsharesId();
} }
@ -393,7 +462,7 @@ public class BitsharesAccountManager implements CryptoAccountManager, CryptoNetI
* @param idAccount database id of the account * @param idAccount database id of the account
* @param context The android context of this application * @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); CrystalDatabase db = CrystalDatabase.getAppDatabase(context);
List<CryptoCoinTransaction> transactions = db.transactionDao().getByIdAccount(idAccount); List<CryptoCoinTransaction> transactions = db.transactionDao().getByIdAccount(idAccount);
CryptoNetAccount account = db.cryptoNetAccountDao().getById(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 answer The answer, this object depends on the kind of request is made to the api
* @param idPetition the id of the ApiRequest petition * @param idPetition the id of the ApiRequest petition
*/ */
@Override @Override
public void success(Object answer, int idPetition) { public void success(Object answer, int idPetition) {
List<HistoricalTransfer> transfers = (List<HistoricalTransfer>) answer ; List<HistoricalTransfer> transfers = (List<HistoricalTransfer>) answer ;
for(HistoricalTransfer transfer : transfers) { for(final HistoricalTransfer transfer : transfers) {
if (transfer.getOperation() != null){ if (transfer.getOperation() != null){
CryptoCoinTransaction transaction = new CryptoCoinTransaction(); final CryptoCoinTransaction transaction = new CryptoCoinTransaction();
transaction.setAccountId(account.getId()); transaction.setAccountId(account.getId());
transaction.setAmount(transfer.getOperation().getAssetAmount().getAmount().longValue()); transaction.setAmount(transfer.getOperation().getAssetAmount().getAmount().longValue());
BitsharesAssetInfo info = db.bitsharesAssetDao().getBitsharesAssetInfoById(transfer.getOperation().getAssetAmount().getAsset().getObjectId()); BitsharesAssetInfo info = db.bitsharesAssetDao().getBitsharesAssetInfoById(transfer.getOperation().getAssetAmount().getAsset().getObjectId());
if (info == null) { if (info == null) {
//The cryptoCurrency is not in the database, queringfor its data //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() { ApiRequest assetRequest = new ApiRequest(0, new ApiRequestListener() {
@Override @Override
public void success(Object answer, int idPetition) { public void success(Object answer, int idPetition) {
@ -476,39 +544,24 @@ public class BitsharesAccountManager implements CryptoAccountManager, CryptoNetI
info.setCryptoCurrencyId(idCryptoCurrency); info.setCryptoCurrencyId(idCryptoCurrency);
asset.setId((int)idCryptoCurrency); asset.setId((int)idCryptoCurrency);
db.bitsharesAssetDao().insertBitsharesAssetInfo(info); db.bitsharesAssetDao().insertBitsharesAssetInfo(info);
saveTransaction(transaction,info,transfer);
} }
synchronized (SYNC){
SYNC.notifyAll();
}
} }
@Override @Override
public void fail(int idPetition) { public void fail(int idPetition) {
synchronized (SYNC){ //TODO Error
SYNC.notifyAll();
}
} }
}); });
ArrayList<String> assets = new ArrayList<>(); ArrayList<String> assets = new ArrayList<>();
assets.add(transfer.getOperation().getAssetAmount().getAsset().getObjectId()); assets.add(transfer.getOperation().getAssetAmount().getAsset().getObjectId());
GrapheneApiGenerator.getAssetById(assets,assetRequest); GrapheneApiGenerator.getAssetById(assets,assetRequest);
synchronized (SYNC){ }else{
try {SYNC.wait(60000);} catch (InterruptedException ignore) {} saveTransaction(transaction,info,transfer);
} }
info = db.bitsharesAssetDao().getBitsharesAssetInfoById(transfer.getOperation().getAssetAmount().getAsset().getObjectId());
}
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){ if(transfers.size()>= limit){
@ -524,6 +577,16 @@ public class BitsharesAccountManager implements CryptoAccountManager, CryptoNetI
public void fail(int idPetition) { 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); GrapheneApiGenerator.getEquivalentValue(fromAsset.getBitsharesId(),toAsset.getBitsharesId(), getEquivalentRequest);
}else{ }else{
//TODO error //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 * Class to retrieve the account id or the account name, if one of those is missing
*/ */
private class AccountIdOrNameListener implements ApiRequestListener{ private class AccountIdOrNameListener implements ApiRequestListener{
final Object SYNC; final ManagerRequest request;
boolean ready = false;
GrapheneAccount account; GrapheneAccount account;
AccountIdOrNameListener(Object SYNC) { AccountIdOrNameListener(ManagerRequest request) {
this.SYNC = SYNC; this.request = request;
} }
@Override @Override
@ -572,18 +635,12 @@ public class BitsharesAccountManager implements CryptoAccountManager, CryptoNetI
account.setName(props.name); account.setName(props.name);
} }
synchronized (SYNC){ request.success(account);
ready = true;
SYNC.notifyAll();
}
} }
@Override @Override
public void fail(int idPetition) { public void fail(int idPetition) {
synchronized (SYNC){ request.fail();
ready = true;
SYNC.notifyAll();
}
} }
} }
@ -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 * Class to retrieve the asset id or the asset name, if one of those is missing
*/ */
private class AssetIdOrNameListener implements ApiRequestListener{ private class AssetIdOrNameListener implements ApiRequestListener{
final Object SYNC; final ManagerRequest request;
boolean ready = false;
BitsharesAsset asset; BitsharesAsset asset;
AssetIdOrNameListener(Object SYNC) { AssetIdOrNameListener(ManagerRequest request) {
this.SYNC = SYNC; this.request = request;
} }
@Override @Override
public void success(Object answer, int idPetition) { public void success(Object answer, int idPetition) {
if(answer instanceof ArrayList) { if(answer instanceof ArrayList) {
if (((ArrayList) answer).get(0) instanceof BitsharesAsset) { if (((ArrayList) answer).get(0) instanceof BitsharesAsset) {
asset = (BitsharesAsset) ((ArrayList) answer).get(0); asset = (BitsharesAsset) ((ArrayList) answer).get(0);
request.success(asset);
} }
} }
synchronized (SYNC){
ready = true;
SYNC.notifyAll();
}
} }
@Override @Override
public void fail(int idPetition) { public void fail(int idPetition) {
synchronized (SYNC){ //TODO fail asset retrieve
ready = true;
SYNC.notifyAll();
}
} }
} }
@ -637,7 +685,7 @@ public class BitsharesAccountManager implements CryptoAccountManager, CryptoNetI
*/ */
TransactionDao transactionDao; TransactionDao transactionDao;
public GetTransactionDate(CryptoCoinTransaction transaction, TransactionDao transactionDao) { GetTransactionDate(CryptoCoinTransaction transaction, TransactionDao transactionDao) {
this.transaction = transaction; this.transaction = transaction;
this.transactionDao = transactionDao; this.transactionDao = transactionDao;
} }
@ -645,11 +693,13 @@ public class BitsharesAccountManager implements CryptoAccountManager, CryptoNetI
@Override @Override
public void success(Object answer, int idPetition) { public void success(Object answer, int idPetition) {
if(answer instanceof BlockHeader){ if(answer instanceof BlockHeader){
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); @SuppressLint("SimpleDateFormat") SimpleDateFormat dateFormat = new SimpleDateFormat(SIMPLE_DATE_FORMAT);
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT")); dateFormat.setTimeZone(TimeZone.getTimeZone(DEFAULT_TIME_ZONE));
try { try {
transaction.setDate(dateFormat.parse(((BlockHeader) answer).timestamp)); transaction.setDate(dateFormat.parse(((BlockHeader) answer).timestamp));
if (transactionDao.getByTransaction(transaction.getDate(),transaction.getFrom(),transaction.getTo(),transaction.getAmount()) == null) {
transactionDao.insertTransaction(transaction); transactionDao.insertTransaction(transaction);
}
} catch (ParseException e) { } catch (ParseException e) {
e.printStackTrace(); e.printStackTrace();
} }

View file

@ -18,14 +18,14 @@ public interface CryptoAccountManager {
* @param account The values to be created, * @param account The values to be created,
* @returnThe CruptoNetAccount created, or null if it couldn't 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 * Imports a CryptoCoin account from a seed
* @param account A CryptoNetAccount with the parameters to be imported * @param account A CryptoNetAccount with the parameters to be imported
* @returnThe CruptoNetAccount imported * @returnThe CruptoNetAccount imported
*/ */
public CryptoNetAccount importAccountFromSeed(CryptoNetAccount account, Context context); public void importAccountFromSeed(CryptoNetAccount account, Context context);
/** /**
* Loads account data from the database * Loads account data from the database

View file

@ -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();
}

View file

@ -10,8 +10,11 @@ import android.arch.persistence.room.PrimaryKey;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.v7.recyclerview.extensions.DiffCallback; import android.support.v7.recyclerview.extensions.DiffCallback;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import cy.agorise.crystalwallet.enums.CryptoNet;
/** /**
* Represents a user contact * Represents a user contact
* *
@ -19,7 +22,7 @@ import java.util.List;
*/ */
@Entity(tableName="contact", @Entity(tableName="contact",
indices = {@Index("id"),@Index(value = {"name"}, unique=true)}) indices = {@Index("id"),@Index(value = {"name"}, unique=true),@Index("email")})
public class Contact { public class Contact {
/** /**
@ -32,6 +35,9 @@ public class Contact {
@ColumnInfo(name="name") @ColumnInfo(name="name")
private String mName; private String mName;
@ColumnInfo(name="email")
private String mEmail;
@ColumnInfo(name = "gravatar") @ColumnInfo(name = "gravatar")
private String mGravatar; private String mGravatar;
@ -62,6 +68,14 @@ public class Contact {
this.mGravatar = gravatar; this.mGravatar = gravatar;
} }
public String getEmail() {
return this.mEmail;
}
public void setEmail(String email) {
this.mEmail = email;
}
public int addressesCount(){ public int addressesCount(){
return this.mAddresses.size(); return this.mAddresses.size();
} }
@ -70,8 +84,30 @@ public class Contact {
return this.mAddresses.get(index); return this.mAddresses.get(index);
} }
public void clearAddresses(){
if (this.mAddresses != null) {
this.mAddresses.clear();
}
}
public void addAddress(ContactAddress address){ public void addAddress(ContactAddress address){
if (this.mAddresses == null) {
this.mAddresses = new ArrayList<ContactAddress>();
}
this.mAddresses.add(address); 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<Contact> DIFF_CALLBACK = new DiffCallback<Contact>() { public static final DiffCallback<Contact> DIFF_CALLBACK = new DiffCallback<Contact>() {

View file

@ -4,9 +4,12 @@ package cy.agorise.crystalwallet.models;
import android.arch.persistence.room.ColumnInfo; import android.arch.persistence.room.ColumnInfo;
import android.arch.persistence.room.Entity; import android.arch.persistence.room.Entity;
import android.arch.persistence.room.Index; import android.arch.persistence.room.Index;
import android.arch.persistence.room.PrimaryKey;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.v7.recyclerview.extensions.DiffCallback; import android.support.v7.recyclerview.extensions.DiffCallback;
import cy.agorise.crystalwallet.enums.CryptoNet;
/** /**
* Represents a user contact address * Represents a user contact address
* *
@ -14,19 +17,37 @@ import android.support.v7.recyclerview.extensions.DiffCallback;
*/ */
@Entity(tableName="contact_address", @Entity(tableName="contact_address",
primaryKeys = {"contact_id","crypto_currency_id"}, indices = {@Index(value = {"id"}, unique=true),@Index(value = {"contact_id","crypto_net"}, unique=true)})
indices = {@Index(value = {"contact_id","crypto_currency_id"}, unique=true)})
public class ContactAddress { public class ContactAddress {
/**
* The id on the database
*/
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "id")
private long mId;
@ColumnInfo(name = "contact_id") @ColumnInfo(name = "contact_id")
private long mContactId; 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") @ColumnInfo(name="address")
private String mAddress; private String mAddress;
public long getId() {
return mId;
}
public void setId(long id) {
this.mId = id;
}
public long getContactId() { public long getContactId() {
return mContactId; return mContactId;
} }
@ -35,12 +56,12 @@ public class ContactAddress {
this.mContactId = contactId; this.mContactId = contactId;
} }
public long getCryptoCurrencyId() { public CryptoNet getCryptoNet() {
return mCryptoCurrencyId; return mCryptoNet;
} }
public void setCryptoCurrencyId(long cryptoCurrencyId) { public void setCryptoNet(CryptoNet cryptoNet) {
this.mCryptoCurrencyId = cryptoCurrencyId; this.mCryptoNet = cryptoNet;
} }
public String getAddress() { public String getAddress() {
@ -56,7 +77,7 @@ public class ContactAddress {
public boolean areItemsTheSame( public boolean areItemsTheSame(
@NonNull ContactAddress oldContactAddress, @NonNull ContactAddress newContactAddress) { @NonNull ContactAddress oldContactAddress, @NonNull ContactAddress newContactAddress) {
return (oldContactAddress.getContactId() == newContactAddress.getContactId()) return (oldContactAddress.getContactId() == newContactAddress.getContactId())
&& (oldContactAddress.getCryptoCurrencyId() == newContactAddress.getCryptoCurrencyId()); && (oldContactAddress.getCryptoNet() == newContactAddress.getCryptoNet());
} }
@Override @Override
public boolean areContentsTheSame( public boolean areContentsTheSame(
@ -73,7 +94,7 @@ public class ContactAddress {
ContactAddress that = (ContactAddress) o; ContactAddress that = (ContactAddress) o;
if (mContactId != that.mContactId) return false; if (mContactId != that.mContactId) return false;
if (mCryptoCurrencyId != that.mCryptoCurrencyId) return false; if (mCryptoNet != that.mCryptoNet) return false;
return mAddress.equals(that.mAddress); return mAddress.equals(that.mAddress);
} }
} }

View file

@ -17,6 +17,7 @@ public class GeneralSetting {
public final static String SETTING_NAME_PREFERED_COUNTRY = "PREFERED_COUNTRY"; 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_NAME_PREFERED_CURRENCY = "PREFERED_CURRENCY";
public final static String SETTING_PASSWORD = "PASSWORD";
/** /**
* The id on the database * The id on the database

View file

@ -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;
}
}

View file

@ -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<CryptoNet,HashSet<String>> CryptoNetURLs = new HashMap<>();
/**
* This map contains the list of urls been tested and ordered by the fastests
*/
private static HashMap<CryptoNet,ArrayList<TestedURL>> 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<String>());
}
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<String>());
}
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 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());
}
}
}

View file

@ -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();
}

View file

@ -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<String, List<String>> headers) throws Exception {
ApiCall getAccountByName = new ApiCall(0, "get_chain_id", new ArrayList<Serializable>(), 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<WitnessResponse<String>>(){}.getType();
GsonBuilder builder = new GsonBuilder();
WitnessResponse<List<String>> 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());
}
}

View file

@ -109,8 +109,7 @@ public class CrystalWalletService extends LifecycleService {
if (LoadEquivalencesThread != null) { if (LoadEquivalencesThread != null) {
LoadEquivalencesThread.stopLoadingEquivalences(); LoadEquivalencesThread.stopLoadingEquivalences();
} };
;
LoadEquivalencesThread = new EquivalencesThread(service, generalSetting.getValue(), bitsharesAssets); LoadEquivalencesThread = new EquivalencesThread(service, generalSetting.getValue(), bitsharesAssets);
LoadEquivalencesThread.start(); LoadEquivalencesThread.start();
} }
@ -184,7 +183,7 @@ public class CrystalWalletService extends LifecycleService {
} }
//if (LoadEquivalencesThread == null) { //if (LoadEquivalencesThread == null) {
// LoadEquivalencesThread = new Thread() { // LoadEquivalencesThread = new EquivalencesThread() {
// @Override // @Override
// public void run() { // public void run() {
loadEquivalentsValues(); loadEquivalentsValues();

View file

@ -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";
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -6,6 +6,7 @@ import android.arch.lifecycle.LiveData;
import android.arch.paging.PagedList; import android.arch.paging.PagedList;
import cy.agorise.crystalwallet.dao.CrystalDatabase; import cy.agorise.crystalwallet.dao.CrystalDatabase;
import cy.agorise.crystalwallet.enums.CryptoNet;
import cy.agorise.crystalwallet.models.Contact; import cy.agorise.crystalwallet.models.Contact;
import cy.agorise.crystalwallet.models.CryptoCoinTransaction; 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<PagedList<Contact>> getContactList(){ public LiveData<PagedList<Contact>> getContactList(){
return this.contactList; return this.contactList;
} }
public boolean contactExists(String name){
return this.db.contactDao().existsByName(name);
}
} }

View file

@ -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> contact;
private LiveData<List<ContactAddress>> 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<Contact> getContact(){
return this.contact;
}
public LiveData<List<ContactAddress>> getContactAddresses(){
return this.contactAddresses;
}
public boolean modifyContact(Contact contact){
this.db.contactDao().update(contact);
for (int i=0;i<contact.addressesCount();i++){
ContactAddress nextAddress = contact.getAddress(i);
if (nextAddress.getId() > 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<contact.addressesCount();i++){
ContactAddress nextAddress = contact.getAddress(i);
nextAddress.setContactId(newContactId);
this.db.contactDao().addAddresses(nextAddress);
}
return contactWasAdded;
}
}

View file

@ -24,7 +24,7 @@ public class CryptoNetAccountViewModel extends AndroidViewModel {
this.db = CrystalDatabase.getAppDatabase(application.getApplicationContext()); this.db = CrystalDatabase.getAppDatabase(application.getApplicationContext());
} }
public void loadCryptoNetAccount(int accountId){ public void loadCryptoNetAccount(long accountId){
this.cryptoNetAccount = this.db.cryptoNetAccountDao().getByIdLiveData(accountId); this.cryptoNetAccount = this.db.cryptoNetAccountDao().getByIdLiveData(accountId);
} }
@ -36,5 +36,4 @@ public class CryptoNetAccountViewModel extends AndroidViewModel {
public LiveData<CryptoNetAccount> getCryptoNetAccount(){ public LiveData<CryptoNetAccount> getCryptoNetAccount(){
return this.cryptoNetAccount; return this.cryptoNetAccount;
} }
} }

View file

@ -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));
}
}

View file

@ -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));
}
}

View file

@ -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));
}
}

View file

@ -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);
}
}
}

View file

@ -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<List<GeneralSetting>> generalSettingsLiveData = generalSettingListViewModel.getGeneralSettingList();
generalSettingsLiveData.observe((LifecycleOwner) this.view.getContext(), new Observer<List<GeneralSetting>>() {
@Override
public void onChanged(@Nullable List<GeneralSetting> 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);
}
}
}
}
}

View file

@ -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);
}
}
}

View file

@ -23,11 +23,11 @@ public class PinConfirmationValidationField extends ValidationField {
String newConfirmationValue = pinConfirmationField.getText().toString(); String newConfirmationValue = pinConfirmationField.getText().toString();
String newValue = pinField.getText().toString(); String newValue = pinField.getText().toString();
String mixedValue = newValue + "_" + newConfirmationValue; String mixedValue = newValue + "_" + newConfirmationValue;
if (mixedValue != this.getLastValue()) {
if (!newConfirmationValue.equals("")) {
if (!mixedValue.equals(this.getLastValue())) {
this.setLastValue(mixedValue); this.setLastValue(mixedValue);
this.startValidating(); this.startValidating();
if (!newConfirmationValue.equals(newValue)) { if (!newConfirmationValue.equals(newValue)) {
this.setMessageForValue(mixedValue, this.validator.getContext().getResources().getString(R.string.mismatch_pin)); this.setMessageForValue(mixedValue, this.validator.getContext().getResources().getString(R.string.mismatch_pin));
this.setValidForValue(mixedValue, false); this.setValidForValue(mixedValue, false);
@ -35,5 +35,11 @@ public class PinConfirmationValidationField extends ValidationField {
this.setValidForValue(mixedValue, true); this.setValidForValue(mixedValue, true);
} }
} }
} else {
this.setLastValue("");
this.startValidating();
this.setMessageForValue("", "");
this.setValidForValue("", false);
}
} }
} }

View file

@ -20,7 +20,8 @@ public class PinValidationField extends ValidationField {
public void validate(){ public void validate(){
String newValue = pinField.getText().toString(); String newValue = pinField.getText().toString();
if (newValue != this.getLastValue()) { if (!newValue.equals("")) {
if (!newValue.equals(this.getLastValue())) {
this.setLastValue(newValue); this.setLastValue(newValue);
this.startValidating(); this.startValidating();
@ -31,5 +32,11 @@ public class PinValidationField extends ValidationField {
this.setValidForValue(newValue, true); this.setValidForValue(newValue, true);
} }
} }
} else {
this.setLastValue("");
this.startValidating();
this.setMessageForValue("", "");
this.setValidForValue("", false);
}
} }
} }

View file

@ -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<ContactAddress, ContactAddressViewHolder> {
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();
}
}
}

View file

@ -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<CryptoNet> 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<CryptoNet>(
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<cryptoNetArray.length;i++){
nextCryptoNet = cryptoNetArray[i];
if (nextCryptoNet.equals(contactAddress.getCryptoNet())){
spCryptoNet.setSelection(i);
break;
}
}
spCryptoNet.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> 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());
}
}
}

View file

@ -12,9 +12,7 @@ import android.widget.RelativeLayout;
import cy.agorise.crystalwallet.R; import cy.agorise.crystalwallet.R;
import cy.agorise.crystalwallet.models.Contact; import cy.agorise.crystalwallet.models.Contact;
import cy.agorise.crystalwallet.models.CryptoCoinTransaction;
import cy.agorise.crystalwallet.viewmodels.ContactListViewModel; import cy.agorise.crystalwallet.viewmodels.ContactListViewModel;
import cy.agorise.crystalwallet.viewmodels.TransactionListViewModel;
/** /**
* Created by Henry Varona on 1/15/2018. * Created by Henry Varona on 1/15/2018.

View file

@ -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<Contact, ContactSelectionViewHolder> 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);
}
}

View file

@ -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);
}
}

View file

@ -1,13 +1,24 @@
package cy.agorise.crystalwallet.views; package cy.agorise.crystalwallet.views;
import android.content.Context; 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.RecyclerView;
import android.support.v7.widget.ThemedSpinnerAdapter;
import android.view.View; import android.view.View;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import com.squareup.picasso.Picasso;
import cy.agorise.crystalwallet.R; 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.models.Contact;
import cy.agorise.crystalwallet.util.CircleTransformation;
import cy.agorise.crystalwallet.util.MD5Hash;
/** /**
* Created by Henry Varona on 1/17/2017. * Created by Henry Varona on 1/17/2017.
@ -19,6 +30,7 @@ public class ContactViewHolder extends RecyclerView.ViewHolder {
private TextView tvName; private TextView tvName;
private ImageView ivThumbnail; private ImageView ivThumbnail;
private TextView tvLastPaid; private TextView tvLastPaid;
private ImageView ivDeleteContact;
private Context context; private Context context;
public ContactViewHolder(View itemView) { public ContactViewHolder(View itemView) {
@ -27,6 +39,7 @@ public class ContactViewHolder extends RecyclerView.ViewHolder {
tvName = (TextView) itemView.findViewById(R.id.tvContactName); tvName = (TextView) itemView.findViewById(R.id.tvContactName);
ivThumbnail = (ImageView) itemView.findViewById(R.id.ivContactThumbnail); ivThumbnail = (ImageView) itemView.findViewById(R.id.ivContactThumbnail);
tvLastPaid = (TextView) itemView.findViewById(R.id.tvLastPaid); tvLastPaid = (TextView) itemView.findViewById(R.id.tvLastPaid);
ivDeleteContact = (ImageView) itemView.findViewById(R.id.ivDeleteContact);
this.context = itemView.getContext(); this.context = itemView.getContext();
} }
@ -49,6 +62,47 @@ public class ContactViewHolder extends RecyclerView.ViewHolder {
} else { } else {
tvName.setText(contact.getName()); tvName.setText(contact.getName());
tvLastPaid.setText("Paid: 1 Jan, 2001 01:01"); 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();
}
});
} }
} }
} }

View file

@ -17,16 +17,22 @@ import cy.agorise.crystalwallet.models.CryptoCoinBalance;
public class CryptoCoinBalanceListAdapter extends ListAdapter<CryptoCoinBalance, CryptoCoinBalanceViewHolder> { public class CryptoCoinBalanceListAdapter extends ListAdapter<CryptoCoinBalance, CryptoCoinBalanceViewHolder> {
CryptoNetBalanceViewHolder cryptoNetBalanceViewHolder;
public CryptoCoinBalanceListAdapter() { public CryptoCoinBalanceListAdapter() {
super(CryptoCoinBalance.DIFF_CALLBACK); super(CryptoCoinBalance.DIFF_CALLBACK);
} }
public void setCryptoNetBalanceViewHolder(CryptoNetBalanceViewHolder cryptoNetBalanceViewHolder){
this.cryptoNetBalanceViewHolder = cryptoNetBalanceViewHolder;
}
@Override @Override
public CryptoCoinBalanceViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { public CryptoCoinBalanceViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.crypto_coin_balance_list_item,parent,false); 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 @Override

View file

@ -83,7 +83,7 @@ public class CryptoCoinBalanceListView extends RelativeLayout {
* *
* @param data the list of crypto coin balances that will be show to the user * @param data the list of crypto coin balances that will be show to the user
*/ */
public void setData(List<CryptoCoinBalance> data){ public void setData(List<CryptoCoinBalance> data, CryptoNetBalanceViewHolder cryptoNetBalanceViewHolder){
//initializes the list adapter //initializes the list adapter
if (this.listAdapter == null) { if (this.listAdapter == null) {
this.listAdapter = new CryptoCoinBalanceListAdapter(); this.listAdapter = new CryptoCoinBalanceListAdapter();
@ -92,6 +92,7 @@ public class CryptoCoinBalanceListView extends RelativeLayout {
//sets the data of the list adapter //sets the data of the list adapter
if (data != null) { if (data != null) {
this.listAdapter.setCryptoNetBalanceViewHolder(cryptoNetBalanceViewHolder);
this.listAdapter.setList(data); this.listAdapter.setList(data);
} }
} }

View file

@ -39,12 +39,15 @@ public class CryptoCoinBalanceViewHolder extends RecyclerView.ViewHolder {
private Context context; private Context context;
public CryptoCoinBalanceViewHolder(View itemView) { private CryptoNetBalanceViewHolder cryptoNetBalanceViewHolder;
public CryptoCoinBalanceViewHolder(View itemView, CryptoNetBalanceViewHolder cryptoNetBalanceViewHolder) {
super(itemView); super(itemView);
//TODO: use ButterKnife to load this //TODO: use ButterKnife to load this
cryptoCoinName = (TextView) itemView.findViewById(R.id.tvCryptoCoinName); cryptoCoinName = (TextView) itemView.findViewById(R.id.tvCryptoCoinName);
cryptoCoinBalance = (TextView) itemView.findViewById(R.id.tvCryptoCoinBalanceAmount); cryptoCoinBalance = (TextView) itemView.findViewById(R.id.tvCryptoCoinBalanceAmount);
cryptoCoinBalanceEquivalence = (TextView) itemView.findViewById(R.id.tvCryptoCoinBalanceEquivalence); cryptoCoinBalanceEquivalence = (TextView) itemView.findViewById(R.id.tvCryptoCoinBalanceEquivalence);
this.cryptoNetBalanceViewHolder = cryptoNetBalanceViewHolder;
this.context = itemView.getContext(); this.context = itemView.getContext();
} }
@ -80,7 +83,13 @@ public class CryptoCoinBalanceViewHolder extends RecyclerView.ViewHolder {
public void onChanged(@Nullable GeneralSetting generalSetting) { public void onChanged(@Nullable GeneralSetting generalSetting) {
if (generalSetting != null) { if (generalSetting != null) {
//Gets the currency object of the preferred currency //Gets the currency object of the preferred currency
CryptoCurrency currencyTo = CrystalDatabase.getAppDatabase(context).cryptoCurrencyDao().getByName(generalSetting.getValue()); LiveData<CryptoCurrency> currencyToLiveData = CrystalDatabase.getAppDatabase(context).cryptoCurrencyDao().getLiveDataByName(generalSetting.getValue());
currencyToLiveData.observe((LifecycleOwner) context, new Observer<CryptoCurrency>() {
@Override
public void onChanged(@Nullable CryptoCurrency cryptoCurrency) {
if (cryptoCurrency != null) {
CryptoCurrency currencyTo = cryptoCurrency;
//Retrieves the equivalent value of this balance using the "From" currency and the "To" currency //Retrieves the equivalent value of this balance using the "From" currency and the "To" currency
LiveData<CryptoCurrencyEquivalence> currencyEquivalenceLiveData = CrystalDatabase.getAppDatabase(context) LiveData<CryptoCurrencyEquivalence> currencyEquivalenceLiveData = CrystalDatabase.getAppDatabase(context)
@ -96,12 +105,14 @@ public class CryptoCoinBalanceViewHolder extends RecyclerView.ViewHolder {
public void onChanged(@Nullable CryptoCurrencyEquivalence cryptoCurrencyEquivalence) { public void onChanged(@Nullable CryptoCurrencyEquivalence cryptoCurrencyEquivalence) {
if (cryptoCurrencyEquivalence != null) { if (cryptoCurrencyEquivalence != null) {
CryptoCurrency toCurrency = CrystalDatabase.getAppDatabase(context).cryptoCurrencyDao().getById(cryptoCurrencyEquivalence.getFromCurrencyId()); 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( String equivalenceString = String.format(
"%.2f", "%.2f",
(balance.getBalance()/Math.pow(10,currencyFrom.getPrecision()))/ equivalentValue
(cryptoCurrencyEquivalence.getValue()/Math.pow(10,toCurrency.getPrecision()))
); );
cryptoNetBalanceViewHolder.setEquivalentBalance(balance,equivalentValue);
cryptoCoinBalanceEquivalence.setText( cryptoCoinBalanceEquivalence.setText(
equivalenceString + " " + toCurrency.getName()); equivalenceString + " " + toCurrency.getName());
} }
@ -112,4 +123,7 @@ public class CryptoCoinBalanceViewHolder extends RecyclerView.ViewHolder {
}); });
} }
} }
});
}
}
} }

View file

@ -16,6 +16,7 @@ import android.widget.Button;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import java.util.HashMap;
import java.util.List; import java.util.List;
import butterknife.BindView; import butterknife.BindView;
@ -48,11 +49,18 @@ public class CryptoNetBalanceViewHolder extends RecyclerView.ViewHolder {
*/ */
TextView cryptoNetName; 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 * The list view of the crypto coins balances of this crypto net balance
*/ */
CryptoCoinBalanceListView cryptoCoinBalanceListView; CryptoCoinBalanceListView cryptoCoinBalanceListView;
HashMap<CryptoCoinBalance, Double> equivalentTotalHashMap;
/* /*
* The button for sending transactions from this crypto net balance account * 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 //TODO: use ButterKnife to load the views
cryptoNetIcon = (ImageView) itemView.findViewById(R.id.ivCryptoNetIcon); cryptoNetIcon = (ImageView) itemView.findViewById(R.id.ivCryptoNetIcon);
cryptoNetName = (TextView) itemView.findViewById(R.id.tvCryptoNetName); cryptoNetName = (TextView) itemView.findViewById(R.id.tvCryptoNetName);
cryptoNetEquivalentTotal = (TextView) itemView.findViewById(R.id.tvCryptoNetEquivalentTotal);
cryptoCoinBalanceListView = (CryptoCoinBalanceListView) itemView.findViewById(R.id.cryptoCoinBalancesListView); cryptoCoinBalanceListView = (CryptoCoinBalanceListView) itemView.findViewById(R.id.cryptoCoinBalancesListView);
btnSendFromThisAccount = (Button) itemView.findViewById(R.id.btnSendFromThisAccount); btnSendFromThisAccount = (Button) itemView.findViewById(R.id.btnSendFromThisAccount);
btnReceiveToThisAccount = (Button) itemView.findViewById(R.id.btnReceiveWithThisAccount); btnReceiveToThisAccount = (Button) itemView.findViewById(R.id.btnReceiveWithThisAccount);
@ -157,6 +166,23 @@ public class CryptoNetBalanceViewHolder extends RecyclerView.ViewHolder {
newFragment.show(ft, "ReceiveDialog"); newFragment.show(ft, "ReceiveDialog");
} }
public void setEquivalentBalance(CryptoCoinBalance cryptoCoinBalance, double equivalentValue){
if (this.equivalentTotalHashMap == null){
this.equivalentTotalHashMap = new HashMap<CryptoCoinBalance, Double>();
}
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 * 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){ if (balance == null){
cryptoNetName.setText("loading..."); cryptoNetName.setText("loading...");
} else { } else {
final CryptoNetBalanceViewHolder thisViewHolder = this;
this.cryptoNetAccountId = balance.getAccountId(); this.cryptoNetAccountId = balance.getAccountId();
cryptoNetName.setText(balance.getCryptoNet().getLabel()); cryptoNetName.setText(balance.getCryptoNet().getLabel());
@ -172,13 +199,15 @@ public class CryptoNetBalanceViewHolder extends RecyclerView.ViewHolder {
cryptoCoinBalanceListViewModel.init(balance.getAccountId()); cryptoCoinBalanceListViewModel.init(balance.getAccountId());
LiveData<List<CryptoCoinBalance>> cryptoCoinBalanceData = cryptoCoinBalanceListViewModel.getCryptoCoinBalanceList(); LiveData<List<CryptoCoinBalance>> 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 //Observes the livedata, so any of its changes on the database will be reloaded here
cryptoCoinBalanceData.observe((LifecycleOwner)this.itemView.getContext(), new Observer<List<CryptoCoinBalance>>() { cryptoCoinBalanceData.observe((LifecycleOwner)this.itemView.getContext(), new Observer<List<CryptoCoinBalance>>() {
@Override @Override
public void onChanged(List<CryptoCoinBalance> cryptoCoinBalances) { public void onChanged(List<CryptoCoinBalance> cryptoCoinBalances) {
cryptoCoinBalanceListView.setData(cryptoCoinBalances); cryptoCoinBalanceListView.setData(cryptoCoinBalances, thisViewHolder);
} }
}); });
} }

View file

@ -1,20 +1,30 @@
package cy.agorise.crystalwallet.views; package cy.agorise.crystalwallet.views;
import android.arch.lifecycle.LifecycleOwner; import android.arch.lifecycle.LifecycleOwner;
import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.Observer;
import android.arch.lifecycle.ViewModelProviders; import android.arch.lifecycle.ViewModelProviders;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment; import android.support.v4.app.Fragment;
import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView;
import android.view.View; import android.view.View;
import android.widget.RelativeLayout; import android.widget.RelativeLayout;
import android.widget.TextView; 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.R;
import cy.agorise.crystalwallet.activities.CryptoCoinTransactionReceiptActivity; import cy.agorise.crystalwallet.activities.CryptoCoinTransactionReceiptActivity;
import cy.agorise.crystalwallet.models.CryptoCoinTransaction; import cy.agorise.crystalwallet.models.CryptoCoinTransaction;
import cy.agorise.crystalwallet.models.CryptoCurrency; import cy.agorise.crystalwallet.models.CryptoCurrency;
import cy.agorise.crystalwallet.models.CryptoNetAccount;
import cy.agorise.crystalwallet.viewmodels.CryptoCurrencyViewModel; import cy.agorise.crystalwallet.viewmodels.CryptoCurrencyViewModel;
import cy.agorise.crystalwallet.viewmodels.CryptoNetAccountViewModel;
/** /**
* Created by Henry Varona on 17/9/2017. * Created by Henry Varona on 17/9/2017.
@ -26,16 +36,18 @@ public class TransactionViewHolder extends RecyclerView.ViewHolder {
/* /*
* The view holding the transaction "from" * The view holding the transaction "from"
*/ */
private TextView transactionFrom; private TextView tvFrom;
/* /*
* The view holding the transaction "to" * The view holding the transaction "to"
*/ */
private TextView transactionTo; private TextView tvTo;
/* /*
* The view holding the transaction amount * The view holding the transaction amount
*/ */
private TextView transactionAmount; private TextView tvAmount;
private TextView tvEquivalent;
private TextView tvTransactionDate; private TextView tvTransactionDate;
private TextView tvTransactionHour;
private View rootView; private View rootView;
private Fragment fragment; private Fragment fragment;
@ -48,10 +60,12 @@ public class TransactionViewHolder extends RecyclerView.ViewHolder {
this.cryptoCoinTransactionId = -1; this.cryptoCoinTransactionId = -1;
rootView = itemView.findViewById(R.id.rlTransactionItem); rootView = itemView.findViewById(R.id.rlTransactionItem);
transactionFrom = (TextView) itemView.findViewById(R.id.fromText); tvFrom = (TextView) itemView.findViewById(R.id.fromText);
transactionTo = (TextView) itemView.findViewById(R.id.toText); tvTo = (TextView) itemView.findViewById(R.id.toText);
transactionAmount = (TextView) itemView.findViewById(R.id.amountText); tvAmount = (TextView) itemView.findViewById(R.id.tvAmount);
tvEquivalent = (TextView) itemView.findViewById(R.id.tvEquivalent);
tvTransactionDate = (TextView) itemView.findViewById(R.id.tvTransactionDate); tvTransactionDate = (TextView) itemView.findViewById(R.id.tvTransactionDate);
tvTransactionHour = (TextView) itemView.findViewById(R.id.tvTransactionHour);
this.fragment = fragment; this.fragment = fragment;
rootView.setOnClickListener(new View.OnClickListener() { rootView.setOnClickListener(new View.OnClickListener() {
@ -85,9 +99,12 @@ public class TransactionViewHolder extends RecyclerView.ViewHolder {
* Clears all info in this element view * Clears all info in this element view
*/ */
public void clear(){ public void clear(){
transactionFrom.setText("loading..."); tvFrom.setText("loading...");
transactionTo.setText(""); tvTo.setText("");
transactionAmount.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*/) { public void bindTo(final CryptoCoinTransaction transaction/*, final OnTransactionClickListener listener*/) {
if (transaction == null){ if (transaction == null){
transactionFrom.setText("loading..."); clear();
transactionTo.setText("");
transactionAmount.setText("");
} else { } else {
this.cryptoCoinTransactionId = transaction.getId(); this.cryptoCoinTransactionId = transaction.getId();
CryptoCurrencyViewModel cryptoCurrencyViewModel = ViewModelProviders.of(this.fragment).get(CryptoCurrencyViewModel.class); CryptoCurrencyViewModel cryptoCurrencyViewModel = ViewModelProviders.of(this.fragment).get(CryptoCurrencyViewModel.class);
CryptoCurrency cryptoCurrency = cryptoCurrencyViewModel.getCryptoCurrencyById(transaction.getIdCurrency()); 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())); String amountString = String.format("%.2f",transaction.getAmount()/Math.pow(10,cryptoCurrency.getPrecision()));
tvTransactionDate.setText(transaction.getDate().toString()); DateFormat dateFormat = new SimpleDateFormat("dd MMM");
transactionFrom.setText(transaction.getFrom()); dateFormat.setTimeZone(TimeZone.getTimeZone("cet"));
transactionTo.setText(transaction.getTo()); 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<CryptoNetAccount> cryptoNetAccountLiveData = cryptoNetAccountViewModel.getCryptoNetAccount();
cryptoNetAccountLiveData.observe(this.fragment, new Observer<CryptoNetAccount>() {
@Override
public void onChanged(@Nullable CryptoNetAccount cryptoNetAccount) {
if (transaction.getInput()){ if (transaction.getInput()){
transactionAmount.setTextColor(itemView.getContext().getResources().getColor(R.color.green)); tvTo.setText(cryptoNetAccount.getName());
} else { } else {
transactionAmount.setTextColor(itemView.getContext().getResources().getColor(R.color.red)); tvFrom.setText(cryptoNetAccount.getName());
} }
transactionAmount.setText( }
amountString });
String finalAmountText = "";
if (transaction.getInput()) {
tvAmount.setTextColor(itemView.getContext().getResources().getColor(R.color.green));
finalAmountText = "+ "+amountString
+ " " + " "
+ cryptoCurrency.getName()); + cryptoCurrency.getName();
} else {
tvAmount.setTextColor(itemView.getContext().getResources().getColor(R.color.red));
finalAmountText = amountString
+ " "
+ cryptoCurrency.getName();
}
tvAmount.setText(finalAmountText);
//This will load the transaction receipt when the user clicks this view //This will load the transaction receipt when the user clicks this view
/*itemView.setOnClickListener(new View.OnClickListener() { /*itemView.setOnClickListener(new View.OnClickListener() {
@Override @Override

View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="rectangle">
<stroke android:color="#5FBD8A" android:width="4dp"/>
</shape>
</item>
<item android:left="3dp">
<shape android:shape="rectangle">
<solid android:color="@android:color/white" />
</shape>
</item>
</layer-list>

View file

@ -0,0 +1,174 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="0dp"
android:paddingLeft="0dp"
android:paddingRight="0dp"
android:paddingTop="@dimen/activity_vertical_margin">
<TextView
android:id="@+id/tvName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/activity_horizontal_margin"
android:layout_marginRight="@dimen/activity_horizontal_margin"
android:layout_marginTop="10dp"
android:text="Contact Name"
android:textStyle="bold" />
<EditText
android:id="@+id/etName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/activity_horizontal_margin"
android:layout_marginRight="@dimen/activity_horizontal_margin"
android:background="@drawable/edittext_bg"
android:maxLines="1"
android:textColor="@color/black" />
<TextView
android:id="@+id/tvNameError"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/activity_horizontal_margin"
android:layout_marginRight="@dimen/activity_horizontal_margin"
android:maxLines="1"
android:textColor="@color/red" />
<TextView
android:id="@+id/tvEmail"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/activity_horizontal_margin"
android:layout_marginRight="@dimen/activity_horizontal_margin"
android:layout_marginTop="10dp"
android:text="Email"
android:textStyle="bold" />
<EditText
android:id="@+id/etEmail"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/activity_horizontal_margin"
android:layout_marginRight="@dimen/activity_horizontal_margin"
android:background="@drawable/edittext_bg"
android:maxLines="1"
android:textColor="@color/black" />
<TextView
android:id="@+id/tvEmailError"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/activity_horizontal_margin"
android:layout_marginRight="@dimen/activity_horizontal_margin"
android:maxLines="1"
android:textColor="@color/red" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.RecyclerView
android:id="@+id/rvContactAddresses"
android:layout_width="match_parent"
android:layout_height="wrap_content"></android.support.v7.widget.RecyclerView>
<Button
android:id="@+id/btnAddAddress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/rvContactAddresses"
android:text="Add"/>
</RelativeLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/activity_horizontal_margin"
android:layout_marginRight="@dimen/activity_horizontal_margin"
android:layout_marginTop="10dp"
android:gravity="center"
android:orientation="horizontal">
<Button
android:id="@+id/btnCancel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="10dp"
android:background="@color/pink"
android:text="@string/cancel"
android:textColor="@color/white" />
<Button
android:id="@+id/btnCreate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="10dp"
android:background="@color/green"
android:padding="10dp"
android:text="Create Contact"
android:textColor="@color/white" />
<Button
android:id="@+id/btnModify"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="10dp"
android:background="@color/green"
android:padding="10dp"
android:text="Modify Contact"
android:textColor="@color/white" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white"
android:gravity="bottom"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/black"></LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="35dp"
android:background="@color/bottomBarColor"
android:gravity="bottom"
android:orientation="horizontal">
<TextView
android:id="@+id/tvAppVersion_brain_key_activity"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:text="@string/v_1_0_beta" />
<TextView
android:id="@+id/tvBlockNumberHead_brain_key_activity"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="2"
android:gravity="center"
android:text="@string/block_number" />
<ImageView
android:id="@+id/ivSocketConnected_brain_key_activity"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_weight="0.5" />
<ImageView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_weight="0.5"
android:src="@drawable/icon_setting"
android:visibility="invisible" />
</LinearLayout>
</LinearLayout>
</LinearLayout>

View file

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:padding="5dp"
android:background="@color/colorPrimary">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/white"
android:textSize="30dp"
android:padding="10dp"
android:background="@color/colorPrimary"
android:text="Enter Pin:"/>
<EditText
android:id="@+id/etPassword"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="numberPassword" />
</LinearLayout>
</LinearLayout>

View file

@ -14,7 +14,7 @@
android:layout_alignParentStart="true" android:layout_alignParentStart="true"
android:layout_alignParentTop="true"> android:layout_alignParentTop="true">
<LinearLayout <RelativeLayout
android:id="@+id/cryptoNetBalanceTitleBarLayout" android:id="@+id/cryptoNetBalanceTitleBarLayout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -31,20 +31,30 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:ems="10" android:ems="10"
android:layout_toRightOf="@id/ivCryptoNetIcon"
android:text="unknown coin" /> android:text="unknown coin" />
<TextView
android:id="@+id/tvCryptoNetEquivalentTotal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:text="0.003€" />
<Button <Button
android:id="@+id/btnSendFromThisAccount" android:id="@+id/btnSendFromThisAccount"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="send"/> android:text="send"
android:visibility="gone" />
<Button <Button
android:id="@+id/btnReceiveWithThisAccount" android:id="@+id/btnReceiveWithThisAccount"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="receive"/> android:text="receive"
</LinearLayout> android:visibility="gone" />
</RelativeLayout>
<cy.agorise.crystalwallet.views.CryptoCoinBalanceListView <cy.agorise.crystalwallet.views.CryptoCoinBalanceListView
android:id="@+id/cryptoCoinBalancesListView" android:id="@+id/cryptoCoinBalancesListView"

View file

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:paddingTop="10dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true">
<Spinner
android:layout_width="@dimen/icon_size"
android:layout_height="@dimen/icon_size"
android:id="@+id/spCryptoNet"/>
<EditText
android:id="@+id/etAddress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@+id/spCryptoNet"
android:layout_alignParentEnd="true"
android:textStyle="bold" />
</RelativeLayout>
</LinearLayout>

View file

@ -34,6 +34,14 @@
android:layout_below="@+id/tvContactName" android:layout_below="@+id/tvContactName"
android:layout_toRightOf="@+id/ivContactThumbnail" android:layout_toRightOf="@+id/ivContactThumbnail"
android:text="Paid: Jan 1, 2001, 01:01" android:text="Paid: Jan 1, 2001, 01:01"
android:textColor="@android:color/darker_gray" /> android:textColor="@android:color/darker_gray"
android:visibility="gone" />
<ImageView
android:id="@+id/ivDeleteContact"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:src="@drawable/deleteicon" />
</RelativeLayout> </RelativeLayout>
</LinearLayout> </LinearLayout>

View file

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:paddingTop="10dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true">
<ImageView
android:layout_width="@dimen/icon_size"
android:layout_height="@dimen/icon_size"
android:id="@+id/ivContactThumbnail"/>
<TextView
android:id="@+id/tvContactName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@+id/ivContactThumbnail"
android:text="Loading name..."
android:textStyle="bold" />
<TextView
android:id="@+id/tvLastPaid"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/tvContactName"
android:layout_toRightOf="@+id/ivContactThumbnail"
android:text="Paid: Jan 1, 2001, 01:01"
android:textColor="@android:color/darker_gray" />
</RelativeLayout>
</LinearLayout>

View file

@ -56,9 +56,9 @@
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
app:layout_constraintTop_toBottomOf="@+id/tvTaxableCountry" app:layout_constraintEnd_toEndOf="@id/spBackupAsset"
app:layout_constraintStart_toStartOf="@id/spBackupAsset" app:layout_constraintStart_toStartOf="@id/spBackupAsset"
app:layout_constraintEnd_toEndOf="@id/spBackupAsset"/> app:layout_constraintTop_toBottomOf="@+id/tvTaxableCountry" />
<View <View
android:id="@+id/vTaxableCountry" android:id="@+id/vTaxableCountry"

View file

@ -28,6 +28,13 @@
app:layout_constraintTop_toBottomOf="@+id/tvCurrentPin" app:layout_constraintTop_toBottomOf="@+id/tvCurrentPin"
tools:ignore="LabelFor" /> tools:ignore="LabelFor" />
<TextView
android:id="@+id/tvCurrentPinError"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/red"
app:layout_constraintTop_toBottomOf="@+id/etCurrentPin" />
<EditText <EditText
android:id="@+id/etNewPin" android:id="@+id/etNewPin"
android:layout_width="0dp" android:layout_width="0dp"
@ -39,7 +46,13 @@
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5" app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/etCurrentPin" /> app:layout_constraintTop_toBottomOf="@+id/tvCurrentPinError" />
<TextView
android:id="@+id/tvNewPinError"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/red"
app:layout_constraintTop_toBottomOf="@+id/etNewPin" />
<EditText <EditText
android:id="@+id/etConfirmPin" android:id="@+id/etConfirmPin"
@ -52,6 +65,11 @@
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5" app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/etNewPin" /> app:layout_constraintTop_toBottomOf="@+id/tvNewPinError" />
<TextView
android:id="@+id/tvConfirmPinError"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/red"
app:layout_constraintTop_toBottomOf="@+id/etConfirmPin" />
</android.support.constraint.ConstraintLayout> </android.support.constraint.ConstraintLayout>

View file

@ -4,9 +4,22 @@
android:layout_height="match_parent" android:layout_height="match_parent"
tools:context="cy.agorise.crystalwallet.fragments.TransactionsFragment"> tools:context="cy.agorise.crystalwallet.fragments.TransactionsFragment">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!--<TextView
android:id="@+id/tvTransactionSearch"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android: />
<Spinner
android:layout_width="match_parent"
android:layout_height="match_parent"></Spinner>
-->
<cy.agorise.crystalwallet.views.TransactionListView <cy.agorise.crystalwallet.views.TransactionListView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:id="@+id/vTransactionListView" /> android:id="@+id/vTransactionListView" />
</RelativeLayout>
</android.support.v4.widget.NestedScrollView> </android.support.v4.widget.NestedScrollView>

View file

@ -1,59 +1,111 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical" android:orientation="vertical"
android:paddingBottom="5dp"
android:paddingLeft="10dp" android:paddingLeft="10dp"
android:paddingRight="10dp" android:paddingRight="10dp"
android:paddingTop="10dp"> android:paddingTop="5dp">
<RelativeLayout <RelativeLayout
android:id="@+id/rlTransactionItem" android:id="@+id/rlTransactionItem"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentStart="true" android:layout_alignParentStart="true"
android:layout_alignParentTop="true"> android:layout_alignParentTop="true"
android:background="@drawable/transaction_list_item_background">
<TextView
android:id="@+id/tvTransactionDate"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:ems="10"
android:gravity="center"
android:text="date" />
<TextView <TextView
android:id="@+id/fromText" android:id="@+id/fromText"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_below="@+id/tvTransactionDate" android:layout_alignParentStart="true"
android:layout_marginLeft="5dp"
android:layout_toStartOf="@+id/ivArrowFromTo"
android:ems="10" android:ems="10"
android:inputType="text" android:inputType="text"
android:text="from" /> android:text="from" />
<ImageView
android:id="@+id/ivArrowFromTo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:tint="@color/black"
app:srcCompat="@drawable/ic_arrow_forward" />
<TextView <TextView
android:id="@+id/toText" android:id="@+id/toText"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_below="@+id/tvTransactionDate"
android:layout_alignParentBottom="false"
android:layout_alignParentRight="true" android:layout_alignParentRight="true"
android:layout_toEndOf="@+id/ivArrowFromTo"
android:ems="10" android:ems="10"
android:inputType="text" android:inputType="text"
android:text="to" android:text="to"
android:textAlignment="textEnd" /> android:textAlignment="textEnd" />
<Space
android:id="@+id/sAfterFromTo"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/fromText"/>
<TextView <TextView
android:id="@+id/amountText" android:id="@+id/tvTransactionDate"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/sAfterFromTo"
android:layout_marginLeft="5dp"
android:layout_toStartOf="@+id/ivArrowFromTo"
android:ems="10"
android:text="02 Oct"
android:textSize="14sp"
android:textStyle="bold" />
<TextView
android:id="@+id/tvTransactionHour"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/tvTransactionDate"
android:layout_marginLeft="5dp"
android:layout_toStartOf="@+id/ivArrowFromTo"
android:ems="10"
android:text="15:01:18 CET" />
<TextView
android:id="@+id/tvAmount"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignWithParentIfMissing="false" android:layout_alignWithParentIfMissing="false"
android:layout_below="@+id/fromText" android:layout_below="@id/sAfterFromTo"
android:layout_centerHorizontal="true" android:layout_toEndOf="@+id/ivArrowFromTo"
android:ems="10" android:ems="10"
android:gravity="center"
android:inputType="text" android:inputType="text"
android:text="amount" /> android:text="+ 1 BTS"
android:textAlignment="textEnd"
android:textSize="14sp"
android:textStyle="bold" />
<TextView
android:id="@+id/tvEquivalent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignWithParentIfMissing="false"
android:layout_below="@id/tvAmount"
android:layout_toEndOf="@+id/ivArrowFromTo"
android:ems="10"
android:inputType="text"
android:text="0.005€"
android:textAlignment="textEnd" />
</RelativeLayout> </RelativeLayout>
</LinearLayout> </LinearLayout>