remove the distinction between "readable" and "writable" database connections
[mobile-ledger.git] / app / src / main / java / net / ktnx / mobileledger / ui / activity / MainActivity.java
1 /*
2  * Copyright © 2019 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.res.ColorStateList;
23 import android.graphics.Color;
24 import android.os.AsyncTask;
25 import android.os.Build;
26 import android.os.Bundle;
27 import android.util.Log;
28 import android.view.View;
29 import android.view.ViewGroup;
30 import android.view.ViewPropertyAnimator;
31 import android.view.animation.Animation;
32 import android.view.animation.AnimationUtils;
33 import android.widget.LinearLayout;
34 import android.widget.ProgressBar;
35 import android.widget.TextView;
36 import android.widget.Toast;
37
38 import com.google.android.material.floatingactionbutton.FloatingActionButton;
39
40 import net.ktnx.mobileledger.R;
41 import net.ktnx.mobileledger.async.RefreshDescriptionsTask;
42 import net.ktnx.mobileledger.async.RetrieveTransactionsTask;
43 import net.ktnx.mobileledger.model.Data;
44 import net.ktnx.mobileledger.model.LedgerAccount;
45 import net.ktnx.mobileledger.model.MobileLedgerProfile;
46 import net.ktnx.mobileledger.ui.account_summary.AccountSummaryFragment;
47 import net.ktnx.mobileledger.ui.profiles.ProfileDetailFragment;
48 import net.ktnx.mobileledger.ui.profiles.ProfilesRecyclerViewAdapter;
49 import net.ktnx.mobileledger.ui.transaction_list.TransactionListFragment;
50 import net.ktnx.mobileledger.ui.transaction_list.TransactionListViewModel;
51 import net.ktnx.mobileledger.utils.Colors;
52 import net.ktnx.mobileledger.utils.MLDB;
53
54 import java.lang.ref.WeakReference;
55 import java.text.DateFormat;
56 import java.util.ArrayList;
57 import java.util.Date;
58 import java.util.List;
59 import java.util.Observable;
60 import java.util.Observer;
61
62 import androidx.appcompat.app.ActionBarDrawerToggle;
63 import androidx.appcompat.widget.Toolbar;
64 import androidx.core.view.GravityCompat;
65 import androidx.drawerlayout.widget.DrawerLayout;
66 import androidx.fragment.app.Fragment;
67 import androidx.fragment.app.FragmentManager;
68 import androidx.fragment.app.FragmentPagerAdapter;
69 import androidx.recyclerview.widget.LinearLayoutManager;
70 import androidx.recyclerview.widget.RecyclerView;
71 import androidx.viewpager.widget.ViewPager;
72
73 public class MainActivity extends ProfileThemedActivity {
74     private static final String STATE_CURRENT_PAGE = "current_page";
75     private static final String BUNDLE_SAVED_STATE = "bundle_savedState";
76     public AccountSummaryFragment mAccountSummaryFragment;
77     DrawerLayout drawer;
78     private LinearLayout profileListContainer;
79     private View profileListHeadArrow, profileListHeadMore, profileListHeadCancel;
80     private LinearLayout profileListHeadMoreAndCancel;
81     private FragmentManager fragmentManager;
82     private TextView tvLastUpdate;
83     private RetrieveTransactionsTask retrieveTransactionsTask;
84     private View bTransactionListCancelDownload;
85     private ProgressBar progressBar;
86     private LinearLayout progressLayout;
87     private SectionsPagerAdapter mSectionsPagerAdapter;
88     private ViewPager mViewPager;
89     private FloatingActionButton fab;
90     private boolean profileModificationEnabled = false;
91     private boolean profileListExpanded = false;
92     private ProfilesRecyclerViewAdapter mProfileListAdapter;
93     @Override
94     protected void onStart() {
95         super.onStart();
96
97         setupProfile();
98
99         updateLastUpdateTextFromDB();
100         Date lastUpdate = Data.lastUpdateDate.get();
101
102         long now = new Date().getTime();
103         if ((lastUpdate == null) || (now > (lastUpdate.getTime() + (24 * 3600 * 1000)))) {
104             if (lastUpdate == null) Log.d("db::", "WEB data never fetched. scheduling a fetch");
105             else Log.d("db",
106                     String.format("WEB data last fetched at %1.3f and now is %1.3f. re-fetching",
107                             lastUpdate.getTime() / 1000f, now / 1000f));
108
109             scheduleTransactionListRetrieval();
110         }
111     }
112     @Override
113     protected void onSaveInstanceState(Bundle outState) {
114         super.onSaveInstanceState(outState);
115         outState.putInt(STATE_CURRENT_PAGE, mViewPager.getCurrentItem());
116     }
117     @Override
118     protected void onCreate(Bundle savedInstanceState) {
119         super.onCreate(savedInstanceState);
120
121         setContentView(R.layout.activity_main);
122
123         fab = findViewById(R.id.btn_add_transaction);
124         profileListContainer = findViewById(R.id.nav_profile_list_container);
125         profileListHeadArrow = findViewById(R.id.nav_profiles_arrow);
126         profileListHeadMore = findViewById(R.id.nav_profiles_start_edit);
127         profileListHeadCancel = findViewById(R.id.nav_profiles_cancel_edit);
128         profileListHeadMoreAndCancel = findViewById(R.id.nav_profile_list_head_buttons);
129         drawer = findViewById(R.id.drawer_layout);
130         tvLastUpdate = findViewById(R.id.transactions_last_update);
131         bTransactionListCancelDownload = findViewById(R.id.transaction_list_cancel_download);
132         progressBar = findViewById(R.id.transaction_list_progress_bar);
133         progressLayout = findViewById(R.id.transaction_progress_layout);
134         fragmentManager = getSupportFragmentManager();
135         mSectionsPagerAdapter = new SectionsPagerAdapter(fragmentManager);
136         mViewPager = findViewById(R.id.root_frame);
137
138         Bundle extra = getIntent().getBundleExtra(BUNDLE_SAVED_STATE);
139         if (extra != null && savedInstanceState == null) savedInstanceState = extra;
140
141
142         Toolbar toolbar = findViewById(R.id.toolbar);
143         setSupportActionBar(toolbar);
144
145         Data.profile.addObserver((o, arg) -> {
146             MobileLedgerProfile profile = Data.profile.get();
147             runOnUiThread(() -> {
148                 if (profile == null) setTitle(R.string.app_name);
149                 else setTitle(profile.getName());
150                 updateLastUpdateTextFromDB();
151                 if (profile.isPostingPermitted()) {
152                     toolbar.setSubtitle(null);
153                     fab.show();
154                 }
155                 else {
156                     toolbar.setSubtitle(R.string.profile_subitlte_read_only);
157                     fab.hide();
158                 }
159
160                 int newProfileTheme = profile.getThemeId();
161                 if (newProfileTheme != Colors.profileThemeId) {
162                     Log.d("profiles", String.format("profile theme %d → %d", Colors.profileThemeId,
163                             newProfileTheme));
164                     profileThemeChanged();
165                     Colors.profileThemeId = newProfileTheme;
166                 }
167             });
168         });
169         Data.profiles.addObserver((o, arg) -> {
170             findViewById(R.id.nav_profile_list).setMinimumHeight(
171                     (int) (getResources().getDimension(R.dimen.thumb_row_height) *
172                            Data.profiles.size()));
173             mProfileListAdapter.notifyDataSetChanged();
174         });
175
176         ActionBarDrawerToggle toggle =
177                 new ActionBarDrawerToggle(this, drawer, toolbar, R.string.navigation_drawer_open,
178                         R.string.navigation_drawer_close);
179         drawer.addDrawerListener(toggle);
180         toggle.syncState();
181
182         TextView ver = drawer.findViewById(R.id.drawer_version_text);
183
184         try {
185             PackageInfo pi =
186                     getApplicationContext().getPackageManager().getPackageInfo(getPackageName(), 0);
187             ver.setText(pi.versionName);
188         }
189         catch (Exception e) {
190             e.printStackTrace();
191         }
192
193         if (progressBar == null)
194             throw new RuntimeException("Can't get hold on the transaction value progress bar");
195         if (progressLayout == null) throw new RuntimeException(
196                 "Can't get hold on the transaction value progress bar layout");
197
198         markDrawerItemCurrent(R.id.nav_account_summary);
199
200         mViewPager.setAdapter(mSectionsPagerAdapter);
201         mViewPager.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
202             @Override
203             public void onPageSelected(int position) {
204                 switch (position) {
205                     case 0:
206                         markDrawerItemCurrent(R.id.nav_account_summary);
207                         break;
208                     case 1:
209                         markDrawerItemCurrent(R.id.nav_latest_transactions);
210                         break;
211                     default:
212                         Log.e("MainActivity", String.format("Unexpected page index %d", position));
213                 }
214
215                 super.onPageSelected(position);
216             }
217         });
218
219         if (savedInstanceState != null) {
220             int currentPage = savedInstanceState.getInt(STATE_CURRENT_PAGE, -1);
221             if (currentPage != -1) {
222                 mViewPager.setCurrentItem(currentPage, false);
223             }
224         }
225
226         Data.lastUpdateDate.addObserver((o, arg) -> {
227             Log.d("main", "lastUpdateDate changed");
228             runOnUiThread(this::updateLastUpdateDisplay);
229         });
230
231         updateLastUpdateDisplay();
232
233         findViewById(R.id.btn_no_profiles_add)
234                 .setOnClickListener(v -> startEditProfileActivity(null));
235
236         findViewById(R.id.btn_add_transaction).setOnClickListener(this::fabNewTransactionClicked);
237
238         findViewById(R.id.nav_new_profile_button)
239                 .setOnClickListener(v -> startEditProfileActivity(null));
240
241         RecyclerView root = findViewById(R.id.nav_profile_list);
242         if (root == null)
243             throw new RuntimeException("Can't get hold on the transaction value view");
244
245         mProfileListAdapter = new ProfilesRecyclerViewAdapter();
246         root.setAdapter(mProfileListAdapter);
247
248         mProfileListAdapter.addEditingProfilesObserver(new Observer() {
249             @Override
250             public void update(Observable o, Object arg) {
251                 if (mProfileListAdapter.isEditingProfiles()) {
252                     profileListHeadArrow.clearAnimation();
253                     profileListHeadArrow.setVisibility(View.GONE);
254                     profileListHeadMore.setVisibility(View.GONE);
255                     profileListHeadCancel.setVisibility(View.VISIBLE);
256                 }
257                 else {
258                     profileListHeadArrow.setRotation(180f);
259                     profileListHeadArrow.setVisibility(View.VISIBLE);
260                     profileListHeadCancel.setVisibility(View.GONE);
261                     profileListHeadMore.setVisibility(View.GONE);
262                     profileListHeadMore
263                             .setVisibility(profileListExpanded ? View.VISIBLE : View.GONE);
264                 }
265             }
266         });
267
268         LinearLayoutManager llm = new LinearLayoutManager(this);
269
270         llm.setOrientation(RecyclerView.VERTICAL);
271         root.setLayoutManager(llm);
272
273         profileListHeadMore.setOnClickListener((v) -> mProfileListAdapter.flipEditingProfiles());
274         profileListHeadCancel.setOnClickListener((v) -> mProfileListAdapter.flipEditingProfiles());
275         profileListHeadMoreAndCancel
276                 .setOnClickListener((v) -> mProfileListAdapter.flipEditingProfiles());
277
278         drawer.addDrawerListener(new DrawerLayout.SimpleDrawerListener() {
279             @Override
280             public void onDrawerClosed(View drawerView) {
281                 super.onDrawerClosed(drawerView);
282                 collapseProfileList();
283             }
284         });
285     }
286     private void updateLastUpdateDisplay() {
287         TextView v = findViewById(R.id.transactions_last_update);
288         Date date = Data.lastUpdateDate.get();
289         if (date == null) {
290             v.setText(R.string.transaction_last_update_never);
291             Log.d("main", "no last update date :(");
292         }
293         else {
294             final String text = DateFormat.getDateTimeInstance().format(date);
295             v.setText(text);
296             Log.d("main", String.format("Date formatted: %s", text));
297         }
298     }
299     private void profileThemeChanged() {
300         setupProfileColors();
301
302         Bundle bundle = new Bundle();
303         onSaveInstanceState(bundle);
304         // restart activity to reflect theme change
305         finish();
306         Intent intent = new Intent(this, this.getClass());
307         intent.putExtra(BUNDLE_SAVED_STATE, bundle);
308         startActivity(intent);
309     }
310     public void startEditProfileActivity(MobileLedgerProfile profile) {
311         Intent intent = new Intent(this, ProfileDetailActivity.class);
312         Bundle args = new Bundle();
313         if (profile != null) {
314             int index = Data.getProfileIndex(profile);
315             if (index != -1) intent.putExtra(ProfileDetailFragment.ARG_ITEM_ID, index);
316         }
317         intent.putExtras(args);
318         startActivity(intent, args);
319     }
320     private void setupProfile() {
321         String profileUUID = MLDB.getOption(MLDB.OPT_PROFILE_UUID, null);
322         MobileLedgerProfile profile;
323
324         profile = MobileLedgerProfile.loadAllFromDB(profileUUID);
325
326         if (Data.profiles.getList().isEmpty()) {
327             findViewById(R.id.no_profiles_layout).setVisibility(View.VISIBLE);
328             findViewById(R.id.pager_layout).setVisibility(View.GONE);
329             return;
330         }
331
332         findViewById(R.id.pager_layout).setVisibility(View.VISIBLE);
333         findViewById(R.id.no_profiles_layout).setVisibility(View.GONE);
334
335         if (profile == null) profile = Data.profiles.get(0);
336
337         if (profile == null) throw new AssertionError("profile must have a value");
338
339         Data.setCurrentProfile(profile);
340     }
341     public void fabNewTransactionClicked(View view) {
342         Intent intent = new Intent(this, NewTransactionActivity.class);
343         startActivity(intent);
344         overridePendingTransition(R.anim.slide_in_right, R.anim.dummy);
345     }
346     public void navSettingsClicked(View view) {
347         Intent intent = new Intent(this, SettingsActivity.class);
348         startActivity(intent);
349         drawer.closeDrawers();
350     }
351     public void markDrawerItemCurrent(int id) {
352         TextView item = drawer.findViewById(id);
353         item.setBackgroundColor(Colors.tableRowDarkBG);
354
355         LinearLayout actions = drawer.findViewById(R.id.nav_actions);
356         for (int i = 0; i < actions.getChildCount(); i++) {
357             View view = actions.getChildAt(i);
358             if (view.getId() != id) {
359                 view.setBackgroundColor(Color.TRANSPARENT);
360             }
361         }
362     }
363     public void onAccountSummaryClicked(View view) {
364         drawer.closeDrawers();
365
366         showAccountSummaryFragment();
367     }
368     private void showAccountSummaryFragment() {
369         mViewPager.setCurrentItem(0, true);
370         TransactionListFragment.accountFilter.set(null);
371 //        FragmentTransaction ft = fragmentManager.beginTransaction();
372 //        accountSummaryFragment = new AccountSummaryFragment();
373 //        ft.replace(R.id.root_frame, accountSummaryFragment);
374 //        ft.commit();
375 //        currentFragment = accountSummaryFragment;
376     }
377     public void onLatestTransactionsClicked(View view) {
378         drawer.closeDrawers();
379
380         showTransactionsFragment(null);
381     }
382     private void resetFragmentBackStack() {
383 //        fragmentManager.popBackStack(0, FragmentManager.POP_BACK_STACK_INCLUSIVE);
384     }
385     private void showTransactionsFragment(LedgerAccount account) {
386         if (account != null) TransactionListFragment.accountFilter.set(account.getName());
387         mViewPager.setCurrentItem(1, true);
388 //        FragmentTransaction ft = fragmentManager.beginTransaction();
389 //        if (transactionListFragment == null) {
390 //            Log.d("flow", "MainActivity creating TransactionListFragment");
391 //            transactionListFragment = new TransactionListFragment();
392 //        }
393 //        Bundle bundle = new Bundle();
394 //        if (account != null) {
395 //            bundle.putString(TransactionListFragment.BUNDLE_KEY_FILTER_ACCOUNT_NAME,
396 //                    account.getName());
397 //        }
398 //        transactionListFragment.setArguments(bundle);
399 //        ft.replace(R.id.root_frame, transactionListFragment);
400 //        if (account != null)
401 //            ft.addToBackStack(getResources().getString(R.string.title_activity_transaction_list));
402 //        ft.commit();
403 //
404 //        currentFragment = transactionListFragment;
405     }
406     public void showAccountTransactions(LedgerAccount account) {
407         showTransactionsFragment(account);
408     }
409     @Override
410     public void onBackPressed() {
411         DrawerLayout drawer = findViewById(R.id.drawer_layout);
412         if (drawer.isDrawerOpen(GravityCompat.START)) {
413             drawer.closeDrawer(GravityCompat.START);
414         }
415         else {
416             Log.d("fragments",
417                     String.format("manager stack: %d", fragmentManager.getBackStackEntryCount()));
418
419             super.onBackPressed();
420         }
421     }
422     public void updateLastUpdateTextFromDB() {
423         {
424             final MobileLedgerProfile profile = Data.profile.get();
425             long last_update =
426                     (profile != null) ? profile.getLongOption(MLDB.OPT_LAST_SCRAPE, 0L) : 0;
427
428             Log.d("transactions", String.format("Last update = %d", last_update));
429             if (last_update == 0) {
430                 Data.lastUpdateDate.set(null);
431             }
432             else {
433                 Data.lastUpdateDate.set(new Date(last_update));
434             }
435         }
436     }
437     public void scheduleTransactionListRetrieval() {
438         if (Data.profile.get() == null) return;
439
440         retrieveTransactionsTask = new RetrieveTransactionsTask(new WeakReference<>(this));
441
442         retrieveTransactionsTask.execute();
443     }
444     public void onStopTransactionRefreshClick(View view) {
445         Log.d("interactive", "Cancelling transactions refresh");
446         if (retrieveTransactionsTask != null) retrieveTransactionsTask.cancel(false);
447         bTransactionListCancelDownload.setEnabled(false);
448     }
449     public void onRetrieveDone(String error) {
450         progressLayout.setVisibility(View.GONE);
451
452         if (error == null) {
453             updateLastUpdateTextFromDB();
454
455             new RefreshDescriptionsTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
456             TransactionListViewModel.scheduleTransactionListReload();
457         }
458         else Toast.makeText(this, error, Toast.LENGTH_LONG).show();
459     }
460     public void onRetrieveStart() {
461         bTransactionListCancelDownload.setEnabled(true);
462         progressBar.setIndeterminateTintList(ColorStateList.valueOf(Colors.primary));
463         progressBar.setProgressTintList(ColorStateList.valueOf(Colors.primary));
464         progressBar.setIndeterminate(true);
465         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) progressBar.setProgress(0, false);
466         else progressBar.setProgress(0);
467         progressLayout.setVisibility(View.VISIBLE);
468     }
469     public void onRetrieveProgress(RetrieveTransactionsTask.Progress progress) {
470         if ((progress.getTotal() == RetrieveTransactionsTask.Progress.INDETERMINATE) ||
471             (progress.getTotal() == 0))
472         {
473             progressBar.setIndeterminate(true);
474         }
475         else {
476             if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
477                 progressBar.setMin(0);
478             }
479             progressBar.setMax(progress.getTotal());
480             if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
481                 progressBar.setProgress(progress.getProgress(), true);
482             }
483             else progressBar.setProgress(progress.getProgress());
484             progressBar.setIndeterminate(false);
485         }
486     }
487     public void fabShouldShow() {
488         MobileLedgerProfile profile = Data.profile.get();
489         if ((profile != null) && profile.isPostingPermitted()) fab.show();
490     }
491     public void navProfilesHeadClicked(View view) {
492         if (profileListExpanded) {
493             collapseProfileList();
494         }
495         else {
496             expandProfileList();
497         }
498     }
499     private void expandProfileList() {
500         profileListExpanded = true;
501
502
503         profileListContainer.setVisibility(View.VISIBLE);
504         profileListContainer.startAnimation(AnimationUtils.loadAnimation(this, R.anim.slide_down));
505         profileListHeadArrow.startAnimation(AnimationUtils.loadAnimation(this, R.anim.rotate_180));
506         profileListHeadMore.setVisibility(View.VISIBLE);
507         profileListHeadMore.startAnimation(AnimationUtils.loadAnimation(this, R.anim.fade_in));
508         findViewById(R.id.nav_profile_list).setMinimumHeight(
509                 (int) (getResources().getDimension(R.dimen.thumb_row_height) *
510                        Data.profiles.size()));
511     }
512     private void collapseProfileList() {
513         profileListExpanded = false;
514
515         final Animation animation = AnimationUtils.loadAnimation(this, R.anim.slide_up);
516         animation.setAnimationListener(new Animation.AnimationListener() {
517             @Override
518             public void onAnimationStart(Animation animation) {
519
520             }
521             @Override
522             public void onAnimationEnd(Animation animation) {
523                 profileListContainer.setVisibility(View.GONE);
524             }
525             @Override
526             public void onAnimationRepeat(Animation animation) {
527
528             }
529         });
530         mProfileListAdapter.stopEditingProfiles();
531
532         profileListContainer.startAnimation(animation);
533         profileListHeadArrow.setRotation(0f);
534         profileListHeadArrow
535                 .startAnimation(AnimationUtils.loadAnimation(this, R.anim.rotate_180_back));
536         profileListHeadMore.setVisibility(View.GONE);
537     }
538     public void onProfileRowClicked(View v) {
539         Data.setCurrentProfile((MobileLedgerProfile) v.getTag());
540     }
541     public void enableProfileModifications() {
542         profileModificationEnabled = true;
543         ViewGroup profileList = findViewById(R.id.nav_profile_list);
544         for (int i = 0; i < profileList.getChildCount(); i++) {
545             View aRow = profileList.getChildAt(i);
546             aRow.findViewById(R.id.profile_list_edit_button).setVisibility(View.VISIBLE);
547             aRow.findViewById(R.id.profile_list_rearrange_handle).setVisibility(View.VISIBLE);
548         }
549         // FIXME enable rearranging
550
551     }
552     public void disableProfileModifications() {
553         profileModificationEnabled = false;
554         ViewGroup profileList = findViewById(R.id.nav_profile_list);
555         for (int i = 0; i < profileList.getChildCount(); i++) {
556             View aRow = profileList.getChildAt(i);
557             aRow.findViewById(R.id.profile_list_edit_button).setVisibility(View.GONE);
558             aRow.findViewById(R.id.profile_list_rearrange_handle).setVisibility(View.INVISIBLE);
559         }
560         // FIXME disable rearranging
561
562     }
563     public void onAccountSummaryRowViewClicked(View view) {
564         ViewGroup row = (ViewGroup) view.getParent();
565         LedgerAccount acc = (LedgerAccount) row.getTag();
566         switch (view.getId()) {
567             case R.id.account_row_acc_name:
568             case R.id.account_expander_container:
569                 Log.d("accounts", "Account expander clicked");
570                 if (!acc.hasSubAccounts()) return;
571
572                 boolean wasExpanded = acc.isExpanded();
573
574                 View arrow = row.findViewById(R.id.account_expander_container);
575
576                 arrow.clearAnimation();
577                 ViewPropertyAnimator animator = arrow.animate();
578
579                 acc.toggleExpanded();
580                 Data.profile.get().storeAccount(MLDB.getDatabase(), acc);
581
582                 if (wasExpanded) {
583                     Log.d("accounts", String.format("Collapsing account '%s'", acc.getName()));
584                     arrow.setRotation(0);
585                     animator.rotationBy(180);
586
587                     // removing all child accounts from the view
588                     int start = -1, count = 0;
589                     int i = 0;
590                     final ArrayList<LedgerAccount> accountList = Data.accounts.get();
591                     for (LedgerAccount a : accountList) {
592                         if (acc.isParentOf(a)) {
593                             if (start == -1) {
594                                 start = i;
595                             }
596                             count++;
597                         }
598                         else {
599                             if (start != -1) {
600                                 break;
601                             }
602                         }
603                         i++;
604                     }
605
606                     if (start != -1) {
607                         for (int j = 0; j < count; j++) {
608                             Log.d("accounts", String.format("Removing item %d: %s", start + j,
609                                     accountList.get(start).getName()));
610                             accountList.remove(start);
611                         }
612
613                         mAccountSummaryFragment.modelAdapter.notifyItemRangeRemoved(start, count);
614                     }
615                 }
616                 else {
617                     Log.d("accounts", String.format("Expanding account '%s'", acc.getName()));
618                     arrow.setRotation(180);
619                     animator.rotationBy(-180);
620                     ArrayList<LedgerAccount> accounts = Data.accounts.get();
621                     List<LedgerAccount> children =
622                             Data.profile.get().loadVisibleChildAccountsOf(acc);
623                     int parentPos = accounts.indexOf(acc);
624                     if (parentPos == -1) throw new RuntimeException(
625                             "Can't find index of clicked account " + acc.getName());
626                     accounts.addAll(parentPos + 1, children);
627                     mAccountSummaryFragment.modelAdapter
628                             .notifyItemRangeInserted(parentPos + 1, children.size());
629                 }
630                 break;
631             case R.id.account_row_acc_amounts:
632                 showAccountTransactions(acc);
633                 break;
634         }
635     }
636
637     public class SectionsPagerAdapter extends FragmentPagerAdapter {
638
639         public SectionsPagerAdapter(FragmentManager fm) {
640             super(fm);
641         }
642
643         @Override
644         public Fragment getItem(int position) {
645             Log.d("main", String.format("Switching to fragment %d", position));
646             switch (position) {
647                 case 0:
648 //                    Log.d("flow", "Creating account summary fragment");
649                     return mAccountSummaryFragment = new AccountSummaryFragment();
650                 case 1:
651                     return new TransactionListFragment();
652                 default:
653                     throw new IllegalStateException(
654                             String.format("Unexpected fragment index: " + "%d", position));
655             }
656         }
657
658         @Override
659         public int getCount() {
660             return 2;
661         }
662     }
663 }