/*
- * Copyright © 2019 Damyan Ivanov.
+ * Copyright © 2021 Damyan Ivanov.
* This file is part of MoLe.
* MoLe is free software: you can distribute it and/or modify it
* under the term of the GNU General Public License as published by
package net.ktnx.mobileledger.ui.activity;
+import android.app.Activity;
import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
import android.os.Bundle;
import android.renderscript.RSInvalidStateException;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
+import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.ProgressBar;
+import androidx.activity.result.ActivityResultLauncher;
+import androidx.activity.result.contract.ActivityResultContract;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.lifecycle.ViewModelProvider;
-import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.snackbar.Snackbar;
import net.ktnx.mobileledger.R;
+import net.ktnx.mobileledger.json.API;
import net.ktnx.mobileledger.model.Data;
import net.ktnx.mobileledger.model.LedgerTransaction;
import net.ktnx.mobileledger.model.LedgerTransactionAccount;
import net.ktnx.mobileledger.model.MobileLedgerProfile;
import net.ktnx.mobileledger.utils.Logger;
import net.ktnx.mobileledger.utils.Misc;
+import net.ktnx.mobileledger.utils.SimpleDate;
import org.jetbrains.annotations.NotNull;
-import java.util.Date;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
/**
* A simple {@link Fragment} subclass.
* {@link OnNewTransactionFragmentInteractionListener} interface
* to handle interaction events.
*/
+
+// TODO: offer to undo account remove-on-swipe
+
public class NewTransactionFragment extends Fragment {
private NewTransactionItemsAdapter listAdapter;
private NewTransactionModel viewModel;
- private RecyclerView list;
+ final ActivityResultLauncher<Void> scanQrLauncher =
+ registerForActivityResult(new ActivityResultContract<Void, String>() {
+ @NonNull
+ @Override
+ public Intent createIntent(@NonNull Context context, Void input) {
+ final Intent intent = new Intent("com.google.zxing.client.android.SCAN");
+ intent.putExtra("SCAN_MODE", "QR_CODE_MODE");
+ return intent;
+ }
+ @Override
+ public String parseResult(int resultCode, @Nullable Intent intent) {
+ if (resultCode == Activity.RESULT_CANCELED)
+ return null;
+ return intent.getStringExtra("SCAN_RESULT");
+ }
+ }, this::onQrScanned);
private FloatingActionButton fab;
private OnNewTransactionFragmentInteractionListener mListener;
private MobileLedgerProfile mProfile;
// Required empty public constructor
setHasOptionsMenu(true);
}
+ private void onQrScanned(String text) {
+ Logger.debug("qr", String.format("Got QR scan result [%s]", text));
+ Pattern p =
+ Pattern.compile("^(\\d+)\\*(\\d+)\\*(\\d+)-(\\d+)-(\\d+)\\*([:\\d]+)\\*([\\d.]+)$");
+ Matcher m = p.matcher(text);
+ if (m.matches()) {
+ float amount = Float.parseFloat(m.group(7));
+ viewModel.setDate(
+ new SimpleDate(Integer.parseInt(m.group(3)), Integer.parseInt(m.group(4)),
+ Integer.parseInt(m.group(5))));
+
+ if (viewModel.accountsInInitialState()) {
+ {
+ NewTransactionModel.Item firstItem = viewModel.getItem(1);
+ if (firstItem == null) {
+ viewModel.addAccount(new LedgerTransactionAccount("разход:пазар"));
+ listAdapter.notifyItemInserted(viewModel.items.size() - 1);
+ }
+ else {
+ firstItem.setAccountName("разход:пазар");
+ firstItem.getAccount()
+ .resetAmount();
+ listAdapter.notifyItemChanged(1);
+ }
+ }
+ {
+ NewTransactionModel.Item secondItem = viewModel.getItem(2);
+ if (secondItem == null) {
+ viewModel.addAccount(
+ new LedgerTransactionAccount("актив:кеш:дам", -amount, null, null));
+ listAdapter.notifyItemInserted(viewModel.items.size() - 1);
+ }
+ else {
+ secondItem.setAccountName("актив:кеш:дам");
+ secondItem.getAccount()
+ .setAmount(-amount);
+ listAdapter.notifyItemChanged(2);
+ }
+ }
+ }
+ else {
+ viewModel.addAccount(new LedgerTransactionAccount("разход:пазар"));
+ viewModel.addAccount(
+ new LedgerTransactionAccount("актив:кеш:дам", -amount, null, null));
+ listAdapter.notifyItemRangeInserted(viewModel.items.size() - 1, 2);
+ }
+
+ listAdapter.checkTransactionSubmittable();
+ }
+ }
@Override
public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
+ final FragmentActivity activity = getActivity();
+
inflater.inflate(R.menu.new_transaction_fragment, menu);
+
+ menu.findItem(R.id.scan_qr)
+ .setOnMenuItemClickListener(this::onScanQrAction);
+
menu.findItem(R.id.action_reset_new_transaction_activity)
.setOnMenuItemClickListener(item -> {
listAdapter.reset();
return true;
});
+
+ final MenuItem toggleCurrencyItem = menu.findItem(R.id.toggle_currency);
+ toggleCurrencyItem.setOnMenuItemClickListener(item -> {
+ viewModel.toggleCurrencyVisible();
+ return true;
+ });
+ if (activity != null)
+ viewModel.showCurrency.observe(activity, toggleCurrencyItem::setChecked);
+
+ final MenuItem toggleCommentsItem = menu.findItem(R.id.toggle_comments);
+ toggleCommentsItem.setOnMenuItemClickListener(item -> {
+ viewModel.toggleShowComments();
+ return true;
+ });
+ if (activity != null)
+ viewModel.showComments.observe(activity, toggleCommentsItem::setChecked);
+ }
+ private boolean onScanQrAction(MenuItem item) {
+ try {
+ scanQrLauncher.launch(null);
+ }
+ catch (Exception e) {
+ Logger.debug("qr", "Error launching QR scanner", e);
+ }
+
+ return true;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
}
@Override
- public void onActivityCreated(@Nullable Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
+ public void onViewCreated(@NotNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
FragmentActivity activity = getActivity();
if (activity == null)
throw new RSInvalidStateException(
"getActivity() returned null within onActivityCreated()");
- list = activity.findViewById(R.id.new_transaction_accounts);
viewModel = new ViewModelProvider(activity).get(NewTransactionModel.class);
- mProfile = Data.profile.getValue();
+ viewModel.observeDataProfile(this);
+ mProfile = Data.getProfile();
listAdapter = new NewTransactionItemsAdapter(viewModel, mProfile);
+
+ RecyclerView list = activity.findViewById(R.id.new_transaction_accounts);
list.setAdapter(listAdapter);
list.setLayoutManager(new LinearLayoutManager(activity));
- Data.profile.observe(getViewLifecycleOwner(), profile -> {
+
+ Data.observeProfile(getViewLifecycleOwner(), profile -> {
mProfile = profile;
listAdapter.setProfile(profile);
});
listAdapter.notifyDataSetChanged();
- new ItemTouchHelper(new ItemTouchHelper.Callback() {
- @Override
- public int getMovementFlags(@NonNull RecyclerView recyclerView,
- @NonNull RecyclerView.ViewHolder viewHolder) {
- int flags = makeFlag(ItemTouchHelper.ACTION_STATE_IDLE, ItemTouchHelper.END);
- // the top item is always there (date and description)
- if (viewHolder.getAdapterPosition() > 0) {
- if (viewModel.getAccountCount() > 2) {
- flags |= 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) {
- return false;
- }
- @Override
- public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
- if (viewModel.getAccountCount() == 2)
- Snackbar.make(list, R.string.msg_at_least_two_accounts_are_required,
- Snackbar.LENGTH_LONG)
- .setAction("Action", null)
- .show();
- else {
- int pos = viewHolder.getAdapterPosition();
- viewModel.removeItem(pos - 1);
- listAdapter.notifyItemRemoved(pos);
- viewModel.sendCountNotifications(); // needed after items re-arrangement
- viewModel.checkTransactionSubmittable(listAdapter);
- }
- }
- }).attachToRecyclerView(list);
-
viewModel.isSubmittable()
.observe(getViewLifecycleOwner(), isSubmittable -> {
if (isSubmittable) {
if (fab != null) {
fab.show();
- fab.setEnabled(true);
}
}
else {
}
}
});
- viewModel.checkTransactionSubmittable(listAdapter);
+// viewModel.checkTransactionSubmittable(listAdapter);
fab = activity.findViewById(R.id.fab);
fab.setOnClickListener(v -> onFabPressed());
if (args != null) {
String error = args.getString("error");
if (error != null) {
- // TODO display error
Logger.debug("new-trans-f", String.format("Got error: %s", error));
- Snackbar.make(list, error, Snackbar.LENGTH_LONG)
- .show();
+
+ Context context = getContext();
+ if (context != null) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ final Resources resources = context.getResources();
+ final StringBuilder message = new StringBuilder();
+ message.append(resources.getString(R.string.err_json_send_error_head));
+ message.append("\n\n");
+ message.append(error);
+ if (mProfile.getApiVersion()
+ .equals(API.auto))
+ message.append(
+ resources.getString(R.string.err_json_send_error_unsupported));
+ else {
+ message.append(resources.getString(R.string.err_json_send_error_tail));
+ builder.setPositiveButton(R.string.btn_profile_options, (dialog, which) -> {
+ Logger.debug("error", "will start profile editor");
+ MobileLedgerProfile.startEditProfileActivity(context, mProfile);
+ });
+ }
+ builder.setMessage(message);
+ builder.create()
+ .show();
+ }
+ else {
+ Snackbar.make(list, error, Snackbar.LENGTH_INDEFINITE)
+ .show();
+ }
keep = true;
}
}
else {
viewModel.setFocusedItem(focused);
}
+
+ ProgressBar p = activity.findViewById(R.id.progressBar);
+ viewModel.observeBusyFlag(getViewLifecycleOwner(), isBusy -> {
+ if (isBusy) {
+// Handler h = new Handler();
+// h.postDelayed(() -> {
+// if (viewModel.getBusyFlag())
+// p.setVisibility(View.VISIBLE);
+//
+// }, 10);
+ p.setVisibility(View.VISIBLE);
+ }
+ else
+ p.setVisibility(View.INVISIBLE);
+ });
}
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
outState.putInt("focused", focusedItem);
}
private void onFabPressed() {
- fab.setEnabled(false);
+ fab.hide();
Misc.hideSoftKeyboard(this);
if (mListener != null) {
- Date date = viewModel.getDate();
+ SimpleDate date = viewModel.getDate();
LedgerTransaction tr =
new LedgerTransaction(null, date, viewModel.getDescription(), mProfile);
+ tr.setComment(viewModel.getComment());
LedgerTransactionAccount emptyAmountAccount = null;
float emptyAmountAccountBalance = 0;
for (int i = 0; i < viewModel.getAccountCount(); i++) {