mirror of
https://github.com/iSoron/uhabits.git
synced 2025-12-06 01:08:50 -06:00
Add sync preferences to settings screen
This commit is contained in:
@@ -26,17 +26,15 @@ import android.os.*;
|
||||
import android.provider.*;
|
||||
import android.util.*;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.preference.ListPreference;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceCategory;
|
||||
import androidx.preference.PreferenceFragmentCompat;
|
||||
import androidx.annotation.*;
|
||||
import androidx.preference.*;
|
||||
|
||||
import org.isoron.uhabits.R;
|
||||
import org.isoron.uhabits.*;
|
||||
import org.isoron.uhabits.core.preferences.*;
|
||||
import org.isoron.uhabits.core.ui.*;
|
||||
import org.isoron.uhabits.core.utils.*;
|
||||
import org.isoron.uhabits.intents.*;
|
||||
import org.isoron.uhabits.notifications.*;
|
||||
import org.isoron.uhabits.widgets.*;
|
||||
|
||||
@@ -47,7 +45,7 @@ import static android.os.Build.VERSION.*;
|
||||
import static org.isoron.uhabits.activities.habits.list.ListHabitsScreenKt.*;
|
||||
|
||||
public class SettingsFragment extends PreferenceFragmentCompat
|
||||
implements SharedPreferences.OnSharedPreferenceChangeListener
|
||||
implements SharedPreferences.OnSharedPreferenceChangeListener
|
||||
{
|
||||
private static int RINGTONE_REQUEST_CODE = 1;
|
||||
|
||||
@@ -55,7 +53,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
||||
|
||||
private RingtoneManager ringtoneManager;
|
||||
|
||||
@Nullable
|
||||
@NonNull
|
||||
private Preferences prefs;
|
||||
|
||||
@Nullable
|
||||
@@ -93,6 +91,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
||||
setResultOnPreferenceClick("exportDB", RESULT_EXPORT_DB);
|
||||
setResultOnPreferenceClick("repairDB", RESULT_REPAIR_DB);
|
||||
setResultOnPreferenceClick("bugReport", RESULT_BUG_REPORT);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -142,24 +141,31 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
||||
sharedPrefs = getPreferenceManager().getSharedPreferences();
|
||||
sharedPrefs.registerOnSharedPreferenceChangeListener(this);
|
||||
|
||||
if (prefs != null && !prefs.isDeveloper())
|
||||
if (!prefs.isDeveloper())
|
||||
{
|
||||
PreferenceCategory devCategory =
|
||||
(PreferenceCategory) findPreference("devCategory");
|
||||
(PreferenceCategory) findPreference("devCategory");
|
||||
devCategory.removeAll();
|
||||
devCategory.setVisible(false);
|
||||
}
|
||||
|
||||
updateWeekdayPreference();
|
||||
updateSyncPreferences();
|
||||
|
||||
// Temporarily disable this; we now always ask
|
||||
findPreference("reminderSound").setVisible(false);
|
||||
findPreference("pref_snooze_interval").setVisible(false);
|
||||
}
|
||||
|
||||
private void updateSyncPreferences()
|
||||
{
|
||||
findPreference("pref_sync_base_url").setSummary(prefs.getSyncBaseURL());
|
||||
findPreference("pref_sync_key").setSummary(prefs.getSyncKey());
|
||||
findPreference("pref_sync_display").setVisible(prefs.isSyncEnabled());
|
||||
}
|
||||
|
||||
private void updateWeekdayPreference()
|
||||
{
|
||||
if (prefs == null) return;
|
||||
ListPreference weekdayPref = (ListPreference) findPreference("pref_first_weekday");
|
||||
int currentFirstWeekday = prefs.getFirstWeekday();
|
||||
String[] dayNames = DateUtils.getLongWeekdayNames(Calendar.SATURDAY);
|
||||
@@ -179,8 +185,18 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
||||
Log.d("SettingsFragment", "updating widgets");
|
||||
widgetUpdater.updateWidgets();
|
||||
}
|
||||
if (key.equals("pref_first_weekday")) updateWeekdayPreference();
|
||||
if (key.equals("pref_sync_enabled"))
|
||||
{
|
||||
Context context = getActivity();
|
||||
if (prefs.isSyncEnabled())
|
||||
{
|
||||
Intent intent = new IntentFactory().startSyncActivity(context);
|
||||
context.startActivity(intent);
|
||||
}
|
||||
}
|
||||
BackupManager.dataChanged("org.isoron.uhabits");
|
||||
updateWeekdayPreference();
|
||||
updateSyncPreferences();
|
||||
}
|
||||
|
||||
private void setResultOnPreferenceClick(String key, final int result)
|
||||
|
||||
@@ -33,6 +33,7 @@ import org.isoron.androidbase.utils.*
|
||||
import org.isoron.androidbase.utils.InterfaceUtils.getFontAwesome
|
||||
import org.isoron.uhabits.*
|
||||
import org.isoron.uhabits.activities.*
|
||||
import org.isoron.uhabits.core.preferences.*
|
||||
import org.isoron.uhabits.core.tasks.*
|
||||
import org.isoron.uhabits.databinding.*
|
||||
import org.isoron.uhabits.sync.*
|
||||
@@ -40,6 +41,7 @@ import org.isoron.uhabits.sync.*
|
||||
|
||||
class SyncActivity : BaseActivity() {
|
||||
|
||||
private lateinit var preferences: Preferences
|
||||
private lateinit var taskRunner: TaskRunner
|
||||
private lateinit var baseScreen: BaseScreen
|
||||
private lateinit var themeSwitcher: AndroidThemeSwitcher
|
||||
@@ -54,6 +56,7 @@ class SyncActivity : BaseActivity() {
|
||||
themeSwitcher = AndroidThemeSwitcher(this, component.preferences)
|
||||
themeSwitcher.apply()
|
||||
taskRunner = component.taskRunner
|
||||
preferences = component.preferences
|
||||
|
||||
binding = ActivitySyncBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
@@ -74,27 +77,41 @@ class SyncActivity : BaseActivity() {
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
if(preferences.syncKey.isBlank()) {
|
||||
register()
|
||||
} else {
|
||||
displayCurrentKey()
|
||||
}
|
||||
}
|
||||
|
||||
private fun displayCurrentKey() {
|
||||
displayLink("${preferences.syncBaseURL}/sync/${preferences.syncKey}")
|
||||
displayPassword("6B2W9F5X")
|
||||
}
|
||||
|
||||
private fun register() {
|
||||
displayLoading()
|
||||
taskRunner.execute(object : Task {
|
||||
private var key = ""
|
||||
private var error = false
|
||||
override fun doInBackground() {
|
||||
runBlocking {
|
||||
val server = RemoteSyncServer()
|
||||
val server = RemoteSyncServer(baseURL = preferences.syncBaseURL)
|
||||
try {
|
||||
key = server.register()
|
||||
} catch(e: ServiceUnavailable) {
|
||||
} catch (e: ServiceUnavailable) {
|
||||
error = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPostExecute() {
|
||||
if(error) {
|
||||
if (error) {
|
||||
displayError()
|
||||
} else {
|
||||
displayLink("https://loophabits.org/sync/$key")
|
||||
displayPassword("6B2W9F5X")
|
||||
return;
|
||||
}
|
||||
preferences.syncKey = key;
|
||||
displayCurrentKey()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -21,13 +21,13 @@ package org.isoron.uhabits.intents
|
||||
|
||||
import android.content.*
|
||||
import android.net.*
|
||||
import org.isoron.androidbase.activities.*
|
||||
import org.isoron.uhabits.*
|
||||
import org.isoron.uhabits.activities.about.*
|
||||
import org.isoron.uhabits.activities.habits.edit.*
|
||||
import org.isoron.uhabits.activities.habits.show.*
|
||||
import org.isoron.uhabits.activities.intro.*
|
||||
import org.isoron.uhabits.activities.settings.*
|
||||
import org.isoron.uhabits.activities.sync.*
|
||||
import org.isoron.uhabits.core.models.*
|
||||
import javax.inject.*
|
||||
|
||||
@@ -100,4 +100,8 @@ class IntentFactory
|
||||
intent.putExtra("habitType", habitType)
|
||||
return intent
|
||||
}
|
||||
|
||||
fun startSyncActivity(context: Context): Intent {
|
||||
return Intent(context, SyncActivity::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,8 +87,6 @@ class SharedPreferencesStorage
|
||||
preferences.setNotificationsSticky(getBoolean(key, false))
|
||||
"pref_led_notifications" ->
|
||||
preferences.setNotificationsLed(getBoolean(key, false))
|
||||
"pref_feature_sync" ->
|
||||
preferences.isSyncEnabled = getBoolean(key, false)
|
||||
}
|
||||
sharedPreferences.registerOnSharedPreferenceChangeListener(this)
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
<string name="translateURL">http://translate.loophabits.org/</string>
|
||||
<string name="bugReportTo">dev@loophabits.org</string>
|
||||
<string name="bugReportSubject">Bug Report - Loop Habit Tracker</string>
|
||||
<string name="syncBaseURL" formatted="false">https://sync.loophabits.org</string>
|
||||
|
||||
<string-array name="snooze_interval_names">
|
||||
<item>@string/interval_15_minutes</item>
|
||||
|
||||
@@ -204,10 +204,10 @@
|
||||
<string name="pref_skip_title">Enable skip days</string>
|
||||
<string name="pref_skip_description">Toggle twice to add a skip instead of a checkmark. Skips keep your score unchanged and don\'t break your streak.</string>
|
||||
<string name="device_sync">Device sync</string>
|
||||
<string name="pref_sync_summary">This feature allows you to synchronize data between multiple devices. When enabled, an encrypted copy of your data will be uploaded to Loop Habit Tracker servers. See privacy policy for more details.</string>
|
||||
<string name="pref_sync_title">Enable device sync</string>
|
||||
<string name="pref_sync_summary">When enabled, an encrypted copy of your data will be uploaded to our servers. See privacy policy.</string>
|
||||
<string name="pref_sync_title">Sync data across devices</string>
|
||||
<string name="display_sync_code">Display sync instructions</string>
|
||||
<string name="sync_instructions"><![CDATA[<b>Instructions:</b><br/>1. Install Loop in your second device.<br/>2. Open the link below in your second device.<br/>3. Provide the 8-character password below.<br/><b>Important:</b> Do not publish this information. It gives anyone access to your entire data.]]></string>
|
||||
<string name="sync_instructions"><![CDATA[<b>Instructions:</b><br/>1. Install Loop in your second device.<br/>2. Open the link below in your second device.<br/>3. Provide the 8-character password below.<br/><b>Important:</b> Do not publish this information. It gives anyone access to your data.]]></string>
|
||||
<string name="sync_link">Sync link</string>
|
||||
<string name="sync_link_qr">Sync link (QR code)</string>
|
||||
<string name="password">Password</string>
|
||||
|
||||
@@ -116,21 +116,24 @@
|
||||
|
||||
<CheckBoxPreference
|
||||
android:defaultValue="false"
|
||||
android:key="pref_enable_device_sync"
|
||||
android:key="pref_sync_enabled"
|
||||
android:summary="@string/pref_sync_summary"
|
||||
android:title="@string/pref_sync_title"
|
||||
app:iconSpaceReserved="false" />
|
||||
|
||||
<Preference
|
||||
android:key="viewSync"
|
||||
android:key="pref_sync_display"
|
||||
android:title="@string/display_sync_code"
|
||||
app:iconSpaceReserved="false"
|
||||
/>
|
||||
app:iconSpaceReserved="false">
|
||||
<intent
|
||||
android:action="android.intent.action.VIEW"
|
||||
android:targetClass="org.isoron.uhabits.activities.sync.SyncActivity"
|
||||
android:targetPackage="org.isoron.uhabits" />
|
||||
</Preference>
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
|
||||
|
||||
<PreferenceCategory
|
||||
android:key="databaseCategory"
|
||||
android:title="@string/database">
|
||||
@@ -218,6 +221,20 @@
|
||||
android:title="Enable widget stacks"
|
||||
app:iconSpaceReserved="false" />
|
||||
|
||||
<EditTextPreference
|
||||
android:defaultValue="@string/syncBaseURL"
|
||||
android:key="pref_sync_base_url"
|
||||
android:title="Sync server"
|
||||
app:iconSpaceReserved="false"
|
||||
/>
|
||||
|
||||
<EditTextPreference
|
||||
android:defaultValue=""
|
||||
android:key="pref_sync_key"
|
||||
android:title="Sync key"
|
||||
app:iconSpaceReserved="false"
|
||||
/>
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
</PreferenceScreen>
|
||||
@@ -29,10 +29,6 @@ import java.util.*;
|
||||
|
||||
public class Preferences
|
||||
{
|
||||
|
||||
public static final String DEFAULT_SYNC_SERVER =
|
||||
"https://sync.loophabits.org";
|
||||
|
||||
@NonNull
|
||||
private final Storage storage;
|
||||
|
||||
@@ -130,16 +126,6 @@ public class Preferences
|
||||
else return new Timestamp(unixTime);
|
||||
}
|
||||
|
||||
public long getLastSync()
|
||||
{
|
||||
return storage.getLong("last_sync", 0);
|
||||
}
|
||||
|
||||
public void setLastSync(long timestamp)
|
||||
{
|
||||
storage.putLong("last_sync", timestamp);
|
||||
}
|
||||
|
||||
public boolean getShowArchived()
|
||||
{
|
||||
return storage.getBoolean("pref_show_archived", false);
|
||||
@@ -170,39 +156,6 @@ public class Preferences
|
||||
storage.putString("pref_snooze_interval", String.valueOf(interval));
|
||||
}
|
||||
|
||||
public String getSyncAddress()
|
||||
{
|
||||
return storage.getString("pref_sync_address", DEFAULT_SYNC_SERVER);
|
||||
}
|
||||
|
||||
public void setSyncAddress(String address)
|
||||
{
|
||||
storage.putString("pref_sync_address", address);
|
||||
for (Listener l : listeners) l.onSyncFeatureChanged();
|
||||
}
|
||||
|
||||
public String getSyncClientId()
|
||||
{
|
||||
String id = storage.getString("pref_sync_client_id", "");
|
||||
if (!id.isEmpty()) return id;
|
||||
|
||||
id = UUID.randomUUID().toString();
|
||||
storage.putString("pref_sync_client_id", id);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getSyncKey()
|
||||
{
|
||||
return storage.getString("pref_sync_key", "");
|
||||
}
|
||||
|
||||
public void setSyncKey(String key)
|
||||
{
|
||||
storage.putString("pref_sync_key", key);
|
||||
for (Listener l : listeners) l.onSyncFeatureChanged();
|
||||
}
|
||||
|
||||
public int getTheme()
|
||||
{
|
||||
return storage.getInt("pref_theme", ThemeSwitcher.THEME_AUTOMATIC);
|
||||
@@ -263,17 +216,6 @@ public class Preferences
|
||||
storage.putBoolean("pref_short_toggle", enabled);
|
||||
}
|
||||
|
||||
public boolean isSyncEnabled()
|
||||
{
|
||||
return storage.getBoolean("pref_feature_sync", false);
|
||||
}
|
||||
|
||||
public void setSyncEnabled(boolean isEnabled)
|
||||
{
|
||||
storage.putBoolean("pref_feature_sync", isEnabled);
|
||||
for (Listener l : listeners) l.onSyncFeatureChanged();
|
||||
}
|
||||
|
||||
public boolean isWidgetStackEnabled()
|
||||
{
|
||||
return storage.getBoolean("pref_feature_widget_stack", false);
|
||||
@@ -367,6 +309,26 @@ public class Preferences
|
||||
storage.putBoolean("pref_skip_enabled", value);
|
||||
}
|
||||
|
||||
public String getSyncBaseURL()
|
||||
{
|
||||
return storage.getString("pref_sync_base_url", "");
|
||||
}
|
||||
|
||||
public String getSyncKey()
|
||||
{
|
||||
return storage.getString("pref_sync_key", "");
|
||||
}
|
||||
|
||||
public void setSyncKey(String key)
|
||||
{
|
||||
storage.putString("pref_sync_key", key);
|
||||
}
|
||||
|
||||
public boolean isSyncEnabled()
|
||||
{
|
||||
return storage.getBoolean("pref_sync_enabled", false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return An integer representing the first day of the week. Sunday
|
||||
@@ -390,10 +352,6 @@ public class Preferences
|
||||
default void onNotificationsChanged()
|
||||
{
|
||||
}
|
||||
|
||||
default void onSyncFeatureChanged()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public interface Storage
|
||||
|
||||
@@ -109,37 +109,6 @@ public class PreferencesTest extends BaseUnitTest
|
||||
assertThat(prefs.getLastHintTimestamp(), equalTo(Timestamp.ZERO.plus(100)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSync() throws Exception
|
||||
{
|
||||
assertThat(prefs.getLastSync(), equalTo(0L));
|
||||
prefs.setLastSync(100);
|
||||
assertThat(prefs.getLastSync(), equalTo(100L));
|
||||
|
||||
assertThat(prefs.getSyncAddress(),
|
||||
equalTo(Preferences.DEFAULT_SYNC_SERVER));
|
||||
prefs.setSyncAddress("example");
|
||||
assertThat(prefs.getSyncAddress(), equalTo("example"));
|
||||
verify(listener).onSyncFeatureChanged();
|
||||
reset(listener);
|
||||
|
||||
assertThat(prefs.getSyncKey(), equalTo(""));
|
||||
prefs.setSyncKey("123");
|
||||
assertThat(prefs.getSyncKey(), equalTo("123"));
|
||||
verify(listener).onSyncFeatureChanged();
|
||||
reset(listener);
|
||||
|
||||
assertFalse(prefs.isSyncEnabled());
|
||||
prefs.setSyncEnabled(true);
|
||||
assertTrue(prefs.isSyncEnabled());
|
||||
verify(listener).onSyncFeatureChanged();
|
||||
reset(listener);
|
||||
|
||||
String id = prefs.getSyncClientId();
|
||||
assertFalse(id.isEmpty());
|
||||
assertThat(prefs.getSyncClientId(), equalTo(id));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTheme() throws Exception
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user