]> git.ktnx.net Git - mobile-ledger.git/blob - app/src/main/java/net/ktnx/mobileledger/ui/new_transaction/NewTransactionItemHolder.java
format amounts when the input field loses focus
[mobile-ledger.git] / app / src / main / java / net / ktnx / mobileledger / ui / new_transaction / NewTransactionItemHolder.java
1 /*
2  * Copyright © 2021 Damyan Ivanov.
3  * This file is part of MoLe.
4  * MoLe is free software: you can distribute it and/or modify it
5  * under the term of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your opinion), any later version.
8  *
9  * MoLe is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License terms for details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with MoLe. If not, see <https://www.gnu.org/licenses/>.
16  */
17
18 package net.ktnx.mobileledger.ui.new_transaction;
19
20 import android.annotation.SuppressLint;
21 import android.graphics.Typeface;
22 import android.text.Editable;
23 import android.text.TextUtils;
24 import android.text.TextWatcher;
25 import android.view.Gravity;
26 import android.view.View;
27 import android.view.inputmethod.EditorInfo;
28 import android.widget.EditText;
29 import android.widget.SimpleCursorAdapter;
30 import android.widget.TextView;
31
32 import androidx.annotation.ColorInt;
33 import androidx.annotation.NonNull;
34 import androidx.constraintlayout.widget.ConstraintLayout;
35 import androidx.recyclerview.widget.RecyclerView;
36
37 import net.ktnx.mobileledger.R;
38 import net.ktnx.mobileledger.async.DescriptionSelectedCallback;
39 import net.ktnx.mobileledger.databinding.NewTransactionRowBinding;
40 import net.ktnx.mobileledger.db.AccountAutocompleteAdapter;
41 import net.ktnx.mobileledger.model.Currency;
42 import net.ktnx.mobileledger.model.Data;
43 import net.ktnx.mobileledger.model.MobileLedgerProfile;
44 import net.ktnx.mobileledger.ui.CurrencySelectorFragment;
45 import net.ktnx.mobileledger.ui.DatePickerFragment;
46 import net.ktnx.mobileledger.ui.TextViewClearHelper;
47 import net.ktnx.mobileledger.utils.DimensionUtils;
48 import net.ktnx.mobileledger.utils.Logger;
49 import net.ktnx.mobileledger.utils.MLDB;
50 import net.ktnx.mobileledger.utils.Misc;
51 import net.ktnx.mobileledger.utils.SimpleDate;
52
53 import java.text.DecimalFormatSymbols;
54 import java.text.ParseException;
55 import java.util.Objects;
56
57 class NewTransactionItemHolder extends RecyclerView.ViewHolder
58         implements DatePickerFragment.DatePickedListener, DescriptionSelectedCallback {
59     private final String decimalDot = ".";
60     private final MobileLedgerProfile mProfile;
61     private final NewTransactionRowBinding b;
62     private final NewTransactionItemsAdapter mAdapter;
63     private boolean ignoreFocusChanges = false;
64     private String decimalSeparator;
65     private boolean inUpdate = false;
66     private boolean syncingData = false;
67     //TODO multiple amounts with different currencies per posting?
68     NewTransactionItemHolder(@NonNull NewTransactionRowBinding b,
69                              NewTransactionItemsAdapter adapter) {
70         super(b.getRoot());
71         this.b = b;
72         this.mAdapter = adapter;
73         new TextViewClearHelper().attachToTextView(b.comment);
74
75         b.newTransactionDescription.setNextFocusForwardId(View.NO_ID);
76         b.accountRowAccName.setNextFocusForwardId(View.NO_ID);
77         b.accountRowAccAmounts.setNextFocusForwardId(View.NO_ID); // magic!
78
79         b.newTransactionDate.setOnClickListener(v -> pickTransactionDate());
80
81         b.accountCommentButton.setOnClickListener(v -> {
82             b.comment.setVisibility(View.VISIBLE);
83             b.comment.requestFocus();
84         });
85
86         b.transactionCommentButton.setOnClickListener(v -> {
87             b.transactionComment.setVisibility(View.VISIBLE);
88             b.transactionComment.requestFocus();
89         });
90
91         mProfile = Data.getProfile();
92
93         @SuppressLint("DefaultLocale") View.OnFocusChangeListener focusMonitor = (v, hasFocus) -> {
94             final int id = v.getId();
95             if (hasFocus) {
96                 boolean wasSyncing = syncingData;
97                 syncingData = true;
98                 try {
99                     final int pos = getAdapterPosition();
100                     if (id == R.id.account_row_acc_name) {
101                         adapter.noteFocusIsOnAccount(pos);
102                     }
103                     else if (id == R.id.account_row_acc_amounts) {
104                         adapter.noteFocusIsOnAmount(pos);
105                     }
106                     else if (id == R.id.comment) {
107                         adapter.noteFocusIsOnComment(pos);
108                     }
109                     else if (id == R.id.transaction_comment) {
110                         adapter.noteFocusIsOnTransactionComment(pos);
111                     }
112                     else if (id == R.id.new_transaction_description) {
113                         adapter.noteFocusIsOnDescription(pos);
114                     }
115                     else
116                         throw new IllegalStateException("Where is the focus?");
117                 }
118                 finally {
119                     syncingData = wasSyncing;
120                 }
121             }
122             else {  // lost focus
123                 if (id == R.id.account_row_acc_amounts) {
124                     try {
125                         String input = String.valueOf(b.accountRowAccAmounts.getText());
126                         input = input.replace(decimalSeparator, decimalDot);
127                         b.accountRowAccAmounts.setText(
128                                 String.format("%4.2f", Float.parseFloat(input)));
129                     }
130                     catch (NumberFormatException ex) {
131                         // ignored
132                     }
133                 }
134             }
135
136             if (id == R.id.comment) {
137                 commentFocusChanged(b.comment, hasFocus);
138             }
139             else if (id == R.id.transaction_comment) {
140                 commentFocusChanged(b.transactionComment, hasFocus);
141             }
142         };
143
144         b.newTransactionDescription.setOnFocusChangeListener(focusMonitor);
145         b.accountRowAccName.setOnFocusChangeListener(focusMonitor);
146         b.accountRowAccAmounts.setOnFocusChangeListener(focusMonitor);
147         b.comment.setOnFocusChangeListener(focusMonitor);
148         b.transactionComment.setOnFocusChangeListener(focusMonitor);
149
150         NewTransactionActivity activity = (NewTransactionActivity) b.getRoot()
151                                                                     .getContext();
152
153         MLDB.hookAutocompletionAdapter(activity, b.newTransactionDescription,
154                 MLDB.DESCRIPTION_HISTORY_TABLE, "description", false, activity, mProfile);
155         b.accountRowAccName.setAdapter(new AccountAutocompleteAdapter(b.getRoot()
156                                                                        .getContext(), mProfile));
157
158         decimalSeparator = "";
159         Data.locale.observe(activity, locale -> decimalSeparator = String.valueOf(
160                 DecimalFormatSymbols.getInstance(locale)
161                                     .getMonetaryDecimalSeparator()));
162
163         final TextWatcher tw = new TextWatcher() {
164             @Override
165             public void beforeTextChanged(CharSequence s, int start, int count, int after) {
166             }
167
168             @Override
169             public void onTextChanged(CharSequence s, int start, int before, int count) {
170             }
171
172             @Override
173             public void afterTextChanged(Editable s) {
174 //                debug("input", "text changed");
175                 if (inUpdate)
176                     return;
177
178                 Logger.debug("textWatcher", "calling syncData()");
179                 syncData();
180                 Logger.debug("textWatcher",
181                         "syncData() returned, checking if transaction is submittable");
182                 adapter.model.checkTransactionSubmittable(null);
183                 Logger.debug("textWatcher", "done");
184             }
185         };
186         final TextWatcher amountWatcher = new TextWatcher() {
187             @Override
188             public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
189             @Override
190             public void onTextChanged(CharSequence s, int start, int before, int count) {}
191             @Override
192             public void afterTextChanged(Editable s) {
193                 checkAmountValid(s.toString());
194
195                 if (syncData())
196                     adapter.model.checkTransactionSubmittable(null);
197             }
198         };
199         b.newTransactionDescription.addTextChangedListener(tw);
200         monitorComment(b.transactionComment);
201         b.accountRowAccName.addTextChangedListener(tw);
202         monitorComment(b.comment);
203         b.accountRowAccAmounts.addTextChangedListener(amountWatcher);
204
205         b.currencyButton.setOnClickListener(v -> {
206             CurrencySelectorFragment cpf = new CurrencySelectorFragment();
207             cpf.showPositionAndPadding();
208             cpf.setOnCurrencySelectedListener(
209                     c -> adapter.setItemCurrency(getAdapterPosition(), c.getName()));
210             cpf.show(activity.getSupportFragmentManager(), "currency-selector");
211         });
212
213         commentFocusChanged(b.transactionComment, false);
214         commentFocusChanged(b.comment, false);
215
216         adapter.model.getFocusInfo()
217                      .observe(activity, focusInfo -> {
218                          if (ignoreFocusChanges) {
219                              Logger.debug("new-trans", "Ignoring focus change");
220                              return;
221                          }
222                          ignoreFocusChanges = true;
223                          try {
224                              if (((focusInfo == null) || (focusInfo.element == null) ||
225                                   focusInfo.position != getAdapterPosition()) ||
226                                  itemView.hasFocus())
227                                  return;
228
229                              NewTransactionModel.Item item = getItem();
230                              if (item instanceof NewTransactionModel.TransactionHead) {
231                                  NewTransactionModel.TransactionHead head =
232                                          item.toTransactionHead();
233                                  // bad idea - double pop-up, and not really necessary.
234                                  // the user can tap the input to get the calendar
235                                  //if (!tvDate.hasFocus()) tvDate.requestFocus();
236                                  switch (focusInfo.element) {
237                                      case TransactionComment:
238                                          b.transactionComment.setVisibility(View.VISIBLE);
239                                          b.transactionComment.requestFocus();
240                                          break;
241                                      case Description:
242                                          boolean focused =
243                                                  b.newTransactionDescription.requestFocus();
244 //                            tvDescription.dismissDropDown();
245                                          if (focused)
246                                              Misc.showSoftKeyboard(
247                                                      (NewTransactionActivity) b.getRoot()
248                                                                                .getContext());
249                                          break;
250                                  }
251                              }
252                              else if (item instanceof NewTransactionModel.TransactionAccount) {
253                                  NewTransactionModel.TransactionAccount acc =
254                                          item.toTransactionAccount();
255                                  switch (focusInfo.element) {
256                                      case Amount:
257                                          b.accountRowAccAmounts.requestFocus();
258                                          break;
259                                      case Comment:
260                                          b.comment.setVisibility(View.VISIBLE);
261                                          b.comment.requestFocus();
262                                          break;
263                                      case Account:
264                                          boolean focused = b.accountRowAccName.requestFocus();
265 //                                         b.accountRowAccName.dismissDropDown();
266                                          if (focused)
267                                              Misc.showSoftKeyboard(
268                                                      (NewTransactionActivity) b.getRoot()
269                                                                                .getContext());
270                                          break;
271                                  }
272                              }
273                          }
274                          finally {
275                              ignoreFocusChanges = false;
276                          }
277                      });
278
279         Data.currencyGap.observe(activity,
280                 hasGap -> updateCurrencyPositionAndPadding(Data.currencySymbolPosition.getValue(),
281                         hasGap));
282
283         Data.currencySymbolPosition.observe(activity,
284                 position -> updateCurrencyPositionAndPadding(position,
285                         Data.currencyGap.getValue()));
286
287         adapter.model.getShowCurrency()
288                      .observe(activity, showCurrency -> {
289                          if (showCurrency) {
290                              b.currency.setVisibility(View.VISIBLE);
291                              b.currencyButton.setVisibility(View.VISIBLE);
292                              b.currency.setText(mProfile.getDefaultCommodity());
293                          }
294                          else {
295                              b.currency.setVisibility(View.GONE);
296                              b.currencyButton.setVisibility(View.GONE);
297                              b.currency.setText(null);
298                          }
299                      });
300
301         adapter.model.getShowComments()
302                      .observe(activity, show -> {
303                          ConstraintLayout.LayoutParams amountLayoutParams =
304                                  (ConstraintLayout.LayoutParams) b.amountLayout.getLayoutParams();
305                          ConstraintLayout.LayoutParams accountParams =
306                                  (ConstraintLayout.LayoutParams) b.accountRowAccName.getLayoutParams();
307
308                          if (show) {
309                              accountParams.endToStart = ConstraintLayout.LayoutParams.UNSET;
310                              accountParams.endToEnd = ConstraintLayout.LayoutParams.PARENT_ID;
311
312                              amountLayoutParams.topToTop = ConstraintLayout.LayoutParams.UNSET;
313                              amountLayoutParams.topToBottom = b.accountRowAccName.getId();
314
315                              b.commentLayout.setVisibility(View.VISIBLE);
316                          }
317                          else {
318                              accountParams.endToStart = b.amountLayout.getId();
319                              accountParams.endToEnd = ConstraintLayout.LayoutParams.UNSET;
320
321                              amountLayoutParams.topToBottom = ConstraintLayout.LayoutParams.UNSET;
322                              amountLayoutParams.topToTop = ConstraintLayout.LayoutParams.PARENT_ID;
323
324                              b.commentLayout.setVisibility(View.GONE);
325                          }
326
327                          b.accountRowAccName.setLayoutParams(accountParams);
328                          b.amountLayout.setLayoutParams(amountLayoutParams);
329
330                          b.transactionCommentLayout.setVisibility(show ? View.VISIBLE : View.GONE);
331                      });
332     }
333     public void checkAmountValid(String s) {
334         boolean valid = true;
335         try {
336             if (s.length() > 0) {
337                 float ignored = Float.parseFloat(s.replace(decimalSeparator, decimalDot));
338             }
339         }
340         catch (NumberFormatException ex) {
341             valid = false;
342         }
343
344         displayAmountValidity(valid);
345     }
346     private void displayAmountValidity(boolean valid) {
347         b.accountRowAccAmounts.setCompoundDrawablesRelativeWithIntrinsicBounds(
348                 valid ? 0 : R.drawable.ic_error_outline_black_24dp, 0, 0, 0);
349         b.accountRowAccAmounts.setMinEms(valid ? 4 : 5);
350     }
351     private void monitorComment(EditText editText) {
352         editText.addTextChangedListener(new TextWatcher() {
353             @Override
354             public void beforeTextChanged(CharSequence s, int start, int count, int after) {
355             }
356             @Override
357             public void onTextChanged(CharSequence s, int start, int before, int count) {
358             }
359             @Override
360             public void afterTextChanged(Editable s) {
361 //                debug("input", "text changed");
362                 if (inUpdate)
363                     return;
364
365                 Logger.debug("textWatcher", "calling syncData()");
366                 syncData();
367                 Logger.debug("textWatcher",
368                         "syncData() returned, checking if transaction is submittable");
369                 styleComment(editText, s.toString());
370                 Logger.debug("textWatcher", "done");
371             }
372         });
373     }
374     private void commentFocusChanged(TextView textView, boolean hasFocus) {
375         @ColorInt int textColor;
376         textColor = b.dummyText.getTextColors()
377                                .getDefaultColor();
378         if (hasFocus) {
379             textView.setTypeface(null, Typeface.NORMAL);
380             textView.setHint(R.string.transaction_account_comment_hint);
381         }
382         else {
383             int alpha = (textColor >> 24 & 0xff);
384             alpha = 3 * alpha / 4;
385             textColor = (alpha << 24) | (0x00ffffff & textColor);
386             textView.setTypeface(null, Typeface.ITALIC);
387             textView.setHint("");
388             if (TextUtils.isEmpty(textView.getText())) {
389                 textView.setVisibility(View.INVISIBLE);
390             }
391         }
392         textView.setTextColor(textColor);
393
394     }
395     private void updateCurrencyPositionAndPadding(Currency.Position position, boolean hasGap) {
396         ConstraintLayout.LayoutParams amountLP =
397                 (ConstraintLayout.LayoutParams) b.accountRowAccAmounts.getLayoutParams();
398         ConstraintLayout.LayoutParams currencyLP =
399                 (ConstraintLayout.LayoutParams) b.currency.getLayoutParams();
400
401         if (position == Currency.Position.before) {
402             currencyLP.startToStart = ConstraintLayout.LayoutParams.PARENT_ID;
403             currencyLP.endToEnd = ConstraintLayout.LayoutParams.UNSET;
404
405             amountLP.endToEnd = ConstraintLayout.LayoutParams.PARENT_ID;
406             amountLP.endToStart = ConstraintLayout.LayoutParams.UNSET;
407             amountLP.startToStart = ConstraintLayout.LayoutParams.UNSET;
408             amountLP.startToEnd = b.currency.getId();
409
410             b.currency.setGravity(Gravity.END);
411         }
412         else {
413             currencyLP.startToStart = ConstraintLayout.LayoutParams.UNSET;
414             currencyLP.endToEnd = ConstraintLayout.LayoutParams.PARENT_ID;
415
416             amountLP.startToStart = ConstraintLayout.LayoutParams.PARENT_ID;
417             amountLP.startToEnd = ConstraintLayout.LayoutParams.UNSET;
418             amountLP.endToEnd = ConstraintLayout.LayoutParams.UNSET;
419             amountLP.endToStart = b.currency.getId();
420
421             b.currency.setGravity(Gravity.START);
422         }
423
424         amountLP.resolveLayoutDirection(b.accountRowAccAmounts.getLayoutDirection());
425         currencyLP.resolveLayoutDirection(b.currency.getLayoutDirection());
426
427         b.accountRowAccAmounts.setLayoutParams(amountLP);
428         b.currency.setLayoutParams(currencyLP);
429
430         // distance between the amount and the currency symbol
431         int gapSize = DimensionUtils.sp2px(b.currency.getContext(), 5);
432
433         if (position == Currency.Position.before) {
434             b.currency.setPaddingRelative(0, 0, hasGap ? gapSize : 0, 0);
435         }
436         else {
437             b.currency.setPaddingRelative(hasGap ? gapSize : 0, 0, 0, 0);
438         }
439     }
440     private void setCurrencyString(String currency) {
441         @ColorInt int textColor = b.dummyText.getTextColors()
442                                              .getDefaultColor();
443         if ((currency == null) || currency.isEmpty()) {
444             b.currency.setText(R.string.currency_symbol);
445             int alpha = (textColor >> 24) & 0xff;
446             alpha = alpha * 3 / 4;
447             b.currency.setTextColor((alpha << 24) | (0x00ffffff & textColor));
448         }
449         else {
450             b.currency.setText(currency);
451             b.currency.setTextColor(textColor);
452         }
453     }
454     private void setCurrency(Currency currency) {
455         setCurrencyString((currency == null) ? null : currency.getName());
456     }
457     private void setEditable(Boolean editable) {
458         b.newTransactionDate.setEnabled(editable);
459         b.newTransactionDescription.setEnabled(editable);
460         b.accountRowAccName.setEnabled(editable);
461         b.accountRowAccAmounts.setEnabled(editable);
462     }
463     private void beginUpdates() {
464         if (inUpdate)
465             throw new RuntimeException("Already in update mode");
466         inUpdate = true;
467     }
468     private void endUpdates() {
469         if (!inUpdate)
470             throw new RuntimeException("Not in update mode");
471         inUpdate = false;
472     }
473     /**
474      * syncData()
475      * <p>
476      * Stores the data from the UI elements into the model item
477      * Returns true if there were changes made that suggest transaction has to be
478      * checked for being submittable
479      */
480     private boolean syncData() {
481         if (syncingData) {
482             Logger.debug("new-trans", "skipping syncData() loop");
483             return false;
484         }
485
486         NewTransactionModel.Item item = getItem();
487
488         syncingData = true;
489
490         try {
491             if (item instanceof NewTransactionModel.TransactionHead) {
492                 NewTransactionModel.TransactionHead head = item.toTransactionHead();
493
494                 head.setDate(String.valueOf(b.newTransactionDate.getText()));
495                 head.setDescription(String.valueOf(b.newTransactionDescription.getText()));
496                 head.setComment(String.valueOf(b.transactionComment.getText()));
497             }
498             else if (item instanceof NewTransactionModel.TransactionAccount) {
499                 NewTransactionModel.TransactionAccount acc = item.toTransactionAccount();
500                 acc.setAccountName(String.valueOf(b.accountRowAccName.getText()));
501
502                 acc.setComment(String.valueOf(b.comment.getText()));
503
504                 String amount = String.valueOf(b.accountRowAccAmounts.getText());
505                 amount = amount.trim();
506
507                 if (amount.isEmpty()) {
508                     acc.resetAmount();
509                     acc.setAmountValid(true);
510                 }
511                 else {
512                     try {
513                         amount = amount.replace(decimalSeparator, decimalDot);
514                         acc.setAmount(Float.parseFloat(amount));
515                         acc.setAmountValid(true);
516                     }
517                     catch (NumberFormatException e) {
518                         Logger.debug("new-trans", String.format(
519                                 "assuming amount is not set due to number format exception. " +
520                                 "input was '%s'", amount));
521                         acc.setAmountValid(false);
522                     }
523                     final String curr = String.valueOf(b.currency.getText());
524                     if (curr.equals(b.currency.getContext()
525                                               .getResources()
526                                               .getString(R.string.currency_symbol)) ||
527                         curr.isEmpty())
528                         acc.setCurrency(null);
529                     else
530                         acc.setCurrency(curr);
531                 }
532             }
533             else {
534                 throw new RuntimeException("Should not happen");
535             }
536
537             return true;
538         }
539         catch (ParseException e) {
540             throw new RuntimeException("Should not happen", e);
541         }
542         finally {
543             syncingData = false;
544         }
545     }
546     private void pickTransactionDate() {
547         DatePickerFragment picker = new DatePickerFragment();
548         picker.setFutureDates(mProfile.getFutureDates());
549         picker.setOnDatePickedListener(this);
550         picker.setCurrentDateFromText(b.newTransactionDate.getText());
551         picker.show(((NewTransactionActivity) b.getRoot()
552                                                .getContext()).getSupportFragmentManager(), null);
553     }
554     /**
555      * bind
556      *
557      * @param item updates the UI elements with the data from the model item
558      */
559     @SuppressLint("DefaultLocale")
560     public void bind(@NonNull NewTransactionModel.Item item) {
561         beginUpdates();
562         try {
563             syncingData = true;
564             try {
565                 if (item instanceof NewTransactionModel.TransactionHead) {
566                     NewTransactionModel.TransactionHead head = item.toTransactionHead();
567                     b.newTransactionDate.setText(head.getFormattedDate());
568
569                     // avoid triggering completion pop-up
570                     SimpleCursorAdapter a =
571                             (SimpleCursorAdapter) b.newTransactionDescription.getAdapter();
572                     try {
573                         b.newTransactionDescription.setAdapter(null);
574                         b.newTransactionDescription.setText(head.getDescription());
575                     }
576                     finally {
577                         b.newTransactionDescription.setAdapter(a);
578                     }
579
580                     b.transactionComment.setText(head.getComment());
581                     //styleComment(b.transactionComment, head.getComment());
582
583                     b.ntrData.setVisibility(View.VISIBLE);
584                     b.ntrAccount.setVisibility(View.GONE);
585                     b.ntrPadding.setVisibility(View.GONE);
586                     setEditable(true);
587                 }
588                 else if (item instanceof NewTransactionModel.TransactionAccount) {
589                     NewTransactionModel.TransactionAccount acc = item.toTransactionAccount();
590
591                     // avoid triggering completion pop-up
592                     AccountAutocompleteAdapter a =
593                             (AccountAutocompleteAdapter) b.accountRowAccName.getAdapter();
594                     try {
595                         b.accountRowAccName.setAdapter(null);
596                         b.accountRowAccName.setText(acc.getAccountName());
597                     }
598                     finally {
599                         b.accountRowAccName.setAdapter(a);
600                     }
601
602                     final String amountHint = acc.getAmountHint();
603                     if (amountHint == null) {
604                         b.accountRowAccAmounts.setHint(R.string.zero_amount);
605                     }
606                     else {
607                         b.accountRowAccAmounts.setHint(amountHint);
608                     }
609
610                     b.accountRowAccAmounts.setImeOptions(
611                             acc.isLast() ? EditorInfo.IME_ACTION_DONE : EditorInfo.IME_ACTION_NEXT);
612
613                     setCurrencyString(acc.getCurrency());
614                     b.accountRowAccAmounts.setText(
615                             acc.isAmountSet() ? String.format("%4.2f", acc.getAmount()) : null);
616                     displayAmountValidity(true);
617
618                     b.comment.setText(acc.getComment());
619
620                     b.ntrData.setVisibility(View.GONE);
621                     b.ntrAccount.setVisibility(View.VISIBLE);
622                     b.ntrPadding.setVisibility(View.GONE);
623
624                     setEditable(true);
625                 }
626                 else {
627                     throw new RuntimeException("Don't know how to handle " + item);
628                 }
629             }
630             finally {
631                 syncingData = false;
632             }
633         }
634         finally {
635             endUpdates();
636         }
637     }
638     private void styleComment(EditText editText, String comment) {
639         final View focusedView = editText.findFocus();
640         editText.setTypeface(null, (focusedView == editText) ? Typeface.NORMAL : Typeface.ITALIC);
641         editText.setVisibility(
642                 ((focusedView != editText) && TextUtils.isEmpty(comment)) ? View.INVISIBLE
643                                                                           : View.VISIBLE);
644     }
645     @Override
646     public void onDatePicked(int year, int month, int day) {
647         final NewTransactionModel.TransactionHead head = getItem().toTransactionHead();
648         head.setDate(new SimpleDate(year, month + 1, day));
649         b.newTransactionDate.setText(head.getFormattedDate());
650
651         boolean focused = b.newTransactionDescription.requestFocus();
652         if (focused)
653             Misc.showSoftKeyboard((NewTransactionActivity) b.getRoot()
654                                                             .getContext());
655
656     }
657     private NewTransactionModel.Item getItem() {
658         return Objects.requireNonNull(mAdapter.model.getItems()
659                                                     .getValue())
660                       .get(getAdapterPosition());
661     }
662     @Override
663     public void descriptionSelected(String description) {
664         b.accountRowAccName.setText(description);
665         b.accountRowAccAmounts.requestFocus(View.FOCUS_FORWARD);
666     }
667 }