]> git.ktnx.net Git - mobile-ledger.git/blob - app/src/main/java/net/ktnx/mobileledger/ui/activity/MainActivity.java
first step towards pattern-assisted auto-filling of transactions
[mobile-ledger.git] / app / src / main / java / net / ktnx / mobileledger / ui / activity / MainActivity.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.content.Intent;
21 import android.content.pm.PackageInfo;
22 import android.content.pm.ShortcutInfo;
23 import android.content.pm.ShortcutManager;
24 import android.content.res.ColorStateList;
25 import android.graphics.Color;
26 import android.graphics.drawable.Icon;
27 import android.os.Build;
28 import android.os.Bundle;
29 import android.text.format.DateUtils;
30 import android.util.Log;
31 import android.view.View;
32 import android.view.animation.AnimationUtils;
33 import android.widget.TextView;
34
35 import androidx.annotation.NonNull;
36 import androidx.appcompat.app.ActionBarDrawerToggle;
37 import androidx.appcompat.app.AlertDialog;
38 import androidx.core.view.GravityCompat;
39 import androidx.drawerlayout.widget.DrawerLayout;
40 import androidx.fragment.app.Fragment;
41 import androidx.fragment.app.FragmentActivity;
42 import androidx.lifecycle.ViewModelProvider;
43 import androidx.recyclerview.widget.LinearLayoutManager;
44 import androidx.recyclerview.widget.RecyclerView;
45 import androidx.viewpager2.adapter.FragmentStateAdapter;
46 import androidx.viewpager2.widget.ViewPager2;
47
48 import com.google.android.material.snackbar.Snackbar;
49
50 import net.ktnx.mobileledger.R;
51 import net.ktnx.mobileledger.async.RetrieveTransactionsTask;
52 import net.ktnx.mobileledger.databinding.ActivityMainBinding;
53 import net.ktnx.mobileledger.model.Data;
54 import net.ktnx.mobileledger.model.MobileLedgerProfile;
55 import net.ktnx.mobileledger.ui.MainModel;
56 import net.ktnx.mobileledger.ui.account_summary.AccountSummaryFragment;
57 import net.ktnx.mobileledger.ui.profiles.ProfilesRecyclerViewAdapter;
58 import net.ktnx.mobileledger.ui.transaction_list.TransactionListFragment;
59 import net.ktnx.mobileledger.utils.Colors;
60 import net.ktnx.mobileledger.utils.Logger;
61 import net.ktnx.mobileledger.utils.MLDB;
62
63 import org.jetbrains.annotations.NotNull;
64
65 import java.util.ArrayList;
66 import java.util.Date;
67 import java.util.List;
68 import java.util.Locale;
69 import java.util.Objects;
70
71 /*
72  * TODO: reports
73  *  */
74
75 public class MainActivity extends ProfileThemedActivity {
76     public static final String STATE_CURRENT_PAGE = "current_page";
77     public static final String BUNDLE_SAVED_STATE = "bundle_savedState";
78     public static final String STATE_ACC_FILTER = "account_filter";
79     private SectionsPagerAdapter mSectionsPagerAdapter;
80     private ProfilesRecyclerViewAdapter mProfileListAdapter;
81     private int mCurrentPage;
82     private boolean mBackMeansToAccountList = false;
83     private DrawerLayout.SimpleDrawerListener drawerListener;
84     private ActionBarDrawerToggle barDrawerToggle;
85     private ViewPager2.OnPageChangeCallback pageChangeCallback;
86     private MobileLedgerProfile profile;
87     private MainModel mainModel;
88     private ActivityMainBinding b;
89     @Override
90     protected void onStart() {
91         super.onStart();
92
93         Logger.debug("MainActivity", "onStart()");
94
95         b.mainPager.setCurrentItem(mCurrentPage, false);
96     }
97     @Override
98     protected void onSaveInstanceState(@NotNull Bundle outState) {
99         super.onSaveInstanceState(outState);
100         outState.putInt(STATE_CURRENT_PAGE, b.mainPager.getCurrentItem());
101         if (mainModel.getAccountFilter()
102                      .getValue() != null)
103             outState.putString(STATE_ACC_FILTER, mainModel.getAccountFilter()
104                                                           .getValue());
105     }
106     @Override
107     protected void onDestroy() {
108         mSectionsPagerAdapter = null;
109         b.navProfileList.setAdapter(null);
110         b.drawerLayout.removeDrawerListener(drawerListener);
111         drawerListener = null;
112         b.drawerLayout.removeDrawerListener(barDrawerToggle);
113         barDrawerToggle = null;
114         b.mainPager.unregisterOnPageChangeCallback(pageChangeCallback);
115         pageChangeCallback = null;
116         super.onDestroy();
117     }
118     @Override
119     protected void onResume() {
120         super.onResume();
121         fabShouldShow();
122     }
123     @Override
124     protected void onCreate(Bundle savedInstanceState) {
125         Logger.debug("MainActivity", "onCreate()/entry");
126         super.onCreate(savedInstanceState);
127         Logger.debug("MainActivity", "onCreate()/after super");
128         b = ActivityMainBinding.inflate(getLayoutInflater());
129         setContentView(b.getRoot());
130
131         mainModel = new ViewModelProvider(this).get(MainModel.class);
132
133         mSectionsPagerAdapter = new SectionsPagerAdapter(this);
134
135         Bundle extra = getIntent().getBundleExtra(BUNDLE_SAVED_STATE);
136         if (extra != null && savedInstanceState == null)
137             savedInstanceState = extra;
138
139
140         setSupportActionBar(b.toolbar);
141
142         Data.observeProfile(this, this::onProfileChanged);
143
144         Data.profiles.observe(this, this::onProfileListChanged);
145         Data.backgroundTaskProgress.observe(this, this::onRetrieveProgress);
146         Data.backgroundTasksRunning.observe(this, this::onRetrieveRunningChanged);
147
148         if (barDrawerToggle == null) {
149             barDrawerToggle = new ActionBarDrawerToggle(this, b.drawerLayout, b.toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
150             b.drawerLayout.addDrawerListener(barDrawerToggle);
151         }
152         barDrawerToggle.syncState();
153
154         try {
155             PackageInfo pi = getApplicationContext().getPackageManager()
156                                                     .getPackageInfo(getPackageName(), 0);
157             ((TextView) b.navUpper.findViewById(R.id.drawer_version_text)).setText(pi.versionName);
158             ((TextView) b.noProfilesLayout.findViewById(R.id.drawer_version_text)).setText(pi.versionName);
159         }
160         catch (Exception e) {
161             e.printStackTrace();
162         }
163
164         markDrawerItemCurrent(R.id.nav_account_summary);
165
166         b.mainPager.setAdapter(mSectionsPagerAdapter);
167
168         if (pageChangeCallback == null) {
169             pageChangeCallback = new ViewPager2.OnPageChangeCallback() {
170                 @Override
171                 public void onPageSelected(int position) {
172                     mCurrentPage = position;
173                     switch (position) {
174                         case 0:
175                             markDrawerItemCurrent(R.id.nav_account_summary);
176                             break;
177                         case 1:
178                             markDrawerItemCurrent(R.id.nav_latest_transactions);
179                             break;
180                         default:
181                             Log.e("MainActivity",
182                                     String.format("Unexpected page index %d", position));
183                     }
184
185                     super.onPageSelected(position);
186                 }
187             };
188             b.mainPager.registerOnPageChangeCallback(pageChangeCallback);
189         }
190
191         mCurrentPage = 0;
192         if (savedInstanceState != null) {
193             int currentPage = savedInstanceState.getInt(STATE_CURRENT_PAGE, -1);
194             if (currentPage != -1) {
195                 mCurrentPage = currentPage;
196             }
197             mainModel.getAccountFilter()
198                      .setValue(savedInstanceState.getString(STATE_ACC_FILTER, null));
199         }
200
201         b.btnNoProfilesAdd.setOnClickListener(v -> MobileLedgerProfile.startEditProfileActivity(this, null));
202
203         b.btnAddTransaction.setOnClickListener(this::fabNewTransactionClicked);
204
205         b.navNewProfileButton.setOnClickListener(v -> MobileLedgerProfile.startEditProfileActivity(this, null));
206
207         b.transactionListCancelDownload.setOnClickListener(this::onStopTransactionRefreshClick);
208
209         if (mProfileListAdapter == null)
210             mProfileListAdapter = new ProfilesRecyclerViewAdapter();
211         b.navProfileList.setAdapter(mProfileListAdapter);
212
213         mProfileListAdapter.editingProfiles.observe(this, newValue -> {
214             if (newValue) {
215                 b.navProfilesStartEdit.setVisibility(View.GONE);
216                 b.navProfilesCancelEdit.setVisibility(View.VISIBLE);
217                 b.navNewProfileButton.setVisibility(View.VISIBLE);
218                 if (b.drawerLayout.isDrawerOpen(GravityCompat.START)) {
219                     b.navProfilesStartEdit.startAnimation(
220                             AnimationUtils.loadAnimation(MainActivity.this, R.anim.fade_out));
221                     b.navProfilesCancelEdit.startAnimation(
222                             AnimationUtils.loadAnimation(MainActivity.this, R.anim.fade_in));
223                     b.navNewProfileButton.startAnimation(
224                             AnimationUtils.loadAnimation(MainActivity.this, R.anim.fade_in));
225                 }
226             }
227             else {
228                 b.navProfilesCancelEdit.setVisibility(View.GONE);
229                 b.navProfilesStartEdit.setVisibility(View.VISIBLE);
230                 b.navNewProfileButton.setVisibility(View.GONE);
231                 if (b.drawerLayout.isDrawerOpen(GravityCompat.START)) {
232                     b.navProfilesCancelEdit.startAnimation(
233                             AnimationUtils.loadAnimation(MainActivity.this, R.anim.fade_out));
234                     b.navProfilesStartEdit.startAnimation(
235                             AnimationUtils.loadAnimation(MainActivity.this, R.anim.fade_in));
236                     b.navNewProfileButton.startAnimation(
237                             AnimationUtils.loadAnimation(MainActivity.this, R.anim.fade_out));
238                 }
239             }
240
241             mProfileListAdapter.notifyDataSetChanged();
242         });
243
244         LinearLayoutManager llm = new LinearLayoutManager(this);
245
246         llm.setOrientation(RecyclerView.VERTICAL);
247         b.navProfileList.setLayoutManager(llm);
248
249         b.navProfilesStartEdit.setOnClickListener((v) -> mProfileListAdapter.flipEditingProfiles());
250         b.navProfilesCancelEdit.setOnClickListener((v) -> mProfileListAdapter.flipEditingProfiles());
251         b.navProfileListHeadButtons.setOnClickListener((v) -> mProfileListAdapter.flipEditingProfiles());
252         if (drawerListener == null) {
253             drawerListener = new DrawerLayout.SimpleDrawerListener() {
254                 @Override
255                 public void onDrawerSlide(@NonNull View drawerView, float slideOffset) {
256                     if (slideOffset > 0.2)
257                         fabHide();
258                 }
259                 @Override
260                 public void onDrawerClosed(View drawerView) {
261                     super.onDrawerClosed(drawerView);
262                     mProfileListAdapter.setAnimationsEnabled(false);
263                     mProfileListAdapter.editingProfiles.setValue(false);
264                     Data.drawerOpen.setValue(false);
265                     fabShouldShow();
266                 }
267                 @Override
268                 public void onDrawerOpened(View drawerView) {
269                     super.onDrawerOpened(drawerView);
270                     mProfileListAdapter.setAnimationsEnabled(true);
271                     Data.drawerOpen.setValue(true);
272                     fabHide();
273                 }
274             };
275             b.drawerLayout.addDrawerListener(drawerListener);
276         }
277
278         Data.drawerOpen.observe(this, open -> {
279             if (open)
280                 b.drawerLayout.open();
281             else
282                 b.drawerLayout.close();
283         });
284
285         mainModel.getUpdateError()
286                  .observe(this, (error) -> {
287                      if (error == null)
288                          return;
289
290                      Snackbar.make(b.mainPager, error, Snackbar.LENGTH_LONG)
291                              .show();
292                      mainModel.clearUpdateError();
293                  });
294         Data.locale.observe(this, l -> refreshLastUpdateInfo());
295         Data.lastUpdateDate.observe(this, date -> refreshLastUpdateInfo());
296         Data.lastUpdateTransactionCount.observe(this, date -> refreshLastUpdateInfo());
297         Data.lastUpdateAccountCount.observe(this, date -> refreshLastUpdateInfo());
298         b.navAccountSummary.setOnClickListener(this::onAccountSummaryClicked);
299         b.navLatestTransactions.setOnClickListener(this::onLatestTransactionsClicked);
300         b.navPatterns.setOnClickListener(this::onPatternsClick);
301     }
302     private void onPatternsClick(View view) {
303         Intent intent = new Intent(this, PatternsActivity.class);
304         startActivity(intent);
305     }
306     private void scheduleDataRetrievalIfStale(long lastUpdate) {
307         long now = new Date().getTime();
308         if ((lastUpdate == 0) || (now > (lastUpdate + (24 * 3600 * 1000)))) {
309             if (lastUpdate == 0)
310                 Logger.debug("db::", "WEB data never fetched. scheduling a fetch");
311             else
312                 Logger.debug("db", String.format(Locale.ENGLISH,
313                         "WEB data last fetched at %1.3f and now is %1.3f. re-fetching",
314                         lastUpdate / 1000f, now / 1000f));
315
316             mainModel.scheduleTransactionListRetrieval();
317         }
318     }
319     private void createShortcuts(List<MobileLedgerProfile> list) {
320         if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N_MR1)
321             return;
322
323         ShortcutManager sm = getSystemService(ShortcutManager.class);
324         List<ShortcutInfo> shortcuts = new ArrayList<>();
325         int i = 0;
326         for (MobileLedgerProfile p : list) {
327             if (shortcuts.size() >= sm.getMaxShortcutCountPerActivity())
328                 break;
329
330             if (!p.isPostingPermitted())
331                 continue;
332
333             final ShortcutInfo.Builder builder =
334                     new ShortcutInfo.Builder(this, "new_transaction_" + p.getUuid());
335             ShortcutInfo si = builder.setShortLabel(p.getName())
336                                      .setIcon(Icon.createWithResource(this,
337                                              R.drawable.thick_plus_icon))
338                                      .setIntent(new Intent(Intent.ACTION_VIEW, null, this,
339                                              NewTransactionActivity.class).putExtra("profile_uuid",
340                                              p.getUuid()))
341                                      .setRank(i)
342                                      .build();
343             shortcuts.add(si);
344             i++;
345         }
346         sm.setDynamicShortcuts(shortcuts);
347     }
348     private void onProfileListChanged(List<MobileLedgerProfile> newList) {
349         if ((newList == null) || newList.isEmpty()) {
350             b.noProfilesLayout.setVisibility(View.VISIBLE);
351             b.mainAppLayout.setVisibility(View.GONE);
352             return;
353         }
354
355         b.mainAppLayout.setVisibility(View.VISIBLE);
356         b.noProfilesLayout.setVisibility(View.GONE);
357
358         b.navProfileList.setMinimumHeight(
359                 (int) (getResources().getDimension(R.dimen.thumb_row_height) * newList.size()));
360
361         Logger.debug("profiles", "profile list changed");
362         mProfileListAdapter.notifyDataSetChanged();
363
364         createShortcuts(newList);
365     }
366     /**
367      * called when the current profile has changed
368      */
369     private void onProfileChanged(MobileLedgerProfile profile) {
370         if (this.profile == null) {
371             if (profile == null)
372                 return;
373         }
374         else {
375             if (this.profile.equals(profile))
376                 return;
377         }
378
379         boolean haveProfile = profile != null;
380
381         if (haveProfile)
382             setTitle(profile.getName());
383         else
384             setTitle(R.string.app_name);
385
386         mainModel.setProfile(profile);
387
388         this.profile = profile;
389
390         int newProfileTheme = haveProfile ? profile.getThemeHue() : -1;
391         if (newProfileTheme != Colors.profileThemeId) {
392             Logger.debug("profiles",
393                     String.format(Locale.ENGLISH, "profile theme %d → %d", Colors.profileThemeId,
394                             newProfileTheme));
395             Colors.profileThemeId = newProfileTheme;
396             profileThemeChanged();
397             // profileThemeChanged would restart the activity, so no need to reload the
398             // data sets below
399             return;
400         }
401
402         b.noProfilesLayout.setVisibility(haveProfile ? View.GONE : View.VISIBLE);
403         b.pagerLayout.setVisibility(haveProfile ? View.VISIBLE : View.VISIBLE);
404
405         mProfileListAdapter.notifyDataSetChanged();
406
407         mainModel.clearAccounts();
408         mainModel.clearTransactions();
409
410         if (haveProfile) {
411             mainModel.scheduleAccountListReload();
412             Logger.debug("transactions", "requesting list reload");
413             mainModel.scheduleTransactionListReload();
414
415             if (profile.isPostingPermitted()) {
416                 b.toolbar.setSubtitle(null);
417                 b.btnAddTransaction.show();
418             }
419             else {
420                 b.toolbar.setSubtitle(R.string.profile_subtitle_read_only);
421                 b.btnAddTransaction.hide();
422             }
423         }
424         else {
425             b.toolbar.setSubtitle(null);
426             b.btnAddTransaction.hide();
427         }
428
429         updateLastUpdateTextFromDB();
430     }
431     private void profileThemeChanged() {
432         // un-hook all observed LiveData
433         Data.removeProfileObservers(this);
434         Data.profiles.removeObservers(this);
435         Data.lastUpdateTransactionCount.removeObservers(this);
436         Data.lastUpdateAccountCount.removeObservers(this);
437         Data.lastUpdateDate.removeObservers(this);
438
439         recreate();
440     }
441     public void fabNewTransactionClicked(View view) {
442         Intent intent = new Intent(this, NewTransactionActivity.class);
443         startActivity(intent);
444         overridePendingTransition(R.anim.slide_in_up, R.anim.dummy);
445     }
446     public void markDrawerItemCurrent(int id) {
447         TextView item = b.drawerLayout.findViewById(id);
448         item.setBackgroundColor(Colors.tableRowDarkBG);
449
450         for (int i = 0; i < b.navActions.getChildCount(); i++) {
451             View view = b.navActions.getChildAt(i);
452             if (view.getId() != id) {
453                 view.setBackgroundColor(Color.TRANSPARENT);
454             }
455         }
456     }
457     public void onAccountSummaryClicked(View view) {
458         b.drawerLayout.closeDrawers();
459
460         showAccountSummaryFragment();
461     }
462     private void showAccountSummaryFragment() {
463         b.mainPager.setCurrentItem(0, true);
464         mainModel.getAccountFilter()
465                  .setValue(null);
466     }
467     public void onLatestTransactionsClicked(View view) {
468         b.drawerLayout.closeDrawers();
469
470         showTransactionsFragment(null);
471     }
472     public void showTransactionsFragment(String accName) {
473         mainModel.getAccountFilter()
474                  .setValue(accName);
475         b.mainPager.setCurrentItem(1, true);
476     }
477     public void showAccountTransactions(String accountName) {
478         mBackMeansToAccountList = true;
479         showTransactionsFragment(accountName);
480     }
481     @Override
482     public void onBackPressed() {
483         if (b.drawerLayout.isDrawerOpen(GravityCompat.START)) {
484             b.drawerLayout.closeDrawer(GravityCompat.START);
485         }
486         else {
487             if (mBackMeansToAccountList && (b.mainPager.getCurrentItem() == 1)) {
488                 mainModel.getAccountFilter()
489                          .setValue(null);
490                 showAccountSummaryFragment();
491                 mBackMeansToAccountList = false;
492             }
493             else {
494                 Logger.debug("fragments", String.format(Locale.ENGLISH, "manager stack: %d",
495                         getSupportFragmentManager().getBackStackEntryCount()));
496
497                 super.onBackPressed();
498             }
499         }
500     }
501     public void updateLastUpdateTextFromDB() {
502         if (profile == null)
503             return;
504
505         long lastUpdate = profile.getLongOption(MLDB.OPT_LAST_SCRAPE, 0L);
506
507         Logger.debug("transactions", String.format(Locale.ENGLISH, "Last update = %d", lastUpdate));
508         if (lastUpdate == 0) {
509             Data.lastUpdateDate.postValue(null);
510         }
511         else {
512             Data.lastUpdateDate.postValue(new Date(lastUpdate));
513         }
514
515         scheduleDataRetrievalIfStale(lastUpdate);
516
517     }
518     private void refreshLastUpdateInfo() {
519         final int formatFlags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR |
520                                 DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_NUMERIC_DATE;
521         String templateForTransactions =
522                 getResources().getString(R.string.transaction_count_summary);
523         String templateForAccounts = getResources().getString(R.string.account_count_summary);
524         Integer accountCount = Data.lastUpdateAccountCount.getValue();
525         Integer transactionCount = Data.lastUpdateTransactionCount.getValue();
526         Date lastUpdate = Data.lastUpdateDate.getValue();
527         if (lastUpdate == null) {
528             Data.lastTransactionsUpdateText.set("----");
529             Data.lastAccountsUpdateText.set("----");
530         }
531         else {
532             Data.lastTransactionsUpdateText.set(
533                     String.format(Objects.requireNonNull(Data.locale.getValue()),
534                             templateForTransactions,
535                             transactionCount == null ? 0 : transactionCount,
536                             DateUtils.formatDateTime(this, lastUpdate.getTime(), formatFlags)));
537             Data.lastAccountsUpdateText.set(
538                     String.format(Objects.requireNonNull(Data.locale.getValue()),
539                             templateForAccounts, accountCount == null ? 0 : accountCount,
540                             DateUtils.formatDateTime(this, lastUpdate.getTime(), formatFlags)));
541         }
542     }
543     public void onStopTransactionRefreshClick(View view) {
544         Logger.debug("interactive", "Cancelling transactions refresh");
545         mainModel.stopTransactionsRetrieval();
546         b.transactionListCancelDownload.setEnabled(false);
547     }
548     public void onRetrieveRunningChanged(Boolean running) {
549         if (running) {
550             b.transactionListCancelDownload.setEnabled(true);
551             ColorStateList csl = Colors.getColorStateList();
552             b.transactionListProgressBar.setIndeterminateTintList(csl);
553             b.transactionListProgressBar.setProgressTintList(csl);
554             b.transactionListProgressBar.setIndeterminate(true);
555             if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
556                 b.transactionListProgressBar.setProgress(0, false);
557             }
558             else {
559                 b.transactionListProgressBar.setProgress(0);
560             }
561             b.transactionProgressLayout.setVisibility(View.VISIBLE);
562         }
563         else {
564             b.transactionProgressLayout.setVisibility(View.GONE);
565         }
566     }
567     public void onRetrieveProgress(RetrieveTransactionsTask.Progress progress) {
568         if (progress.getState() == RetrieveTransactionsTask.ProgressState.FINISHED) {
569             Logger.debug("progress", "Done");
570             b.transactionProgressLayout.setVisibility(View.GONE);
571
572             mainModel.transactionRetrievalDone();
573
574             String error = progress.getError();
575             if (error != null) {
576                 if (error.equals(RetrieveTransactionsTask.Result.ERR_JSON_PARSER_ERROR))
577                     error = getResources().getString(R.string.err_json_parser_error);
578
579                 AlertDialog.Builder builder = new AlertDialog.Builder(this);
580                 builder.setMessage(error);
581                 builder.setPositiveButton(R.string.btn_profile_options, (dialog, which) -> {
582                     Logger.debug("error", "will start profile editor");
583                     MobileLedgerProfile.startEditProfileActivity(this, profile);
584                 });
585                 builder.create()
586                        .show();
587                 return;
588             }
589
590             return;
591         }
592
593
594         b.transactionListCancelDownload.setEnabled(true);
595 //        ColorStateList csl = Colors.getColorStateList();
596 //        progressBar.setIndeterminateTintList(csl);
597 //        progressBar.setProgressTintList(csl);
598 //        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
599 //            progressBar.setProgress(0, false);
600 //        else
601 //            progressBar.setProgress(0);
602         b.transactionProgressLayout.setVisibility(View.VISIBLE);
603
604         if (progress.isIndeterminate() || (progress.getTotal() <= 0)) {
605             b.transactionListProgressBar.setIndeterminate(true);
606             Logger.debug("progress", "indeterminate");
607         }
608         else {
609             if (b.transactionListProgressBar.isIndeterminate()) {
610                 b.transactionListProgressBar.setIndeterminate(false);
611             }
612 //            Logger.debug("progress",
613 //                    String.format(Locale.US, "%d/%d", progress.getProgress(), progress.getTotal
614 //                    ()));
615             b.transactionListProgressBar.setMax(progress.getTotal());
616             // for some reason animation doesn't work - no progress is shown (stick at 0)
617             // on lineageOS 14.1 (Nougat, 7.1.2)
618             if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
619                 b.transactionListProgressBar.setProgress(progress.getProgress(), false);
620             else
621                 b.transactionListProgressBar.setProgress(progress.getProgress());
622         }
623     }
624     public void fabShouldShow() {
625         if ((profile != null) && profile.isPostingPermitted() && !b.drawerLayout.isOpen())
626             b.btnAddTransaction.show();
627         else
628             fabHide();
629     }
630     public void fabHide() {
631         b.btnAddTransaction.hide();
632     }
633
634     public static class SectionsPagerAdapter extends FragmentStateAdapter {
635
636         public SectionsPagerAdapter(@NonNull FragmentActivity fragmentActivity) {
637             super(fragmentActivity);
638         }
639         @NotNull
640         @Override
641         public Fragment createFragment(int position) {
642             Logger.debug("main",
643                     String.format(Locale.ENGLISH, "Switching to fragment %d", position));
644             switch (position) {
645                 case 0:
646 //                    debug("flow", "Creating account summary fragment");
647                     return new AccountSummaryFragment();
648                 case 1:
649                     return new TransactionListFragment();
650                 default:
651                     throw new IllegalStateException(
652                             String.format("Unexpected fragment index: " + "%d", position));
653             }
654         }
655
656         @Override
657         public int getItemCount() {
658             return 2;
659         }
660     }
661 }