]> git.ktnx.net Git - mobile-ledger.git/blobdiff - app/src/main/java/net/ktnx/mobileledger/ui/activity/MainActivity.java
more pronounced day/month delimiters in the transaction list
[mobile-ledger.git] / app / src / main / java / net / ktnx / mobileledger / ui / activity / MainActivity.java
index c917bb9e1c8d53abc25a5e1ad0189ae49b0bcd02..e686d96fdb37233dd2a0ffcd372b6ebdf359ba96 100644 (file)
@@ -41,6 +41,8 @@ import androidx.core.view.GravityCompat;
 import androidx.drawerlayout.widget.DrawerLayout;
 import androidx.fragment.app.Fragment;
 import androidx.fragment.app.FragmentActivity;
+import androidx.lifecycle.LiveData;
+import androidx.lifecycle.MutableLiveData;
 import androidx.lifecycle.ViewModelProvider;
 import androidx.recyclerview.widget.LinearLayoutManager;
 import androidx.recyclerview.widget.RecyclerView;
@@ -49,12 +51,17 @@ import androidx.viewpager2.widget.ViewPager2;
 
 import com.google.android.material.snackbar.Snackbar;
 
+import net.ktnx.mobileledger.BackupsActivity;
 import net.ktnx.mobileledger.R;
 import net.ktnx.mobileledger.async.RetrieveTransactionsTask;
+import net.ktnx.mobileledger.async.TransactionAccumulator;
 import net.ktnx.mobileledger.databinding.ActivityMainBinding;
 import net.ktnx.mobileledger.db.DB;
+import net.ktnx.mobileledger.db.Option;
 import net.ktnx.mobileledger.db.Profile;
+import net.ktnx.mobileledger.db.TransactionWithAccounts;
 import net.ktnx.mobileledger.model.Data;
+import net.ktnx.mobileledger.model.LedgerTransaction;
 import net.ktnx.mobileledger.ui.FabManager;
 import net.ktnx.mobileledger.ui.MainModel;
 import net.ktnx.mobileledger.ui.account_summary.AccountSummaryFragment;
@@ -65,7 +72,7 @@ import net.ktnx.mobileledger.ui.templates.TemplatesActivity;
 import net.ktnx.mobileledger.ui.transaction_list.TransactionListFragment;
 import net.ktnx.mobileledger.utils.Colors;
 import net.ktnx.mobileledger.utils.Logger;
-import net.ktnx.mobileledger.utils.MLDB;
+import net.ktnx.mobileledger.utils.Misc;
 
 import org.jetbrains.annotations.NotNull;
 
