Add sync preferences to settings screen

pull/699/head
Alinson S. Xavier 5 years ago
parent a2400172e2
commit 23f2978a64

@ -26,17 +26,15 @@ import android.os.*;
import android.provider.*; import android.provider.*;
import android.util.*; import android.util.*;
import androidx.annotation.Nullable; import androidx.annotation.*;
import androidx.preference.ListPreference; import androidx.preference.*;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceFragmentCompat;
import org.isoron.uhabits.R; import org.isoron.uhabits.R;
import org.isoron.uhabits.*; import org.isoron.uhabits.*;
import org.isoron.uhabits.core.preferences.*; import org.isoron.uhabits.core.preferences.*;
import org.isoron.uhabits.core.ui.*; import org.isoron.uhabits.core.ui.*;
import org.isoron.uhabits.core.utils.*; import org.isoron.uhabits.core.utils.*;
import org.isoron.uhabits.intents.*;
import org.isoron.uhabits.notifications.*; import org.isoron.uhabits.notifications.*;
import org.isoron.uhabits.widgets.*; import org.isoron.uhabits.widgets.*;
@ -55,7 +53,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
private RingtoneManager ringtoneManager; private RingtoneManager ringtoneManager;
@Nullable @NonNull
private Preferences prefs; private Preferences prefs;
@Nullable @Nullable
@ -93,6 +91,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
setResultOnPreferenceClick("exportDB", RESULT_EXPORT_DB); setResultOnPreferenceClick("exportDB", RESULT_EXPORT_DB);
setResultOnPreferenceClick("repairDB", RESULT_REPAIR_DB); setResultOnPreferenceClick("repairDB", RESULT_REPAIR_DB);
setResultOnPreferenceClick("bugReport", RESULT_BUG_REPORT); setResultOnPreferenceClick("bugReport", RESULT_BUG_REPORT);
} }
@Override @Override
@ -142,7 +141,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
sharedPrefs = getPreferenceManager().getSharedPreferences(); sharedPrefs = getPreferenceManager().getSharedPreferences();
sharedPrefs.registerOnSharedPreferenceChangeListener(this); sharedPrefs.registerOnSharedPreferenceChangeListener(this);
if (prefs != null && !prefs.isDeveloper()) if (!prefs.isDeveloper())
{ {
PreferenceCategory devCategory = PreferenceCategory devCategory =
(PreferenceCategory) findPreference("devCategory"); (PreferenceCategory) findPreference("devCategory");
@ -151,15 +150,22 @@ public class SettingsFragment extends PreferenceFragmentCompat
} }
updateWeekdayPreference(); updateWeekdayPreference();
updateSyncPreferences();
// Temporarily disable this; we now always ask // Temporarily disable this; we now always ask
findPreference("reminderSound").setVisible(false); findPreference("reminderSound").setVisible(false);
findPreference("pref_snooze_interval").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() private void updateWeekdayPreference()
{ {
if (prefs == null) return;
ListPreference weekdayPref = (ListPreference) findPreference("pref_first_weekday"); ListPreference weekdayPref = (ListPreference) findPreference("pref_first_weekday");
int currentFirstWeekday = prefs.getFirstWeekday(); int currentFirstWeekday = prefs.getFirstWeekday();
String[] dayNames = DateUtils.getLongWeekdayNames(Calendar.SATURDAY); String[] dayNames = DateUtils.getLongWeekdayNames(Calendar.SATURDAY);
@ -179,8 +185,18 @@ public class SettingsFragment extends PreferenceFragmentCompat
Log.d("SettingsFragment", "updating widgets"); Log.d("SettingsFragment", "updating widgets");
widgetUpdater.updateWidgets(); 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"); BackupManager.dataChanged("org.isoron.uhabits");
updateWeekdayPreference();
updateSyncPreferences();
} }
private void setResultOnPreferenceClick(String key, final int result) 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.androidbase.utils.InterfaceUtils.getFontAwesome
import org.isoron.uhabits.* import org.isoron.uhabits.*
import org.isoron.uhabits.activities.* import org.isoron.uhabits.activities.*
import org.isoron.uhabits.core.preferences.*
import org.isoron.uhabits.core.tasks.* import org.isoron.uhabits.core.tasks.*
import org.isoron.uhabits.databinding.* import org.isoron.uhabits.databinding.*
import org.isoron.uhabits.sync.* import org.isoron.uhabits.sync.*
@ -40,6 +41,7 @@ import org.isoron.uhabits.sync.*
class SyncActivity : BaseActivity() { class SyncActivity : BaseActivity() {
private lateinit var preferences: Preferences
private lateinit var taskRunner: TaskRunner private lateinit var taskRunner: TaskRunner
private lateinit var baseScreen: BaseScreen private lateinit var baseScreen: BaseScreen
private lateinit var themeSwitcher: AndroidThemeSwitcher private lateinit var themeSwitcher: AndroidThemeSwitcher
@ -54,6 +56,7 @@ class SyncActivity : BaseActivity() {
themeSwitcher = AndroidThemeSwitcher(this, component.preferences) themeSwitcher = AndroidThemeSwitcher(this, component.preferences)
themeSwitcher.apply() themeSwitcher.apply()
taskRunner = component.taskRunner taskRunner = component.taskRunner
preferences = component.preferences
binding = ActivitySyncBinding.inflate(layoutInflater) binding = ActivitySyncBinding.inflate(layoutInflater)
setContentView(binding.root) setContentView(binding.root)
@ -74,27 +77,41 @@ class SyncActivity : BaseActivity() {
override fun onResume() { override fun onResume() {
super.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() displayLoading()
taskRunner.execute(object : Task { taskRunner.execute(object : Task {
private var key = "" private var key = ""
private var error = false private var error = false
override fun doInBackground() { override fun doInBackground() {
runBlocking { runBlocking {
val server = RemoteSyncServer() val server = RemoteSyncServer(baseURL = preferences.syncBaseURL)
try { try {
key = server.register() key = server.register()
} catch(e: ServiceUnavailable) { } catch (e: ServiceUnavailable) {
error = true error = true
} }
} }
} }
override fun onPostExecute() { override fun onPostExecute() {
if(error) { if (error) {
displayError() displayError()
} else { return;
displayLink("https://loophabits.org/sync/$key")
displayPassword("6B2W9F5X")
} }
preferences.syncKey = key;
displayCurrentKey()
} }
}) })
} }

@ -21,13 +21,13 @@ package org.isoron.uhabits.intents
import android.content.* import android.content.*
import android.net.* import android.net.*
import org.isoron.androidbase.activities.*
import org.isoron.uhabits.* import org.isoron.uhabits.*
import org.isoron.uhabits.activities.about.* import org.isoron.uhabits.activities.about.*
import org.isoron.uhabits.activities.habits.edit.* import org.isoron.uhabits.activities.habits.edit.*
import org.isoron.uhabits.activities.habits.show.* import org.isoron.uhabits.activities.habits.show.*
import org.isoron.uhabits.activities.intro.* import org.isoron.uhabits.activities.intro.*
import org.isoron.uhabits.activities.settings.* import org.isoron.uhabits.activities.settings.*
import org.isoron.uhabits.activities.sync.*
import org.isoron.uhabits.core.models.* import org.isoron.uhabits.core.models.*
import javax.inject.* import javax.inject.*
@ -100,4 +100,8 @@ class IntentFactory
intent.putExtra("habitType", habitType) intent.putExtra("habitType", habitType)
return intent return intent
} }
fun startSyncActivity(context: Context): Intent {
return Intent(context, SyncActivity::class.java)
}
} }

