X-Git-Url: https://git.ktnx.net/?a=blobdiff_plain;f=app%2Fsrc%2Fmain%2Fjava%2Fnet%2Fktnx%2Fmobileledger%2Fui%2Fnew_transaction%2FNewTransactionFragment.java;h=7741d18ac6656902485662583ba02d924078761c;hb=5df10dc0b58df4d4be4e9ab34f1e0f477ca46766;hp=140b3a74e0512d68e39159366af8817cbec3fe85;hpb=ea6e0ebc968bb3a3b7fb913d9e0282d374957815;p=mobile-ledger.git diff --git a/app/src/main/java/net/ktnx/mobileledger/ui/new_transaction/NewTransactionFragment.java b/app/src/main/java/net/ktnx/mobileledger/ui/new_transaction/NewTransactionFragment.java index 140b3a74..7741d18a 100644 --- a/app/src/main/java/net/ktnx/mobileledger/ui/new_transaction/NewTransactionFragment.java +++ b/app/src/main/java/net/ktnx/mobileledger/ui/new_transaction/NewTransactionFragment.java @@ -18,11 +18,8 @@ package net.ktnx.mobileledger.ui.new_transaction; import android.content.Context; -import android.content.Intent; import android.content.res.Resources; -import android.database.AbstractCursor; import android.os.Bundle; -import android.os.ParcelFormatException; import android.renderscript.RSInvalidStateException; import android.view.LayoutInflater; import android.view.Menu; @@ -37,39 +34,24 @@ import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentActivity; -import androidx.lifecycle.LiveData; import androidx.lifecycle.ViewModelProvider; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; -import com.google.android.material.dialog.MaterialAlertDialogBuilder; -import com.google.android.material.floatingactionbutton.FloatingActionButton; -import com.google.android.material.snackbar.BaseTransientBottomBar; import com.google.android.material.snackbar.Snackbar; import net.ktnx.mobileledger.R; -import net.ktnx.mobileledger.db.DB; -import net.ktnx.mobileledger.db.PatternAccount; -import net.ktnx.mobileledger.db.PatternHeader; +import net.ktnx.mobileledger.db.Profile; 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.ui.QRScanCapableFragment; -import net.ktnx.mobileledger.ui.patterns.PatternsActivity; +import net.ktnx.mobileledger.ui.FabManager; +import net.ktnx.mobileledger.ui.QR; +import net.ktnx.mobileledger.ui.profiles.ProfileDetailActivity; 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.ArrayList; -import java.util.List; -import java.util.Locale; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - /** * A simple {@link Fragment} subclass. * Activities that contain this fragment must implement the @@ -79,292 +61,15 @@ import java.util.regex.Pattern; // TODO: offer to undo account remove-on-swipe -public class NewTransactionFragment extends QRScanCapableFragment { +public class NewTransactionFragment extends Fragment { private NewTransactionItemsAdapter listAdapter; private NewTransactionModel viewModel; - private FloatingActionButton fab; private OnNewTransactionFragmentInteractionListener mListener; - private MobileLedgerProfile mProfile; + private Profile mProfile; public NewTransactionFragment() { // Required empty public constructor setHasOptionsMenu(true); } - private void startNewPatternActivity(String scanned) { - Intent intent = new Intent(requireContext(), PatternsActivity.class); - Bundle args = new Bundle(); - args.putString(PatternsActivity.ARG_ADD_PATTERN, scanned); - requireContext().startActivity(intent, args); - } - private void alertNoPatternMatch(String scanned) { - MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(requireContext()); - builder.setCancelable(true) - .setMessage(R.string.no_pattern_matches) - .setPositiveButton(R.string.add_button, - (dialog, which) -> startNewPatternActivity(scanned)) - .create() - .show(); - } - protected void onQrScanned(String text) { - Logger.debug("qr", String.format("Got QR scan result [%s]", text)); - - if (Misc.emptyIsNull(text) == null) - return; - - LiveData> allPatterns = DB.get() - .getPatternDAO() - .getPatterns(); - allPatterns.observe(getViewLifecycleOwner(), patternHeaders -> { - ArrayList matchingPatterns = new ArrayList<>(); - - for (PatternHeader ph : patternHeaders) { - String patternSource = ph.getRegularExpression(); - if (Misc.emptyIsNull(patternSource) == null) - continue; - try { - Pattern pattern = Pattern.compile(patternSource); - Matcher matcher = pattern.matcher(text); - if (!matcher.matches()) - continue; - - Logger.debug("pattern", - String.format("Pattern '%s' [%s] matches '%s'", ph.getName(), - patternSource, text)); - matchingPatterns.add(ph); - } - catch (ParcelFormatException e) { - // ignored - Logger.debug("pattern", - String.format("Error compiling regular expression '%s'", patternSource), - e); - } - } - - if (matchingPatterns.isEmpty()) - alertNoPatternMatch(text); - else if (matchingPatterns.size() == 1) - applyPattern(matchingPatterns.get(0), text); - else - choosePattern(matchingPatterns, text); - }); - } - private void choosePattern(ArrayList matchingPatterns, String matchedText) { - final String patternNameColumn = getString(R.string.pattern_name); - AbstractCursor cursor = new AbstractCursor() { - @Override - public int getCount() { - return matchingPatterns.size(); - } - @Override - public String[] getColumnNames() { - return new String[]{patternNameColumn}; - } - @Override - public String getString(int column) { - return matchingPatterns.get(getPosition()) - .getName(); - } - @Override - public short getShort(int column) { - return -1; - } - @Override - public int getInt(int column) { - return -1; - } - @Override - public long getLong(int column) { - return -1; - } - @Override - public float getFloat(int column) { - return -1; - } - @Override - public double getDouble(int column) { - return -1; - } - @Override - public boolean isNull(int column) { - return false; - } - }; - - MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(requireContext()); - builder.setCancelable(true) - .setTitle(R.string.choose_pattern_to_apply) - .setSingleChoiceItems(cursor, -1, patternNameColumn, - (dialog, which) -> applyPattern(matchingPatterns.get(which), matchedText)) - .create() - .show(); - } - private void applyPattern(PatternHeader patternHeader, String text) { - Pattern pattern = Pattern.compile(patternHeader.getRegularExpression()); - - Matcher m = pattern.matcher(text); - - if (!m.matches()) { - Snackbar.make(requireView(), R.string.pattern_does_not_match, - BaseTransientBottomBar.LENGTH_INDEFINITE) - .show(); - return; - } - - SimpleDate transactionDate; - { - int day = extractIntFromMatches(m, patternHeader.getDateDayMatchGroup(), - patternHeader.getDateDay()); - int month = extractIntFromMatches(m, patternHeader.getDateMonthMatchGroup(), - patternHeader.getDateMonth()); - int year = extractIntFromMatches(m, patternHeader.getDateYearMatchGroup(), - patternHeader.getDateYear()); - - SimpleDate today = SimpleDate.today(); - if (year <= 0) - year = today.year; - if (month <= 0) - month = today.month; - if (day <= 0) - day = today.day; - - transactionDate = new SimpleDate(year, month, day); - - Logger.debug("pattern", "setting transaction date to " + transactionDate); - } - - NewTransactionModel.Item head = viewModel.getItem(0); - head.ensureType(NewTransactionModel.ItemType.generalData); - final String transactionDescription = - extractStringFromMatches(m, patternHeader.getTransactionDescriptionMatchGroup(), - patternHeader.getTransactionDescription()); - head.setDescription(transactionDescription); - Logger.debug("pattern", "Setting transaction description to " + transactionDescription); - final String transactionComment = - extractStringFromMatches(m, patternHeader.getTransactionCommentMatchGroup(), - patternHeader.getTransactionComment()); - head.setTransactionComment(transactionComment); - Logger.debug("pattern", "Setting transaction comment to " + transactionComment); - head.setDate(transactionDate); - listAdapter.notifyItemChanged(0); - - DB.get() - .getPatternDAO() - .getPatternWithAccounts(patternHeader.getId()) - .observe(getViewLifecycleOwner(), entry -> { - int rowIndex = 0; - final boolean accountsInInitialState = viewModel.accountsInInitialState(); - for (PatternAccount acc : entry.accounts) { - rowIndex++; - - String accountName = extractStringFromMatches(m, acc.getAccountNameMatchGroup(), - acc.getAccountName()); - String accountComment = - extractStringFromMatches(m, acc.getAccountCommentMatchGroup(), - acc.getAccountComment()); - Float amount = - extractFloatFromMatches(m, acc.getAmountMatchGroup(), acc.getAmount()); - if (amount != null && acc.getNegateAmount() != null && acc.getNegateAmount()) - amount = -amount; - - if (accountsInInitialState) { - NewTransactionModel.Item item = viewModel.getItem(rowIndex); - if (item == null) { - Logger.debug("pattern", String.format(Locale.US, - "Adding new account item [%s][c:%s][a:%s]", accountName, - accountComment, amount)); - final LedgerTransactionAccount ledgerAccount = - new LedgerTransactionAccount(accountName); - ledgerAccount.setComment(accountComment); - if (amount != null) - ledgerAccount.setAmount(amount); - // TODO currency - viewModel.addAccount(ledgerAccount); - listAdapter.notifyItemInserted(viewModel.items.size() - 1); - } - else { - Logger.debug("pattern", String.format(Locale.US, - "Stamping account item #%d [%s][c:%s][a:%s]", rowIndex, - accountName, accountComment, amount)); - - item.setAccountName(accountName); - item.setComment(accountComment); - if (amount != null) - item.getAccount() - .setAmount(amount); - - listAdapter.notifyItemChanged(rowIndex); - } - } - else { - final LedgerTransactionAccount transactionAccount = - new LedgerTransactionAccount(accountName); - transactionAccount.setComment(accountComment); - if (amount != null) - transactionAccount.setAmount(amount); - // TODO currency - Logger.debug("pattern", String.format(Locale.US, - "Adding trailing account item [%s][c:%s][a:%s]", accountName, - accountComment, amount)); - - viewModel.addAccount(transactionAccount); - listAdapter.notifyItemInserted(viewModel.items.size() - 1); - } - } - - listAdapter.checkTransactionSubmittable(); - }); - } - private int extractIntFromMatches(Matcher m, Integer group, Integer literal) { - if (literal != null) - return literal; - - if (group != null) { - int grp = group; - if (grp > 0 & grp <= m.groupCount()) - try { - return Integer.parseInt(m.group(grp)); - } - catch (NumberFormatException e) { - Snackbar.make(requireView(), - "Error extracting transaction date: " + e.getMessage(), - BaseTransientBottomBar.LENGTH_INDEFINITE) - .show(); - } - } - - return 0; - } - private String extractStringFromMatches(Matcher m, Integer group, String literal) { - if (literal != null) - return literal; - - if (group != null) { - int grp = group; - if (grp > 0 & grp <= m.groupCount()) - return m.group(grp); - } - - return null; - } - private Float extractFloatFromMatches(Matcher m, Integer group, Float literal) { - if (literal != null) - return literal; - - if (group != null) { - int grp = group; - if (grp > 0 & grp <= m.groupCount()) - try { - return Float.valueOf(m.group(grp)); - } - catch (NumberFormatException e) { - Snackbar.make(requireView(), - "Error extracting transaction amount: " + e.getMessage(), - BaseTransientBottomBar.LENGTH_INDEFINITE) - .show(); - } - } - - return null; - } @Override public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) { super.onCreateOptionsMenu(menu, inflater); @@ -377,7 +82,7 @@ public class NewTransactionFragment extends QRScanCapableFragment { menu.findItem(R.id.action_reset_new_transaction_activity) .setOnMenuItemClickListener(item -> { - listAdapter.reset(); + viewModel.reset(); return true; }); @@ -387,7 +92,8 @@ public class NewTransactionFragment extends QRScanCapableFragment { return true; }); if (activity != null) - viewModel.showCurrency.observe(activity, toggleCurrencyItem::setChecked); + viewModel.getShowCurrency() + .observe(activity, toggleCurrencyItem::setChecked); final MenuItem toggleCommentsItem = menu.findItem(R.id.toggle_comments); toggleCommentsItem.setOnMenuItemClickListener(item -> { @@ -395,11 +101,14 @@ public class NewTransactionFragment extends QRScanCapableFragment { return true; }); if (activity != null) - viewModel.showComments.observe(activity, toggleCommentsItem::setChecked); + viewModel.getShowComments() + .observe(activity, toggleCommentsItem::setChecked); } private boolean onScanQrAction(MenuItem item) { try { - scanQrLauncher.launch(null); + Context ctx = requireContext(); + if (ctx instanceof QR.QRScanTrigger) + ((QR.QRScanTrigger) ctx).triggerQRScan(); } catch (Exception e) { Logger.debug("qr", "Error launching QR scanner", e); @@ -427,6 +136,9 @@ public class NewTransactionFragment extends QRScanCapableFragment { mProfile = Data.getProfile(); listAdapter = new NewTransactionItemsAdapter(viewModel, mProfile); + viewModel.getItems() + .observe(getViewLifecycleOwner(), newList -> listAdapter.setItems(newList)); + RecyclerView list = activity.findViewById(R.id.new_transaction_accounts); list.setAdapter(listAdapter); list.setLayoutManager(new LinearLayoutManager(activity)); @@ -435,25 +147,6 @@ public class NewTransactionFragment extends QRScanCapableFragment { mProfile = profile; listAdapter.setProfile(profile); }); - listAdapter.notifyDataSetChanged(); - viewModel.isSubmittable() - .observe(getViewLifecycleOwner(), isSubmittable -> { - if (isSubmittable) { - if (fab != null) { - fab.show(); - } - } - else { - if (fab != null) { - fab.hide(); - } - } - }); -// viewModel.checkTransactionSubmittable(listAdapter); - - fab = activity.findViewById(R.id.fabAdd); - fab.setOnClickListener(v -> onFabPressed()); - boolean keep = false; Bundle args = getArguments(); @@ -467,18 +160,19 @@ public class NewTransactionFragment extends QRScanCapableFragment { 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_head)) + .append("\n\n") + .append(error) + .append("\n\n"); + if (API.valueOf(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); + ProfileDetailActivity.start(context, mProfile); }); } builder.setMessage(message); @@ -494,73 +188,48 @@ public class NewTransactionFragment extends QRScanCapableFragment { } int focused = 0; + FocusedElement element = null; if (savedInstanceState != null) { keep |= savedInstanceState.getBoolean("keep", true); - focused = savedInstanceState.getInt("focused", 0); + focused = savedInstanceState.getInt("focused-item", 0); + element = FocusedElement.valueOf(savedInstanceState.getString("focused-element")); } if (!keep) viewModel.reset(); else { - viewModel.setFocusedItem(focused); + viewModel.noteFocusChanged(focused, element); } ProgressBar p = activity.findViewById(R.id.progressBar); - viewModel.observeBusyFlag(getViewLifecycleOwner(), isBusy -> { - if (isBusy) { + viewModel.getBusyFlag() + .observe(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); - }); + p.setVisibility(View.VISIBLE); + } + else + p.setVisibility(View.INVISIBLE); + }); + + if (activity instanceof FabManager.FabHandler) + FabManager.handle((FabManager.FabHandler) activity, list); } @Override public void onSaveInstanceState(@NonNull Bundle outState) { super.onSaveInstanceState(outState); outState.putBoolean("keep", true); - final int focusedItem = viewModel.getFocusedItem(); - outState.putInt("focused", focusedItem); - } - private void onFabPressed() { - fab.hide(); - Misc.hideSoftKeyboard(this); - if (mListener != null) { - 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++) { - LedgerTransactionAccount acc = - new LedgerTransactionAccount(viewModel.getAccount(i)); - if (acc.getAccountName() - .trim() - .isEmpty()) - continue; - - if (acc.isAmountSet()) { - emptyAmountAccountBalance += acc.getAmount(); - } - else { - emptyAmountAccount = acc; - } - - tr.addAccount(acc); - } - - if (emptyAmountAccount != null) - emptyAmountAccount.setAmount(-emptyAmountAccountBalance); - - mListener.onTransactionSave(tr); - } + final NewTransactionModel.FocusInfo focusInfo = viewModel.getFocusInfo() + .getValue(); + final int focusedItem = focusInfo.position; + if (focusedItem >= 0) + outState.putInt("focused-item", focusedItem); + outState.putString("focused-element", focusInfo.element.toString()); } @Override