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;
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;
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;
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();
package net.ktnx.mobileledger.dao;
+import androidx.lifecycle.LiveData;
import androidx.room.Dao;
import androidx.room.Delete;
import androidx.room.Insert;
@Query("select * from profiles where id = :profileId")
public abstract Profile getByIdSync(long profileId);
+
+ @Query("SELECT * FROM profiles WHERE id=:profileId")
+ public abstract LiveData<Profile> getById(long profileId);
+
+ @Query("SELECT * FROM profiles LIMIT 1")
+ public abstract Profile getAnySync();
}
package net.ktnx.mobileledger.db;
import android.content.res.Resources;
+import android.database.Cursor;
import android.database.SQLException;
import androidx.annotation.NonNull;
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);
+ }
+ }
+ }
+ }
}
};
}
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;
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) {
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;
Observer<MobileLedgerProfile> 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);
}
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;
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;
}
.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);
}
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);
}
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() {
}
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));
}
}
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 {
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
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<Void> qrScanLauncher;
}
@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() {
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;
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