import android.view.LayoutInflater;
import android.view.ViewGroup;
import android.widget.LinearLayout;
-import android.widget.TableRow;
import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.RecyclerView;
import net.ktnx.mobileledger.App;
implements DescriptionSelectedCallback {
NewTransactionModel model;
private MobileLedgerProfile mProfile;
+ private ItemTouchHelper touchHelper;
+ private RecyclerView recyclerView;
NewTransactionItemsAdapter(NewTransactionModel viewModel, MobileLedgerProfile profile) {
super();
model = viewModel;
String.format(Locale.US, "%d accounts is too little, Calling addRow()", size));
size = addRow();
}
+
+ NewTransactionItemsAdapter adapter = this;
+
+ touchHelper = new ItemTouchHelper(new ItemTouchHelper.Callback() {
+ @Override
+ public boolean isLongPressDragEnabled() {
+ return true;
+ }
+ @Override
+ public boolean canDropOver(@NonNull RecyclerView recyclerView,
+ @NonNull RecyclerView.ViewHolder current,
+ @NonNull RecyclerView.ViewHolder target) {
+ final int adapterPosition = target.getAdapterPosition();
+
+ // first and last items are immovable
+ if (adapterPosition == 0)
+ return false;
+ if (adapterPosition == adapter.getItemCount() - 1)
+ return false;
+
+ return super.canDropOver(recyclerView, current, target);
+ }
+ @Override
+ public int getMovementFlags(@NonNull RecyclerView recyclerView,
+ @NonNull RecyclerView.ViewHolder viewHolder) {
+ int flags = makeFlag(ItemTouchHelper.ACTION_STATE_IDLE, ItemTouchHelper.END);
+ // the top (date and description) and the bottom (padding) items are always there
+ final int adapterPosition = viewHolder.getAdapterPosition();
+ if ((adapterPosition > 0) && (adapterPosition < adapter.getItemCount() - 1)) {
+ flags |= makeFlag(ItemTouchHelper.ACTION_STATE_DRAG,
+ ItemTouchHelper.UP | ItemTouchHelper.DOWN) |
+ makeFlag(ItemTouchHelper.ACTION_STATE_SWIPE,
+ ItemTouchHelper.START | ItemTouchHelper.END);
+ }
+
+ return flags;
+ }
+ @Override
+ public boolean onMove(@NonNull RecyclerView recyclerView,
+ @NonNull RecyclerView.ViewHolder viewHolder,
+ @NonNull RecyclerView.ViewHolder target) {
+
+ model.swapItems(viewHolder.getAdapterPosition(), target.getAdapterPosition());
+ notifyItemMoved(viewHolder.getAdapterPosition(), target.getAdapterPosition());
+ return true;
+ }
+ @Override
+ public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
+ int pos = viewHolder.getAdapterPosition();
+ viewModel.removeItem(pos - 1);
+ notifyItemRemoved(pos);
+ viewModel.sendCountNotifications(); // needed after items re-arrangement
+ viewModel.checkTransactionSubmittable(adapter);
+ }
+ });
}
public void setProfile(MobileLedgerProfile profile) {
mProfile = profile;
}
int addRow() {
- final int newAccountCount = model.addAccount(new LedgerTransactionAccount(""));
+ return addRow(null);
+ }
+ int addRow(String commodity) {
+ final int newAccountCount = model.addAccount(new LedgerTransactionAccount("", commodity));
Logger.debug("new-transaction",
String.format(Locale.US, "invoking notifyItemInserted(%d)", newAccountCount));
// the header is at position 0
notifyItemInserted(newAccountCount);
+ model.sendCountNotifications(); // needed after holders' positions have changed
return newAccountCount;
}
@NonNull
LinearLayout row = (LinearLayout) LayoutInflater.from(parent.getContext())
.inflate(R.layout.new_transaction_row,
parent, false);
+
return new NewTransactionItemHolder(row, this);
}
@Override
public void onBindViewHolder(@NonNull NewTransactionItemHolder holder, int position) {
Logger.debug("bind", String.format(Locale.US, "Binding item at position %d", position));
- holder.setData(model.getItem(position));
- Logger.debug("bind", String.format(Locale.US, "Bound item at position %d", position));
+ NewTransactionModel.Item item = model.getItem(position);
+ holder.setData(item);
+ Logger.debug("bind", String.format(Locale.US, "Bound %s item at position %d", item.getType()
+ .toString(),
+ position));
}
@Override
public int getItemCount() {
- final int itemCount = model.getAccountCount() + 2;
- Logger.debug("new-transaction",
- String.format(Locale.US, "getItemCount() returning %d", itemCount));
- return itemCount;
+ return model.getAccountCount() + 2;
}
boolean accountListIsEmpty() {
for (int i = 0; i < model.getAccountCount(); i++) {
LedgerTransactionAccount acc = model.getAccount(i);
if (!acc.getAccountName()
- .isEmpty()) return false;
- if (acc.isAmountSet()) return false;
+ .isEmpty())
+ return false;
+ if (acc.isAmountSet())
+ return false;
}
return true;
}
+ @Override
+ public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
+ super.onAttachedToRecyclerView(recyclerView);
+ this.recyclerView = recyclerView;
+ touchHelper.attachToRecyclerView(recyclerView);
+ }
+ @Override
+ public void onDetachedFromRecyclerView(@NonNull RecyclerView recyclerView) {
+ touchHelper.attachToRecyclerView(null);
+ super.onDetachedFromRecyclerView(recyclerView);
+ this.recyclerView = null;
+ }
public void descriptionSelected(String description) {
debug("descr selected", description);
- if (!accountListIsEmpty()) return;
+ if (!accountListIsEmpty())
+ return;
String accFilter = mProfile.getPreferredAccountsFilter();
try (Cursor c = App.getDatabase()
.rawQuery(sql, params.toArray(new String[]{})))
{
- if (!c.moveToNext()) return;
+ if (!c.moveToNext())
+ return;
String profileUUID = c.getString(0);
int transactionId = c.getInt(1);
LedgerTransaction tr;
MobileLedgerProfile profile = Data.getProfile(profileUUID);
- if (profile == null) throw new RuntimeException(String.format(
- "Unable to find profile %s, which is supposed to contain " +
- "transaction %d with description %s", profileUUID, transactionId, description));
+ if (profile == null)
+ throw new RuntimeException(String.format(
+ "Unable to find profile %s, which is supposed to contain " +
+ "transaction %d with description %s", profileUUID, transactionId,
+ description));
tr = profile.loadTransaction(transactionId);
ArrayList<LedgerTransactionAccount> accounts = tr.getAccounts();
- TableRow firstNegative = null;
+ NewTransactionModel.Item firstNegative = null;
+ NewTransactionModel.Item firstPositive = null;
+ int singleNegativeIndex = -1;
+ int singlePositiveIndex = -1;
int negativeCount = 0;
for (int i = 0; i < accounts.size(); i++) {
LedgerTransactionAccount acc = accounts.get(i);
NewTransactionModel.Item item;
- if (model.getAccountCount() < i) {
+ if (model.getAccountCount() < i + 1) {
model.addAccount(acc);
notifyItemInserted(i + 1);
}
item.getAccount()
.setAccountName(acc.getAccountName());
- if (acc.isAmountSet()) item.getAccount()
- .setAmount(acc.getAmount());
- else item.getAccount()
- .resetAmount();
+ if (acc.isAmountSet()) {
+ item.getAccount()
+ .setAmount(acc.getAmount());
+ if (acc.getAmount() < 0) {
+ if (firstNegative == null) {
+ firstNegative = item;
+ singleNegativeIndex = i;
+ }
+ else
+ singleNegativeIndex = -1;
+ }
+ else {
+ if (firstPositive == null) {
+ firstPositive = item;
+ singlePositiveIndex = i;
+ }
+ else
+ singlePositiveIndex = -1;
+ }
+ }
+ else
+ item.getAccount()
+ .resetAmount();
notifyItemChanged(i + 1);
}
+
+ if (singleNegativeIndex != -1) {
+ firstNegative.getAccount()
+ .resetAmount();
+ model.moveItemLast(singleNegativeIndex);
+ }
+ else if (singlePositiveIndex != -1) {
+ firstPositive.getAccount()
+ .resetAmount();
+ model.moveItemLast(singlePositiveIndex);
+ }
}
model.checkTransactionSubmittable(this);
model.setFocusedItem(1);
}
public void toggleAllEditing(boolean editable) {
- for (int i = 0; i < model.getAccountCount(); i++) {
- model.getItem(i + 1)
+ // item 0 is the header
+ for (int i = 0; i <= model.getAccountCount(); i++) {
+ model.getItem(i)
.setEditable(editable);
- notifyItemChanged(i + 1);
- // TODO perhaps do only one notification about the whole range [1…count]?
+ notifyItemChanged(i);
+ // TODO perhaps do only one notification about the whole range (notifyDatasetChanged)?
}
}
public void reset() {
if (presentItemCount > 2)
notifyItemRangeRemoved(3, presentItemCount - 2); // all the rest are gone
}
- public void removeItem(int pos) {
- model.removeItem(pos - 1, this);
- notifyItemRemoved(pos);
+ public void updateFocusedItem(int position) {
+ model.updateFocusedItem(position);
+ }
+ public void noteFocusIsOnAccount(int position) {
+ model.noteFocusChanged(position, NewTransactionModel.FocusedElement.Account);
+ }
+ public void noteFocusIsOnAmount(int position) {
+ model.noteFocusChanged(position, NewTransactionModel.FocusedElement.Amount);
+ }
+ public void noteFocusIsOnComment(int position) {
+ model.noteFocusChanged(position, NewTransactionModel.FocusedElement.Comment);
+ }
+ public void toggleComment(int position) {
+ model.toggleComment(position);
}
}