diff --git a/android/android-base/src/main/java/org/isoron/androidbase/activities/BaseScreen.kt b/android/android-base/src/main/java/org/isoron/androidbase/activities/BaseScreen.kt index 443344e87..ab223523d 100644 --- a/android/android-base/src/main/java/org/isoron/androidbase/activities/BaseScreen.kt +++ b/android/android-base/src/main/java/org/isoron/androidbase/activities/BaseScreen.kt @@ -127,8 +127,7 @@ open class BaseScreen(@JvmField protected var activity: BaseActivity) { * * @param stringId the string resource id for this message. */ - fun showMessage(@StringRes stringId: Int?) { - val rootView = this.rootView + fun showMessage(@StringRes stringId: Int?, rootView: View?) { var snackbar = this.snackbar if (stringId == null || rootView == null) return if (snackbar == null) { @@ -142,6 +141,10 @@ open class BaseScreen(@JvmField protected var activity: BaseActivity) { snackbar.show() } + fun showMessage(@StringRes stringId: Int?) { + showMessage(stringId, this.rootView) + } + fun showSendEmailScreen(@StringRes toId: Int, @StringRes subjectId: Int, content: String?) { val to = activity.getString(toId) val subject = activity.getString(subjectId) diff --git a/android/uhabits-android/build.gradle b/android/uhabits-android/build.gradle index 45d2a0416..a4ce06526 100644 --- a/android/uhabits-android/build.gradle +++ b/android/uhabits-android/build.gradle @@ -93,6 +93,7 @@ dependencies { implementation "com.google.code.findbugs:jsr305:3.0.2" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$KOTLIN_VERSION" implementation "androidx.constraintlayout:constraintlayout:2.0.0-beta4" + implementation 'com.google.zxing:core:3.4.1' implementation 'androidx.constraintlayout:constraintlayout:1.1.3' compileOnly "javax.annotation:jsr250-api:1.0" diff --git a/android/uhabits-android/src/main/AndroidManifest.xml b/android/uhabits-android/src/main/AndroidManifest.xml index 1a268ce57..549e635e9 100644 --- a/android/uhabits-android/src/main/AndroidManifest.xml +++ b/android/uhabits-android/src/main/AndroidManifest.xml @@ -41,6 +41,14 @@ android:value=".activities.habits.list.ListHabitsActivity" /> + + + + 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 new file mode 100644 index 000000000..2cf8cb1aa --- /dev/null +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/activities/sync/SyncActivity.kt @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2016-2020 Álinson Santos Xavier + * + * 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.sync + +import android.content.* +import android.content.ClipboardManager +import android.graphics.* +import android.os.* +import android.text.* +import com.google.zxing.* +import com.google.zxing.qrcode.* +import org.isoron.androidbase.activities.* +import org.isoron.androidbase.utils.* +import org.isoron.uhabits.* +import org.isoron.uhabits.activities.* +import org.isoron.uhabits.databinding.* + + +class SyncActivity : BaseActivity() { + + private lateinit var baseScreen: BaseScreen + private lateinit var themeSwitcher: AndroidThemeSwitcher + private lateinit var binding: ActivitySyncBinding + + override fun onCreate(state: Bundle?) { + super.onCreate(state) + + baseScreen = BaseScreen(this) + + val component = (application as HabitsApplication).component + themeSwitcher = AndroidThemeSwitcher(this, component.preferences) + themeSwitcher.apply() + + binding = ActivitySyncBinding.inflate(layoutInflater) + setContentView(binding.root) + + setSupportActionBar(binding.toolbar) + supportActionBar?.setDisplayHomeAsUpEnabled(true) + supportActionBar?.setDisplayShowHomeEnabled(true) + supportActionBar?.elevation = 10.0f + + binding.instructions.setText(Html.fromHtml(resources.getString(R.string.sync_instructions))) + + displayLink("https://loophabits.org/sync/KA9GvblSWrcLk9iwJrplHvWiWdE6opAokdf2qqRl6n6ECX8IUhvcksqlfkQACoMM") + displayPassword("6B2W9F5X") + + binding.syncLink.setOnClickListener { + copyToClipboard() + } + } + + private fun copyToClipboard() { + val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager + clipboard.setPrimaryClip(ClipData.newPlainText("Loop Sync Link", binding.syncLink.text)) + baseScreen.showMessage(R.string.copied_to_the_clipboard, binding.root) + } + + private fun displayPassword(pin: String) { + binding.password.text = pin + } + + private fun displayLink(link: String) { + 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) + } + } + 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 new file mode 100644 index 000000000..b2f8dcb52 --- /dev/null +++ b/android/uhabits-android/src/main/res/layout/activity_sync.xml @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/uhabits-android/src/main/res/values/strings.xml b/android/uhabits-android/src/main/res/values/strings.xml index 7f807dccd..bdd797672 100644 --- a/android/uhabits-android/src/main/res/values/strings.xml +++ b/android/uhabits-android/src/main/res/values/strings.xml @@ -203,4 +203,13 @@ Decrement Enable skip days Toggle twice to add a skip instead of a checkmark. Skips keep your score unchanged and don\'t break your streak. + Device sync + 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. + Enable device sync + 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 entire data.]]>
+ Sync link + Sync link (QR code) + Password + Copied to the clipboard \ No newline at end of file diff --git a/android/uhabits-android/src/main/res/xml/preferences.xml b/android/uhabits-android/src/main/res/xml/preferences.xml index 7b0d75489..39524368d 100644 --- a/android/uhabits-android/src/main/res/xml/preferences.xml +++ b/android/uhabits-android/src/main/res/xml/preferences.xml @@ -110,6 +110,27 @@ + + + + + + + + + +