]> git.ktnx.net Git - mobile-ledger.git/blob - app/src/main/java/net/ktnx/mobileledger/ui/activity/MainActivity.java
f446e1ab7ccd9ca17922265188b450a7973b9658
[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
170         ActionBarDrawerToggle toggle =
171                 new ActionBarDrawerToggle(this, drawer, toolbar, R.string.navigation_drawer_open,
172                         R.string.navigation_drawer_close);
173         drawer.addDrawerListener(toggle);
174         toggle.syncState();
175
176         TextView ver = drawer.findViewById(R.id.drawer_version_text);
177
178         try {
179             PackageInfo pi =
180                     getApplicationContext().getPackageManager().getPackageInfo(getPackageName(), 0);
181             ver.setText(pi.versionName);
182         }
183         catch (Exception e) {
184             e.printStackTrace();
185         }
186
187         if (progressBar == null)
188             throw new RuntimeException("Can't get hold on the transaction value progress bar");
189         if (progressLayout == null) throw new RuntimeException(
190                 "Can't get hold on the transaction value progress bar layout");
191
192         markDrawerItemCurrent(R.id.nav_account_summary);
193
194         mViewPager.setAdapter(mSectionsPagerAdapter);
195         mViewPager.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
196             @Override
197             public void onPageSelected(int position) {
198                 switch (position) {
199                     case 0:
200                         markDrawerItemCurrent(R.id.nav_account_summary);
201                         break;
202                     case 1:
203                         markDrawerItemCurrent(R.id.nav_latest_transactions);
204                         break;
205                     default:
206                         Log.e("MainActivity", String.format("Unexpected page index %d", position));
207                 }
208
209                 super.onPageSelected(position);
210             }
211         });
212
213         if (savedInstanceState != null) {
214             int currentPage = savedInstanceState.getInt(STATE_CURRENT_PAGE, -1);
215             if (currentPage != -1) {
216                 mViewPager.setCurrentItem(currentPage, false);
217             }
218         }
219
220         Data.lastUpdateDate.addObserver((o, arg) -> {
221             Log.d("main", "lastUpdateDate changed");
222             runOnUiThread(this::updateLastUpdateDisplay);
223         });
224
225         updateLastUpdateDisplay();
226
227         findViewById(R.id.btn_no_profiles_add)
228                 .setOnClickListener(v -> startEditProfileActivity(null));
229
230         findViewById(R.id.btn_add_transaction).setOnClickListener(this::fabNewTransactionClicked);
231
232         findViewById(R.id.nav_new_profile_button)
233                 .setOnClickListener(v -> startEditProfileActivity(null));
234
235         RecyclerView root = findViewById(R.id.nav_profile_list);
236         if (root == null)
237             throw new RuntimeException("Can't get hold on the transaction value view");
238
239         mProfileListAdapter = new ProfilesRecyclerViewAdapter();
240         root.setAdapter(mProfileListAdapter);
241
242         mProfileListAdapter.addEditingProfilesObserver(new Observer() {
243             @Override
244             public void update(Observable o, Object arg) {
245                 if (mProfileListAdapter.isEditingProfiles()) {
246                     profileListHeadArrow.clearAnimation();
247                     profileListHeadArrow.setVisibility(View.GONE);
248                     profileListHeadMore.setVisibility(View.GONE);
249                     profileListHeadCancel.setVisibility(View.VISIBLE);
250                 }
251                 else {
252                     profileListHeadArrow.setRotation(180f);
253                     profileListHeadArrow.setVisibility(View.VISIBLE);
254                     profileListHeadCancel.setVisibility(View.GONE);
255                     profileListHeadMore.setVisibility(View.GONE);
256                     profileListHeadMore
257                             .setVisibility(profileListExpanded ? View.VISIBLE : View.GONE);
258                 }
259             }
260         });
261
262         LinearLayoutManager llm = new LinearLayoutManager(this);
263
264         llm.setOrientation(RecyclerView.VERTICAL);
265         root.setLayoutManager(llm);
266
267         profileListHeadMore.setOnClickListener((v) -> mProfileListAdapter.flipEditingProfiles());
268         profileListHeadCancel.setOnClickListener((v) -> mProfileListAdapter.flipEditingProfiles());
269         profileListHeadMoreAndCancel
270                 .setOnClickListener((v) -> mProfileListAdapter.flipEditingProfiles());
271
272         drawer.addDrawerListener(new DrawerLayout.SimpleDrawerListener() {
273             @Override
274             public void onDrawerClosed(View drawerView) {
275                 super.onDrawerClosed(drawerView);
276                 collapseProfileList();
277             }
278         });
279     }
280     private void updateLastUpdateDisplay() {
281         TextView v = findViewById(R.id.transactions_last_update);
282         Date date = Data.lastUpdateDate.get();
283         if (date == null) {
284             v.setText(R.string.transaction_last_update_never);
285             Log.d("main", "no last update date :(");
286         }
287         else {
288             final String text = DateFormat.getDateTimeInstance().format(date);
289             v.setText(text);
290             Log.d("main", String.format("Date formatted: %s", text));
291         }
292     }
293     private void profileThemeChanged() {
294         setupProfileColors();
295
296         Bundle bundle = new Bundle();
297         onSaveInstanceState(bundle);
298         // restart activity to reflect theme change
299         finish();
300         Intent intent = new Intent(this, this.getClass());
301         intent.putExtra(BUNDLE_SAVED_STATE, bundle);
302         startActivity(intent);
303     }
304     public void startEditProfileActivity(MobileLedgerProfile profile) {
305         Intent intent = new Intent(this, ProfileDetailActivity.class);
306         Bundle args = new Bundle();
307         if (profile != null) {
308             int index = Data.getProfileIndex(profile);
309             if (index != -1) intent.putExtra(ProfileDetailFragment.ARG_ITEM_ID, index);
310         }
311         intent.putExtras(args);
312         startActivity(intent, args);
313     }
314     private void setupProfile() {
315         String profileUUID = MLDB.getOption(MLDB.OPT_PROFILE_UUID, null);
316         MobileLedgerProfile profile;
317
318         profile = MobileLedgerProfile.loadAllFromDB(profileUUID);
319
320         if (Data.profiles.getList().isEmpty()) {
321             findViewById(R.id.no_profiles_layout).setVisibility(View.VISIBLE);
322             findViewById(R.id.pager_layout).setVisibility(View.GONE);
323             return;
324         }
325
326         findViewById(R.id.pager_layout).setVisibility(View.VISIBLE);
327         findViewById(R.id.no_profiles_layout).setVisibility(View.GONE);
328
329         if (profile == null) profile = Data.profiles.get(0);
330
331         if (profile == null) throw new AssertionError("profile must have a value");
332
333         Data.setCurrentProfile(profile);
334     }
335     public void fabNewTransactionClicked(View view) {
336         Intent intent = new Intent(this, NewTransactionActivity.class);
337         startActivity(intent);
338         overridePendingTransition(R.anim.slide_in_right, R.anim.dummy);
339     }
340     public void navSettingsClicked(View view) {
341         Intent intent = new Intent(this, SettingsActivity.class);
342         startActivity(intent);
343         drawer.closeDrawers();
344     }
345     public void markDrawerItemCurrent(int id) {
346         TextView item = drawer.findViewById(id);
347         item.setBackgroundColor(Colors.tableRowDarkBG);
348
349         LinearLayout actions = drawer.findViewById(R.id.nav_actions);
350         for (int i = 0; i < actions.getChildCount(); i++) {
351             View view = actions.getChildAt(i);
352             if (view.getId() != id) {
353                 view.setBackgroundColor(Color.TRANSPARENT);
354             }
355         }
356     }
357     public void onAccountSummaryClicked(View view) {
358         drawer.closeDrawers();
359
360         showAccountSummaryFragment();
361     }
362     private void showAccountSummaryFragment() {
363         mViewPager.setCurrentItem(0, true);
364         TransactionListFragment.accountFilter.set(null);
365 //        FragmentTransaction ft = fragmentManager.beginTransaction();
366 //        accountSummaryFragment = new AccountSummaryFragment();
367 //        ft.replace(R.id.root_frame, accountSummaryFragment);
368 //        ft.commit();
369 //        currentFragment = accountSummaryFragment;
370     }
371     public void onLatestTransactionsClicked(View view) {
372         drawer.closeDrawers();
373
374         showTransactionsFragment(null);
375     }
376     private void resetFragmentBackStack() {
377 //        fragmentManager.popBackStack(0, FragmentManager.POP_BACK_STACK_INCLUSIVE);
378     }
379     private void showTransactionsFragment(LedgerAccount account) {
380         if (account != null) TransactionListFragment.accountFilter.set(account.getName());
381         mViewPager.setCurrentItem(1, true);
382 //        FragmentTransaction ft = fragmentManager.beginTransaction();
383 //        if (transactionListFragment == null) {
384 //            Log.d("flow", "MainActivity creating TransactionListFragment");
385 //            transactionListFragment = new TransactionListFragment();
386 //        }
387 //        Bundle bundle = new Bundle();
388 //        if (account != null) {
389 //            bundle.putString(TransactionListFragment.BUNDLE_KEY_FILTER_ACCOUNT_NAME,
390 //                    account.getName());
391 //        }
392 //        transactionListFragment.setArguments(bundle);
393 //        ft.replace(R.id.root_frame, transactionListFragment);
394 //        if (account != null)
395 //            ft.addToBackStack(getResources().getString(R.string.title_activity_transaction_list));
396 //        ft.commit();
397 //
398 //        currentFragment = transactionListFragment;
399     }
400     public void showAccountTransactions(LedgerAccount account) {
401         showTransactionsFragment(account);
402     }
403     @Override
404     public void onBackPressed() {
405         DrawerLayout drawer = findViewById(R.id.drawer_layout);
406         if (drawer.isDrawerOpen(GravityCompat.START)) {
407             drawer.closeDrawer(GravityCompat.START);
408         }
409         else {
410             Log.d("fragments",
411                     String.format("manager stack: %d", fragmentManager.getBackStackEntryCount()));
412
413             super.onBackPressed();
414         }
415     }
416     public void updateLastUpdateTextFromDB() {
417         {
418             final MobileLedgerProfile profile = Data.profile.get();
419             long last_update =
420                     (profile != null) ? profile.getLongOption(MLDB.OPT_LAST_SCRAPE, 0L) : 0;
421
422             Log.d("transactions", String.format("Last update = %d", last_update));
423             if (last_update == 0) {
424                 Data.lastUpdateDate.set(null);
425             }
426             else {
427                 Data.lastUpdateDate.set(new Date(last_update));
428             }
429         }
430     }
431     public void scheduleTransactionListRetrieval() {
432         if (Data.profile.get() == null) return;
433
434         retrieveTransactionsTask = new RetrieveTransactionsTask(new WeakReference<>(this));
435
436         retrieveTransactionsTask.execute();
437     }
438     public void onStopTransactionRefreshClick(View view) {
439         Log.d("interactive", "Cancelling transactions refresh");
440         if (retrieveTransactionsTask != null) retrieveTransactionsTask.cancel(false);
441         bTransactionListCancelDownload.setEnabled(false);
442     }
443     public void onRetrieveDone(String error) {
444         progressLayout.setVisibility(View.GONE);
445
446         if (error == null) {
447             updateLastUpdateTextFromDB();
448
449             new RefreshDescriptionsTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
450             TransactionListViewModel.scheduleTransactionListReload();
451         }
452         else Toast.makeText(this, error, Toast.LENGTH_LONG).show();
453     }
454     public void onRetrieveStart() {
455         bTransactionListCancelDownload.setEnabled(true);
456         progressBar.setIndeterminateTintList(ColorStateList.valueOf(Colors.primary));
457         progressBar.setProgressTintList(ColorStateList.valueOf(Colors.primary));
458         progressBar.setIndeterminate(true);
459         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) progressBar.setProgress(0, false);
460         else progressBar.setProgress(0);
461         progressLayout.setVisibility(View.VISIBLE);
462     }
463     public void onRetrieveProgress(RetrieveTransactionsTask.Progress progress) {
464         if ((progress.getTotal() == RetrieveTransactionsTask.Progress.INDETERMINATE) ||
465             (progress.getTotal() == 0))
466         {
467             progressBar.setIndeterminate(true);
468         }
469         else {
470             if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
471                 progressBar.setMin(0);
472             }
473             progressBar.setMax(progress.getTotal());
474             if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
475                 progressBar.setProgress(progress.getProgress(), true);
476             }
477             else progressBar.setProgress(progress.getProgress());
478             progressBar.setIndeterminate(false);
479         }
480     }
481     public void fabShouldShow() {
482         MobileLedgerProfile profile = Data.profile.get();
483         if ((profile != null) && profile.isPostingPermitted()) fab.show();
484     }
485     public void navProfilesHeadClicked(View view) {
486         if (profileListExpanded) {
487             collapseProfileList();
488         }
489         else {
490             expandProfileList();
491         }
492     }
493     private void expandProfileList() {
494         profileListExpanded = true;
495
496
497         profileListContainer.setVisibility(View.VISIBLE);
498         profileListContainer.startAnimation(AnimationUtils.loadAnimation(this, R.anim.slide_down));
499         profileListHeadArrow.startAnimation(AnimationUtils.loadAnimation(this, R.anim.rotate_180));
500         profileListHeadMore.setVisibility(View.VISIBLE);
501         profileListHeadMore.startAnimation(AnimationUtils.loadAnimation(this, R.anim.fade_in));
502         findViewById(R.id.nav_profile_list).setMinimumHeight(
503                 (int) (getResources().getDimension(R.dimen.thumb_row_height) *
504                        Data.profiles.size()));
505     }
506     private void collapseProfileList() {
507         profileListExpanded = false;
508
509         final Animation animation = AnimationUtils.loadAnimation(this, R.anim.slide_up);
510         animation.setAnimationListener(new Animation.AnimationListener() {
511             @Override
512             public void onAnimationStart(Animation animation) {
513
514             }
515             @Override
516             public void onAnimationEnd(Animation animation) {
517                 profileListContainer.setVisibility(View.GONE);
518             }
519             @Override
520             public void onAnimationRepeat(Animation animation) {
521
522             }
523         });
524         mProfileListAdapter.stopEditingProfiles();
525
526         profileListContainer.startAnimation(animation);
527         profileListHeadArrow.setRotation(0f);
528         profileListHeadArrow
529                 .startAnimation(AnimationUtils.loadAnimation(this, R.anim.rotate_180_back));
530         profileListHeadMore.setVisibility(View.GONE);
531     }
532     public void onProfileRowClicked(View v) {
533         Data.setCurrentProfile((MobileLedgerProfile) v.getTag());
534     }
535     public void enableProfileModifications() {
536         profileModificationEnabled = true;
537         ViewGroup profileList = findViewById(R.id.nav_profile_list);
538         for (int i = 0; i < profileList.getChildCount(); i++) {
539             View aRow = profileList.getChildAt(i);
540             aRow.findViewById(R.id.profile_list_edit_button).setVisibility(View.VISIBLE);
541             aRow.findViewById(R.id.profile_list_rearrange_handle).setVisibility(View.VISIBLE);
542         }
543         // FIXME enable rearranging
544
545     }
546     public void disableProfileModifications() {
547         profileModificationEnabled = false;
548         ViewGroup profileList = findViewById(R.id.nav_profile_list);
549         for (int i = 0; i < profileList.getChildCount(); i++) {
550             View aRow = profileList.getChildAt(i);
551             aRow.findViewById(R.id.profile_list_edit_button).setVisibility(View.GONE);
552             aRow.findViewById(R.id.profile_list_rearrange_handle).setVisibility(View.INVISIBLE);
553         }
554         // FIXME disable rearranging
555
556     }
557     public void onAccountSummaryRowViewClicked(View view) {
558         ViewGroup row = (ViewGroup) view.getParent();
559         if (view.getId() == R.id.account_expander_container) {
560             Log.d("accounts", "Account expander clicked");
561             LedgerAccount acc = (LedgerAccount) row.getTag();
562             if (!acc.hasSubAccounts()) return;
563
564             boolean wasExpanded = acc.isExpanded();
565
566             view.clearAnimation();
567             ViewPropertyAnimator animator = view.animate();
568
569             acc.toggleExpanded();
570             Data.profile.get().storeAccount(MLDB.getWritableDatabase(), acc);
571
572             if (wasExpanded) {
573                 Log.d("accounts", String.format("Collapsing account '%s'", acc.getName()));
574                 view.setRotation(0);
575                 animator.rotationBy(180);
576
577                 // removing all child accounts from the view
578                 int start = -1, count = 0;
579                 int i = 0;
580                 final ArrayList<LedgerAccount> accountList = Data.accounts.get();
581                 for (LedgerAccount a : accountList) {
582                     if (acc.isParentOf(a)) {
583                         if (start == -1) {
584                             start = i;
585                         }
586                         count++;
587                     }
588                     else {
589                         if (start != -1) {
590                             break;
591                         }
592                     }
593                     i++;
594                 }
595
596                 if (start != -1) {
597                     for (int j = 0; j < count; j++) {
598                         Log.d("accounts", String.format("Removing item %d: %s", start + j,
599                                 accountList.get(start).getName()));
600                         accountList.remove(start);
601                     }
602
603                     mAccountSummaryFragment.modelAdapter.notifyItemRangeRemoved(start, count);
604                 }
605             }
606             else {
607                 Log.d("accounts", String.format("Expanding account '%s'", acc.getName()));
608                 view.setRotation(180);
609                 animator.rotationBy(-180);
610                 ArrayList<LedgerAccount> accounts = Data.accounts.get();
611                 List<LedgerAccount> children = Data.profile.get().loadVisibleChildAccountsOf(acc);
612                 int parentPos = accounts.indexOf(acc);
613                 if (parentPos == -1) throw new RuntimeException(
614                         "Can't find index of clicked account " + acc.getName());
615                 accounts.addAll(parentPos + 1, children);
616                 mAccountSummaryFragment.modelAdapter
617                         .notifyItemRangeInserted(parentPos + 1, children.size());
618             }
619         }
620     }
621
622     public class SectionsPagerAdapter extends FragmentPagerAdapter {
623
624         public SectionsPagerAdapter(FragmentManager fm) {
625             super(fm);
626         }
627
628         @Override
629         public Fragment getItem(int position) {
630             Log.d("main", String.format("Switching to fragment %d", position));
631             switch (position) {
632                 case 0:
633 //                    Log.d("flow", "Creating account summary fragment");
634                     return mAccountSummaryFragment = new AccountSummaryFragment();
635                 case 1:
636                     return new TransactionListFragment();
637                 default:
638                     throw new IllegalStateException(
639                             String.format("Unexpected fragment index: " + "%d", position));
640             }
641         }
642
643         @Override
644         public int getCount() {
645             return 2;
646         }
647     }
648 }