diff --git a/app/build.gradle b/app/build.gradle index 403a2dc..199a7e6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -53,4 +53,7 @@ dependencies { compile 'org.bitcoinj:bitcoinj-core:0.14.3' compile 'com.neovisionaries:nv-websocket-client:1.30' compile 'org.tukaani:xz:1.6' + compile 'com.jakewharton:butterknife:8.8.1' + apt 'com.jakewharton:butterknife-compiler:8.8.1' + } diff --git a/app/src/androidTest/java/carbon/crypto/com/carbon/TransactionListTest.java b/app/src/androidTest/java/carbon/crypto/com/carbon/TransactionListTest.java index 60f8a33..07089cf 100644 --- a/app/src/androidTest/java/carbon/crypto/com/carbon/TransactionListTest.java +++ b/app/src/androidTest/java/carbon/crypto/com/carbon/TransactionListTest.java @@ -10,13 +10,10 @@ import android.support.test.InstrumentationRegistry; import android.support.test.rule.ActivityTestRule; import android.support.test.runner.AndroidJUnit4; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Date; import java.util.List; import carbon.crypto.com.carbon.Assertions.RecyclerViewItemsCountAssertion; -import cy.agorise.crystalwallet.IntroActivity; +import cy.agorise.crystalwallet.activities.IntroActivity; import cy.agorise.crystalwallet.R; import cy.agorise.crystalwallet.dao.CrystalDatabase; import cy.agorise.crystalwallet.models.CryptoCoinTransaction; @@ -25,9 +22,6 @@ import cy.agorise.crystalwallet.randomdatagenerators.RandomTransactionsGenerator import static android.support.test.espresso.Espresso.onView; import static android.support.test.espresso.matcher.ViewMatchers.withId; -import java.util.Random; -import java.util.concurrent.ThreadLocalRandom; - /** * Created by Henry Varona on 19/9/2017. diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 04eb050..716cd8e 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -10,13 +10,17 @@ android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> - + + + + + \ No newline at end of file diff --git a/app/src/main/java/cy/agorise/crystalwallet/activities/AccountSeedsManagementActivity.java b/app/src/main/java/cy/agorise/crystalwallet/activities/AccountSeedsManagementActivity.java new file mode 100644 index 0000000..3a1a2af --- /dev/null +++ b/app/src/main/java/cy/agorise/crystalwallet/activities/AccountSeedsManagementActivity.java @@ -0,0 +1,57 @@ +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.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.widget.Button; + +import java.util.List; + +import butterknife.BindView; +import butterknife.ButterKnife; +import butterknife.OnClick; +import cy.agorise.crystalwallet.R; +import cy.agorise.crystalwallet.dao.CrystalDatabase; +import cy.agorise.crystalwallet.models.AccountSeed; +import cy.agorise.crystalwallet.viewmodels.AccountSeedListViewModel; +import cy.agorise.crystalwallet.views.AccountSeedListView; + +public class AccountSeedsManagementActivity extends AppCompatActivity { + + AccountSeedsManagementActivity accountSeedsManagementActivity; + AccountSeedListViewModel accountSeedListViewModel; + + @BindView(R.id.vAccountSeedList) + AccountSeedListView vAccountSeedList; + + @BindView(R.id.btnImportAccountSeed) + Button btnImportAccountSeed; + + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.account_seeds_management); + ButterKnife.bind(this); + + accountSeedListViewModel = ViewModelProviders.of(this).get(AccountSeedListViewModel.class); + LiveData> accountSeedData = accountSeedListViewModel.getAccountSeedList(); + vAccountSeedList.setData(null); + + accountSeedData.observe(this, new Observer>() { + @Override + public void onChanged(List accountSeeds) { + vAccountSeedList.setData(accountSeeds); + } + }); + } + + @OnClick (R.id.btnImportAccountSeed) + public void importAccountSeed(){ + Intent intent = new Intent(this, ImportSeedActivity.class); + startActivity(intent); + } +} diff --git a/app/src/main/java/cy/agorise/crystalwallet/activities/ImportSeedActivity.java b/app/src/main/java/cy/agorise/crystalwallet/activities/ImportSeedActivity.java new file mode 100644 index 0000000..ac5673a --- /dev/null +++ b/app/src/main/java/cy/agorise/crystalwallet/activities/ImportSeedActivity.java @@ -0,0 +1,66 @@ +package cy.agorise.crystalwallet.activities; + +import android.arch.lifecycle.LifecycleActivity; +import android.arch.lifecycle.ViewModelProviders; +import android.content.Intent; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +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 cy.agorise.crystalwallet.R; +import cy.agorise.crystalwallet.models.AccountSeed; +import cy.agorise.crystalwallet.viewmodels.AccountSeedListViewModel; +import cy.agorise.crystalwallet.viewmodels.AccountSeedViewModel; +import cy.agorise.crystalwallet.viewmodels.TransactionListViewModel; +import cy.agorise.crystalwallet.views.TransactionListView; + +public class ImportSeedActivity extends AppCompatActivity { + + AccountSeedViewModel accountSeedViewModel; + + @BindView(R.id.tvPin) + TextView tvPin; + + @BindView(R.id.tvPinConfirmation) + TextView tvPinConfirmation; + + @BindView(R.id.etSeedWords) + EditText etSeedWords; + + @BindView (R.id.etAccountName) + EditText etAccountName; + + @BindView(R.id.btnImport) + Button btnImport; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.import_seed); + + ButterKnife.bind(this); + + accountSeedViewModel = ViewModelProviders.of(this).get(AccountSeedViewModel.class); + //this.seed = new AccountSeed(); + } + + @OnClick(R.id.btnImport) + public void importSeed(){ + AccountSeed seed = new AccountSeed(); + + //TODO verify if PIN and PIN confirmation are not null and are the same + //TODO verify if words are already in the db + //TODO check if name has been asigned to other seed + seed.setMasterSeed(etSeedWords.getText().toString()); + seed.setName(etAccountName.getText().toString()); + + accountSeedViewModel.addSeed(seed); + } +} diff --git a/app/src/main/java/cy/agorise/crystalwallet/IntroActivity.java b/app/src/main/java/cy/agorise/crystalwallet/activities/IntroActivity.java similarity index 78% rename from app/src/main/java/cy/agorise/crystalwallet/IntroActivity.java rename to app/src/main/java/cy/agorise/crystalwallet/activities/IntroActivity.java index 19acc98..b5aa54a 100644 --- a/app/src/main/java/cy/agorise/crystalwallet/IntroActivity.java +++ b/app/src/main/java/cy/agorise/crystalwallet/activities/IntroActivity.java @@ -1,4 +1,4 @@ -package cy.agorise.crystalwallet; +package cy.agorise.crystalwallet.activities; import android.arch.lifecycle.LifecycleActivity; import android.arch.lifecycle.LiveData; @@ -6,6 +6,7 @@ import android.arch.lifecycle.Observer; import android.arch.lifecycle.ViewModelProvider; import android.arch.lifecycle.ViewModelProviders; import android.arch.paging.PagedList; +import android.content.Intent; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v7.app.AppCompatActivity; @@ -14,6 +15,7 @@ import android.widget.Button; import java.util.List; +import cy.agorise.crystalwallet.R; import cy.agorise.crystalwallet.dao.CrystalDatabase; import cy.agorise.crystalwallet.models.AccountSeed; import cy.agorise.crystalwallet.models.CryptoCoinTransaction; @@ -21,12 +23,13 @@ import cy.agorise.crystalwallet.models.CryptoNetAccount; import cy.agorise.crystalwallet.randomdatagenerators.RandomCryptoNetAccountGenerator; import cy.agorise.crystalwallet.randomdatagenerators.RandomSeedGenerator; import cy.agorise.crystalwallet.randomdatagenerators.RandomTransactionsGenerator; +import cy.agorise.crystalwallet.viewmodels.AccountSeedListViewModel; import cy.agorise.crystalwallet.viewmodels.TransactionListViewModel; import cy.agorise.crystalwallet.views.TransactionListView; import static cy.agorise.crystalwallet.R.string.transactions; -public class IntroActivity extends LifecycleActivity { +public class IntroActivity extends AppCompatActivity { TransactionListViewModel transactionListViewModel; TransactionListView transactionListView; @@ -36,6 +39,16 @@ public class IntroActivity extends LifecycleActivity { super.onCreate(savedInstanceState); setContentView(R.layout.activity_intro); + //Checks if the user has any seed created + AccountSeedListViewModel accountSeedListViewModel = ViewModelProviders.of(this).get(AccountSeedListViewModel.class); + + //if (accountSeedListViewModel.accountSeedsCount() == 0){ + //If the user doesn't have any seeds created, then + //send the user to create/import an account + Intent intent = new Intent(this, AccountSeedsManagementActivity.class); + startActivity(intent); + //} + /*CrystalDatabase db = CrystalDatabase.getAppDatabase(getApplicationContext()); List seeds = RandomSeedGenerator.generateSeeds(2); for(int i=0;i> transactionData = transactionListViewModel.getTransactionList(); @@ -64,6 +77,6 @@ public class IntroActivity extends LifecycleActivity { public void onChanged(PagedList cryptoCoinTransactions) { transactionListView.setData(cryptoCoinTransactions); } - }); + });*/ } } diff --git a/app/src/main/java/cy/agorise/crystalwallet/apigenerator/GrapheneApiGenerator.java b/app/src/main/java/cy/agorise/crystalwallet/apigenerator/GrapheneApiGenerator.java index b5a2362..47926aa 100644 --- a/app/src/main/java/cy/agorise/crystalwallet/apigenerator/GrapheneApiGenerator.java +++ b/app/src/main/java/cy/agorise/crystalwallet/apigenerator/GrapheneApiGenerator.java @@ -10,6 +10,7 @@ import java.util.List; import cy.agorise.graphenej.Address; import cy.agorise.graphenej.UserAccount; +import cy.agorise.graphenej.api.GetAccountByName; import cy.agorise.graphenej.api.GetAccounts; import cy.agorise.graphenej.api.GetKeyReferences; import cy.agorise.graphenej.api.GetRelativeAccountHistory; @@ -165,12 +166,45 @@ public class GrapheneApiGenerator { } /** - * Gets if an Account Name is avaible to be used for a new account + * Retrieves the account id by the name of the account * * @param accountName The account Name to find * @param request The Api request object, to answer this petition */ - public static void isAccountNameAvaible(String accountName, ApiRequest request){ - //TODO implement + public static void getAccountIdByName(String accountName, final ApiRequest request){ + WebSocketFactory factory = new WebSocketFactory().setConnectionTimeout(connectionTimeout); + try { + final WebSocket webSocket = factory.createSocket(url); + webSocket.addListener(new GetAccountByName(accountName, new WitnessResponseListener() { + @Override + public void onSuccess(WitnessResponse response) { + AccountProperties accountProperties = ((WitnessResponse) response).result; + if(accountProperties != null){ + request.getListener().success(null,request.getId()); + }else{ + request.getListener().success(accountProperties.id,request.getId()); + } + } + + @Override + public void onError(BaseResponse.Error error) { + request.getListener().fail(request.getId()); + } + })); + Thread thread = new Thread(){ + public void run(){ + try { + webSocket.connect(); + } catch (WebSocketException e) { + e.printStackTrace(); + request.getListener().fail(request.getId()); + } + } + }; + thread.start(); + } catch (IOException e) { + e.printStackTrace(); + request.getListener().fail(request.getId()); + } } } diff --git a/app/src/main/java/cy/agorise/crystalwallet/cryptonetinforequests/CryptoNetInfoRequest.java b/app/src/main/java/cy/agorise/crystalwallet/cryptonetinforequests/CryptoNetInfoRequest.java new file mode 100644 index 0000000..24c11e9 --- /dev/null +++ b/app/src/main/java/cy/agorise/crystalwallet/cryptonetinforequests/CryptoNetInfoRequest.java @@ -0,0 +1,24 @@ +package cy.agorise.crystalwallet.cryptonetinforequests; + +import cy.agorise.crystalwallet.enums.CryptoCoin; + +/** + * Created by Henry Varona on 1/10/2017. + */ + +public abstract class CryptoNetInfoRequest { + protected CryptoCoin coin; + protected CryptoNetInfoRequestListener listener; + + public CryptoNetInfoRequest(CryptoCoin coin){ + this.coin = coin; + } + + public void setListener(CryptoNetInfoRequestListener listener){ + this.listener = listener; + } + + public void _fireOnCarryOutEvent(){ + listener.onCarryOut(); + } +} diff --git a/app/src/main/java/cy/agorise/crystalwallet/cryptonetinforequests/CryptoNetInfoRequestListener.java b/app/src/main/java/cy/agorise/crystalwallet/cryptonetinforequests/CryptoNetInfoRequestListener.java new file mode 100644 index 0000000..8b3d63a --- /dev/null +++ b/app/src/main/java/cy/agorise/crystalwallet/cryptonetinforequests/CryptoNetInfoRequestListener.java @@ -0,0 +1,10 @@ +package cy.agorise.crystalwallet.cryptonetinforequests; + +/** + * Created by Henry Varona on 1/10/2017. + */ + +interface CryptoNetInfoRequestListener { + + public void onCarryOut(); +} diff --git a/app/src/main/java/cy/agorise/crystalwallet/cryptonetinforequests/CryptoNetInfoRequests.java b/app/src/main/java/cy/agorise/crystalwallet/cryptonetinforequests/CryptoNetInfoRequests.java new file mode 100644 index 0000000..74d9126 --- /dev/null +++ b/app/src/main/java/cy/agorise/crystalwallet/cryptonetinforequests/CryptoNetInfoRequests.java @@ -0,0 +1,48 @@ +package cy.agorise.crystalwallet.cryptonetinforequests; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by Henry Varona on 1/10/2017. + */ + +public class CryptoNetInfoRequests { + private List requests; + private List listeners; + private CryptoNetInfoRequests instance; + + private void CryptoNetInfoRequests(){ + //Private constructor for singleton pattern + } + + public CryptoNetInfoRequests getInstance(){ + if (this.instance == null){ + this.instance = new CryptoNetInfoRequests(); + this.requests = new ArrayList(); + this.listeners = new ArrayList(); + } + + return this.instance; + } + + public void addRequest(CryptoNetInfoRequest request){ + this.requests.add(request); + + this._fireNewRequestEvent(request); + } + + public void removeRequest(CryptoNetInfoRequest request){ + this.requests.remove(request); + } + + public void addListener(CryptoNetInfoRequestsListener listener){ + this.listeners.add(listener); + } + + private void _fireNewRequestEvent(CryptoNetInfoRequest request){ + for (int i=0;i getAll(); + LiveData> getAll(); + + @Query("SELECT * FROM account_seed WHERE id = :id") + LiveData findById(long id); + + @Query("SELECT COUNT(*) from account_seed") + int countAccountSeeds(); @Insert(onConflict = OnConflictStrategy.REPLACE) - public long[] insertAccountSeed(AccountSeed... seeds); + public long[] insertAccountSeeds(AccountSeed... seeds); + @Insert(onConflict = OnConflictStrategy.REPLACE) + public long insertAccountSeed(AccountSeed seed); } diff --git a/app/src/main/java/cy/agorise/crystalwallet/dao/converters/Converters.java b/app/src/main/java/cy/agorise/crystalwallet/dao/converters/Converters.java index 020933c..9fa6cb4 100644 --- a/app/src/main/java/cy/agorise/crystalwallet/dao/converters/Converters.java +++ b/app/src/main/java/cy/agorise/crystalwallet/dao/converters/Converters.java @@ -6,6 +6,7 @@ import java.util.Date; import cy.agorise.crystalwallet.enums.CryptoCoin; import cy.agorise.crystalwallet.enums.CryptoNet; +import cy.agorise.crystalwallet.enums.SeedType; import cy.agorise.crystalwallet.models.CryptoNetAccount; import static cy.agorise.crystalwallet.R.string.account; @@ -84,4 +85,22 @@ public class Converters { return CryptoNet.valueOf(value); } } + + @TypeConverter + public String seedTypeToName(SeedType value) { + if (value == null) { + return ""; + } else { + return value.name(); + } + } + + @TypeConverter + public SeedType nameToSeedType(String value) { + if (value.equals("")){ + return null; + } else { + return SeedType.valueOf(value); + } + } } diff --git a/app/src/main/java/cy/agorise/crystalwallet/enums/SeedType.java b/app/src/main/java/cy/agorise/crystalwallet/enums/SeedType.java new file mode 100644 index 0000000..505a13f --- /dev/null +++ b/app/src/main/java/cy/agorise/crystalwallet/enums/SeedType.java @@ -0,0 +1,10 @@ +package cy.agorise.crystalwallet.enums; + +/** + * Created by Henry Varona on 30/9/2017. + */ + +public enum SeedType { + BIP39, + BRAINKEY +} diff --git a/app/src/main/java/cy/agorise/crystalwallet/manager/BitsharesAccountManager.java b/app/src/main/java/cy/agorise/crystalwallet/manager/BitsharesAccountManager.java index 9d75dcc..7d66cce 100644 --- a/app/src/main/java/cy/agorise/crystalwallet/manager/BitsharesAccountManager.java +++ b/app/src/main/java/cy/agorise/crystalwallet/manager/BitsharesAccountManager.java @@ -1,12 +1,15 @@ package cy.agorise.crystalwallet.manager; +import cy.agorise.crystalwallet.cryptonetinforequests.CryptoNetInfoRequest; +import cy.agorise.crystalwallet.cryptonetinforequests.CryptoNetInfoRequestsListener; +import cy.agorise.crystalwallet.cryptonetinforequests.ValidateImportBitsharesAccountRequest; import cy.agorise.crystalwallet.models.CryptoNetAccount; /** * Created by henry on 26/9/2017. */ -public class BitsharesAccountManager implements CryptoAccountManager { +public class BitsharesAccountManager implements CryptoAccountManager, CryptoNetInfoRequestsListener { @Override public CryptoNetAccount createAccountFromSeed(CryptoNetAccount account) { return null; @@ -21,4 +24,10 @@ public class BitsharesAccountManager implements CryptoAccountManager { public void loadAccountFromDB(CryptoNetAccount account) { } + + @Override + public void onNewRequest(CryptoNetInfoRequest request) { + if (request instanceof ValidateImportBitsharesAccountRequest){ + } + } } diff --git a/app/src/main/java/cy/agorise/crystalwallet/models/AccountSeed.java b/app/src/main/java/cy/agorise/crystalwallet/models/AccountSeed.java index b00cbf4..40294d4 100644 --- a/app/src/main/java/cy/agorise/crystalwallet/models/AccountSeed.java +++ b/app/src/main/java/cy/agorise/crystalwallet/models/AccountSeed.java @@ -4,6 +4,10 @@ package cy.agorise.crystalwallet.models; import android.arch.persistence.room.ColumnInfo; import android.arch.persistence.room.Entity; import android.arch.persistence.room.PrimaryKey; +import android.support.annotation.NonNull; +import android.support.v7.recyclerview.extensions.DiffCallback; + +import cy.agorise.crystalwallet.enums.SeedType; /** * Represents a type of crypto seed for HD wallets @@ -32,6 +36,11 @@ public class AccountSeed { @ColumnInfo(name = "master_seed") private String mMasterSeed; + /** + * The type of this seed: BIP39, BRAINKEY + */ + private SeedType type; + public long getId() { return mId; } @@ -56,5 +65,36 @@ public class AccountSeed { this.mMasterSeed = mMasterSeed; } + public SeedType getType() { + return type; + } + public void setType(SeedType type) { + this.type = type; + } + + public static final DiffCallback DIFF_CALLBACK = new DiffCallback() { + @Override + public boolean areItemsTheSame( + @NonNull AccountSeed oldAccountSeed, @NonNull AccountSeed newAccountSeed) { + return oldAccountSeed.getId() == newAccountSeed.getId(); + } + @Override + public boolean areContentsTheSame( + @NonNull AccountSeed oldAccountSeed, @NonNull AccountSeed newAccountSeed) { + return oldAccountSeed.equals(newAccountSeed); + } + }; + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + AccountSeed that = (AccountSeed) o; + + if (mId != that.mId) return false; + return mMasterSeed.equals(that.mMasterSeed); + + } } diff --git a/app/src/main/java/cy/agorise/crystalwallet/viewmodels/AccountSeedListViewModel.java b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/AccountSeedListViewModel.java new file mode 100644 index 0000000..b1b9ae6 --- /dev/null +++ b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/AccountSeedListViewModel.java @@ -0,0 +1,36 @@ +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.AccountSeed; +import cy.agorise.crystalwallet.models.CryptoCoinTransaction; + +/** + * Created by Henry Varona on 27/9/2017. + */ + +public class AccountSeedListViewModel extends AndroidViewModel { + + private LiveData> accountSeedList; + private CrystalDatabase db; + + public AccountSeedListViewModel(Application application) { + super(application); + this.db = CrystalDatabase.getAppDatabase(application.getApplicationContext()); + this.accountSeedList = this.db.accountSeedDao().getAll(); + } + + public LiveData> getAccountSeedList(){ + return this.accountSeedList; + } + + public int accountSeedsCount(){ + return this.db.accountSeedDao().countAccountSeeds(); + } +} diff --git a/app/src/main/java/cy/agorise/crystalwallet/viewmodels/AccountSeedViewModel.java b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/AccountSeedViewModel.java new file mode 100644 index 0000000..c36f4d4 --- /dev/null +++ b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/AccountSeedViewModel.java @@ -0,0 +1,38 @@ +package cy.agorise.crystalwallet.viewmodels; + +import android.app.Application; +import android.arch.lifecycle.AndroidViewModel; +import android.arch.lifecycle.LiveData; +import android.arch.lifecycle.MutableLiveData; + +import java.util.List; + +import cy.agorise.crystalwallet.dao.CrystalDatabase; +import cy.agorise.crystalwallet.models.AccountSeed; + +/** + * Created by Henry Varona on 27/9/2017. + */ + +public class AccountSeedViewModel extends AndroidViewModel { + + private LiveData accountSeed; + private CrystalDatabase db; + + public AccountSeedViewModel(Application application) { + super(application); + this.db = CrystalDatabase.getAppDatabase(application.getApplicationContext()); + } + + public void loadSeed(int seedId){ + this.accountSeed = this.db.accountSeedDao().findById(seedId); + } + + public void addSeed(AccountSeed seed){ + this.db.accountSeedDao().insertAccountSeed(seed); + } + + public LiveData getAccountSeed(){ + return this.accountSeed; + } +} diff --git a/app/src/main/java/cy/agorise/crystalwallet/views/AccountSeedListAdapter.java b/app/src/main/java/cy/agorise/crystalwallet/views/AccountSeedListAdapter.java new file mode 100644 index 0000000..8208deb --- /dev/null +++ b/app/src/main/java/cy/agorise/crystalwallet/views/AccountSeedListAdapter.java @@ -0,0 +1,40 @@ +package cy.agorise.crystalwallet.views; + + +import android.arch.paging.PagedListAdapter; +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.AccountSeed; +import cy.agorise.crystalwallet.models.CryptoCoinTransaction; + +/** + * Created by Henry Varona on 11/9/2017. + */ + +public class AccountSeedListAdapter extends ListAdapter { + + public AccountSeedListAdapter() { + super(AccountSeed.DIFF_CALLBACK); + } + + @Override + public AccountSeedViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.account_seed_list_item,parent,false); + + return new AccountSeedViewHolder(v); + } + + @Override + public void onBindViewHolder(AccountSeedViewHolder holder, int position) { + AccountSeed accountSeed = getItem(position); + if (accountSeed != null) { + holder.bindTo(accountSeed); + } else { + holder.clear(); + } + } +} diff --git a/app/src/main/java/cy/agorise/crystalwallet/views/AccountSeedListView.java b/app/src/main/java/cy/agorise/crystalwallet/views/AccountSeedListView.java new file mode 100644 index 0000000..2e8ea55 --- /dev/null +++ b/app/src/main/java/cy/agorise/crystalwallet/views/AccountSeedListView.java @@ -0,0 +1,73 @@ +package cy.agorise.crystalwallet.views; + +import android.arch.paging.PagedList; +import android.content.Context; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.RelativeLayout; + +import java.util.List; + +import cy.agorise.crystalwallet.R; +import cy.agorise.crystalwallet.models.AccountSeed; +import cy.agorise.crystalwallet.models.CryptoCoinTransaction; +import cy.agorise.crystalwallet.viewmodels.AccountSeedListViewModel; +import cy.agorise.crystalwallet.viewmodels.TransactionListViewModel; + +/** + * Created by Henry Varona on 10/9/2017. + */ + +public class AccountSeedListView extends RelativeLayout { + + LayoutInflater mInflater; + + View rootView; + RecyclerView listView; + AccountSeedListAdapter listAdapter; + + AccountSeedListViewModel accountSeedListViewModel; + + public AccountSeedListView(Context context){ + super(context); + this.mInflater = LayoutInflater.from(context); + init(); + } + + public AccountSeedListView(Context context, AttributeSet attrs) { + super(context, attrs); + this.mInflater = LayoutInflater.from(context); + init(); + } + + public AccountSeedListView(Context context, AttributeSet attrs, int defStyle){ + super(context, attrs, defStyle); + this.mInflater = LayoutInflater.from(context); + init(); + } + + public void init(){ + rootView = mInflater.inflate(R.layout.account_seed_list, this, true); + this.listView = rootView.findViewById(R.id.accountSeedListView); + + final LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this.getContext()); + this.listView.setLayoutManager(linearLayoutManager); + this.listView.setNestedScrollingEnabled(false); + } + + public void setData(List data){ + if (this.listAdapter == null) { + this.listAdapter = new AccountSeedListAdapter(); + this.listView.setAdapter(this.listAdapter); + } + + if (data != null) { + this.listAdapter.setList(data); + } + } + + +} diff --git a/app/src/main/java/cy/agorise/crystalwallet/views/AccountSeedViewHolder.java b/app/src/main/java/cy/agorise/crystalwallet/views/AccountSeedViewHolder.java new file mode 100644 index 0000000..1b0389c --- /dev/null +++ b/app/src/main/java/cy/agorise/crystalwallet/views/AccountSeedViewHolder.java @@ -0,0 +1,34 @@ +package cy.agorise.crystalwallet.views; + +import android.support.v7.widget.RecyclerView; +import android.view.View; +import android.widget.TextView; + +import cy.agorise.crystalwallet.R; +import cy.agorise.crystalwallet.models.AccountSeed; +import cy.agorise.crystalwallet.models.CryptoCoinTransaction; + +/** + * Created by Henry Varona on 17/9/2017. + */ + +public class AccountSeedViewHolder extends RecyclerView.ViewHolder { + private TextView tvAccountSeedName; + + public AccountSeedViewHolder(View itemView) { + super(itemView); + tvAccountSeedName = (TextView) itemView.findViewById(R.id.tvAccountSeedName); + } + + public void clear(){ + tvAccountSeedName.setText("loading..."); + } + + public void bindTo(final AccountSeed accountSeed) { + if (accountSeed == null){ + tvAccountSeedName.setText("loading..."); + } else { + tvAccountSeedName.setText(accountSeed.getName()); + } + } +} diff --git a/app/src/main/java/cy/agorise/graphenej/AuthorityType.java b/app/src/main/java/cy/agorise/graphenej/AuthorityType.java new file mode 100644 index 0000000..2fb52b8 --- /dev/null +++ b/app/src/main/java/cy/agorise/graphenej/AuthorityType.java @@ -0,0 +1,13 @@ +package cy.agorise.graphenej; + +/** + * Enum-type used to specify the different roles of an authority. + * + * @see Authority + */ + +public enum AuthorityType { + OWNER, + ACTIVE, + MEMO +} diff --git a/app/src/main/java/cy/agorise/graphenej/api/GetRelativeAccountHistory.java b/app/src/main/java/cy/agorise/graphenej/api/GetRelativeAccountHistory.java index e3d837e..935618f 100644 --- a/app/src/main/java/cy/agorise/graphenej/api/GetRelativeAccountHistory.java +++ b/app/src/main/java/cy/agorise/graphenej/api/GetRelativeAccountHistory.java @@ -20,6 +20,7 @@ import cy.agorise.graphenej.models.ApiCall; import cy.agorise.graphenej.models.BaseResponse; import cy.agorise.graphenej.models.HistoricalTransfer; import cy.agorise.graphenej.models.WitnessResponse; +import cy.agorise.graphenej.objects.Memo; import cy.agorise.graphenej.operations.TransferOperation; /** @@ -161,6 +162,7 @@ public class GetRelativeAccountHistory extends BaseGrapheneHandler { GsonBuilder gsonBuilder = new GsonBuilder(); gsonBuilder.registerTypeAdapter(TransferOperation.class, new TransferOperation.TransferDeserializer()); gsonBuilder.registerTypeAdapter(AssetAmount.class, new AssetAmount.AssetAmountDeserializer()); + gsonBuilder.registerTypeAdapter(Memo.class, new Memo.MemoDeserializer()); WitnessResponse> transfersResponse = gsonBuilder.create().fromJson(response, RelativeAccountHistoryResponse); mListener.onSuccess(transfersResponse); } diff --git a/app/src/main/java/cy/agorise/graphenej/api/SubscriptionMessagesHub.java b/app/src/main/java/cy/agorise/graphenej/api/SubscriptionMessagesHub.java index 5373bb6..f1bd5f5 100644 --- a/app/src/main/java/cy/agorise/graphenej/api/SubscriptionMessagesHub.java +++ b/app/src/main/java/cy/agorise/graphenej/api/SubscriptionMessagesHub.java @@ -26,6 +26,7 @@ import cy.agorise.graphenej.models.ApiCall; import cy.agorise.graphenej.models.DynamicGlobalProperties; import cy.agorise.graphenej.models.SubscriptionResponse; import cy.agorise.graphenej.models.WitnessResponse; +import cy.agorise.graphenej.objects.Memo; import cy.agorise.graphenej.operations.LimitOrderCreateOperation; import cy.agorise.graphenej.operations.TransferOperation; @@ -91,6 +92,7 @@ public class SubscriptionMessagesHub extends BaseGrapheneHandler implements Subs builder.registerTypeAdapter(AssetAmount.class, new AssetAmount.AssetAmountDeserializer()); builder.registerTypeAdapter(UserAccount.class, new UserAccount.UserAccountSimpleDeserializer()); builder.registerTypeAdapter(DynamicGlobalProperties.class, new DynamicGlobalProperties.DynamicGlobalPropertiesDeserializer()); + builder.registerTypeAdapter(Memo.class, new Memo.MemoDeserializer()); this.gson = builder.create(); } diff --git a/app/src/main/java/cy/agorise/graphenej/objects/Memo.java b/app/src/main/java/cy/agorise/graphenej/objects/Memo.java index 1ba540c..669c5e4 100644 --- a/app/src/main/java/cy/agorise/graphenej/objects/Memo.java +++ b/app/src/main/java/cy/agorise/graphenej/objects/Memo.java @@ -1,17 +1,25 @@ package cy.agorise.graphenej.objects; import com.google.common.primitives.Bytes; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; + import cy.agorise.graphenej.Address; import cy.agorise.graphenej.PublicKey; import cy.agorise.graphenej.Util; import cy.agorise.graphenej.errors.ChecksumException; +import cy.agorise.graphenej.errors.MalformedAddressException; import cy.agorise.graphenej.interfaces.ByteSerializable; import cy.agorise.graphenej.interfaces.JsonSerializable; +import cy.agorise.graphenej.operations.TransferOperation; + import org.bitcoinj.core.ECKey; import org.spongycastle.math.ec.ECPoint; +import java.lang.reflect.Type; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Arrays; @@ -269,4 +277,30 @@ public class Memo implements ByteSerializable, JsonSerializable { } return memoObject; } + + /** + * Class used to deserialize a memo + */ + public static class MemoDeserializer implements JsonDeserializer { + + @Override + public Memo deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + JsonObject jsonObject = json.getAsJsonObject(); + String fromAddress = jsonObject.get(KEY_FROM).getAsString(); + String toAddress = jsonObject.get(KEY_TO).getAsString(); + long nonce = jsonObject.get(KEY_NONCE).getAsLong(); + String msg = jsonObject.get(KEY_MESSAGE).getAsString(); + + Memo memo = null; + try{ + Address from = new Address(fromAddress); + Address to = new Address(toAddress); + byte[] message = Util.hexToBytes(msg); + memo = new Memo(from, to, nonce, message); + }catch(MalformedAddressException e){ + System.out.println("MalformedAddressException. Msg: "+e.getMessage()); + } + return memo; + } + } } \ No newline at end of file diff --git a/app/src/main/java/cy/agorise/graphenej/operations/TransferOperation.java b/app/src/main/java/cy/agorise/graphenej/operations/TransferOperation.java index 33698d9..0988640 100644 --- a/app/src/main/java/cy/agorise/graphenej/operations/TransferOperation.java +++ b/app/src/main/java/cy/agorise/graphenej/operations/TransferOperation.java @@ -1,7 +1,6 @@ package cy.agorise.graphenej.operations; import com.google.common.primitives.Bytes; -import com.google.common.primitives.UnsignedLong; import com.google.gson.GsonBuilder; import com.google.gson.JsonArray; import com.google.gson.JsonDeserializationContext; @@ -14,13 +13,10 @@ import com.google.gson.JsonSerializer; import java.lang.reflect.Type; -import cy.agorise.graphenej.Address; import cy.agorise.graphenej.AssetAmount; import cy.agorise.graphenej.BaseOperation; import cy.agorise.graphenej.OperationType; import cy.agorise.graphenej.UserAccount; -import cy.agorise.graphenej.Util; -import cy.agorise.graphenej.errors.MalformedAddressException; import cy.agorise.graphenej.objects.Memo; /** @@ -171,7 +167,6 @@ public class TransferOperation extends BaseOperation { @Override public TransferOperation deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { - System.out.println("Deserialized bitch start. Msg: "+ json.getAsString()); if(json.isJsonArray()){ // This block is used just to check if we are in the first step of the deserialization // when we are dealing with an array. @@ -197,20 +192,10 @@ public class TransferOperation extends BaseOperation { UserAccount to = new UserAccount(jsonObject.get(KEY_TO).getAsString()); TransferOperation transfer = new TransferOperation(from, to, amount, fee); - // Deserializing Memo if it exists - System.out.println("Deserialized bitch. Msg: "+ jsonObject.getAsString()); - if(jsonObject.get(KEY_MEMO) != null){ - JsonObject memoObj = jsonObject.get(KEY_MEMO).getAsJsonObject(); - try{ - Address memoFrom = new Address(memoObj.get(Memo.KEY_FROM).getAsString()); - Address memoTo = new Address(memoObj.get(KEY_TO).getAsString()); - long nonce = UnsignedLong.valueOf(memoObj.get(Memo.KEY_NONCE).getAsString()).longValue(); - byte[] message = Util.hexToBytes(memoObj.get(Memo.KEY_MESSAGE).getAsString()); - Memo memo = new Memo(memoFrom, memoTo, nonce, message); - transfer.setMemo(memo); - }catch(MalformedAddressException e){ - System.out.println("MalformedAddressException. Msg: "+e.getMessage()); - } + // If the transfer had a memo, deserialize it + if(jsonObject.has(KEY_MEMO)){ + Memo memo = context.deserialize(jsonObject.get(KEY_MEMO), Memo.class); + transfer.setMemo(memo); } return transfer; diff --git a/app/src/main/res/layout/account_seed_list.xml b/app/src/main/res/layout/account_seed_list.xml new file mode 100644 index 0000000..ca509d3 --- /dev/null +++ b/app/src/main/res/layout/account_seed_list.xml @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/account_seed_list_item.xml b/app/src/main/res/layout/account_seed_list_item.xml new file mode 100644 index 0000000..ed9e854 --- /dev/null +++ b/app/src/main/res/layout/account_seed_list_item.xml @@ -0,0 +1,24 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/account_seeds_management.xml b/app/src/main/res/layout/account_seeds_management.xml new file mode 100644 index 0000000..c55d574 --- /dev/null +++ b/app/src/main/res/layout/account_seeds_management.xml @@ -0,0 +1,20 @@ + + + +