Temporarily remove device sync

This commit is contained in:
2021-04-17 19:53:24 -05:00
parent f27a9f9103
commit da018fc64d
26 changed files with 0 additions and 1411 deletions

View File

@@ -1,45 +0,0 @@
/*
* Copyright (C) 2016-2021 Álinson Santos Xavier <git@axavier.org>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.isoron.uhabits.activities.common.dialogs
import android.content.Context
import android.content.DialogInterface
import androidx.appcompat.app.AlertDialog
import org.isoron.uhabits.R
import org.isoron.uhabits.core.ui.callbacks.OnConfirmedCallback
import org.isoron.uhabits.inject.ActivityContext
class ConfirmSyncKeyDialog(
@ActivityContext context: Context,
callback: OnConfirmedCallback
) : AlertDialog(context) {
init {
setTitle(R.string.device_sync)
val res = context.resources
setMessage(res.getString(R.string.sync_confirm))
setButton(
BUTTON_POSITIVE,
res.getString(R.string.yes)
) { dialog: DialogInterface?, which: Int -> callback.onConfirmed() }
setButton(
BUTTON_NEGATIVE,
res.getString(R.string.no)
) { dialog: DialogInterface?, which: Int -> }
}
}

View File

@@ -26,12 +26,10 @@ import android.view.MenuItem
import androidx.appcompat.app.AppCompatActivity
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.isoron.uhabits.BaseExceptionHandler
import org.isoron.uhabits.HabitsApplication
import org.isoron.uhabits.activities.habits.list.views.HabitCardListAdapter
import org.isoron.uhabits.core.preferences.Preferences
import org.isoron.uhabits.core.sync.SyncManager
import org.isoron.uhabits.core.tasks.TaskRunner
import org.isoron.uhabits.core.ui.ThemeSwitcher.Companion.THEME_DARK
import org.isoron.uhabits.core.utils.MidnightTimer
@@ -49,7 +47,6 @@ class ListHabitsActivity : AppCompatActivity() {
lateinit var screen: ListHabitsScreen
lateinit var prefs: Preferences
lateinit var midnightTimer: MidnightTimer
lateinit var syncManager: SyncManager
private val scope = CoroutineScope(Dispatchers.Main)
private lateinit var menu: ListHabitsMenu
@@ -66,7 +63,6 @@ class ListHabitsActivity : AppCompatActivity() {
component.themeSwitcher.apply()
prefs = appComponent.preferences
syncManager = appComponent.syncManager
pureBlack = prefs.isPureBlackEnabled
midnightTimer = appComponent.midnightTimer
rootView = component.listHabitsRootView
@@ -83,9 +79,6 @@ class ListHabitsActivity : AppCompatActivity() {
midnightTimer.onPause()
screen.onDettached()
adapter.cancelRefresh()
scope.launch {
syncManager.onPause()
}
super.onPause()
}
@@ -94,9 +87,6 @@ class ListHabitsActivity : AppCompatActivity() {
screen.onAttached()
rootView.postInvalidate()
midnightTimer.onResume()
scope.launch {
syncManager.onResume()
}
taskRunner.run {
AutoBackup(this@ListHabitsActivity).run()
}

View File

@@ -22,13 +22,11 @@ package org.isoron.uhabits.activities.habits.list
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import dagger.Lazy
import org.isoron.uhabits.R
import org.isoron.uhabits.activities.common.dialogs.ColorPickerDialogFactory
import org.isoron.uhabits.activities.common.dialogs.ConfirmDeleteDialog
import org.isoron.uhabits.activities.common.dialogs.ConfirmSyncKeyDialog
import org.isoron.uhabits.activities.common.dialogs.NumberPickerFactory
import org.isoron.uhabits.activities.habits.edit.HabitTypeDialog
import org.isoron.uhabits.activities.habits.list.views.HabitCardListAdapter
@@ -53,8 +51,6 @@ import org.isoron.uhabits.core.ui.screens.habits.list.ListHabitsBehavior.Message
import org.isoron.uhabits.core.ui.screens.habits.list.ListHabitsBehavior.Message.FILE_NOT_RECOGNIZED
import org.isoron.uhabits.core.ui.screens.habits.list.ListHabitsBehavior.Message.IMPORT_FAILED
import org.isoron.uhabits.core.ui.screens.habits.list.ListHabitsBehavior.Message.IMPORT_SUCCESSFUL
import org.isoron.uhabits.core.ui.screens.habits.list.ListHabitsBehavior.Message.SYNC_ENABLED
import org.isoron.uhabits.core.ui.screens.habits.list.ListHabitsBehavior.Message.SYNC_KEY_ALREADY_INSTALLED
import org.isoron.uhabits.core.ui.screens.habits.list.ListHabitsMenuBehavior
import org.isoron.uhabits.core.ui.screens.habits.list.ListHabitsSelectionMenuBehavior
import org.isoron.uhabits.inject.ActivityContext
@@ -103,14 +99,6 @@ class ListHabitsScreen
fun onAttached() {
commandRunner.addListener(this)
if (activity.intent.action == "android.intent.action.VIEW") {
val uri = activity.intent.data!!.toString()
val parts = uri.replace(Regex("^.*sync/"), "").split("#")
val syncKey = parts[0]
val encKey = parts[1]
Log.i("ListHabitsScreen", "sync: $syncKey enc: $encKey")
behavior.get().onSyncKeyOffer(syncKey, encKey)
}
}
fun onDettached() {
@@ -208,8 +196,6 @@ class ListHabitsScreen
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
SYNC_KEY_ALREADY_INSTALLED -> R.string.sync_key_already_installed
}
)
)
@@ -244,10 +230,6 @@ class ListHabitsScreen
numberPickerFactory.create(value, unit, callback).show()
}
override fun showConfirmInstallSyncKey(callback: OnConfirmedCallback) {
ConfirmSyncKeyDialog(activity, callback).show()
}
private fun getExecuteString(command: Command): String? {
when (command) {
is ArchiveHabitsCommand -> {

View File

@@ -19,7 +19,6 @@
package org.isoron.uhabits.activities.settings
import android.app.backup.BackupManager
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.content.SharedPreferences.OnSharedPreferenceChangeListener
@@ -28,7 +27,6 @@ import android.os.Build.VERSION
import android.os.Bundle
import android.provider.Settings
import android.util.Log
import androidx.preference.CheckBoxPreference
import androidx.preference.ListPreference
import androidx.preference.Preference
import androidx.preference.PreferenceCategory
@@ -43,7 +41,6 @@ import org.isoron.uhabits.activities.habits.list.RESULT_REPAIR_DB
import org.isoron.uhabits.core.preferences.Preferences
import org.isoron.uhabits.core.ui.NotificationTray
import org.isoron.uhabits.core.utils.DateUtils.Companion.getLongWeekdayNames
import org.isoron.uhabits.intents.IntentFactory
import org.isoron.uhabits.notifications.AndroidNotificationTray.Companion.createAndroidNotificationChannel
import org.isoron.uhabits.notifications.RingtoneManager
import org.isoron.uhabits.widgets.WidgetUpdater
@@ -100,13 +97,6 @@ class SettingsFragment : PreferenceFragmentCompat(), OnSharedPreferenceChangeLis
intent.putExtra(Settings.EXTRA_CHANNEL_ID, NotificationTray.REMINDERS_CHANNEL_ID)
startActivity(intent)
return true
} else if (key == "pref_sync_enabled_dummy") {
if (prefs.isSyncEnabled) {
prefs.disableSync()
} else {
val context: Context? = activity
context!!.startActivity(IntentFactory().startSyncActivity(context))
}
}
return super.onPreferenceTreeClick(preference)
}
@@ -121,7 +111,6 @@ class SettingsFragment : PreferenceFragmentCompat(), OnSharedPreferenceChangeLis
devCategory.isVisible = false
}
updateWeekdayPreference()
updateSyncPreferences()
if (VERSION.SDK_INT < Build.VERSION_CODES.O)
findPreference("reminderCustomize").isVisible = false
@@ -130,12 +119,6 @@ class SettingsFragment : PreferenceFragmentCompat(), OnSharedPreferenceChangeLis
}
}
private fun updateSyncPreferences() {
findPreference("pref_sync_display").isVisible = prefs.isSyncEnabled
(findPreference("pref_sync_enabled_dummy") as CheckBoxPreference).isChecked =
prefs.isSyncEnabled
}
private fun updateWeekdayPreference() {
val weekdayPref = findPreference("pref_first_weekday") as ListPreference
val currentFirstWeekday = prefs.firstWeekday.daysSinceSunday + 1
@@ -157,7 +140,6 @@ class SettingsFragment : PreferenceFragmentCompat(), OnSharedPreferenceChangeLis
}
BackupManager.dataChanged("org.isoron.uhabits")
updateWeekdayPreference()
updateSyncPreferences()
}
private fun setResultOnPreferenceClick(key: String, result: Int) {

View File

@@ -1,130 +0,0 @@
/*
* Copyright (C) 2016-2021 Álinson Santos Xavier <git@axavier.org>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.isoron.uhabits.activities.sync
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.graphics.Bitmap
import android.graphics.Color
import android.os.Bundle
import android.text.Html
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import com.google.zxing.BarcodeFormat
import com.google.zxing.qrcode.QRCodeWriter
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.invoke
import kotlinx.coroutines.launch
import org.isoron.uhabits.HabitsApplication
import org.isoron.uhabits.R
import org.isoron.uhabits.activities.AndroidThemeSwitcher
import org.isoron.uhabits.core.models.PaletteColor
import org.isoron.uhabits.core.ui.screens.sync.SyncBehavior
import org.isoron.uhabits.databinding.ActivitySyncBinding
import org.isoron.uhabits.sync.RemoteSyncServer
import org.isoron.uhabits.utils.InterfaceUtils.getFontAwesome
import org.isoron.uhabits.utils.setupToolbar
import org.isoron.uhabits.utils.showMessage
class SyncActivity : AppCompatActivity(), SyncBehavior.Screen {
private lateinit var binding: ActivitySyncBinding
private lateinit var behavior: SyncBehavior
private val scope = CoroutineScope(Dispatchers.Main)
override fun onCreate(savedInstance: Bundle?) {
super.onCreate(savedInstance)
val component = (application as HabitsApplication).component
val preferences = component.preferences
val server = RemoteSyncServer(preferences = preferences)
AndroidThemeSwitcher(this, component.preferences).apply()
behavior = SyncBehavior(this, preferences, server, component.logging)
binding = ActivitySyncBinding.inflate(layoutInflater)
binding.errorIcon.typeface = getFontAwesome(this)
binding.root.setupToolbar(
toolbar = binding.toolbar,
color = PaletteColor(11),
title = resources.getString(R.string.device_sync),
)
binding.syncLink.setOnClickListener { copyToClipboard() }
binding.instructions.text = Html.fromHtml(resources.getString(R.string.sync_instructions))
setContentView(binding.root)
}
override fun onResume() {
super.onResume()
scope.launch {
behavior.onResume()
}
}
private fun copyToClipboard() {
val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
clipboard.setPrimaryClip(ClipData.newPlainText("Loop Sync Link", binding.syncLink.text))
showMessage(resources.getString(R.string.copied_to_the_clipboard))
}
suspend fun generateQR(msg: String): Bitmap = Dispatchers.IO {
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 = Color.WHITE
val fgColor = Color.BLACK
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)
}
}
return@IO bitmap
}
suspend fun showQR(msg: String) {
binding.progress.visibility = View.GONE
binding.qrCode.visibility = View.VISIBLE
binding.qrCode.setImageBitmap(generateQR(msg))
}
override suspend fun showLoadingScreen() {
binding.qrCode.visibility = View.GONE
binding.progress.visibility = View.VISIBLE
binding.errorPanel.visibility = View.GONE
}
override suspend fun showErrorScreen() {
binding.qrCode.visibility = View.GONE
binding.progress.visibility = View.GONE
binding.errorPanel.visibility = View.VISIBLE
}
override suspend fun showLink(link: String) {
binding.qrCode.visibility = View.GONE
binding.progress.visibility = View.VISIBLE
binding.errorPanel.visibility = View.GONE
binding.syncLink.text = link
showQR(link)
}
}

View File

@@ -29,7 +29,6 @@ import org.isoron.uhabits.core.models.ModelFactory
import org.isoron.uhabits.core.preferences.Preferences
import org.isoron.uhabits.core.preferences.WidgetPreferences
import org.isoron.uhabits.core.reminders.ReminderScheduler
import org.isoron.uhabits.core.sync.SyncManager
import org.isoron.uhabits.core.tasks.TaskRunner
import org.isoron.uhabits.core.ui.NotificationTray
import org.isoron.uhabits.core.ui.screens.habits.list.HabitCardListCache
@@ -64,5 +63,4 @@ interface HabitsApplicationComponent {
val taskRunner: TaskRunner
val widgetPreferences: WidgetPreferences
val widgetUpdater: WidgetUpdater
val syncManager: SyncManager
}

View File

@@ -19,7 +19,6 @@
package org.isoron.uhabits.inject
import android.content.Context
import dagger.Module
import dagger.Provides
import org.isoron.uhabits.core.AppScope
@@ -34,8 +33,6 @@ import org.isoron.uhabits.core.models.sqlite.SQLiteHabitList
import org.isoron.uhabits.core.preferences.Preferences
import org.isoron.uhabits.core.preferences.WidgetPreferences
import org.isoron.uhabits.core.reminders.ReminderScheduler
import org.isoron.uhabits.core.sync.AbstractSyncServer
import org.isoron.uhabits.core.sync.NetworkManager
import org.isoron.uhabits.core.tasks.TaskRunner
import org.isoron.uhabits.core.ui.NotificationTray
import org.isoron.uhabits.database.AndroidDatabase
@@ -44,8 +41,6 @@ import org.isoron.uhabits.intents.IntentScheduler
import org.isoron.uhabits.io.AndroidLogging
import org.isoron.uhabits.notifications.AndroidNotificationTray
import org.isoron.uhabits.preferences.SharedPreferencesStorage
import org.isoron.uhabits.sync.AndroidNetworkManager
import org.isoron.uhabits.sync.RemoteSyncServer
import org.isoron.uhabits.utils.DatabaseUtils
import java.io.File
@@ -114,18 +109,6 @@ class HabitsModule(dbFile: File) {
return AndroidLogging()
}
@Provides
@AppScope
fun getNetworkManager(@AppContext context: Context): NetworkManager {
return AndroidNetworkManager(context)
}
@Provides
@AppScope
fun getSyncServer(preferences: Preferences): AbstractSyncServer {
return RemoteSyncServer(preferences)
}
@Provides
@AppScope
fun getDatabase(): Database {

View File

@@ -28,7 +28,6 @@ import org.isoron.uhabits.activities.habits.edit.EditHabitActivity
import org.isoron.uhabits.activities.habits.show.ShowHabitActivity
import org.isoron.uhabits.activities.intro.IntroActivity
import org.isoron.uhabits.activities.settings.SettingsActivity
import org.isoron.uhabits.activities.sync.SyncActivity
import org.isoron.uhabits.core.models.Habit
import javax.inject.Inject
@@ -101,8 +100,4 @@ class IntentFactory
intent.putExtra("habitType", habitType)
return intent
}
fun startSyncActivity(context: Context): Intent {
return Intent(context, SyncActivity::class.java)
}
}

View File

@@ -1,59 +0,0 @@
/*
* Copyright (C) 2016-2021 Álinson Santos Xavier <git@axavier.org>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.isoron.uhabits.sync
import android.content.Context
import android.net.ConnectivityManager
import android.net.Network
import android.net.NetworkRequest
import org.isoron.uhabits.core.sync.NetworkManager
class AndroidNetworkManager(
val context: Context,
) : NetworkManager, ConnectivityManager.NetworkCallback() {
val listeners = mutableListOf<NetworkManager.Listener>()
var connected = false
init {
val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
cm.registerNetworkCallback(NetworkRequest.Builder().build(), this)
}
override fun addListener(listener: NetworkManager.Listener) {
if (connected) listener.onNetworkAvailable()
else listener.onNetworkLost()
listeners.add(listener)
}
override fun remoteListener(listener: NetworkManager.Listener) {
listeners.remove(listener)
}
override fun onAvailable(network: Network) {
connected = true
for (l in listeners) l.onNetworkAvailable()
}
override fun onLost(network: Network) {
connected = false
for (l in listeners) l.onNetworkLost()
}
}

View File

@@ -1,105 +0,0 @@
/*
* Copyright (C) 2016-2021 Álinson Santos Xavier <git@axavier.org>
*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.isoron.uhabits.sync
import android.util.Log
import io.ktor.client.HttpClient
import io.ktor.client.engine.android.Android
import io.ktor.client.features.ClientRequestException
import io.ktor.client.features.ServerResponseException
import io.ktor.client.features.json.JsonFeature
import io.ktor.client.request.get
import io.ktor.client.request.header
import io.ktor.client.request.post
import io.ktor.client.request.put
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.invoke
import org.isoron.uhabits.core.preferences.Preferences
import org.isoron.uhabits.core.sync.AbstractSyncServer
import org.isoron.uhabits.core.sync.EditConflictException
import org.isoron.uhabits.core.sync.GetDataVersionResponse
import org.isoron.uhabits.core.sync.KeyNotFoundException
import org.isoron.uhabits.core.sync.RegisterReponse
import org.isoron.uhabits.core.sync.ServiceUnavailable
import org.isoron.uhabits.core.sync.SyncData
class RemoteSyncServer(
private val preferences: Preferences,
private val httpClient: HttpClient = HttpClient(Android) {
install(JsonFeature)
}
) : AbstractSyncServer {
override suspend fun register(): String = Dispatchers.IO {
try {
val url = "${preferences.syncBaseURL}/register"
Log.i("RemoteSyncServer", "POST $url")
val response: RegisterReponse = httpClient.post(url)
return@IO response.key
} catch (e: ServerResponseException) {
throw ServiceUnavailable()
}
}
override suspend fun put(key: String, newData: SyncData) = Dispatchers.IO {
try {
val url = "${preferences.syncBaseURL}/db/$key"
Log.i("RemoteSyncServer", "PUT $url")
val response: String = httpClient.put(url) {
header("Content-Type", "application/json")
body = newData
}
} catch (e: ServerResponseException) {
throw ServiceUnavailable()
} catch (e: ClientRequestException) {
Log.w("RemoteSyncServer", "ClientRequestException", e)
if (e.message!!.contains("409")) throw EditConflictException()
if (e.message!!.contains("404")) throw KeyNotFoundException()
throw e
}
}
override suspend fun getData(key: String): SyncData = Dispatchers.IO {
try {
val url = "${preferences.syncBaseURL}/db/$key"
Log.i("RemoteSyncServer", "GET $url")
return@IO httpClient.get<SyncData>(url)
} catch (e: ServerResponseException) {
throw ServiceUnavailable()
} catch (e: ClientRequestException) {
Log.w("RemoteSyncServer", "ClientRequestException", e)
throw KeyNotFoundException()
}
}
override suspend fun getDataVersion(key: String): Long = Dispatchers.IO {
try {
val url = "${preferences.syncBaseURL}/db/$key/version"
Log.i("RemoteSyncServer", "GET $url")
val response: GetDataVersionResponse = httpClient.get(url)
return@IO response.version
} catch (e: ServerResponseException) {
throw ServiceUnavailable()
} catch (e: ClientRequestException) {
Log.w("RemoteSyncServer", "ClientRequestException", e)
throw KeyNotFoundException()
}
}
}