mirror of
https://github.com/iSoron/uhabits.git
synced 2025-12-07 09:38:52 -06:00
Make registration functional
This commit is contained in:
@@ -23,6 +23,8 @@
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
|
||||
<application
|
||||
android:name=".HabitsApplication"
|
||||
|
||||
@@ -24,17 +24,23 @@ import android.content.ClipboardManager
|
||||
import android.graphics.*
|
||||
import android.os.*
|
||||
import android.text.*
|
||||
import android.view.*
|
||||
import com.google.zxing.*
|
||||
import com.google.zxing.qrcode.*
|
||||
import kotlinx.coroutines.*
|
||||
import org.isoron.androidbase.activities.*
|
||||
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.tasks.*
|
||||
import org.isoron.uhabits.databinding.*
|
||||
import org.isoron.uhabits.sync.*
|
||||
|
||||
|
||||
class SyncActivity : BaseActivity() {
|
||||
|
||||
private lateinit var taskRunner: TaskRunner
|
||||
private lateinit var baseScreen: BaseScreen
|
||||
private lateinit var themeSwitcher: AndroidThemeSwitcher
|
||||
private lateinit var binding: ActivitySyncBinding
|
||||
@@ -47,10 +53,13 @@ class SyncActivity : BaseActivity() {
|
||||
val component = (application as HabitsApplication).component
|
||||
themeSwitcher = AndroidThemeSwitcher(this, component.preferences)
|
||||
themeSwitcher.apply()
|
||||
taskRunner = component.taskRunner
|
||||
|
||||
binding = ActivitySyncBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
binding.errorIcon.typeface = getFontAwesome(this)
|
||||
|
||||
setSupportActionBar(binding.toolbar)
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
supportActionBar?.setDisplayShowHomeEnabled(true)
|
||||
@@ -58,14 +67,50 @@ class SyncActivity : BaseActivity() {
|
||||
|
||||
binding.instructions.setText(Html.fromHtml(resources.getString(R.string.sync_instructions)))
|
||||
|
||||
displayLink("https://loophabits.org/sync/KA9GvblSWrcLk9iwJrplHvWiWdE6opAokdf2qqRl6n6ECX8IUhvcksqlfkQACoMM")
|
||||
displayPassword("6B2W9F5X")
|
||||
|
||||
binding.syncLink.setOnClickListener {
|
||||
copyToClipboard()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
displayLoading()
|
||||
taskRunner.execute(object : Task {
|
||||
private var key = ""
|
||||
private var error = false
|
||||
override fun doInBackground() {
|
||||
runBlocking {
|
||||
val server = RemoteSyncServer()
|
||||
try {
|
||||
key = server.register()
|
||||
} catch(e: ServiceUnavailable) {
|
||||
error = true
|
||||
}
|
||||
}
|
||||
}
|
||||
override fun onPostExecute() {
|
||||
if(error) {
|
||||
displayError()
|
||||
} else {
|
||||
displayLink("https://loophabits.org/sync/$key")
|
||||
displayPassword("6B2W9F5X")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun displayLoading() {
|
||||
binding.qrCode.visibility = View.GONE
|
||||
binding.progress.visibility = View.VISIBLE
|
||||
binding.errorPanel.visibility = View.GONE
|
||||
}
|
||||
|
||||
private fun displayError() {
|
||||
binding.qrCode.visibility = View.GONE
|
||||
binding.progress.visibility = View.GONE
|
||||
binding.errorPanel.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
private fun copyToClipboard() {
|
||||
val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||
clipboard.setPrimaryClip(ClipData.newPlainText("Loop Sync Link", binding.syncLink.text))
|
||||
@@ -77,6 +122,9 @@ class SyncActivity : BaseActivity() {
|
||||
}
|
||||
|
||||
private fun displayLink(link: String) {
|
||||
binding.qrCode.visibility = View.VISIBLE
|
||||
binding.progress.visibility = View.GONE
|
||||
binding.errorPanel.visibility = View.GONE
|
||||
binding.syncLink.text = link
|
||||
displayQR(link)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (C) 2016-2020 Alinson 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
|
||||
|
||||
interface AbstractSyncServer {
|
||||
/**
|
||||
* Generates and returns a new sync key, which can be used to store and retrive
|
||||
* data.
|
||||
*
|
||||
* @throws ServiceUnavailable If key cannot be generated at this time, for example,
|
||||
* due to insufficient server resources, temporary server maintenance or network problems.
|
||||
*/
|
||||
suspend fun register(): String
|
||||
|
||||
/**
|
||||
* Replaces data for a given sync key.
|
||||
*
|
||||
* @throws KeyNotFoundException If key is not found
|
||||
* @throws EditConflictException If the version of the data provided is not
|
||||
* exactly the current data version plus one.
|
||||
* @throws ServiceUnavailable If data cannot be put at this time, for example, due
|
||||
* to insufficient server resources or network problems.
|
||||
*/
|
||||
suspend fun put(key: String, newData: SyncData)
|
||||
|
||||
/**
|
||||
* Returns data for a given sync key.
|
||||
*
|
||||
* @throws KeyNotFoundException If key is not found
|
||||
* @throws ServiceUnavailable If data cannot be retrieved at this time, for example, due
|
||||
* to insufficient server resources or network problems.
|
||||
*/
|
||||
suspend fun getData(key: String): SyncData
|
||||
|
||||
/**
|
||||
* Returns the current data version for the given key
|
||||
*
|
||||
* @throws KeyNotFoundException If key is not found
|
||||
* @throws ServiceUnavailable If data cannot be retrieved at this time, for example, due
|
||||
* to insufficient server resources or network problems.
|
||||
*/
|
||||
suspend fun getDataVersion(key: String): Long
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (C) 2016-2020 Álinson Santos Xavier <isoron@gmail.com>
|
||||
*
|
||||
* 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 io.ktor.client.*
|
||||
import io.ktor.client.engine.android.*
|
||||
import io.ktor.client.features.*
|
||||
import io.ktor.client.features.json.*
|
||||
import io.ktor.client.request.*
|
||||
|
||||
data class RegisterReponse(val key: String)
|
||||
data class GetDataVersionResponse(val version: Long)
|
||||
|
||||
class RemoteSyncServer(
|
||||
private val baseURL: String = "https://sync.loophabits.org",
|
||||
private val httpClient: HttpClient = HttpClient(Android) {
|
||||
install(JsonFeature)
|
||||
}
|
||||
) : AbstractSyncServer {
|
||||
|
||||
override suspend fun register(): String {
|
||||
try {
|
||||
val response: RegisterReponse = httpClient.post("$baseURL/register")
|
||||
return response.key
|
||||
} catch(e: ServerResponseException) {
|
||||
throw ServiceUnavailable()
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun put(key: String, newData: SyncData) {
|
||||
try {
|
||||
val response: String = httpClient.put("$baseURL/$key") {
|
||||
header("Content-Type", "application/json")
|
||||
body = newData
|
||||
}
|
||||
} catch (e: ServerResponseException) {
|
||||
throw ServiceUnavailable()
|
||||
} catch (e: ClientRequestException) {
|
||||
throw KeyNotFoundException()
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getData(key: String): SyncData {
|
||||
try {
|
||||
return httpClient.get("$baseURL/$key")
|
||||
} catch (e: ServerResponseException) {
|
||||
throw ServiceUnavailable()
|
||||
} catch (e: ClientRequestException) {
|
||||
throw KeyNotFoundException()
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getDataVersion(key: String): Long {
|
||||
try {
|
||||
val response: GetDataVersionResponse = httpClient.get("$baseURL/$key/version")
|
||||
return response.version
|
||||
} catch(e: ServerResponseException) {
|
||||
throw ServiceUnavailable()
|
||||
} catch (e: ClientRequestException) {
|
||||
throw KeyNotFoundException()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (C) 2016-2020 Alinson 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
|
||||
|
||||
data class SyncData(
|
||||
val version: Long,
|
||||
val content: String
|
||||
)
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (C) 2016-2020 Alinson 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
|
||||
|
||||
open class SyncException: RuntimeException()
|
||||
|
||||
class KeyNotFoundException: SyncException()
|
||||
|
||||
class ServiceUnavailable: SyncException()
|
||||
|
||||
class EditConflictException: SyncException()
|
||||
@@ -52,6 +52,55 @@
|
||||
android:id="@+id/instructions"
|
||||
/>
|
||||
|
||||
<!-- Sync Link (QR) -->
|
||||
<FrameLayout style="@style/FormOuterBox">
|
||||
|
||||
<LinearLayout
|
||||
style="@style/FormInnerBox">
|
||||
<TextView
|
||||
style="@style/FormLabel"
|
||||
android:translationZ="0.01dp"
|
||||
android:text="@string/sync_link_qr" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/errorPanel"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="200dp"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/errorIcon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="40dp"
|
||||
android:layout_margin="40dp"
|
||||
android:text="@string/fa_exclamation_circle" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Error generating code. Please try again later."
|
||||
/>
|
||||
</LinearLayout>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progress"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="200dp"
|
||||
android:layout_gravity="center"
|
||||
android:indeterminate="true"
|
||||
/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/qrCode"
|
||||
android:layout_width="200dp"
|
||||
android:layout_height="200dp"
|
||||
android:layout_gravity="center"
|
||||
/>
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
||||
|
||||
<!-- Sync Link -->
|
||||
<FrameLayout style="@style/FormOuterBox">
|
||||
|
||||
@@ -73,22 +122,7 @@
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
||||
|
||||
<!-- Sync Link -->
|
||||
<FrameLayout style="@style/FormOuterBox">
|
||||
|
||||
<LinearLayout style="@style/FormInnerBox">
|
||||
<TextView
|
||||
style="@style/FormLabel"
|
||||
android:text="@string/sync_link_qr" />
|
||||
<ImageView
|
||||
android:layout_width="200dp"
|
||||
android:layout_height="200dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_gravity="center"
|
||||
android:id="@+id/qrCode" />
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
||||
|
||||
<!-- Password -->
|
||||
<FrameLayout style="@style/FormOuterBox">
|
||||
|
||||
<LinearLayout style="@style/FormInnerBox">
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
<string translatable="false" name="fa_skipped"></string>
|
||||
<string translatable="false" name="fa_bell_o"></string>
|
||||
<string translatable="false" name="fa_calendar"></string>
|
||||
<string translatable="false" name="fa_exclamation_circle"></string>
|
||||
|
||||
<!--<string translatable="false" name="fa_glass"></string>-->
|
||||
<!--<string translatable="false" name="fa_music"></string>-->
|
||||
@@ -123,7 +124,6 @@
|
||||
<!--<string translatable="false" name="fa_plus"></string>-->
|
||||
<!--<string translatable="false" name="fa_minus"></string>-->
|
||||
<!--<string translatable="false" name="fa_asterisk"></string>-->
|
||||
<!--<string translatable="false" name="fa_exclamation_circle"></string>-->
|
||||
<!--<string translatable="false" name="fa_gift"></string>-->
|
||||
<!--<string translatable="false" name="fa_leaf"></string>-->
|
||||
<!--<string translatable="false" name="fa_fire"></string>-->
|
||||
|
||||
Reference in New Issue
Block a user