From: Damyan Ivanov Date: Sun, 11 Apr 2021 20:44:52 +0000 (+0000) Subject: asynchronous profile initialisation X-Git-Tag: v0.18.0~89 X-Git-Url: https://git.ktnx.net/?a=commitdiff_plain;h=fac0809065787fb473646db5770f4f2fae0d1e8f;p=mobile-ledger.git asynchronous profile initialisation the problem here was that the startup profile is read from the DB, synchronously, and all DB operations need to happen via Room off the main thread since at activity startup only the theme is needed, that, and the startup profile ID can be stored in the android preferences. actual loading of profile and further data can be done after the theme is setup, in the background, via Room --- diff --git a/app/src/main/java/net/ktnx/mobileledger/App.java b/app/src/main/java/net/ktnx/mobileledger/App.java index 2fc6d65c..a781ccf1 100644 --- a/app/src/main/java/net/ktnx/mobileledger/App.java +++ b/app/src/main/java/net/ktnx/mobileledger/App.java @@ -18,6 +18,7 @@ package net.ktnx.mobileledger; import android.app.Application; +import android.content.SharedPreferences; import android.content.res.Configuration; import android.content.res.Resources; import android.database.sqlite.SQLiteDatabase; @@ -26,6 +27,7 @@ import android.util.Log; import net.ktnx.mobileledger.db.DB; import net.ktnx.mobileledger.model.Data; import net.ktnx.mobileledger.ui.profiles.ProfileDetailModel; +import net.ktnx.mobileledger.utils.Colors; import net.ktnx.mobileledger.utils.Globals; import net.ktnx.mobileledger.utils.Logger; import net.ktnx.mobileledger.utils.MobileLedgerDatabase; @@ -39,6 +41,9 @@ import java.net.URL; import java.util.Locale; public class App extends Application { + public static final String PREF_NAME = "MoLe"; + public static final String PREF_THEME_HUE = "theme-hue"; + public static final String PREF_PROFILE_ID = "profile-id"; public static App instance; private static ProfileDetailModel profileModel; private MobileLedgerDatabase dbHelper; @@ -58,6 +63,23 @@ public class App extends Application { public static void resetAuthenticationData() { profileModel = null; } + public static void storeStartupProfileAndTheme(long currentProfileId, int currentTheme) { + SharedPreferences prefs = + instance.getSharedPreferences(PREF_NAME, MODE_PRIVATE); + SharedPreferences.Editor editor = prefs.edit(); + editor.putLong(PREF_PROFILE_ID, + currentProfileId); + editor.putInt(PREF_THEME_HUE, currentTheme); + editor.apply(); + } + public static long getStartupProfile() { + SharedPreferences prefs = instance.getSharedPreferences(PREF_NAME, MODE_PRIVATE); + return prefs.getLong(PREF_PROFILE_ID, -1); + } + public static int getStartupTheme() { + SharedPreferences prefs = instance.getSharedPreferences(PREF_NAME, MODE_PRIVATE); + return prefs.getInt(PREF_THEME_HUE, Colors.DEFAULT_HUE_DEG); + } private String getAuthURL() { if (profileModel != null) return profileModel.getUrl(); diff --git a/app/src/main/java/net/ktnx/mobileledger/dao/ProfileDAO.java b/app/src/main/java/net/ktnx/mobileledger/dao/ProfileDAO.java index b4eaf935..52071745 100644 --- a/app/src/main/java/net/ktnx/mobileledger/dao/ProfileDAO.java +++ b/app/src/main/java/net/ktnx/mobileledger/dao/ProfileDAO.java @@ -17,6 +17,7 @@ package net.ktnx.mobileledger.dao; +import androidx.lifecycle.LiveData; import androidx.room.Dao; import androidx.room.Delete; import androidx.room.Insert; @@ -38,4 +39,10 @@ public abstract class ProfileDAO extends BaseDAO { @Query("select * from profiles where id = :profileId") public abstract Profile getByIdSync(long profileId); + + @Query("SELECT * FROM profiles WHERE id=:profileId") + public abstract LiveData getById(long profileId); + + @Query("SELECT * FROM profiles LIMIT 1") + public abstract Profile getAnySync(); } diff --git a/app/src/main/java/net/ktnx/mobileledger/db/DB.java b/app/src/main/java/net/ktnx/mobileledger/db/DB.java index 2e7ca655..ca2d9092 100644 --- a/app/src/main/java/net/ktnx/mobileledger/db/DB.java +++ b/app/src/main/java/net/ktnx/mobileledger/db/DB.java @@ -18,6 +18,7 @@ package net.ktnx.mobileledger.db; import android.content.res.Resources; +import android.database.Cursor; import android.database.SQLException; import androidx.annotation.NonNull; @@ -97,6 +98,25 @@ abstract public class DB extends RoomDatabase { String fileName = String.format(Locale.US, "db_%d", toVersion); applyRevisionFile(db, fileName); + + // when migrating to version 59, migrate profile/theme options to the + // SharedPreferences + if (toVersion == 59) { + try (Cursor c = db.query( + "SELECT p.id, p.theme_hue FROM profiles p WHERE p.id=(SELECT o.value " + + "FROM options WHERE o.profile_uid IS NULL AND o.name=?", + new Object[]{"profile_id"})) + { + if (c.moveToFirst()) { + long currentProfileId = c.getLong(0); + int currentTheme = c.getInt(1); + + if (currentProfileId >= 0 && currentTheme >= 0) { + App.storeStartupProfileAndTheme(currentProfileId, currentTheme); + } + } + } + } } }; } diff --git a/app/src/main/java/net/ktnx/mobileledger/model/Data.java b/app/src/main/java/net/ktnx/mobileledger/model/Data.java index 24e3042a..15d78ac0 100644 --- a/app/src/main/java/net/ktnx/mobileledger/model/Data.java +++ b/app/src/main/java/net/ktnx/mobileledger/model/Data.java @@ -17,21 +17,16 @@ package net.ktnx.mobileledger.model; -import android.database.Cursor; -import android.database.sqlite.SQLiteDatabase; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.Observer; -import net.ktnx.mobileledger.App; import net.ktnx.mobileledger.async.RetrieveTransactionsTask; import net.ktnx.mobileledger.utils.LockHolder; import net.ktnx.mobileledger.utils.Locker; import net.ktnx.mobileledger.utils.Logger; -import net.ktnx.mobileledger.utils.MLDB; import java.text.NumberFormat; import java.text.ParseException; @@ -93,11 +88,9 @@ public final class Data { backgroundTasksRunning.postValue(cnt > 0); } public static void setCurrentProfile(@NonNull MobileLedgerProfile newProfile) { - MLDB.setLongOption(MLDB.OPT_PROFILE_ID, newProfile.getId()); profile.setValue(newProfile); } public static void postCurrentProfile(@NonNull MobileLedgerProfile newProfile) { - MLDB.setLongOption(MLDB.OPT_PROFILE_ID, newProfile.getId()); profile.postValue(newProfile); } public static int getProfileIndex(MobileLedgerProfile profile) { @@ -129,21 +122,6 @@ public final class Data { return -1; } } - public static int retrieveCurrentThemeIdFromDb() { - long profileId = MLDB.getLongOption(MLDB.OPT_PROFILE_ID, 0); - if (profileId == 0) - return -1; - - SQLiteDatabase db = App.getDatabase(); - try (Cursor c = db.rawQuery("SELECT theme from profiles where id=?", - new String[]{String.valueOf(profileId)})) - { - if (c.moveToNext()) - return c.getInt(0); - } - - return -1; - } @Nullable public static MobileLedgerProfile getProfile(long profileId) { MobileLedgerProfile profile; @@ -211,19 +189,6 @@ public final class Data { Observer observer) { profile.observe(lifecycleOwner, observer); } - public synchronized static MobileLedgerProfile initProfile() { - MobileLedgerProfile currentProfile = profile.getValue(); - if (currentProfile != null) - return currentProfile; - - long profileId = MLDB.getLongOption(MLDB.OPT_PROFILE_ID, 0); - MobileLedgerProfile startupProfile = getProfile(profileId); - if (startupProfile != null) - setCurrentProfile(startupProfile); - Logger.debug("profile", "initProfile() returning " + startupProfile); - return startupProfile; - } - public static void removeProfileObservers(LifecycleOwner owner) { profile.removeObservers(owner); } diff --git a/app/src/main/java/net/ktnx/mobileledger/model/MobileLedgerProfile.java b/app/src/main/java/net/ktnx/mobileledger/model/MobileLedgerProfile.java index 0016bb37..8cdc9c91 100644 --- a/app/src/main/java/net/ktnx/mobileledger/model/MobileLedgerProfile.java +++ b/app/src/main/java/net/ktnx/mobileledger/model/MobileLedgerProfile.java @@ -40,6 +40,7 @@ import net.ktnx.mobileledger.dao.TransactionDAO; import net.ktnx.mobileledger.db.AccountValue; import net.ktnx.mobileledger.db.AccountWithAmounts; import net.ktnx.mobileledger.db.DB; +import net.ktnx.mobileledger.db.Profile; import net.ktnx.mobileledger.json.API; import net.ktnx.mobileledger.ui.profiles.ProfileDetailActivity; import net.ktnx.mobileledger.ui.profiles.ProfileDetailFragment; @@ -184,6 +185,26 @@ public final class MobileLedgerProfile { intent.putExtras(args); context.startActivity(intent, args); } + public static MobileLedgerProfile fromDBO(Profile newProfile) { + MobileLedgerProfile p = new MobileLedgerProfile(newProfile.getId()); + p.setDetectedVersion(new HledgerVersion(newProfile.getDetectedVersionMajor(), + newProfile.getDetectedVersionMinor())); + p.setApiVersion(newProfile.getApiVersion()); + p.setAuthEnabled(newProfile.useAuthentication()); + p.setAuthUserName(newProfile.getAuthUser()); + p.setAuthPassword(newProfile.getAuthPassword()); + p.setDefaultCommodity(newProfile.getDefaultCommodity()); + p.setFutureDates(newProfile.getFutureDates()); + p.setName(newProfile.getName()); + p.setPostingPermitted(newProfile.permitPosting()); + p.setPreferredAccountsFilter(newProfile.getPreferredAccountsFilter()); + p.setShowCommentsByDefault(newProfile.getShowCommentsByDefault()); + p.setShowCommodityByDefault(newProfile.getShowCommodityByDefault()); + p.setUrl(newProfile.getUrl()); + p.setThemeId(newProfile.getTheme()); + + return p; + } public HledgerVersion getDetectedVersion() { return detectedVersion; } diff --git a/app/src/main/java/net/ktnx/mobileledger/ui/activity/MainActivity.java b/app/src/main/java/net/ktnx/mobileledger/ui/activity/MainActivity.java index aba83f05..28a1af84 100644 --- a/app/src/main/java/net/ktnx/mobileledger/ui/activity/MainActivity.java +++ b/app/src/main/java/net/ktnx/mobileledger/ui/activity/MainActivity.java @@ -352,8 +352,11 @@ public class MainActivity extends ProfileThemedActivity implements FabManager.Fa .setIcon(Icon.createWithResource(this, R.drawable.thick_plus_icon)) .setIntent(new Intent(Intent.ACTION_VIEW, null, this, - NewTransactionActivity.class).putExtra("profile_id", - p.getId())) + NewTransactionActivity.class).putExtra( + ProfileThemedActivity.PARAM_PROFILE_ID, p.getId()) + .putExtra( + ProfileThemedActivity.PARAM_THEME, + p.getThemeHue())) .setRank(i) .build(); shortcuts.add(si); @@ -456,6 +459,8 @@ public class MainActivity extends ProfileThemedActivity implements FabManager.Fa } public void fabNewTransactionClicked(View view) { Intent intent = new Intent(this, NewTransactionActivity.class); + intent.putExtra(ProfileThemedActivity.PARAM_PROFILE_ID, profile.getId()); + intent.putExtra(ProfileThemedActivity.PARAM_THEME, profile.getThemeHue()); startActivity(intent); overridePendingTransition(R.anim.slide_in_up, R.anim.dummy); } diff --git a/app/src/main/java/net/ktnx/mobileledger/ui/activity/ProfileThemedActivity.java b/app/src/main/java/net/ktnx/mobileledger/ui/activity/ProfileThemedActivity.java index 07aae23c..4aa60241 100644 --- a/app/src/main/java/net/ktnx/mobileledger/ui/activity/ProfileThemedActivity.java +++ b/app/src/main/java/net/ktnx/mobileledger/ui/activity/ProfileThemedActivity.java @@ -18,33 +18,52 @@ package net.ktnx.mobileledger.ui.activity; import android.annotation.SuppressLint; +import android.os.AsyncTask; import android.os.Bundle; import androidx.annotation.Nullable; +import net.ktnx.mobileledger.App; +import net.ktnx.mobileledger.dao.ProfileDAO; +import net.ktnx.mobileledger.db.DB; +import net.ktnx.mobileledger.db.Profile; import net.ktnx.mobileledger.model.Data; import net.ktnx.mobileledger.model.MobileLedgerProfile; import net.ktnx.mobileledger.utils.Colors; import net.ktnx.mobileledger.utils.Logger; +import java.util.Locale; + @SuppressLint("Registered") public class ProfileThemedActivity extends CrashReportingActivity { + public static final String TAG = "prf-thm-act"; + protected static final String PARAM_PROFILE_ID = "profile-id"; + protected static final String PARAM_THEME = "theme"; protected MobileLedgerProfile mProfile; private boolean themeSetUp = false; private boolean mIgnoreProfileChange; - protected void setupProfileColors() { - final int themeHue = (mProfile == null) ? -1 : mProfile.getThemeHue(); + private int mThemeHue; + protected void setupProfileColors(int newHue) { + if (themeSetUp && newHue == mThemeHue) { + Logger.debug(TAG, + String.format(Locale.ROOT, "Ignore request to set theme to the same value (%d)", + newHue)); + return; + } - Colors.setupTheme(this, themeHue); + Logger.debug(TAG, + String.format(Locale.ROOT, "Changing theme from %d to %d", mThemeHue, newHue)); + + mThemeHue = newHue; + Colors.setupTheme(this, mThemeHue); if (themeSetUp) { - Logger.debug("prf-thm-act", - "setupProfileColors(): theme already set up, recreating activity"); + Logger.debug(TAG, "setupProfileColors(): theme already set up, recreating activity"); this.recreate(); } themeSetUp = true; - Colors.profileThemeId = Data.retrieveCurrentThemeIdFromDb(); + Colors.profileThemeId = mThemeHue; } @Override protected void onStart() { @@ -53,21 +72,53 @@ public class ProfileThemedActivity extends CrashReportingActivity { } protected void onCreate(@Nullable Bundle savedInstanceState) { initProfile(); - setupProfileColors(); - mIgnoreProfileChange = true; Data.observeProfile(this, profile -> { - if (!mIgnoreProfileChange) { - mProfile = profile; - setupProfileColors(); + if (profile == null) { + Logger.debug(TAG, "No current profile, leaving"); + finish(); + return; } - mIgnoreProfileChange = false; + mProfile = profile; + int hue = profile.getThemeHue(); + + if (hue != mThemeHue) { + storeProfilePref(profile); + setupProfileColors(hue); + } }); super.onCreate(savedInstanceState); } + public void storeProfilePref(MobileLedgerProfile profile) { + App.storeStartupProfileAndTheme(profile.getId(), profile.getThemeHue()); + } protected void initProfile() { - mProfile = Data.initProfile(); + long profileId = App.getStartupProfile(); + int hue = App.getStartupTheme(); + if (profileId == -1) + mThemeHue = Colors.DEFAULT_HUE_DEG; + + setupProfileColors(hue); + + initProfile(profileId); + } + protected void initProfile(long profileId) { + AsyncTask.execute(() -> initProfileAsync(profileId)); + } + private void initProfileAsync(long profileId) { + ProfileDAO dao = DB.get() + .getProfileDAO(); + Profile profile = dao.getByIdSync(profileId); + + if (profile == null) { + Logger.debug(TAG, String.format(Locale.ROOT, "Profile %d not found. Trying any other", + profileId)); + + profile = dao.getAnySync(); + } + + Data.postCurrentProfile(MobileLedgerProfile.fromDBO(profile)); } } diff --git a/app/src/main/java/net/ktnx/mobileledger/ui/activity/SplashActivity.java b/app/src/main/java/net/ktnx/mobileledger/ui/activity/SplashActivity.java index 41f3cddf..9b35b947 100644 --- a/app/src/main/java/net/ktnx/mobileledger/ui/activity/SplashActivity.java +++ b/app/src/main/java/net/ktnx/mobileledger/ui/activity/SplashActivity.java @@ -25,10 +25,8 @@ import android.os.Handler; import androidx.annotation.Nullable; import net.ktnx.mobileledger.R; -import net.ktnx.mobileledger.model.Data; import net.ktnx.mobileledger.model.MobileLedgerProfile; import net.ktnx.mobileledger.utils.Logger; -import net.ktnx.mobileledger.utils.MLDB; import net.ktnx.mobileledger.utils.MobileLedgerDatabase; public class SplashActivity extends CrashReportingActivity { @@ -103,10 +101,6 @@ public class SplashActivity extends CrashReportingActivity { protected Void doInBackground(Void... voids) { MobileLedgerProfile.loadAllFromDB(0); - long profileId = MLDB.getLongOption(MLDB.OPT_PROFILE_ID, 0); - MobileLedgerProfile startupProfile = Data.getProfile(profileId); - if (startupProfile != null) - Data.postCurrentProfile(startupProfile); return null; } @Override diff --git a/app/src/main/java/net/ktnx/mobileledger/ui/new_transaction/NewTransactionActivity.java b/app/src/main/java/net/ktnx/mobileledger/ui/new_transaction/NewTransactionActivity.java index 469e773e..11d014d3 100644 --- a/app/src/main/java/net/ktnx/mobileledger/ui/new_transaction/NewTransactionActivity.java +++ b/app/src/main/java/net/ktnx/mobileledger/ui/new_transaction/NewTransactionActivity.java @@ -72,6 +72,7 @@ public class NewTransactionActivity extends ProfileThemedActivity implements TaskCallback, NewTransactionFragment.OnNewTransactionFragmentInteractionListener, QR.QRScanTrigger, QR.QRScanResultReceiver, DescriptionSelectedCallback, FabManager.FabHandler { + final String TAG = "new-t-a"; private NavController navController; private NewTransactionModel model; private ActivityResultLauncher qrScanLauncher; @@ -115,16 +116,23 @@ public class NewTransactionActivity extends ProfileThemedActivity } @Override protected void initProfile() { - long profileId = getIntent().getLongExtra("profile_id", 0); + long profileId = getIntent().getLongExtra(PARAM_PROFILE_ID, 0); + int profileHue = getIntent().getIntExtra(PARAM_THEME, -1); - if (profileId != 0) { - mProfile = Data.getProfile(profileId); - if (mProfile == null) - finish(); - Data.setCurrentProfile(mProfile); + if (profileHue < 0) { + Logger.debug(TAG, "Started with invalid/missing theme; quitting"); + finish(); + return; } - else - super.initProfile(); + + if (profileId <= 0) { + Logger.debug(TAG, "Started with invalid/missing profile_id; quitting"); + finish(); + return; + } + + setupProfileColors(profileHue); + initProfile(profileId); } @Override public void finish() { diff --git a/app/src/main/java/net/ktnx/mobileledger/ui/profiles/ProfilesRecyclerViewAdapter.java b/app/src/main/java/net/ktnx/mobileledger/ui/profiles/ProfilesRecyclerViewAdapter.java index 21984333..e7346b1b 100644 --- a/app/src/main/java/net/ktnx/mobileledger/ui/profiles/ProfilesRecyclerViewAdapter.java +++ b/app/src/main/java/net/ktnx/mobileledger/ui/profiles/ProfilesRecyclerViewAdapter.java @@ -39,6 +39,7 @@ import androidx.recyclerview.widget.RecyclerView; import net.ktnx.mobileledger.R; import net.ktnx.mobileledger.model.Data; import net.ktnx.mobileledger.model.MobileLedgerProfile; +import net.ktnx.mobileledger.ui.activity.MainActivity; import net.ktnx.mobileledger.utils.Colors; import java.lang.ref.WeakReference; @@ -148,8 +149,10 @@ public class ProfilesRecyclerViewAdapter if (profile == null) throw new IllegalStateException("Profile row without associated profile"); debug("profiles", "Setting profile to " + profile.getName()); - if (Data.getProfile() != profile) + if (Data.getProfile() != profile) { Data.drawerOpen.setValue(false); + ((MainActivity) v.getContext()).storeProfilePref(profile); + } Data.setCurrentProfile(profile); } @NonNull