package com.cashuwallet.android.ui; import android.content.ActivityNotFoundException; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.support.design.widget.Snackbar; import android.support.v4.app.Fragment; import android.support.v4.widget.NestedScrollView; import android.support.v4.widget.SwipeRefreshLayout; import android.support.v7.widget.DividerItemDecoration; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.text.format.DateUtils; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; import com.cashuwallet.android.MainApplication; import com.cashuwallet.android.R; import com.cashuwallet.android.crypto.Coin; import com.cashuwallet.android.crypto.Sync; import com.cashuwallet.android.db.Multiwallet; import com.cashuwallet.android.db.Transaction; import java.math.BigDecimal; import java.math.BigInteger; import java.math.RoundingMode; import java.text.NumberFormat; import java.util.List; public class ActivityFragment extends Fragment { private static final int PAGE_SIZE = 25; // TODO must be larger than what fits the screen private static final int PAGE_BOTTOM = 5; private Sync sync; Multiwallet multiwallet; private Adapter adapter; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_activity, container, false); int id = getArguments().getInt("multiwallet"); DetailActivity activity = (DetailActivity) getActivity(); sync = MainApplication.app().getSync(); multiwallet = sync.findMultiwallet(id); adapter = new Adapter(sync.findTransactions(multiwallet, 0, PAGE_SIZE)); LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getActivity().getApplicationContext()); RecyclerView recyclerView = view.findViewById(R.id.recycler_view); recyclerView.addItemDecoration(new DividerItemDecoration(recyclerView.getContext(), DividerItemDecoration.VERTICAL)); recyclerView.setLayoutManager(linearLayoutManager); recyclerView.setAdapter(adapter); NestedScrollView emptyText = view.findViewById(R.id.empty_list); if (adapter.getItemCount() == 0) { recyclerView.setVisibility(View.GONE); emptyText.setVisibility(View.VISIBLE); } else { recyclerView.setVisibility(View.VISIBLE); emptyText.setVisibility(View.GONE); } recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); int count = linearLayoutManager.getItemCount(); int last = linearLayoutManager.findLastVisibleItemPosition(); if (count - last <= PAGE_BOTTOM) { int size = adapter.getItemCount(); adapter.items.addAll(sync.findTransactions(multiwallet, size, PAGE_SIZE)); int increment = adapter.getItemCount() - size; if (increment > 0) { adapter.notifyItemRangeInserted(size, increment); } } } }); SwipeRefreshLayout swipeRefreshLayout = view.findViewById(R.id.swipe_refresh_layout); swipeRefreshLayout.setOnRefreshListener(() -> refresh(view, true)); if (activity.refreshPending) { activity.refreshPending = false; refresh(view, false); } return view; } void refresh(View view, boolean requested) { DetailActivity activity = (DetailActivity) getActivity(); RecyclerView recyclerView = view.findViewById(R.id.recycler_view); NestedScrollView emptyText = view.findViewById(R.id.empty_list); SwipeRefreshLayout swipeRefreshLayout = view.findViewById(R.id.swipe_refresh_layout); if (!MainApplication.app().networkAvailable()) { if (requested) { Snackbar.make(recyclerView, R.string.network_not_available, Snackbar.LENGTH_LONG).show(); swipeRefreshLayout.setRefreshing(false); } return; } if (!requested) swipeRefreshLayout.setRefreshing(true); sync.sync(multiwallet, () -> { activity.refresh(multiwallet); int size = adapter.getItemCount(); adapter.items.clear(); adapter.items.addAll(sync.findTransactions(multiwallet, 0, PAGE_SIZE)); if (size >= adapter.getItemCount()) { adapter.notifyItemRangeChanged(0, adapter.getItemCount()); adapter.notifyItemRangeRemoved(adapter.getItemCount(), size - adapter.getItemCount()); } else { adapter.notifyItemRangeChanged(0, size); adapter.notifyItemRangeInserted(size, adapter.getItemCount() - size); } if (adapter.getItemCount() == 0) { recyclerView.setVisibility(View.GONE); emptyText.setVisibility(View.VISIBLE); } else { recyclerView.setVisibility(View.VISIBLE); emptyText.setVisibility(View.GONE); } swipeRefreshLayout.setRefreshing(false); }); } private class Adapter extends RecyclerView.Adapter<ViewHolder> { private final List<Transaction> items; Adapter(List<Transaction> items) { this.items = items; } @Override public int getItemCount() { return items.size(); } @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()) .inflate(R.layout.fragment_activity_item, parent, false); return new ViewHolder(view); } @Override public void onBindViewHolder(ViewHolder vh, final int position) { Transaction transaction = items.get(position); vh.transaction = transaction; updateViewHolder(vh, transaction); } private void updateViewHolder(ViewHolder vh, Transaction transaction) { Coin coin = transaction.getCoin(); int res; switch (transaction.getType()) { default: // TODO add icon for Transaction.Type.NONE case NONE: res = R.drawable.ic_sent; break; case INCOMING: res = R.drawable.ic_receive; break; case OUTGOING: res = R.drawable.ic_sent; break; } int color = getResources().getColor(transaction.confirmed ? R.color.status_confirmed : R.color.status_pending); vh.image.setImageResource(res); vh.amount.setText(formatAmount(coin, transaction.getAbsAmount())); vh.time.setText(formatDateTime(transaction.getTime())); vh.status.setTextColor(color); vh.itemView.setOnClickListener((View view) -> { String url = sync.getUrl(transaction); if (url == null) { Snackbar.make(view, R.string.transaction_url_not_available, Snackbar.LENGTH_LONG).show(); return; } try { startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url))); } catch (ActivityNotFoundException e) { Snackbar.make(view, R.string.failure_open_url_activity, Snackbar.LENGTH_LONG).show(); } }); } private String formatDateTime(int time) { long timestamp = 1000 * (long) time; return DateUtils.formatDateTime(getContext(), timestamp,DateUtils.FORMAT_SHOW_DATE|DateUtils.FORMAT_SHOW_TIME); } private String formatAmount(Coin coin, BigInteger amount) { //String symbol = coin.getSymbol(); String code = coin.getCode(); int decimals = coin.getDecimals(); NumberFormat format = NumberFormat.getNumberInstance(); format.setMinimumFractionDigits(decimals); format.setMaximumFractionDigits(decimals); format.setRoundingMode(RoundingMode.UNNECESSARY); BigDecimal decimal = new BigDecimal(amount); decimal = decimal.divide(BigDecimal.TEN.pow(decimals)); String value = format.format(decimal); return /*(symbol == null ? "" : symbol + " ") +*/ value + " " + code; } } private static class ViewHolder extends RecyclerView.ViewHolder { Transaction transaction; ImageView image; TextView amount; TextView time; TextView status; ViewHolder(View view) { super(view); image = view.findViewById(R.id.image); amount = view.findViewById(R.id.amount); time = view.findViewById(R.id.time); status = view.findViewById(R.id.status); } } }