@@ -80,11 +87,13 @@ import java.util.Objects;
  *  */
 
 public class MainActivity extends ProfileThemedActivity implements FabManager.FabHandler {
+    public static final String TAG = "main-act";
     public static final String STATE_CURRENT_PAGE = "current_page";
     public static final String BUNDLE_SAVED_STATE = "bundle_savedState";
     public static final String STATE_ACC_FILTER = "account_filter";
     private static final boolean FAB_HIDDEN = false;
     private static final boolean FAB_SHOWN = true;
+    private ConverterThread converterThread = null;
     private SectionsPagerAdapter mSectionsPagerAdapter;
     private ProfilesRecyclerViewAdapter mProfileListAdapter;
     private int mCurrentPage;
@@ -101,7 +110,7 @@ public class MainActivity extends ProfileThemedActivity implements FabManager.Fa
     protected void onStart() {
         super.onStart();
 
-        Logger.debug("MainActivity", "onStart()");
+        Logger.debug(TAG, "onStart()");
 
         b.mainPager.setCurrentItem(mCurrentPage, false);
     }
@@ -133,9 +142,9 @@ public class MainActivity extends ProfileThemedActivity implements FabManager.Fa
     }
     @Override
     protected void onCreate(Bundle savedInstanceState) {
-        Logger.debug("MainActivity", "onCreate()/entry");
+        Logger.debug(TAG, "onCreate()/entry");
         super.onCreate(savedInstanceState);
-        Logger.debug("MainActivity", "onCreate()/after super");
+        Logger.debug(TAG, "onCreate()/after super");
         b = ActivityMainBinding.inflate(getLayoutInflater());
         setContentView(b.getRoot());
 
@@ -158,8 +167,7 @@ public class MainActivity extends ProfileThemedActivity implements FabManager.Fa
         Data.backgroundTasksRunning.observe(this, this::onRetrieveRunningChanged);
 
         if (barDrawerToggle == null) {
-            barDrawerToggle = new ActionBarDrawerToggle(this, b.drawerLayout, b.toolbar,
-                    R.string.navigation_drawer_open, R.string.navigation_drawer_close);
+            barDrawerToggle = new ActionBarDrawerToggle(this, b.drawerLayout, b.toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
             b.drawerLayout.addDrawerListener(barDrawerToggle);
         }
         barDrawerToggle.syncState();
@@ -168,8 +176,7 @@ public class MainActivity extends ProfileThemedActivity implements FabManager.Fa
             PackageInfo pi = getApplicationContext().getPackageManager()
                                                     .getPackageInfo(getPackageName(), 0);
             ((TextView) b.navUpper.findViewById(R.id.drawer_version_text)).setText(pi.versionName);
-            ((TextView) b.noProfilesLayout.findViewById(R.id.drawer_version_text)).setText(
-                    pi.versionName);
+            ((TextView) b.noProfilesLayout.findViewById(R.id.drawer_version_text)).setText(pi.versionName);
         }
         catch (Exception e) {
             e.printStackTrace();
@@ -178,6 +185,7 @@ public class MainActivity extends ProfileThemedActivity implements FabManager.Fa
         markDrawerItemCurrent(R.id.nav_account_summary);
 
         b.mainPager.setAdapter(mSectionsPagerAdapter);
+        b.mainPager.setOffscreenPageLimit(1);
 
         if (pageChangeCallback == null) {
             pageChangeCallback = new ViewPager2.OnPageChangeCallback() {
@@ -192,8 +200,7 @@ public class MainActivity extends ProfileThemedActivity implements FabManager.Fa
                             markDrawerItemCurrent(R.id.nav_latest_transactions);
                             break;
                         default:
-                            Log.e("MainActivity",
-                                    String.format("Unexpected page index %d", position));
+                            Log.e(TAG, String.format("Unexpected page index %d", position));
                     }
 
                     super.onPageSelected(position);
@@ -213,6 +220,7 @@ public class MainActivity extends ProfileThemedActivity implements FabManager.Fa
         }
 
         b.btnNoProfilesAdd.setOnClickListener(v -> ProfileDetailActivity.start(this, null));
+        b.btnRestore.setOnClickListener(v -> BackupsActivity.start(this));
 
         b.btnAddTransaction.setOnClickListener(this::fabNewTransactionClicked);
 
@@ -263,10 +271,8 @@ public class MainActivity extends ProfileThemedActivity implements FabManager.Fa
         b.navProfileList.setLayoutManager(llm);
 
         b.navProfilesStartEdit.setOnClickListener((v) -> mProfileListAdapter.flipEditingProfiles());
-        b.navProfilesCancelEdit.setOnClickListener(
-                (v) -> mProfileListAdapter.flipEditingProfiles());
-        b.navProfileListHeadButtons.setOnClickListener(
-                (v) -> mProfileListAdapter.flipEditingProfiles());
+        b.navProfilesCancelEdit.setOnClickListener((v) -> mProfileListAdapter.flipEditingProfiles());
+        b.navProfileListHeadButtons.setOnClickListener((v) -> mProfileListAdapter.flipEditingProfiles());
         if (drawerListener == null) {
             drawerListener = new DrawerLayout.SimpleDrawerListener() {
                 @Override
@@ -316,6 +322,11 @@ public class MainActivity extends ProfileThemedActivity implements FabManager.Fa
         b.navAccountSummary.setOnClickListener(this::onAccountSummaryClicked);
         b.navLatestTransactions.setOnClickListener(this::onLatestTransactionsClicked);
         b.navPatterns.setOnClickListener(this::onPatternsClick);
+        b.navBackupRestore.setOnClickListener(this::onBackupRestoreClick);
+    }
+    private void onBackupRestoreClick(View view) {
+        Intent intent = new Intent(this, BackupsActivity.class);
+        startActivity(intent);
     }
     private void onPatternsClick(View view) {
         Intent intent = new Intent(this, TemplatesActivity.class);
@@ -334,7 +345,7 @@ public class MainActivity extends ProfileThemedActivity implements FabManager.Fa
             mainModel.scheduleTransactionListRetrieval();
         }
     }
-    private void createShortcuts(List<Profile> list) {
+    private void createShortcuts(@NotNull List<Profile> list) {
         if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N_MR1)
             return;
 
@@ -366,8 +377,10 @@ public class MainActivity extends ProfileThemedActivity implements FabManager.Fa
         }
         sm.setDynamicShortcuts(shortcuts);
     }
-    private void onProfileListChanged(List<Profile> newList) {
-        if ((newList == null) || newList.isEmpty()) {
+    private void onProfileListChanged(@NotNull List<Profile> newList) {
+        createShortcuts(newList);
+
+        if (newList.isEmpty()) {
             b.noProfilesLayout.setVisibility(View.VISIBLE);
             b.mainAppLayout.setVisibility(View.GONE);
             return;
@@ -382,23 +395,30 @@ public class MainActivity extends ProfileThemedActivity implements FabManager.Fa
         Logger.debug("profiles", "profile list changed");
         mProfileListAdapter.setProfileList(newList);
 
-        createShortcuts(newList);
+        final Profile currentProfile = Data.getProfile();
+        Profile replacementProfile = null;
+        if (currentProfile != null) {
+            for (Profile p : newList) {
+                if (p.getId() == currentProfile.getId()) {
+                    replacementProfile = p;
+                    break;
+                }
+            }
+        }
 
-        Profile currentProfile = Data.getProfile();
-        if (currentProfile == null || !newList.contains(currentProfile)) {
+        if (replacementProfile == null) {
             Logger.debug(TAG, "Switching profile because the current is no longer available");
             Data.setCurrentProfile(newList.get(0));
         }
+        else {
+            Data.setCurrentProfile(replacementProfile);
+        }
     }
     /**
      * called when the current profile has changed
      */
     private void onProfileChanged(@Nullable Profile newProfile) {
-        if (this.profile == null) {
-            if (newProfile == null)
-                return;
-        }
-        else {
+        if (this.profile != null) {
             if (this.profile.equals(newProfile))
                 return;
         }
@@ -410,7 +430,7 @@ public class MainActivity extends ProfileThemedActivity implements FabManager.Fa
         else
             setTitle(R.string.app_name);
 
-        int newProfileTheme = haveProfile ? newProfile.getTheme() : -1;
+        int newProfileTheme = haveProfile ? newProfile.getTheme() : Colors.DEFAULT_HUE_DEG;
         if (newProfileTheme != Colors.profileThemeId) {
             Logger.debug("profiles",
                     String.format(Locale.ENGLISH, "profile theme %d → %d", Colors.profileThemeId,
@@ -455,14 +475,40 @@ public class MainActivity extends ProfileThemedActivity implements FabManager.Fa
             return;
         }
 
-        mainModel.stopTransactionsRetrieval();
+        mainModel.getAccountFilter()
+                 .observe(this, this::onAccountFilterChanged);
 
+        mainModel.stopTransactionsRetrieval();
         mainModel.clearTransactions();
-
-        if (haveProfile) {
-            Logger.debug("transactions", "requesting list reload");
-            mainModel.scheduleTransactionListReload();
+    }
+    private void onAccountFilterChanged(String accFilter) {
+        Logger.debug(TAG, "account filter changed, reloading transactions");
+//                     mainModel.scheduleTransactionListReload();
+        LiveData<List<TransactionWithAccounts>> transactions =
+                new MutableLiveData<>(new ArrayList<>());
+        if (profile != null) {
+            if (accFilter == null || accFilter.isEmpty()) {
+                transactions = DB.get()
+                                 .getTransactionDAO()
+                                 .getAllWithAccounts(profile.getId());
+            }
+            else {
+                transactions = DB.get()
+                                 .getTransactionDAO()
+                                 .getAllWithAccountsFiltered(profile.getId(), accFilter);
+            }
         }
+
+        transactions.observe(this, list -> {
+            Logger.debug(TAG,
+                    String.format(Locale.ROOT, "got transaction list from DB (%d transactions)",
+                            list.size()));
+
+            if (converterThread != null)
+                converterThread.interrupt();
+            converterThread = new ConverterThread(mainModel, list, accFilter);
+            converterThread.start();
+        });
     }
     private void profileThemeChanged() {
         // un-hook all observed LiveData
@@ -472,7 +518,7 @@ public class MainActivity extends ProfileThemedActivity implements FabManager.Fa
         Data.lastUpdateAccountCount.removeObservers(this);
         Data.lastUpdateDate.removeObservers(this);
 
-        Logger.debug("MainActivity", "profileThemeChanged(): recreating activity");
+        Logger.debug(TAG, "profileThemeChanged(): recreating activity");
         recreate();
     }
     public void fabNewTransactionClicked(View view) {
@@ -530,7 +576,7 @@ public class MainActivity extends ProfileThemedActivity implements FabManager.Fa
                 mBackMeansToAccountList = false;
             }
             else {
-                Logger.debug("fragments", String.format(Locale.ENGLISH, "manager stack: %d",
+                Logger.debug(TAG, String.format(Locale.ENGLISH, "manager stack: %d",
                         getSupportFragmentManager().getBackStackEntryCount()));
 
                 super.onBackPressed();
@@ -543,7 +589,7 @@ public class MainActivity extends ProfileThemedActivity implements FabManager.Fa
 
         DB.get()
           .getOptionDAO()
-          .load(profile.getId(), MLDB.OPT_LAST_SCRAPE)
+          .load(profile.getId(), Option.OPT_LAST_SCRAPE)
           .observe(this, opt -> {
               long lastUpdate = 0;
               if (opt != null) {
@@ -592,7 +638,7 @@ public class MainActivity extends ProfileThemedActivity implements FabManager.Fa
         }
     }
     public void onStopTransactionRefreshClick(View view) {
-        Logger.debug("interactive", "Cancelling transactions refresh");
+        Logger.debug(TAG, "Cancelling transactions refresh");
         mainModel.stopTransactionsRetrieval();
         b.transactionListCancelDownload.setEnabled(false);
     }
@@ -619,7 +665,7 @@ public class MainActivity extends ProfileThemedActivity implements FabManager.Fa
         if (progress == null ||
             progress.getState() == RetrieveTransactionsTask.ProgressState.FINISHED)
         {
-            Logger.debug("progress", "Done");
+            Logger.debug(TAG, "progress: Done");
             b.transactionProgressLayout.setVisibility(View.GONE);
 
             mainModel.transactionRetrievalDone();
@@ -632,7 +678,7 @@ public class MainActivity extends ProfileThemedActivity implements FabManager.Fa
                 AlertDialog.Builder builder = new AlertDialog.Builder(this);
                 builder.setMessage(error);
                 builder.setPositiveButton(R.string.btn_profile_options, (dialog, which) -> {
-                    Logger.debug("error", "will start profile editor");
+                    Logger.debug(TAG, "will start profile editor");
                     ProfileDetailActivity.start(this, profile);
                 });
                 builder.create()
@@ -656,14 +702,15 @@ public class MainActivity extends ProfileThemedActivity implements FabManager.Fa
 
         if (progress.isIndeterminate() || (progress.getTotal() <= 0)) {
             b.transactionListProgressBar.setIndeterminate(true);
-            Logger.debug("progress", "indeterminate");
+            Logger.debug(TAG, "progress: indeterminate");
         }
         else {
             if (b.transactionListProgressBar.isIndeterminate()) {
                 b.transactionListProgressBar.setIndeterminate(false);
             }
-//            Logger.debug("progress",
-//                    String.format(Locale.US, "%d/%d", progress.getProgress(), progress.getTotal
+//            Logger.debug(TAG,
+//                    String.format(Locale.US, "progress: %d/%d", progress.getProgress(),
+//                    progress.getTotal
 //                    ()));
             b.transactionListProgressBar.setMax(progress.getTotal());
             // for some reason animation doesn't work - no progress is shown (stick at 0)
@@ -698,11 +745,10 @@ public class MainActivity extends ProfileThemedActivity implements FabManager.Fa
         @NotNull
         @Override
         public Fragment createFragment(int position) {
-            Logger.debug("main",
-                    String.format(Locale.ENGLISH, "Switching to fragment %d", position));
+            Logger.debug(TAG, String.format(Locale.ENGLISH, "Switching to fragment %d", position));
             switch (position) {
                 case 0:
-//                    debug("flow", "Creating account summary fragment");
+//                    debug(TAG, "Creating account summary fragment");
                     return new AccountSummaryFragment();
                 case 1:
                     return new TransactionListFragment();
@@ -717,4 +763,37 @@ public class MainActivity extends ProfileThemedActivity implements FabManager.Fa
             return 2;
         }
     }
+
+    static private class ConverterThread extends Thread {
+        private final List<TransactionWithAccounts> list;
+        private final MainModel model;
+        private final String accFilter;
+        public ConverterThread(@NonNull MainModel model,
+                               @NonNull List<TransactionWithAccounts> list, String accFilter) {
+            this.model = model;
+            this.list = list;
+            this.accFilter = accFilter;
+        }
+        @Override
+        public void run() {
+            TransactionAccumulator accumulator = new TransactionAccumulator(accFilter, accFilter);
+
+            for (TransactionWithAccounts tr : list) {
+                if (isInterrupted()) {
+                    Logger.debug(TAG, "ConverterThread bailing out on interrupt");
+                    return;
+                }
+                accumulator.put(new LedgerTransaction(tr));
+            }
+
+            if (isInterrupted()) {
+                Logger.debug(TAG, "ConverterThread bailing out on interrupt");
+                return;
+            }
+
+            Logger.debug(TAG, "ConverterThread publishing results");
+
+            Misc.onMainThread(() -> accumulator.publishResults(model));
+        }
+    }
 }