diff --git a/android/uhabits-android/src/main/AndroidManifest.xml b/android/uhabits-android/src/main/AndroidManifest.xml
index f043a92fb..a054ce37d 100644
--- a/android/uhabits-android/src/main/AndroidManifest.xml
+++ b/android/uhabits-android/src/main/AndroidManifest.xml
@@ -68,9 +68,16 @@
android:targetActivity=".activities.habits.list.ListHabitsActivity">
-
+
+
+
+
+
+
+ *
+ * This file is part of Loop Habit Tracker.
+ *
+ * Loop Habit Tracker is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * Loop Habit Tracker is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see .
+ */
+
+package org.isoron.uhabits.activities.common.dialogs;
+
+import android.content.*;
+
+import androidx.annotation.*;
+import androidx.appcompat.app.*;
+
+import com.google.auto.factory.*;
+
+import org.isoron.androidbase.activities.*;
+import org.isoron.uhabits.core.ui.callbacks.*;
+import org.isoron.uhabits.R;
+
+import butterknife.*;
+
+@AutoFactory(allowSubclasses = true)
+public class ConfirmSyncKeyDialog extends AlertDialog
+{
+ @BindString(R.string.sync_confirm)
+ protected String question;
+
+ @BindString(R.string.yes)
+ protected String yes;
+
+ @BindString(R.string.no)
+ protected String no;
+
+ protected ConfirmSyncKeyDialog(@Provided @ActivityContext Context context,
+ @NonNull OnConfirmedCallback callback)
+ {
+ super(context);
+ ButterKnife.bind(this);
+
+ setTitle(R.string.device_sync);
+ setMessage(question);
+ setButton(BUTTON_POSITIVE, yes, (dialog, which) -> callback.onConfirmed());
+ setButton(BUTTON_NEGATIVE, no, (dialog, which) -> {});
+ }
+}
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsScreen.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsScreen.kt
index e4c3752a1..4d2d32602 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsScreen.kt
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsScreen.kt
@@ -21,6 +21,7 @@ package org.isoron.uhabits.activities.habits.list
import android.app.*
import android.content.*
+import android.util.*
import androidx.annotation.*
import dagger.*
import org.isoron.androidbase.activities.*
@@ -31,7 +32,6 @@ import org.isoron.uhabits.activities.habits.edit.*
import org.isoron.uhabits.activities.habits.list.views.*
import org.isoron.uhabits.core.commands.*
import org.isoron.uhabits.core.models.*
-import org.isoron.uhabits.core.preferences.*
import org.isoron.uhabits.core.tasks.*
import org.isoron.uhabits.core.ui.*
import org.isoron.uhabits.core.ui.callbacks.*
@@ -42,13 +42,13 @@ import org.isoron.uhabits.tasks.*
import java.io.*
import javax.inject.*
-const val RESULT_IMPORT_DATA = 1
-const val RESULT_EXPORT_CSV = 2
-const val RESULT_EXPORT_DB = 3
-const val RESULT_BUG_REPORT = 4
-const val RESULT_REPAIR_DB = 5
-const val REQUEST_OPEN_DOCUMENT = 6
-const val REQUEST_SETTINGS = 7
+const val RESULT_IMPORT_DATA = 101
+const val RESULT_EXPORT_CSV = 102
+const val RESULT_EXPORT_DB = 103
+const val RESULT_BUG_REPORT = 104
+const val RESULT_REPAIR_DB = 105
+const val REQUEST_OPEN_DOCUMENT = 106
+const val REQUEST_SETTINGS = 107
@ActivityScope
class ListHabitsScreen
@@ -63,6 +63,7 @@ class ListHabitsScreen
private val exportDBFactory: ExportDBTaskFactory,
private val importTaskFactory: ImportDataTaskFactory,
private val confirmDeleteDialogFactory: ConfirmDeleteDialogFactory,
+ private val confirmSyncKeyDialogFactory: ConfirmSyncKeyDialogFactory,
private val colorPickerFactory: ColorPickerDialogFactory,
private val numberPickerFactory: NumberPickerFactory,
private val behavior: Lazy,
@@ -82,6 +83,12 @@ class ListHabitsScreen
setMenu(menu.get())
setSelectionMenu(selectionMenu.get())
commandRunner.addListener(this)
+ if(activity.intent.action == "android.intent.action.VIEW") {
+ val uri = activity.intent.data!!.toString()
+ val key = uri.replace(Regex("^.*sync/"), "")
+ Log.i("ListHabitsScreen", key)
+ behavior.get().onSyncKeyOffer(key)
+ }
}
fun onDettached() {
@@ -171,13 +178,14 @@ class ListHabitsScreen
override fun showMessage(m: ListHabitsBehavior.Message) {
showMessage(when (m) {
- COULD_NOT_EXPORT -> R.string.could_not_export
- IMPORT_SUCCESSFUL -> R.string.habits_imported
- IMPORT_FAILED -> R.string.could_not_import
- DATABASE_REPAIRED -> R.string.database_repaired
- COULD_NOT_GENERATE_BUG_REPORT -> R.string.bug_report_failed
- FILE_NOT_RECOGNIZED -> R.string.file_not_recognized
- })
+ COULD_NOT_EXPORT -> R.string.could_not_export
+ IMPORT_SUCCESSFUL -> R.string.habits_imported
+ IMPORT_FAILED -> R.string.could_not_import
+ DATABASE_REPAIRED -> R.string.database_repaired
+ COULD_NOT_GENERATE_BUG_REPORT -> R.string.bug_report_failed
+ FILE_NOT_RECOGNIZED -> R.string.file_not_recognized
+ SYNC_ENABLED -> R.string.sync_enabled
+ })
}
override fun showSendBugReportToDeveloperScreen(log: String) {
@@ -204,6 +212,10 @@ class ListHabitsScreen
numberPickerFactory.create(value, unit, callback).show()
}
+ override fun showConfirmInstallSyncKey(callback: OnConfirmedCallback) {
+ activity.showDialog(confirmSyncKeyDialogFactory.create(callback))
+ }
+
@StringRes
private fun getExecuteString(command: Command): Int? {
when (command) {
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/settings/SettingsFragment.java b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/settings/SettingsFragment.java
index 1faca385f..64153beda 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/settings/SettingsFragment.java
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/settings/SettingsFragment.java
@@ -145,7 +145,6 @@ public class SettingsFragment extends PreferenceFragmentCompat
{
PreferenceCategory devCategory =
(PreferenceCategory) findPreference("devCategory");
- devCategory.removeAll();
devCategory.setVisible(false);
}
@@ -159,6 +158,10 @@ public class SettingsFragment extends PreferenceFragmentCompat
private void updateSyncPreferences()
{
+ if(prefs.getSyncKey().isEmpty()) {
+ prefs.setSyncEnabled(false);
+ ((CheckBoxPreference) findPreference("pref_sync_enabled")).setChecked(false);
+ }
findPreference("pref_sync_base_url").setSummary(prefs.getSyncBaseURL());
findPreference("pref_sync_key").setSummary(prefs.getSyncKey());
findPreference("pref_sync_display").setVisible(prefs.isSyncEnabled());
diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/sync/SyncActivity.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/sync/SyncActivity.kt
index 231a2a467..7d04793be 100644
--- a/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/sync/SyncActivity.kt
+++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/sync/SyncActivity.kt
@@ -44,17 +44,16 @@ class SyncActivity : BaseActivity() {
private lateinit var preferences: Preferences
private lateinit var taskRunner: TaskRunner
private lateinit var baseScreen: BaseScreen
- private lateinit var themeSwitcher: AndroidThemeSwitcher
private lateinit var binding: ActivitySyncBinding
+ private var styledResources = StyledResources(this)
+
override fun onCreate(state: Bundle?) {
super.onCreate(state)
baseScreen = BaseScreen(this)
val component = (application as HabitsApplication).component
- themeSwitcher = AndroidThemeSwitcher(this, component.preferences)
- themeSwitcher.apply()
taskRunner = component.taskRunner
preferences = component.preferences
@@ -85,7 +84,7 @@ class SyncActivity : BaseActivity() {
}
private fun displayCurrentKey() {
- displayLink("${preferences.syncBaseURL}/sync/${preferences.syncKey}")
+ displayLink("https://loophabits.org/sync/${preferences.syncKey}")
displayPassword("6B2W9F5X")
}
@@ -139,27 +138,36 @@ class SyncActivity : BaseActivity() {
}
private fun displayLink(link: String) {
- binding.qrCode.visibility = View.VISIBLE
- binding.progress.visibility = View.GONE
+ binding.qrCode.visibility = View.GONE
+ binding.progress.visibility = View.VISIBLE
binding.errorPanel.visibility = View.GONE
binding.syncLink.text = link
displayQR(link)
}
private fun displayQR(msg: String) {
- val writer = QRCodeWriter()
- val matrix = writer.encode(msg, BarcodeFormat.QR_CODE, 1024, 1024)
- val height = matrix.height
- val width = matrix.width
- val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565)
- val bgColor = StyledResources(this).getColor(R.attr.highContrastReverseTextColor)
- val fgColor = StyledResources(this).getColor(R.attr.highContrastTextColor)
- for (x in 0 until width) {
- for (y in 0 until height) {
- val color = if (matrix.get(x, y)) fgColor else bgColor
- bitmap.setPixel(x, y, color)
+ taskRunner.execute(object : Task {
+ lateinit var bitmap: Bitmap
+ override fun doInBackground() {
+ val writer = QRCodeWriter()
+ val matrix = writer.encode(msg, BarcodeFormat.QR_CODE, 1024, 1024)
+ val height = matrix.height
+ val width = matrix.width
+ bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565)
+ val bgColor = styledResources.getColor(R.attr.highContrastReverseTextColor)
+ val fgColor = styledResources.getColor(R.attr.highContrastTextColor)
+ for (x in 0 until width) {
+ for (y in 0 until height) {
+ val color = if (matrix.get(x, y)) fgColor else bgColor
+ bitmap.setPixel(x, y, color)
+ }
+ }
}
- }
- binding.qrCode.setImageBitmap(bitmap)
+ override fun onPostExecute() {
+ binding.progress.visibility = View.GONE
+ binding.qrCode.visibility = View.VISIBLE
+ binding.qrCode.setImageBitmap(bitmap)
+ }
+ })
}
}
\ No newline at end of file
diff --git a/android/uhabits-android/src/main/res/layout/activity_sync.xml b/android/uhabits-android/src/main/res/layout/activity_sync.xml
index 660d31bbd..67416d582 100644
--- a/android/uhabits-android/src/main/res/layout/activity_sync.xml
+++ b/android/uhabits-android/src/main/res/layout/activity_sync.xml
@@ -123,7 +123,9 @@
-
+
diff --git a/android/uhabits-android/src/main/res/values/strings.xml b/android/uhabits-android/src/main/res/values/strings.xml
index d0f79b0cd..31afe7709 100644
--- a/android/uhabits-android/src/main/res/values/strings.xml
+++ b/android/uhabits-android/src/main/res/values/strings.xml
@@ -206,10 +206,12 @@
Device sync
When enabled, an encrypted copy of your data will be uploaded to our servers. See privacy policy.
Sync data across devices
- Display sync instructions
- Instructions:
1. Install Loop in your second device.
2. Open the link below in your second device.
3. Provide the 8-character password below.
Important: Do not publish this information. It gives anyone access to your data.]]>
+ Show device sync instructions
+ Instructions:
1. Install Loop in your second device.
2. Open the link below in your second device.
Important: Do not not make this information public. It gives anyone access to your data.]]>
Sync link
Sync link (QR code)
Password
Copied to the clipboard
+
+ Device sync enabled
\ No newline at end of file
diff --git a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/preferences/Preferences.java b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/preferences/Preferences.java
index a2b22fc0e..af1a37bba 100644
--- a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/preferences/Preferences.java
+++ b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/preferences/Preferences.java
@@ -329,6 +329,11 @@ public class Preferences
return storage.getBoolean("pref_sync_enabled", false);
}
+ public void setSyncEnabled(boolean enabled)
+ {
+ storage.putBoolean("pref_sync_enabled", enabled);
+ }
+
/**
* @return An integer representing the first day of the week. Sunday
diff --git a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/habits/list/ListHabitsBehavior.java b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/habits/list/ListHabitsBehavior.java
index 870ad65f3..6f6c3c5a2 100644
--- a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/habits/list/ListHabitsBehavior.java
+++ b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/ui/screens/habits/list/ListHabitsBehavior.java
@@ -25,7 +25,9 @@ import org.isoron.uhabits.core.commands.*;
import org.isoron.uhabits.core.models.*;
import org.isoron.uhabits.core.preferences.*;
import org.isoron.uhabits.core.tasks.*;
+import org.isoron.uhabits.core.ui.callbacks.*;
import org.isoron.uhabits.core.utils.*;
+import org.jetbrains.annotations.*;
import java.io.*;
import java.util.*;
@@ -156,10 +158,19 @@ public class ListHabitsBehavior
habit.getId());
}
+ public void onSyncKeyOffer(@NotNull String key)
+ {
+ screen.showConfirmInstallSyncKey(() -> {
+ prefs.setSyncKey(key);
+ prefs.setSyncEnabled(true);
+ screen.showMessage(Message.SYNC_ENABLED);
+ });
+ }
+
public enum Message
{
COULD_NOT_EXPORT, IMPORT_SUCCESSFUL, IMPORT_FAILED, DATABASE_REPAIRED,
- COULD_NOT_GENERATE_BUG_REPORT, FILE_NOT_RECOGNIZED
+ COULD_NOT_GENERATE_BUG_REPORT, FILE_NOT_RECOGNIZED, SYNC_ENABLED
}
public interface BugReporter
@@ -196,5 +207,7 @@ public class ListHabitsBehavior
void showSendBugReportToDeveloperScreen(String log);
void showSendFileScreen(@NonNull String filename);
+
+ void showConfirmInstallSyncKey(@NonNull OnConfirmedCallback callback);
}
}