]> git.ktnx.net Git - mobile-ledger.git/blob - app/src/main/java/net/ktnx/mobileledger/ui/activity/NewTransactionItemHolder.java
upgrade a couple of library versions
[mobile-ledger.git] / app / src / main / java / net / ktnx / mobileledger / ui / activity / 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.activity;
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.TextView;
30
31 import androidx.annotation.ColorInt;
32 import androidx.annotation.NonNull;
33 import androidx.appcompat.app.AppCompatActivity;
34 import androidx.constraintlayout.widget.ConstraintLayout;
35 import androidx.lifecycle.Observer;
36 import androidx.recyclerview.widget.RecyclerView;
37
38 import net.ktnx.mobileledger.R;
39 import net.ktnx.mobileledger.async.DescriptionSelectedCallback;
40 import net.ktnx.mobileledger.databinding.NewTransactionRowBinding;
41 import net.ktnx.mobileledger.model.Currency;
42 import net.ktnx.mobileledger.model.Data;
43 import net.ktnx.mobileledger.model.LedgerTransactionAccount;
44 import net.ktnx.mobileledger.model.MobileLedgerProfile;
45 import net.ktnx.mobileledger.ui.CurrencySelectorFragment;
46 import net.ktnx.mobileledger.ui.DatePickerFragment;
47 import net.ktnx.mobileledger.ui.TextViewClearHelper;
48 import net.ktnx.mobileledger.utils.DimensionUtils;
49 import net.ktnx.mobileledger.utils.Logger;
50 import net.ktnx.mobileledger.utils.MLDB;
51 import net.ktnx.mobileledger.utils.Misc;
52 import net.ktnx.mobileledger.utils.SimpleDate;
53
54 import java.text.DecimalFormatSymbols;
55 import java.text.ParseException;
56 import java.util.Date;
57 import java.util.Locale;
58
59 import static net.ktnx.mobileledger.ui.activity.NewTransactionModel.ItemType;
60
61 class NewTransactionItemHolder extends RecyclerView.ViewHolder
62         implements DatePickerFragment.DatePickedListener, DescriptionSelectedCallback {
63     private final String decimalDot;
64     private final Observer<Boolean> showCommentsObserver;
65     private final MobileLedgerProfile mProfile;
66     private final Observer<SimpleDate> dateObserver;
67     private final Observer<String> descriptionObserver;
68     private final Observer<String> transactionCommentObserver;
69     private final Observer<String> hintObserver;
70     private final Observer<Integer> focusedAccountObserver;
71     private final Observer<Integer> accountCountObserver;
72     private final Observer<Boolean> editableObserver;
73     private final Observer<Currency.Position> currencyPositionObserver;
74     private final Observer<Boolean> currencyGapObserver;
75     private final Observer<Locale> localeObserver;
76     private final Observer<Currency> currencyObserver;
77     private final Observer<Boolean> showCurrencyObserver;
78     private final Observer<String> commentObserver;
79     private final Observer<Boolean> amountValidityObserver;
80     private final NewTransactionRowBinding b;
81     private String decimalSeparator;
82     private NewTransactionModel.Item item;
83     private Date date;
84     private boolean inUpdate = false;
85     private boolean syncingData = false;
86     //TODO multiple amounts with different currencies per posting
87     NewTransactionItemHolder(@NonNull NewTransactionRowBinding b,
88                              NewTransactionItemsAdapter adapter) {
89         super(b.getRoot());
90         this.b = b;
91         new TextViewClearHelper().attachToTextView((EditText) b.comment);
92
93         b.newTransactionDescription.setNextFocusForwardId(View.NO_ID);
94         b.accountRowAccName.setNextFocusForwardId(View.NO_ID);
95         b.accountRowAccAmounts.setNextFocusForwardId(View.NO_ID); // magic!
96
97         b.newTransactionDate.setOnClickListener(v -> pickTransactionDate());
98
99         b.accountCommentButton.setOnClickListener(v -> {
100             b.comment.setVisibility(View.VISIBLE);
101             b.comment.requestFocus();
102         });
103
104         b.transactionCommentButton.setOnClickListener(v -> {
105             b.transactionComment.setVisibility(View.VISIBLE);
106             b.transactionComment.requestFocus();
107         });
108
109         mProfile = Data.getProfile();
110
111         View.OnFocusChangeListener focusMonitor = (v, hasFocus) -> {
112             final int id = v.getId();
113             if (hasFocus) {
114                 boolean wasSyncing = syncingData;
115                 syncingData = true;
116                 try {
117                     final int pos = getAdapterPosition();
118                     adapter.updateFocusedItem(pos);
119                     if (id == R.id.account_row_acc_name) {
120                         adapter.noteFocusIsOnAccount(pos);
121                     }
122                     else if (id == R.id.account_row_acc_amounts) {
123                         adapter.noteFocusIsOnAmount(pos);
124                     }
125                     else if (id == R.id.comment) {
126                         adapter.noteFocusIsOnComment(pos);
127                     }
128                     else if (id == R.id.transaction_comment) {
129                         adapter.noteFocusIsOnTransactionComment(pos);
130                     }
131                     else if (id == R.id.new_transaction_description) {
132                         adapter.noteFocusIsOnDescription(pos);
133                     }
134                 }
135                 finally {
136                     syncingData = wasSyncing;
137                 }
138             }
139
140             if (id == R.id.comment) {
141                 commentFocusChanged(b.comment, hasFocus);
142             }
143             else if (id == R.id.transaction_comment) {
144                 commentFocusChanged(b.transactionComment, hasFocus);
145             }
146         };
147
148         b.newTransactionDescription.setOnFocusChangeListener(focusMonitor);
149         b.accountRowAccName.setOnFocusChangeListener(focusMonitor);
150         b.accountRowAccAmounts.setOnFocusChangeListener(focusMonitor);
151         b.comment.setOnFocusChangeListener(focusMonitor);
152         b.transactionComment.setOnFocusChangeListener(focusMonitor);
153
154         MLDB.hookAutocompletionAdapter(b.getRoot()
155                                         .getContext(), b.newTransactionDescription,
156                 MLDB.DESCRIPTION_HISTORY_TABLE, "description", false, adapter, mProfile);
157         MLDB.hookAutocompletionAdapter(b.getRoot()
158                                         .getContext(), b.accountRowAccName, MLDB.ACCOUNTS_TABLE,
159                 "name", true, this, mProfile);
160
161         decimalSeparator = String.valueOf(DecimalFormatSymbols.getInstance()
162                                                               .getMonetaryDecimalSeparator());
163         localeObserver = locale -> decimalSeparator = String.valueOf(
164                 DecimalFormatSymbols.getInstance(locale)
165                                     .getMonetaryDecimalSeparator());
166
167         decimalDot = ".";
168
169         final TextWatcher tw = new TextWatcher() {
170             @Override
171             public void beforeTextChanged(CharSequence s, int start, int count, int after) {
172             }
173
174             @Override
175             public void onTextChanged(CharSequence s, int start, int before, int count) {
176             }
177
178             @Override
179             public void afterTextChanged(Editable s) {
180 //                debug("input", "text changed");
181                 if (inUpdate)
182                     return;
183
184                 Logger.debug("textWatcher", "calling syncData()");
185                 syncData();
186                 Logger.debug("textWatcher",
187                         "syncData() returned, checking if transaction is submittable");
188                 adapter.checkTransactionSubmittable();
189                 Logger.debug("textWatcher", "done");
190             }
191         };
192         final TextWatcher amountWatcher = new TextWatcher() {
193             @Override
194             public void beforeTextChanged(CharSequence s, int start, int count, int after) {
195                 Logger.debug("num",
196                         String.format(Locale.US, "beforeTextChanged: start=%d, count=%d, after=%d",
197                                 start, count, after));
198             }
199             @Override
200             public void onTextChanged(CharSequence s, int start, int before, int count) {}
201             @Override
202             public void afterTextChanged(Editable s) {
203
204                 if (syncData())
205                     adapter.checkTransactionSubmittable();
206             }
207         };
208         b.newTransactionDescription.addTextChangedListener(tw);
209         b.transactionComment.addTextChangedListener(tw);
210         b.accountRowAccName.addTextChangedListener(tw);
211         b.comment.addTextChangedListener(tw);
212         b.accountRowAccAmounts.addTextChangedListener(amountWatcher);
213
214         b.currencyButton.setOnClickListener(v -> {
215             CurrencySelectorFragment cpf = new CurrencySelectorFragment();
216             cpf.showPositionAndPadding();
217             cpf.setOnCurrencySelectedListener(c -> item.setCurrency(c));
218             final AppCompatActivity activity = (AppCompatActivity) v.getContext();
219             cpf.show(activity.getSupportFragmentManager(), "currency-selector");
220         });
221
222         dateObserver = date -> {
223             if (syncingData)
224                 return;
225             syncingData = true;
226             try {
227                 b.newTransactionDate.setText(item.getFormattedDate());
228             }
229             finally {
230                 syncingData = false;
231             }
232         };
233         descriptionObserver = description -> {
234             if (syncingData)
235                 return;
236             syncingData = true;
237             try {
238                 b.newTransactionDescription.setText(description);
239             }
240             finally {
241                 syncingData = false;
242             }
243         };
244         transactionCommentObserver = transactionComment -> {
245             final View focusedView = b.transactionComment.findFocus();
246             b.transactionComment.setTypeface(null,
247                     (focusedView == b.transactionComment) ? Typeface.NORMAL : Typeface.ITALIC);
248             b.transactionComment.setVisibility(
249                     ((focusedView != b.transactionComment) && TextUtils.isEmpty(transactionComment))
250                     ? View.INVISIBLE : View.VISIBLE);
251
252         };
253         hintObserver = hint -> {
254             if (syncingData)
255                 return;
256             syncingData = true;
257             try {
258                 if (hint == null)
259                     b.accountRowAccAmounts.setHint(R.string.zero_amount);
260                 else
261                     b.accountRowAccAmounts.setHint(hint);
262             }
263             finally {
264                 syncingData = false;
265             }
266         };
267         editableObserver = this::setEditable;
268         commentFocusChanged(b.transactionComment, false);
269         commentFocusChanged(b.comment, false);
270         focusedAccountObserver = index -> {
271             if ((index == null) || !index.equals(getAdapterPosition()) || itemView.hasFocus())
272                 return;
273
274             switch (item.getType()) {
275                 case generalData:
276                     // bad idea - double pop-up, and not really necessary.
277                     // the user can tap the input to get the calendar
278                     //if (!tvDate.hasFocus()) tvDate.requestFocus();
279                     switch (item.getFocusedElement()) {
280                         case TransactionComment:
281                             b.transactionComment.setVisibility(View.VISIBLE);
282                             b.transactionComment.requestFocus();
283                             break;
284                         case Description:
285                             boolean focused = b.newTransactionDescription.requestFocus();
286 //                            tvDescription.dismissDropDown();
287                             if (focused)
288                                 Misc.showSoftKeyboard((NewTransactionActivity) b.getRoot()
289                                                                                 .getContext());
290                             break;
291                     }
292                     break;
293                 case transactionRow:
294                     switch (item.getFocusedElement()) {
295                         case Amount:
296                             b.accountRowAccAmounts.requestFocus();
297                             break;
298                         case Comment:
299                             b.comment.setVisibility(View.VISIBLE);
300                             b.comment.requestFocus();
301                             break;
302                         case Account:
303                             boolean focused = b.accountRowAccName.requestFocus();
304                             b.accountRowAccName.dismissDropDown();
305                             if (focused)
306                                 Misc.showSoftKeyboard((NewTransactionActivity) b.getRoot()
307                                                                                 .getContext());
308                             break;
309                     }
310
311                     break;
312             }
313         };
314         accountCountObserver = count -> {
315             final int adapterPosition = getAdapterPosition();
316             final int layoutPosition = getLayoutPosition();
317             Logger.debug("holder",
318                     String.format(Locale.US, "count=%d; pos=%d, layoutPos=%d [%s]", count,
319                             adapterPosition, layoutPosition, item.getType()
320                                                                  .toString()
321                                                                  .concat(item.getType() ==
322                                                                          ItemType.transactionRow
323                                                                          ? String.format(Locale.US,
324                                                                          "'%s'=%s",
325                                                                          item.getAccount()
326                                                                              .getAccountName(),
327                                                                          item.getAccount()
328                                                                              .isAmountSet()
329                                                                          ? String.format(Locale.US,
330                                                                                  "%.2f",
331                                                                                  item.getAccount()
332                                                                                      .getAmount())
333                                                                          : "unset") : "")));
334             if (adapterPosition == count)
335                 b.accountRowAccAmounts.setImeOptions(EditorInfo.IME_ACTION_DONE);
336             else
337                 b.accountRowAccAmounts.setImeOptions(EditorInfo.IME_ACTION_NEXT);
338         };
339
340         currencyObserver = currency -> {
341             setCurrency(currency);
342             adapter.checkTransactionSubmittable();
343         };
344
345         currencyGapObserver =
346                 hasGap -> updateCurrencyPositionAndPadding(Data.currencySymbolPosition.getValue(),
347                         hasGap);
348
349         currencyPositionObserver =
350                 position -> updateCurrencyPositionAndPadding(position, Data.currencyGap.getValue());
351
352         showCurrencyObserver = showCurrency -> {
353             if (showCurrency) {
354                 b.currency.setVisibility(View.VISIBLE);
355                 b.currencyButton.setVisibility(View.VISIBLE);
356                 String defaultCommodity = mProfile.getDefaultCommodity();
357                 item.setCurrency(
358                         (defaultCommodity == null) ? null : Currency.loadByName(defaultCommodity));
359             }
360             else {
361                 b.currency.setVisibility(View.GONE);
362                 b.currencyButton.setVisibility(View.GONE);
363                 item.setCurrency(null);
364             }
365         };
366
367         commentObserver = comment -> {
368             final View focusedView = b.comment.findFocus();
369             b.comment.setTypeface(null,
370                     (focusedView == b.comment) ? Typeface.NORMAL : Typeface.ITALIC);
371             b.comment.setVisibility(
372                     ((focusedView != b.comment) && TextUtils.isEmpty(comment)) ? View.INVISIBLE
373                                                                                : View.VISIBLE);
374         };
375
376         showCommentsObserver = show -> {
377             ConstraintLayout.LayoutParams amountLayoutParams =
378                     (ConstraintLayout.LayoutParams) b.amountLayout.getLayoutParams();
379             ConstraintLayout.LayoutParams accountParams =
380                     (ConstraintLayout.LayoutParams) b.accountRowAccName.getLayoutParams();
381             if (show) {
382                 accountParams.endToStart = ConstraintLayout.LayoutParams.UNSET;
383                 accountParams.endToEnd = ConstraintLayout.LayoutParams.PARENT_ID;
384
385                 amountLayoutParams.topToTop = ConstraintLayout.LayoutParams.UNSET;
386                 amountLayoutParams.topToBottom = b.accountRowAccName.getId();
387
388                 b.commentLayout.setVisibility(View.VISIBLE);
389             }
390             else {
391                 accountParams.endToStart = b.amountLayout.getId();
392                 accountParams.endToEnd = ConstraintLayout.LayoutParams.UNSET;
393
394                 amountLayoutParams.topToBottom = ConstraintLayout.LayoutParams.UNSET;
395                 amountLayoutParams.topToTop = ConstraintLayout.LayoutParams.PARENT_ID;
396
397                 b.commentLayout.setVisibility(View.GONE);
398             }
399
400             b.accountRowAccName.setLayoutParams(accountParams);
401             b.amountLayout.setLayoutParams(amountLayoutParams);
402
403             b.transactionCommentLayout.setVisibility(show ? View.VISIBLE : View.GONE);
404         };
405
406         amountValidityObserver = valid -> {
407             b.accountRowAccAmounts.setCompoundDrawablesRelativeWithIntrinsicBounds(
408                     valid ? 0 : R.drawable.ic_error_outline_black_24dp, 0, 0, 0);
409             b.accountRowAccAmounts.setMinEms(valid ? 4 : 5);
410         };
411     }
412     private void commentFocusChanged(TextView textView, boolean hasFocus) {
413         @ColorInt int textColor;
414         textColor = b.dummyText.getTextColors()
415                                .getDefaultColor();
416         if (hasFocus) {
417             textView.setTypeface(null, Typeface.NORMAL);
418             textView.setHint(R.string.transaction_account_comment_hint);
419         }
420         else {
421             int alpha = (textColor >> 24 & 0xff);
422             alpha = 3 * alpha / 4;
423             textColor = (alpha << 24) | (0x00ffffff & textColor);
424             textView.setTypeface(null, Typeface.ITALIC);
425             textView.setHint("");
426             if (TextUtils.isEmpty(textView.getText())) {
427                 textView.setVisibility(View.INVISIBLE);
428             }
429         }
430         textView.setTextColor(textColor);
431
432     }
433     private void updateCurrencyPositionAndPadding(Currency.Position position, boolean hasGap) {
434         ConstraintLayout.LayoutParams amountLP =
435                 (ConstraintLayout.LayoutParams) b.accountRowAccAmounts.getLayoutParams();
436         ConstraintLayout.LayoutParams currencyLP =
437                 (ConstraintLayout.LayoutParams) b.currency.getLayoutParams();
438
439         if (position == Currency.Position.before) {
440             currencyLP.startToStart = ConstraintLayout.LayoutParams.PARENT_ID;
441             currencyLP.endToEnd = ConstraintLayout.LayoutParams.UNSET;
442
443             amountLP.endToEnd = ConstraintLayout.LayoutParams.PARENT_ID;
444             amountLP.endToStart = ConstraintLayout.LayoutParams.UNSET;
445             amountLP.startToStart = ConstraintLayout.LayoutParams.UNSET;
446             amountLP.startToEnd = b.currency.getId();
447
448             b.currency.setGravity(Gravity.END);
449         }
450         else {
451             currencyLP.startToStart = ConstraintLayout.LayoutParams.UNSET;
452             currencyLP.endToEnd = ConstraintLayout.LayoutParams.PARENT_ID;
453
454             amountLP.startToStart = ConstraintLayout.LayoutParams.PARENT_ID;
455             amountLP.startToEnd = ConstraintLayout.LayoutParams.UNSET;
456             amountLP.endToEnd = ConstraintLayout.LayoutParams.UNSET;
457             amountLP.endToStart = b.currency.getId();
458
459             b.currency.setGravity(Gravity.START);
460         }
461
462         amountLP.resolveLayoutDirection(b.accountRowAccAmounts.getLayoutDirection());
463         currencyLP.resolveLayoutDirection(b.currency.getLayoutDirection());
464
465         b.accountRowAccAmounts.setLayoutParams(amountLP);
466         b.currency.setLayoutParams(currencyLP);
467
468         // distance between the amount and the currency symbol
469         int gapSize = DimensionUtils.sp2px(b.currency.getContext(), 5);
470
471         if (position == Currency.Position.before) {
472             b.currency.setPaddingRelative(0, 0, hasGap ? gapSize : 0, 0);
473         }
474         else {
475             b.currency.setPaddingRelative(hasGap ? gapSize : 0, 0, 0, 0);
476         }
477     }
478     private void setCurrencyString(String currency) {
479         @ColorInt int textColor = b.dummyText.getTextColors()
480                                              .getDefaultColor();
481         if ((currency == null) || currency.isEmpty()) {
482             b.currency.setText(R.string.currency_symbol);
483             int alpha = (textColor >> 24) & 0xff;
484             alpha = alpha * 3 / 4;
485             b.currency.setTextColor((alpha << 24) | (0x00ffffff & textColor));
486         }
487         else {
488             b.currency.setText(currency);
489             b.currency.setTextColor(textColor);
490         }
491     }
492     private void setCurrency(Currency currency) {
493         setCurrencyString((currency == null) ? null : currency.getName());
494     }
495     private void setEditable(Boolean editable) {
496         b.newTransactionDate.setEnabled(editable);
497         b.newTransactionDescription.setEnabled(editable);
498         b.accountRowAccName.setEnabled(editable);
499         b.accountRowAccAmounts.setEnabled(editable);
500     }
501     private void beginUpdates() {
502         if (inUpdate)
503             throw new RuntimeException("Already in update mode");
504         inUpdate = true;
505     }
506     private void endUpdates() {
507         if (!inUpdate)
508             throw new RuntimeException("Not in update mode");
509         inUpdate = false;
510     }
511     /**
512      * syncData()
513      * <p>
514      * Stores the data from the UI elements into the model item
515      * Returns true if there were changes made that suggest transaction has to be
516      * checked for being submittable
517      */
518     private boolean syncData() {
519         if (item == null)
520             return false;
521
522         if (syncingData) {
523             Logger.debug("new-trans", "skipping syncData() loop");
524             return false;
525         }
526
527         syncingData = true;
528
529         try {
530             switch (item.getType()) {
531                 case generalData:
532                     item.setDate(String.valueOf(b.newTransactionDate.getText()));
533                     item.setDescription(String.valueOf(b.newTransactionDescription.getText()));
534                     item.setTransactionComment(String.valueOf(b.transactionComment.getText()));
535                     break;
536                 case transactionRow:
537                     final LedgerTransactionAccount account = item.getAccount();
538                     account.setAccountName(String.valueOf(b.accountRowAccName.getText()));
539
540                     item.setComment(String.valueOf(b.comment.getText()));
541
542                     String amount = String.valueOf(b.accountRowAccAmounts.getText());
543                     amount = amount.trim();
544
545                     if (amount.isEmpty()) {
546                         account.resetAmount();
547                         item.validateAmount();
548                     }
549                     else {
550                         try {
551                             amount = amount.replace(decimalSeparator, decimalDot);
552                             account.setAmount(Float.parseFloat(amount));
553                             item.validateAmount();
554                         }
555                         catch (NumberFormatException e) {
556                             Logger.debug("new-trans", String.format(
557                                     "assuming amount is not set due to number format exception. " +
558                                     "input was '%s'", amount));
559                             account.invalidateAmount();
560                             item.invalidateAmount();
561                         }
562                         final String curr = String.valueOf(b.currency.getText());
563                         if (curr.equals(b.currency.getContext()
564                                                   .getResources()
565                                                   .getString(R.string.currency_symbol)) ||
566                             curr.isEmpty())
567                             account.setCurrency(null);
568                         else
569                             account.setCurrency(curr);
570                     }
571
572                     break;
573                 case bottomFiller:
574                     throw new RuntimeException("Should not happen");
575             }
576
577             return true;
578         }
579         catch (ParseException e) {
580             throw new RuntimeException("Should not happen", e);
581         }
582         finally {
583             syncingData = false;
584         }
585     }
586     private void pickTransactionDate() {
587         DatePickerFragment picker = new DatePickerFragment();
588         picker.setFutureDates(mProfile.getFutureDates());
589         picker.setOnDatePickedListener(this);
590         picker.setCurrentDateFromText(b.newTransactionDate.getText());
591         picker.show(((NewTransactionActivity) b.getRoot()
592                                                .getContext()).getSupportFragmentManager(), null);
593     }
594     /**
595      * setData
596      *
597      * @param item updates the UI elements with the data from the model item
598      */
599     @SuppressLint("DefaultLocale")
600     public void setData(NewTransactionModel.Item item) {
601         beginUpdates();
602         try {
603             if (this.item != null && !this.item.equals(item)) {
604                 this.item.stopObservingDate(dateObserver);
605                 this.item.stopObservingDescription(descriptionObserver);
606                 this.item.stopObservingTransactionComment(transactionCommentObserver);
607                 this.item.stopObservingAmountHint(hintObserver);
608                 this.item.stopObservingEditableFlag(editableObserver);
609                 this.item.getModel()
610                          .stopObservingFocusedItem(focusedAccountObserver);
611                 this.item.getModel()
612                          .stopObservingAccountCount(accountCountObserver);
613                 Data.currencySymbolPosition.removeObserver(currencyPositionObserver);
614                 Data.currencyGap.removeObserver(currencyGapObserver);
615                 Data.locale.removeObserver(localeObserver);
616                 this.item.stopObservingCurrency(currencyObserver);
617                 this.item.getModel().showCurrency.removeObserver(showCurrencyObserver);
618                 this.item.stopObservingComment(commentObserver);
619                 this.item.getModel().showComments.removeObserver(showCommentsObserver);
620                 this.item.stopObservingAmountValidity(amountValidityObserver);
621
622                 this.item = null;
623             }
624
625             switch (item.getType()) {
626                 case generalData:
627                     b.newTransactionDate.setText(item.getFormattedDate());
628                     b.newTransactionDescription.setText(item.getDescription());
629                     b.transactionComment.setText(item.getTransactionComment());
630                     b.ntrData.setVisibility(View.VISIBLE);
631                     b.ntrAccount.setVisibility(View.GONE);
632                     b.ntrPadding.setVisibility(View.GONE);
633                     setEditable(true);
634                     break;
635                 case transactionRow:
636                     LedgerTransactionAccount acc = item.getAccount();
637                     b.accountRowAccName.setText(acc.getAccountName());
638                     b.comment.setText(acc.getComment());
639                     if (acc.isAmountSet()) {
640                         b.accountRowAccAmounts.setText(String.format("%1.2f", acc.getAmount()));
641                     }
642                     else {
643                         b.accountRowAccAmounts.setText("");
644 //                        tvAmount.setHint(R.string.zero_amount);
645                     }
646                     b.accountRowAccAmounts.setHint(item.getAmountHint());
647                     setCurrencyString(acc.getCurrency());
648                     b.ntrData.setVisibility(View.GONE);
649                     b.ntrAccount.setVisibility(View.VISIBLE);
650                     b.ntrPadding.setVisibility(View.GONE);
651                     setEditable(true);
652                     break;
653                 case bottomFiller:
654                     b.ntrData.setVisibility(View.GONE);
655                     b.ntrAccount.setVisibility(View.GONE);
656                     b.ntrPadding.setVisibility(View.VISIBLE);
657                     setEditable(false);
658                     break;
659             }
660             if (this.item == null) { // was null or has changed
661                 this.item = item;
662                 final NewTransactionActivity activity = (NewTransactionActivity) b.getRoot()
663                                                                                   .getContext();
664
665                 if (!item.isBottomFiller()) {
666                     item.observeEditableFlag(activity, editableObserver);
667                     item.getModel()
668                         .observeFocusedItem(activity, focusedAccountObserver);
669                     item.getModel()
670                         .observeShowComments(activity, showCommentsObserver);
671                 }
672                 switch (item.getType()) {
673                     case generalData:
674                         item.observeDate(activity, dateObserver);
675                         item.observeDescription(activity, descriptionObserver);
676                         item.observeTransactionComment(activity, transactionCommentObserver);
677                         break;
678                     case transactionRow:
679                         item.observeAmountHint(activity, hintObserver);
680                         Data.currencySymbolPosition.observe(activity, currencyPositionObserver);
681                         Data.currencyGap.observe(activity, currencyGapObserver);
682                         Data.locale.observe(activity, localeObserver);
683                         item.observeCurrency(activity, currencyObserver);
684                         item.getModel().showCurrency.observe(activity, showCurrencyObserver);
685                         item.observeComment(activity, commentObserver);
686                         item.getModel()
687                             .observeAccountCount(activity, accountCountObserver);
688                         item.observeAmountValidity(activity, amountValidityObserver);
689                         break;
690                 }
691             }
692         }
693         finally {
694             endUpdates();
695         }
696     }
697     @Override
698     public void onDatePicked(int year, int month, int day) {
699         item.setDate(new SimpleDate(year, month + 1, day));
700         boolean focused = b.newTransactionDescription.requestFocus();
701         if (focused)
702             Misc.showSoftKeyboard((NewTransactionActivity) b.getRoot()
703                                                             .getContext());
704
705     }
706     @Override
707     public void descriptionSelected(String description) {
708         b.accountRowAccName.setText(description);
709         b.accountRowAccAmounts.requestFocus(View.FOCUS_FORWARD);
710     }
711 }