@ -87,8 +87,6 @@ class SharedPreferencesStorage
preferences.setNotificationsSticky(getBoolean(key, false)) preferences.setNotificationsSticky(getBoolean(key, false))
"pref_led_notifications" -> "pref_led_notifications" ->
preferences.setNotificationsLed(getBoolean(key, false)) preferences.setNotificationsLed(getBoolean(key, false))
"pref_feature_sync" ->
preferences.isSyncEnabled = getBoolean(key, false)
} }
sharedPreferences.registerOnSharedPreferenceChangeListener(this) sharedPreferences.registerOnSharedPreferenceChangeListener(this)
} }

@ -27,6 +27,7 @@
<string name="translateURL">http://translate.loophabits.org/</string> <string name="translateURL">http://translate.loophabits.org/</string>
<string name="bugReportTo">dev@loophabits.org</string> <string name="bugReportTo">dev@loophabits.org</string>
<string name="bugReportSubject">Bug Report - Loop Habit Tracker</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"> <string-array name="snooze_interval_names">
<item>@string/interval_15_minutes</item> <item>@string/interval_15_minutes</item>

@ -204,10 +204,10 @@
<string name="pref_skip_title">Enable skip days</string> <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="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="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_summary">When enabled, an encrypted copy of your data will be uploaded to our servers. See privacy policy.</string>
<string name="pref_sync_title">Enable device sync</string> <string name="pref_sync_title">Sync data across devices</string>
<string name="display_sync_code">Display sync instructions</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">Sync link</string>
<string name="sync_link_qr">Sync link (QR code)</string> <string name="sync_link_qr">Sync link (QR code)</string>
<string name="password">Password</string> <string name="password">Password</string>

