From 09ae532446ea73f4313dbc0333fe2e6a9d1015cf Mon Sep 17 00:00:00 2001 From: Javier Varona Date: Sat, 17 Feb 2018 21:08:38 -0400 Subject: [PATCH] - Now the user can select a contact in the Send Fragment --- .../agorise/crystalwallet/dao/ContactDao.java | 7 + .../fragments/ContactSelectionFragment.java | 129 ++++++++++++++++++ .../fragments/ContactsFragment.java | 1 + .../fragments/SendTransactionFragment.java | 53 +++++++ .../agorise/crystalwallet/models/Contact.java | 14 ++ .../viewmodels/ContactListViewModel.java | 21 +++ .../views/ContactSelectionListAdapter.java | 57 ++++++++ .../views/ContactSelectionViewHolder.java | 79 +++++++++++ .../layout/contact_selection_list_item.xml | 39 ++++++ 9 files changed, 400 insertions(+) create mode 100644 app/src/main/java/cy/agorise/crystalwallet/fragments/ContactSelectionFragment.java create mode 100644 app/src/main/java/cy/agorise/crystalwallet/views/ContactSelectionListAdapter.java create mode 100644 app/src/main/java/cy/agorise/crystalwallet/views/ContactSelectionViewHolder.java create mode 100644 app/src/main/res/layout/contact_selection_list_item.xml diff --git a/app/src/main/java/cy/agorise/crystalwallet/dao/ContactDao.java b/app/src/main/java/cy/agorise/crystalwallet/dao/ContactDao.java index 8447c2b..84cab75 100644 --- a/app/src/main/java/cy/agorise/crystalwallet/dao/ContactDao.java +++ b/app/src/main/java/cy/agorise/crystalwallet/dao/ContactDao.java @@ -3,6 +3,7 @@ package cy.agorise.crystalwallet.dao; import android.arch.lifecycle.LiveData; import android.arch.paging.LivePagedListProvider; import android.arch.persistence.room.Dao; +import android.arch.persistence.room.Delete; import android.arch.persistence.room.Insert; import android.arch.persistence.room.OnConflictStrategy; import android.arch.persistence.room.Query; @@ -26,6 +27,9 @@ public interface ContactDao { @Query("SELECT * FROM contact ORDER BY name ASC") LivePagedListProvider contactsByName(); + @Query("SELECT c.* FROM contact c WHERE c.id IN (SELECT DISTINCT(ca.contact_id) FROM contact_address ca WHERE ca.crypto_net == :cryptoNet) ORDER BY name ASC") + LivePagedListProvider contactsByNameAndCryptoNet(String cryptoNet); + @Query("SELECT * FROM contact WHERE id = :id") LiveData getById(long id); @@ -49,4 +53,7 @@ public interface ContactDao { @Update(onConflict = OnConflictStrategy.REPLACE) public void updateAddressesFields(ContactAddress... contactAddresses); + + @Delete + public void deleteContacts(Contact... contacts); } diff --git a/app/src/main/java/cy/agorise/crystalwallet/fragments/ContactSelectionFragment.java b/app/src/main/java/cy/agorise/crystalwallet/fragments/ContactSelectionFragment.java new file mode 100644 index 0000000..46576d5 --- /dev/null +++ b/app/src/main/java/cy/agorise/crystalwallet/fragments/ContactSelectionFragment.java @@ -0,0 +1,129 @@ +package cy.agorise.crystalwallet.fragments; + +import android.app.Dialog; +import android.arch.lifecycle.LiveData; +import android.arch.lifecycle.Observer; +import android.arch.lifecycle.ViewModelProviders; +import android.arch.paging.PagedList; +import android.content.DialogInterface; +import android.content.Intent; +import android.os.Bundle; +import android.os.Handler; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.design.widget.FloatingActionButton; +import android.support.v4.app.DialogFragment; +import android.support.v7.app.AlertDialog; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.text.Editable; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; +import android.widget.Button; +import android.widget.EditText; +import android.widget.ImageView; +import android.widget.Spinner; +import android.widget.TextView; + +import com.google.zxing.Result; + +import java.util.ArrayList; +import java.util.List; + +import butterknife.BindView; +import butterknife.ButterKnife; +import butterknife.OnClick; +import butterknife.OnItemSelected; +import butterknife.OnTextChanged; +import cy.agorise.crystalwallet.R; +import cy.agorise.crystalwallet.cryptonetinforequests.CryptoNetInfoRequestListener; +import cy.agorise.crystalwallet.cryptonetinforequests.CryptoNetInfoRequests; +import cy.agorise.crystalwallet.cryptonetinforequests.ValidateBitsharesSendRequest; +import cy.agorise.crystalwallet.dao.CrystalDatabase; +import cy.agorise.crystalwallet.enums.CryptoNet; +import cy.agorise.crystalwallet.models.Contact; +import cy.agorise.crystalwallet.models.CryptoCoinBalance; +import cy.agorise.crystalwallet.models.CryptoCurrency; +import cy.agorise.crystalwallet.models.CryptoNetAccount; +import cy.agorise.crystalwallet.models.GrapheneAccount; +import cy.agorise.crystalwallet.viewmodels.ContactListViewModel; +import cy.agorise.crystalwallet.viewmodels.CryptoNetAccountListViewModel; +import cy.agorise.crystalwallet.viewmodels.validators.SendTransactionValidator; +import cy.agorise.crystalwallet.viewmodels.validators.UIValidatorListener; +import cy.agorise.crystalwallet.viewmodels.validators.validationfields.ValidationField; +import cy.agorise.crystalwallet.views.ContactSelectionListAdapter; +import cy.agorise.crystalwallet.views.CryptoCurrencyAdapter; +import cy.agorise.crystalwallet.views.CryptoNetAccountAdapter; +import me.dm7.barcodescanner.zxing.ZXingScannerView; + +public class ContactSelectionFragment extends DialogFragment implements ContactSelectionListAdapter.ContactSelectionListAdapterListener{ + + private CryptoNet cryptoNet; + private CrystalDatabase db; + private AlertDialog.Builder builder; + + @BindView(R.id.contactListView) + RecyclerView contactSelectionListView; + + public static ContactSelectionFragment newInstance(CryptoNet cryptoNet) { + ContactSelectionFragment f = new ContactSelectionFragment(); + + // Supply num input as an argument. + Bundle args = new Bundle(); + args.putString("CRYPTO_NET", cryptoNet.name()); + f.setArguments(args); + + return f; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + @NonNull + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + //AlertDialog.Builder + builder = new AlertDialog.Builder(getActivity(), R.style.SendTransactionTheme); + + LayoutInflater inflater = getActivity().getLayoutInflater(); + View view = inflater.inflate(R.layout.contact_list, null); + ButterKnife.bind(this, view); + + final LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this.getContext()); + this.contactSelectionListView.setLayoutManager(linearLayoutManager); + //Prevents the list to start again when scrolling to the end + this.contactSelectionListView.setNestedScrollingEnabled(false); + final ContactSelectionListAdapter contactSelectionListAdapter = new ContactSelectionListAdapter(); + contactSelectionListAdapter.setListener(this); + contactSelectionListView.setAdapter(contactSelectionListAdapter); + + this.cryptoNet = CryptoNet.valueOf(getArguments().getString("CRYPTO_NET")); + if (this.cryptoNet != null) { + ContactListViewModel contactListViewModel = ViewModelProviders.of(this).get(ContactListViewModel.class); + contactListViewModel.init(this.cryptoNet); + LiveData> contactsLiveData = contactListViewModel.getContactList(); + + contactsLiveData.observe(this, new Observer>() { + @Override + public void onChanged(@Nullable PagedList contacts) { + contactSelectionListAdapter.setList(contacts); + } + }); + } + + return builder.setView(view).create(); + } + + @Override + public void onContactSelected(Contact contact) { + Intent result = new Intent(); + result.putExtra("CONTACT_ID", contact.getId()); + getTargetFragment().onActivityResult(getTargetRequestCode(), 1, result); + this.dismiss(); + } +} diff --git a/app/src/main/java/cy/agorise/crystalwallet/fragments/ContactsFragment.java b/app/src/main/java/cy/agorise/crystalwallet/fragments/ContactsFragment.java index 34b2652..6f34284 100644 --- a/app/src/main/java/cy/agorise/crystalwallet/fragments/ContactsFragment.java +++ b/app/src/main/java/cy/agorise/crystalwallet/fragments/ContactsFragment.java @@ -47,6 +47,7 @@ public class ContactsFragment extends Fragment { ButterKnife.bind(this, v); ContactListViewModel contactListViewModel = ViewModelProviders.of(this).get(ContactListViewModel.class); + contactListViewModel.init(); LiveData> contactsLiveData = contactListViewModel.getContactList(); contactsLiveData.observe(this, new Observer>() { diff --git a/app/src/main/java/cy/agorise/crystalwallet/fragments/SendTransactionFragment.java b/app/src/main/java/cy/agorise/crystalwallet/fragments/SendTransactionFragment.java index 619d688..981a011 100644 --- a/app/src/main/java/cy/agorise/crystalwallet/fragments/SendTransactionFragment.java +++ b/app/src/main/java/cy/agorise/crystalwallet/fragments/SendTransactionFragment.java @@ -5,6 +5,7 @@ import android.arch.lifecycle.LiveData; import android.arch.lifecycle.Observer; import android.arch.lifecycle.ViewModelProviders; import android.content.DialogInterface; +import android.content.Intent; import android.os.Bundle; import android.os.Handler; import android.support.annotation.NonNull; @@ -12,6 +13,7 @@ import android.support.annotation.Nullable; import android.support.design.widget.FloatingActionButton; import android.support.v4.app.DialogFragment; import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentTransaction; import android.support.v7.app.AlertDialog; import android.support.v7.app.AppCompatActivity; import android.text.Editable; @@ -23,8 +25,10 @@ import android.view.Window; import android.view.animation.LinearInterpolator; import android.widget.Button; import android.widget.EditText; +import android.widget.ImageView; import android.widget.Spinner; import android.widget.TextView; +import android.widget.Toast; import com.google.zxing.Result; @@ -41,10 +45,13 @@ import cy.agorise.crystalwallet.cryptonetinforequests.CryptoNetInfoRequestListen import cy.agorise.crystalwallet.cryptonetinforequests.CryptoNetInfoRequests; import cy.agorise.crystalwallet.cryptonetinforequests.ValidateBitsharesSendRequest; import cy.agorise.crystalwallet.dao.CrystalDatabase; +import cy.agorise.crystalwallet.models.Contact; +import cy.agorise.crystalwallet.models.ContactAddress; import cy.agorise.crystalwallet.models.CryptoCoinBalance; import cy.agorise.crystalwallet.models.CryptoCurrency; import cy.agorise.crystalwallet.models.CryptoNetAccount; import cy.agorise.crystalwallet.models.GrapheneAccount; +import cy.agorise.crystalwallet.viewmodels.ContactViewModel; import cy.agorise.crystalwallet.viewmodels.CryptoNetAccountListViewModel; import cy.agorise.crystalwallet.viewmodels.CryptoNetAccountViewModel; import cy.agorise.crystalwallet.viewmodels.validators.SendTransactionValidator; @@ -82,6 +89,8 @@ public class SendTransactionFragment extends DialogFragment implements UIValidat FloatingActionButton btnSend; @BindView(R.id.btnCancel) TextView btnCancel; + @BindView(R.id.ivPeople) + ImageView ivPeople; Button btnScanQrCode; @@ -222,6 +231,50 @@ public class SendTransactionFragment extends DialogFragment implements UIValidat this.sendTransactionValidator.validate(); } + @OnClick(R.id.ivPeople) + public void searchContact(){ + FragmentTransaction ft = this.getActivity().getSupportFragmentManager().beginTransaction(); + Fragment prev = this.getActivity().getSupportFragmentManager().findFragmentByTag("ContactSelectionDialog"); + if (prev != null) { + ft.remove(prev); + } + ft.addToBackStack(null); + + // Show a contact selection list + ContactSelectionFragment contactSelectionFragment = ContactSelectionFragment.newInstance(this.cryptoNetAccount.getCryptoNet()); + contactSelectionFragment.setTargetFragment(this, 1); + contactSelectionFragment.show(ft, "ContactSelectionDialog"); + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode,resultCode,data); + + if(requestCode == 1) { + if(resultCode == 1) { + long contactId = data.getLongExtra("CONTACT_ID",-1); + if (contactId > -1){ + ContactViewModel contactViewModel = ViewModelProviders.of(this).get(ContactViewModel.class); + contactViewModel.init(contactId); + LiveData> contactAddressesLiveData = contactViewModel.getContactAddresses(); + + contactAddressesLiveData.observe(this, new Observer>() { + @Override + public void onChanged(@Nullable List contactAddresses) { + if (contactAddresses != null) { + for (ContactAddress contactAddress : contactAddresses) { + if (contactAddress.getCryptoNet() == cryptoNetAccount.getCryptoNet()) { + etTo.setText(contactAddress.getAddress()); + } + } + } + } + }); + } + } + } + } + @OnClick(R.id.btnCancel) public void cancel(){ this.dismiss(); diff --git a/app/src/main/java/cy/agorise/crystalwallet/models/Contact.java b/app/src/main/java/cy/agorise/crystalwallet/models/Contact.java index b65f3f9..3d45464 100644 --- a/app/src/main/java/cy/agorise/crystalwallet/models/Contact.java +++ b/app/src/main/java/cy/agorise/crystalwallet/models/Contact.java @@ -13,6 +13,8 @@ import android.support.v7.recyclerview.extensions.DiffCallback; import java.util.ArrayList; import java.util.List; +import cy.agorise.crystalwallet.enums.CryptoNet; + /** * Represents a user contact * @@ -85,6 +87,18 @@ public class Contact { address.setContactId(this.getId()); } + public ContactAddress getCryptoNetAddress(CryptoNet cryptoNet){ + if (this.mAddresses != null) { + for (ContactAddress address : this.mAddresses) { + if (address.getCryptoNet() == cryptoNet) { + return address; + } + } + } + + return null; + } + public static final DiffCallback DIFF_CALLBACK = new DiffCallback() { @Override public boolean areItemsTheSame( diff --git a/app/src/main/java/cy/agorise/crystalwallet/viewmodels/ContactListViewModel.java b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/ContactListViewModel.java index 4bd2522..8a3258b 100644 --- a/app/src/main/java/cy/agorise/crystalwallet/viewmodels/ContactListViewModel.java +++ b/app/src/main/java/cy/agorise/crystalwallet/viewmodels/ContactListViewModel.java @@ -6,6 +6,7 @@ import android.arch.lifecycle.LiveData; import android.arch.paging.PagedList; import cy.agorise.crystalwallet.dao.CrystalDatabase; +import cy.agorise.crystalwallet.enums.CryptoNet; import cy.agorise.crystalwallet.models.Contact; import cy.agorise.crystalwallet.models.CryptoCoinTransaction; @@ -30,6 +31,26 @@ public class ContactListViewModel extends AndroidViewModel { ); } + public void init(){ + contactList = this.db.contactDao().contactsByName().create(0, + new PagedList.Config.Builder() + .setEnablePlaceholders(true) + .setPageSize(10) + .setPrefetchDistance(10) + .build() + ); + } + + public void init(CryptoNet cryptoNet){ + contactList = this.db.contactDao().contactsByNameAndCryptoNet(cryptoNet.name()).create(0, + new PagedList.Config.Builder() + .setEnablePlaceholders(true) + .setPageSize(10) + .setPrefetchDistance(10) + .build() + ); + } + public LiveData> getContactList(){ return this.contactList; } diff --git a/app/src/main/java/cy/agorise/crystalwallet/views/ContactSelectionListAdapter.java b/app/src/main/java/cy/agorise/crystalwallet/views/ContactSelectionListAdapter.java new file mode 100644 index 0000000..58f1350 --- /dev/null +++ b/app/src/main/java/cy/agorise/crystalwallet/views/ContactSelectionListAdapter.java @@ -0,0 +1,57 @@ +package cy.agorise.crystalwallet.views; + + +import android.support.v7.recyclerview.extensions.ListAdapter; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import cy.agorise.crystalwallet.R; +import cy.agorise.crystalwallet.models.Contact; + +/** + * Created by Henry Varona on 2/16/2018. + * + * An adapter to show the elements of a list of contacts to be selected by the user + */ + +public class ContactSelectionListAdapter extends ListAdapter implements ContactSelectionViewHolder.ContactSelectionViewHolderListener { + + private ContactSelectionListAdapterListener listener; + + public ContactSelectionListAdapter() { + super(Contact.DIFF_CALLBACK); + } + + @Override + public ContactSelectionViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.contact_selection_list_item,parent,false); + return new ContactSelectionViewHolder(v); + } + + @Override + public void onBindViewHolder(ContactSelectionViewHolder holder, int position) { + Contact contact = getItem(position); + if (contact != null) { + holder.bindTo(contact); + holder.setListener(this); + } else { + holder.clear(); + } + } + + @Override + public void onContactSelected(ContactSelectionViewHolder contactSelectionViewHolder, Contact contact) { + if (this.listener != null){ + this.listener.onContactSelected(contact); + } + } + + public void setListener(ContactSelectionListAdapterListener listener){ + this.listener = listener; + } + + public interface ContactSelectionListAdapterListener{ + public void onContactSelected(Contact contact); + } +} diff --git a/app/src/main/java/cy/agorise/crystalwallet/views/ContactSelectionViewHolder.java b/app/src/main/java/cy/agorise/crystalwallet/views/ContactSelectionViewHolder.java new file mode 100644 index 0000000..c6ed3f0 --- /dev/null +++ b/app/src/main/java/cy/agorise/crystalwallet/views/ContactSelectionViewHolder.java @@ -0,0 +1,79 @@ +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 cy.agorise.crystalwallet.R; +import cy.agorise.crystalwallet.activities.CreateContactActivity; +import cy.agorise.crystalwallet.dao.CrystalDatabase; +import cy.agorise.crystalwallet.models.Contact; + +/** + * 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"); + + this.itemView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + if (listener != null){ + listener.onContactSelected(thisViewHolder, contact); + } + } + }); + } + } + + public interface ContactSelectionViewHolderListener { + public void onContactSelected(ContactSelectionViewHolder contactSelectionViewHolder, Contact contact); + } +} diff --git a/app/src/main/res/layout/contact_selection_list_item.xml b/app/src/main/res/layout/contact_selection_list_item.xml new file mode 100644 index 0000000..121112f --- /dev/null +++ b/app/src/main/res/layout/contact_selection_list_item.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + \ No newline at end of file