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.
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.
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/>.
18 package net.ktnx.mobileledger.ui.activity;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.content.pm.PackageInfo;
23 import android.content.pm.ShortcutInfo;
24 import android.content.pm.ShortcutManager;
25 import android.content.res.ColorStateList;
26 import android.graphics.Color;
27 import android.graphics.drawable.Icon;
28 import android.os.Build;
29 import android.os.Bundle;
30 import android.text.format.DateUtils;
31 import android.util.Log;
32 import android.view.View;
33 import android.view.animation.AnimationUtils;
34 import android.widget.TextView;
36 import androidx.annotation.NonNull;
37 import androidx.annotation.Nullable;
38 import androidx.appcompat.app.ActionBarDrawerToggle;
39 import androidx.appcompat.app.AlertDialog;
40 import androidx.core.view.GravityCompat;
41 import androidx.drawerlayout.widget.DrawerLayout;
42 import androidx.fragment.app.Fragment;
43 import androidx.fragment.app.FragmentActivity;
44 import androidx.lifecycle.ViewModelProvider;
45 import androidx.recyclerview.widget.LinearLayoutManager;
46 import androidx.recyclerview.widget.RecyclerView;
47 import androidx.viewpager2.adapter.FragmentStateAdapter;
48 import androidx.viewpager2.widget.ViewPager2;
50 import com.google.android.material.snackbar.Snackbar;
52 import net.ktnx.mobileledger.R;
53 import net.ktnx.mobileledger.async.RetrieveTransactionsTask;
54 import net.ktnx.mobileledger.databinding.ActivityMainBinding;
55 import net.ktnx.mobileledger.db.DB;
56 import net.ktnx.mobileledger.db.Option;
57 import net.ktnx.mobileledger.db.Profile;
58 import net.ktnx.mobileledger.model.Data;
59 import net.ktnx.mobileledger.ui.FabManager;
60 import net.ktnx.mobileledger.ui.MainModel;
61 import net.ktnx.mobileledger.ui.account_summary.AccountSummaryFragment;
62 import net.ktnx.mobileledger.ui.new_transaction.NewTransactionActivity;
63 import net.ktnx.mobileledger.ui.profiles.ProfileDetailActivity;
64 import net.ktnx.mobileledger.ui.profiles.ProfilesRecyclerViewAdapter;
65 import net.ktnx.mobileledger.ui.templates.TemplatesActivity;
66 import net.ktnx.mobileledger.ui.transaction_list.TransactionListFragment;
67 import net.ktnx.mobileledger.utils.Colors;
68 import net.ktnx.mobileledger.utils.Logger;
70 import org.jetbrains.annotations.NotNull;
72 import java.util.ArrayList;
73 import java.util.Date;
74 import java.util.List;
75 import java.util.Locale;
76 import java.util.Objects;
82 public class MainActivity extends ProfileThemedActivity implements FabManager.FabHandler {
83 public static final String STATE_CURRENT_PAGE = "current_page";
84 public static final String BUNDLE_SAVED_STATE = "bundle_savedState";
85 public static final String STATE_ACC_FILTER = "account_filter";
86 private static final boolean FAB_HIDDEN = false;
87 private static final boolean FAB_SHOWN = true;
88 private SectionsPagerAdapter mSectionsPagerAdapter;
89 private ProfilesRecyclerViewAdapter mProfileListAdapter;
90 private int mCurrentPage;
91 private boolean mBackMeansToAccountList = false;
92 private DrawerLayout.SimpleDrawerListener drawerListener;
93 private ActionBarDrawerToggle barDrawerToggle;
94 private ViewPager2.OnPageChangeCallback pageChangeCallback;
95 private Profile profile;
96 private MainModel mainModel;
97 private ActivityMainBinding b;
98 private int fabVerticalOffset;
99 private FabManager fabManager;
101 protected void onStart() {
104 Logger.debug("MainActivity", "onStart()");
106 b.mainPager.setCurrentItem(mCurrentPage, false);
109 protected void onSaveInstanceState(@NotNull Bundle outState) {
110 super.onSaveInstanceState(outState);
111 outState.putInt(STATE_CURRENT_PAGE, b.mainPager.getCurrentItem());
112 if (mainModel.getAccountFilter()
114 outState.putString(STATE_ACC_FILTER, mainModel.getAccountFilter()
118 protected void onDestroy() {
119 mSectionsPagerAdapter = null;
120 b.navProfileList.setAdapter(null);
121 b.drawerLayout.removeDrawerListener(drawerListener);
122 drawerListener = null;
123 b.drawerLayout.removeDrawerListener(barDrawerToggle);
124 barDrawerToggle = null;
125 b.mainPager.unregisterOnPageChangeCallback(pageChangeCallback);
126 pageChangeCallback = null;
130 protected void onResume() {
135 protected void onCreate(Bundle savedInstanceState) {
136 Logger.debug("MainActivity", "onCreate()/entry");
137 super.onCreate(savedInstanceState);
138 Logger.debug("MainActivity", "onCreate()/after super");
139 b = ActivityMainBinding.inflate(getLayoutInflater());
140 setContentView(b.getRoot());
142 mainModel = new ViewModelProvider(this).get(MainModel.class);
144 mSectionsPagerAdapter = new SectionsPagerAdapter(this);
146 Bundle extra = getIntent().getBundleExtra(BUNDLE_SAVED_STATE);
147 if (extra != null && savedInstanceState == null)
148 savedInstanceState = extra;
151 setSupportActionBar(b.toolbar);
153 Data.observeProfile(this, this::onProfileChanged);
155 Data.profiles.observe(this, this::onProfileListChanged);
157 Data.backgroundTaskProgress.observe(this, this::onRetrieveProgress);
158 Data.backgroundTasksRunning.observe(this, this::onRetrieveRunningChanged);
160 if (barDrawerToggle == null) {
161 barDrawerToggle = new ActionBarDrawerToggle(this, b.drawerLayout, b.toolbar,
162 R.string.navigation_drawer_open, R.string.navigation_drawer_close);
163 b.drawerLayout.addDrawerListener(barDrawerToggle);
165 barDrawerToggle.syncState();
168 PackageInfo pi = getApplicationContext().getPackageManager()
169 .getPackageInfo(getPackageName(), 0);
170 ((TextView) b.navUpper.findViewById(R.id.drawer_version_text)).setText(pi.versionName);
171 ((TextView) b.noProfilesLayout.findViewById(R.id.drawer_version_text)).setText(
174 catch (Exception e) {
178 markDrawerItemCurrent(R.id.nav_account_summary);
180 b.mainPager.setAdapter(mSectionsPagerAdapter);
181 b.mainPager.setOffscreenPageLimit(1);
183 if (pageChangeCallback == null) {
184 pageChangeCallback = new ViewPager2.OnPageChangeCallback() {
186 public void onPageSelected(int position) {
187 mCurrentPage = position;
190 markDrawerItemCurrent(R.id.nav_account_summary);
193 markDrawerItemCurrent(R.id.nav_latest_transactions);
196 Log.e("MainActivity",
197 String.format("Unexpected page index %d", position));
200 super.onPageSelected(position);
203 b.mainPager.registerOnPageChangeCallback(pageChangeCallback);
207 if (savedInstanceState != null) {
208 int currentPage = savedInstanceState.getInt(STATE_CURRENT_PAGE, -1);
209 if (currentPage != -1) {
210 mCurrentPage = currentPage;
212 mainModel.getAccountFilter()
213 .setValue(savedInstanceState.getString(STATE_ACC_FILTER, null));
216 b.btnNoProfilesAdd.setOnClickListener(v -> ProfileDetailActivity.start(this, null));
218 b.btnAddTransaction.setOnClickListener(this::fabNewTransactionClicked);
220 b.navNewProfileButton.setOnClickListener(v -> ProfileDetailActivity.start(this, null));
222 b.transactionListCancelDownload.setOnClickListener(this::onStopTransactionRefreshClick);
224 if (mProfileListAdapter == null)
225 mProfileListAdapter = new ProfilesRecyclerViewAdapter();
226 b.navProfileList.setAdapter(mProfileListAdapter);
228 mProfileListAdapter.editingProfiles.observe(this, newValue -> {
230 b.navProfilesStartEdit.setVisibility(View.GONE);
231 b.navProfilesCancelEdit.setVisibility(View.VISIBLE);
232 b.navNewProfileButton.setVisibility(View.VISIBLE);
233 if (b.drawerLayout.isDrawerOpen(GravityCompat.START)) {
234 b.navProfilesStartEdit.startAnimation(
235 AnimationUtils.loadAnimation(MainActivity.this, R.anim.fade_out));
236 b.navProfilesCancelEdit.startAnimation(
237 AnimationUtils.loadAnimation(MainActivity.this, R.anim.fade_in));
238 b.navNewProfileButton.startAnimation(
239 AnimationUtils.loadAnimation(MainActivity.this, R.anim.fade_in));
243 b.navProfilesCancelEdit.setVisibility(View.GONE);
244 b.navProfilesStartEdit.setVisibility(View.VISIBLE);
245 b.navNewProfileButton.setVisibility(View.GONE);
246 if (b.drawerLayout.isDrawerOpen(GravityCompat.START)) {
247 b.navProfilesCancelEdit.startAnimation(
248 AnimationUtils.loadAnimation(MainActivity.this, R.anim.fade_out));
249 b.navProfilesStartEdit.startAnimation(
250 AnimationUtils.loadAnimation(MainActivity.this, R.anim.fade_in));
251 b.navNewProfileButton.startAnimation(
252 AnimationUtils.loadAnimation(MainActivity.this, R.anim.fade_out));
256 mProfileListAdapter.notifyDataSetChanged();
259 fabManager = new FabManager(b.btnAddTransaction);
261 LinearLayoutManager llm = new LinearLayoutManager(this);
263 llm.setOrientation(RecyclerView.VERTICAL);
264 b.navProfileList.setLayoutManager(llm);
266 b.navProfilesStartEdit.setOnClickListener((v) -> mProfileListAdapter.flipEditingProfiles());
267 b.navProfilesCancelEdit.setOnClickListener(
268 (v) -> mProfileListAdapter.flipEditingProfiles());
269 b.navProfileListHeadButtons.setOnClickListener(
270 (v) -> mProfileListAdapter.flipEditingProfiles());
271 if (drawerListener == null) {
272 drawerListener = new DrawerLayout.SimpleDrawerListener() {
274 public void onDrawerSlide(@NonNull View drawerView, float slideOffset) {
275 if (slideOffset > 0.2)
276 fabManager.hideFab();
279 public void onDrawerClosed(View drawerView) {
280 super.onDrawerClosed(drawerView);
281 mProfileListAdapter.setAnimationsEnabled(false);
282 mProfileListAdapter.editingProfiles.setValue(false);
283 Data.drawerOpen.setValue(false);
287 public void onDrawerOpened(View drawerView) {
288 super.onDrawerOpened(drawerView);
289 mProfileListAdapter.setAnimationsEnabled(true);
290 Data.drawerOpen.setValue(true);
291 fabManager.hideFab();
294 b.drawerLayout.addDrawerListener(drawerListener);
297 Data.drawerOpen.observe(this, open -> {
299 b.drawerLayout.open();
301 b.drawerLayout.close();
304 mainModel.getUpdateError()
305 .observe(this, (error) -> {
309 Snackbar.make(b.mainPager, error, Snackbar.LENGTH_INDEFINITE)
311 mainModel.clearUpdateError();
313 Data.locale.observe(this, l -> refreshLastUpdateInfo());
314 Data.lastUpdateDate.observe(this, date -> refreshLastUpdateInfo());
315 Data.lastUpdateTransactionCount.observe(this, date -> refreshLastUpdateInfo());
316 Data.lastUpdateAccountCount.observe(this, date -> refreshLastUpdateInfo());
317 b.navAccountSummary.setOnClickListener(this::onAccountSummaryClicked);
318 b.navLatestTransactions.setOnClickListener(this::onLatestTransactionsClicked);
319 b.navPatterns.setOnClickListener(this::onPatternsClick);
321 private void onPatternsClick(View view) {
322 Intent intent = new Intent(this, TemplatesActivity.class);
323 startActivity(intent);
325 private void scheduleDataRetrievalIfStale(long lastUpdate) {
326 long now = new Date().getTime();
327 if ((lastUpdate == 0) || (now > (lastUpdate + (24 * 3600 * 1000)))) {
329 Logger.debug("db::", "WEB data never fetched. scheduling a fetch");
331 Logger.debug("db", String.format(Locale.ENGLISH,
332 "WEB data last fetched at %1.3f and now is %1.3f. re-fetching",
333 lastUpdate / 1000f, now / 1000f));
335 mainModel.scheduleTransactionListRetrieval();
338 private void createShortcuts(List<Profile> list) {
339 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N_MR1)
342 ShortcutManager sm = getSystemService(ShortcutManager.class);
343 List<ShortcutInfo> shortcuts = new ArrayList<>();
345 for (Profile p : list) {
346 if (shortcuts.size() >= sm.getMaxShortcutCountPerActivity())
349 if (!p.permitPosting())
352 final ShortcutInfo.Builder builder =
353 new ShortcutInfo.Builder(this, "new_transaction_" + p.getId());
354 ShortcutInfo si = builder.setShortLabel(p.getName())
355 .setIcon(Icon.createWithResource(this,
356 R.drawable.thick_plus_icon))
357 .setIntent(new Intent(Intent.ACTION_VIEW, null, this,
358 NewTransactionActivity.class).putExtra(
359 ProfileThemedActivity.PARAM_PROFILE_ID, p.getId())
361 ProfileThemedActivity.PARAM_THEME,
368 sm.setDynamicShortcuts(shortcuts);
370 private void onProfileListChanged(List<Profile> newList) {
371 if ((newList == null) || newList.isEmpty()) {
372 b.noProfilesLayout.setVisibility(View.VISIBLE);
373 b.mainAppLayout.setVisibility(View.GONE);
377 b.mainAppLayout.setVisibility(View.VISIBLE);
378 b.noProfilesLayout.setVisibility(View.GONE);
380 b.navProfileList.setMinimumHeight(
381 (int) (getResources().getDimension(R.dimen.thumb_row_height) * newList.size()));
383 Logger.debug("profiles", "profile list changed");
384 mProfileListAdapter.setProfileList(newList);
386 createShortcuts(newList);
388 Profile currentProfile = Data.getProfile();
389 if (currentProfile == null || !newList.contains(currentProfile)) {
390 Logger.debug(TAG, "Switching profile because the current is no longer available");
391 Data.setCurrentProfile(newList.get(0));
395 * called when the current profile has changed
397 private void onProfileChanged(@Nullable Profile newProfile) {
398 if (this.profile == null) {
399 if (newProfile == null)
403 if (this.profile.equals(newProfile))
407 boolean haveProfile = newProfile != null;
410 setTitle(newProfile.getName());
412 setTitle(R.string.app_name);
414 int newProfileTheme = haveProfile ? newProfile.getTheme() : -1;
415 if (newProfileTheme != Colors.profileThemeId) {
416 Logger.debug("profiles",
417 String.format(Locale.ENGLISH, "profile theme %d → %d", Colors.profileThemeId,
419 Colors.profileThemeId = newProfileTheme;
420 profileThemeChanged();
421 // profileThemeChanged would restart the activity, so no need to reload the
426 final boolean sameProfileId = (newProfile != null) && (this.profile != null) &&
427 this.profile.getId() == newProfile.getId();
429 this.profile = newProfile;
431 b.noProfilesLayout.setVisibility(haveProfile ? View.GONE : View.VISIBLE);
432 b.pagerLayout.setVisibility(haveProfile ? View.VISIBLE : View.VISIBLE);
434 mProfileListAdapter.notifyDataSetChanged();
437 if (newProfile.permitPosting()) {
438 b.toolbar.setSubtitle(null);
439 b.btnAddTransaction.show();
442 b.toolbar.setSubtitle(R.string.profile_subtitle_read_only);
443 b.btnAddTransaction.hide();
447 b.toolbar.setSubtitle(null);
448 b.btnAddTransaction.hide();
451 updateLastUpdateTextFromDB();
454 Logger.debug(TAG, String.format(Locale.ROOT, "Short-cut profile 'changed' to %d",
455 newProfile.getId()));
459 mainModel.stopTransactionsRetrieval();
461 mainModel.clearTransactions();
464 Logger.debug("transactions", "requesting list reload");
465 mainModel.scheduleTransactionListReload();
468 private void profileThemeChanged() {
469 // un-hook all observed LiveData
470 Data.removeProfileObservers(this);
471 Data.profiles.removeObservers(this);
472 Data.lastUpdateTransactionCount.removeObservers(this);
473 Data.lastUpdateAccountCount.removeObservers(this);
474 Data.lastUpdateDate.removeObservers(this);
476 Logger.debug("MainActivity", "profileThemeChanged(): recreating activity");
479 public void fabNewTransactionClicked(View view) {
480 Intent intent = new Intent(this, NewTransactionActivity.class);
481 intent.putExtra(ProfileThemedActivity.PARAM_PROFILE_ID, profile.getId());
482 intent.putExtra(ProfileThemedActivity.PARAM_THEME, profile.getTheme());
483 startActivity(intent);
484 overridePendingTransition(R.anim.slide_in_up, R.anim.dummy);
486 public void markDrawerItemCurrent(int id) {
487 TextView item = b.drawerLayout.findViewById(id);
488 item.setBackgroundColor(Colors.tableRowDarkBG);
490 for (int i = 0; i < b.navActions.getChildCount(); i++) {
491 View view = b.navActions.getChildAt(i);
492 if (view.getId() != id) {
493 view.setBackgroundColor(Color.TRANSPARENT);
497 public void onAccountSummaryClicked(View view) {
498 b.drawerLayout.closeDrawers();
500 showAccountSummaryFragment();
502 private void showAccountSummaryFragment() {
503 b.mainPager.setCurrentItem(0, true);
504 mainModel.getAccountFilter()
507 public void onLatestTransactionsClicked(View view) {
508 b.drawerLayout.closeDrawers();
510 showTransactionsFragment(null);
512 public void showTransactionsFragment(String accName) {
513 mainModel.getAccountFilter()
515 b.mainPager.setCurrentItem(1, true);
517 public void showAccountTransactions(String accountName) {
518 mBackMeansToAccountList = true;
519 showTransactionsFragment(accountName);
522 public void onBackPressed() {
523 if (b.drawerLayout.isDrawerOpen(GravityCompat.START)) {
524 b.drawerLayout.closeDrawer(GravityCompat.START);
527 if (mBackMeansToAccountList && (b.mainPager.getCurrentItem() == 1)) {
528 mainModel.getAccountFilter()
530 showAccountSummaryFragment();
531 mBackMeansToAccountList = false;
534 Logger.debug("fragments", String.format(Locale.ENGLISH, "manager stack: %d",
535 getSupportFragmentManager().getBackStackEntryCount()));
537 super.onBackPressed();
541 public void updateLastUpdateTextFromDB() {
547 .load(profile.getId(), Option.OPT_LAST_SCRAPE)
548 .observe(this, opt -> {
552 lastUpdate = Long.parseLong(opt.getValue());
554 catch (NumberFormatException ex) {
555 Logger.debug(TAG, String.format("Error parsing '%s' as long", opt.getValue()),
560 if (lastUpdate == 0) {
561 Data.lastUpdateDate.postValue(null);
564 Data.lastUpdateDate.postValue(new Date(lastUpdate));
567 scheduleDataRetrievalIfStale(lastUpdate);
570 private void refreshLastUpdateInfo() {
571 final int formatFlags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR |
572 DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_NUMERIC_DATE;
573 String templateForTransactions =
574 getResources().getString(R.string.transaction_count_summary);
575 String templateForAccounts = getResources().getString(R.string.account_count_summary);
576 Integer accountCount = Data.lastUpdateAccountCount.getValue();
577 Integer transactionCount = Data.lastUpdateTransactionCount.getValue();
578 Date lastUpdate = Data.lastUpdateDate.getValue();
579 if (lastUpdate == null) {
580 Data.lastTransactionsUpdateText.setValue("----");
581 Data.lastAccountsUpdateText.setValue("----");
584 Data.lastTransactionsUpdateText.setValue(
585 String.format(Objects.requireNonNull(Data.locale.getValue()),
586 templateForTransactions,
587 transactionCount == null ? 0 : transactionCount,
588 DateUtils.formatDateTime(this, lastUpdate.getTime(), formatFlags)));
589 Data.lastAccountsUpdateText.setValue(
590 String.format(Objects.requireNonNull(Data.locale.getValue()),
591 templateForAccounts, accountCount == null ? 0 : accountCount,
592 DateUtils.formatDateTime(this, lastUpdate.getTime(), formatFlags)));
595 public void onStopTransactionRefreshClick(View view) {
596 Logger.debug("interactive", "Cancelling transactions refresh");
597 mainModel.stopTransactionsRetrieval();
598 b.transactionListCancelDownload.setEnabled(false);
600 public void onRetrieveRunningChanged(Boolean running) {
602 b.transactionListCancelDownload.setEnabled(true);
603 ColorStateList csl = Colors.getColorStateList();
604 b.transactionListProgressBar.setIndeterminateTintList(csl);
605 b.transactionListProgressBar.setProgressTintList(csl);
606 b.transactionListProgressBar.setIndeterminate(true);
607 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
608 b.transactionListProgressBar.setProgress(0, false);
611 b.transactionListProgressBar.setProgress(0);
613 b.transactionProgressLayout.setVisibility(View.VISIBLE);
616 b.transactionProgressLayout.setVisibility(View.GONE);
619 public void onRetrieveProgress(@Nullable RetrieveTransactionsTask.Progress progress) {
620 if (progress == null ||
621 progress.getState() == RetrieveTransactionsTask.ProgressState.FINISHED)
623 Logger.debug("progress", "Done");
624 b.transactionProgressLayout.setVisibility(View.GONE);
626 mainModel.transactionRetrievalDone();
628 String error = (progress == null) ? null : progress.getError();
630 if (error.equals(RetrieveTransactionsTask.Result.ERR_JSON_PARSER_ERROR))
631 error = getResources().getString(R.string.err_json_parser_error);
633 AlertDialog.Builder builder = new AlertDialog.Builder(this);
634 builder.setMessage(error);
635 builder.setPositiveButton(R.string.btn_profile_options, (dialog, which) -> {
636 Logger.debug("error", "will start profile editor");
637 ProfileDetailActivity.start(this, profile);
648 b.transactionListCancelDownload.setEnabled(true);
649 // ColorStateList csl = Colors.getColorStateList();
650 // progressBar.setIndeterminateTintList(csl);
651 // progressBar.setProgressTintList(csl);
652 // if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
653 // progressBar.setProgress(0, false);
655 // progressBar.setProgress(0);
656 b.transactionProgressLayout.setVisibility(View.VISIBLE);
658 if (progress.isIndeterminate() || (progress.getTotal() <= 0)) {
659 b.transactionListProgressBar.setIndeterminate(true);
660 Logger.debug("progress", "indeterminate");
663 if (b.transactionListProgressBar.isIndeterminate()) {
664 b.transactionListProgressBar.setIndeterminate(false);
666 // Logger.debug("progress",
667 // String.format(Locale.US, "%d/%d", progress.getProgress(), progress.getTotal
669 b.transactionListProgressBar.setMax(progress.getTotal());
670 // for some reason animation doesn't work - no progress is shown (stick at 0)
671 // on lineageOS 14.1 (Nougat, 7.1.2)
672 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
673 b.transactionListProgressBar.setProgress(progress.getProgress(), false);
675 b.transactionListProgressBar.setProgress(progress.getProgress());
678 public void fabShouldShow() {
679 if ((profile != null) && profile.permitPosting() && !b.drawerLayout.isOpen())
680 fabManager.showFab();
683 public Context getContext() {
687 public void showManagedFab() {
691 public void hideManagedFab() {
692 fabManager.hideFab();
694 public static class SectionsPagerAdapter extends FragmentStateAdapter {
696 public SectionsPagerAdapter(@NonNull FragmentActivity fragmentActivity) {
697 super(fragmentActivity);
701 public Fragment createFragment(int position) {
703 String.format(Locale.ENGLISH, "Switching to fragment %d", position));
706 // debug("flow", "Creating account summary fragment");
707 return new AccountSummaryFragment();
709 return new TransactionListFragment();
711 throw new IllegalStateException(
712 String.format("Unexpected fragment index: " + "%d", position));
717 public int getItemCount() {