@ -116,21 +116,24 @@
<CheckBoxPreference <CheckBoxPreference
android:defaultValue="false" android:defaultValue="false"
android:key="pref_enable_device_sync" android:key="pref_sync_enabled"
android:summary="@string/pref_sync_summary" android:summary="@string/pref_sync_summary"
android:title="@string/pref_sync_title" android:title="@string/pref_sync_title"
app:iconSpaceReserved="false" /> app:iconSpaceReserved="false" />
<Preference <Preference
android:key="viewSync" android:key="pref_sync_display"
android:title="@string/display_sync_code" 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>
<PreferenceCategory <PreferenceCategory
android:key="databaseCategory" android:key="databaseCategory"
android:title="@string/database"> android:title="@string/database">
@ -218,6 +221,20 @@
android:title="Enable widget stacks" android:title="Enable widget stacks"
app:iconSpaceReserved="false" /> 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> </PreferenceCategory>
</PreferenceScreen> </PreferenceScreen>

@ -29,10 +29,6 @@ import java.util.*;
public class Preferences public class Preferences
{ {
public static final String DEFAULT_SYNC_SERVER =
"https://sync.loophabits.org";
@NonNull @NonNull
private final Storage storage; private final Storage storage;
@ -130,16 +126,6 @@ public class Preferences
else return new Timestamp(unixTime); 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() public boolean getShowArchived()
{ {
return storage.getBoolean("pref_show_archived", false); return storage.getBoolean("pref_show_archived", false);
@ -170,39 +156,6 @@ public class Preferences
storage.putString("pref_snooze_interval", String.valueOf(interval)); 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() public int getTheme()
{ {
return storage.getInt("pref_theme", ThemeSwitcher.THEME_AUTOMATIC); return storage.getInt("pref_theme", ThemeSwitcher.THEME_AUTOMATIC);
@ -263,17 +216,6 @@ public class Preferences
storage.putBoolean("pref_short_toggle", enabled); 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() public boolean isWidgetStackEnabled()
{ {
return storage.getBoolean("pref_feature_widget_stack", false); return storage.getBoolean("pref_feature_widget_stack", false);
@ -367,6 +309,26 @@ public class Preferences
storage.putBoolean("pref_skip_enabled", value); 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 * @return An integer representing the first day of the week. Sunday
@ -390,10 +352,6 @@ public class Preferences
default void onNotificationsChanged() default void onNotificationsChanged()
{ {
} }
default void onSyncFeatureChanged()
{
}
} }
public interface Storage public interface Storage

@ -109,37 +109,6 @@ public class PreferencesTest extends BaseUnitTest
assertThat(prefs.getLastHintTimestamp(), equalTo(Timestamp.ZERO.plus(100))); 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 @Test
public void testTheme() throws Exception public void testTheme() throws Exception
{ {

Loading…
Cancel
Save