mirror of
https://github.com/etesync/android
synced 2024-12-23 07:08:16 +00:00
Login dialog: add support for login into etebase accounts
This commit is contained in:
parent
476f756307
commit
5da8edd54d
@ -247,10 +247,10 @@ constructor(internal val context: Context, internal val account: Account) {
|
|||||||
val SYNC_INTERVAL_MANUALLY: Long = -1
|
val SYNC_INTERVAL_MANUALLY: Long = -1
|
||||||
|
|
||||||
// XXX: Workaround a bug in Android where passing a bundle to addAccountExplicitly doesn't work.
|
// XXX: Workaround a bug in Android where passing a bundle to addAccountExplicitly doesn't work.
|
||||||
fun setUserData(accountManager: AccountManager, account: Account, uri: URI, userName: String) {
|
fun setUserData(accountManager: AccountManager, account: Account, uri: URI?, userName: String) {
|
||||||
accountManager.setUserData(account, KEY_SETTINGS_VERSION, CURRENT_VERSION.toString())
|
accountManager.setUserData(account, KEY_SETTINGS_VERSION, CURRENT_VERSION.toString())
|
||||||
accountManager.setUserData(account, KEY_USERNAME, userName)
|
accountManager.setUserData(account, KEY_USERNAME, userName)
|
||||||
accountManager.setUserData(account, KEY_URI, uri.toString())
|
accountManager.setUserData(account, KEY_URI, uri?.toString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,9 @@
|
|||||||
package com.etesync.syncadapter.ui.setup
|
package com.etesync.syncadapter.ui.setup
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import com.etebase.client.Account
|
||||||
|
import com.etebase.client.Client
|
||||||
|
import com.etebase.client.exceptions.EtebaseException
|
||||||
import com.etesync.syncadapter.HttpClient
|
import com.etesync.syncadapter.HttpClient
|
||||||
import com.etesync.journalmanager.Crypto
|
import com.etesync.journalmanager.Crypto
|
||||||
import com.etesync.journalmanager.Exceptions
|
import com.etesync.journalmanager.Exceptions
|
||||||
@ -19,6 +22,7 @@ import com.etesync.syncadapter.model.CollectionInfo
|
|||||||
import okhttp3.HttpUrl
|
import okhttp3.HttpUrl
|
||||||
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
|
import okhttp3.Request
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.Serializable
|
import java.io.Serializable
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
@ -31,8 +35,26 @@ class BaseConfigurationFinder(protected val context: Context, protected val cred
|
|||||||
httpClient = HttpClient.Builder(context).build().okHttpClient
|
httpClient = HttpClient.Builder(context).build().okHttpClient
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun isServerEtebase(): Boolean {
|
||||||
|
if (credentials.uri != null) {
|
||||||
|
val remote = credentials.uri.toHttpUrlOrNull()!!.newBuilder()
|
||||||
|
.addPathSegments("api/v1/authentication/is_etebase/")
|
||||||
|
.build()
|
||||||
|
|
||||||
fun findInitialConfiguration(): Configuration {
|
val request = Request.Builder()
|
||||||
|
.get()
|
||||||
|
.url(remote)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
val response = httpClient.newCall(request).execute()
|
||||||
|
|
||||||
|
return response.isSuccessful
|
||||||
|
} else {
|
||||||
|
return !credentials.userName.contains("@")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun findInitialConfigurationLegacy(): Configuration {
|
||||||
var exception: Throwable? = null
|
var exception: Throwable? = null
|
||||||
|
|
||||||
val uri = credentials.uri ?: URI(Constants.serviceUrl.toString())
|
val uri = credentials.uri ?: URI(Constants.serviceUrl.toString())
|
||||||
@ -57,18 +79,63 @@ class BaseConfigurationFinder(protected val context: Context, protected val cred
|
|||||||
|
|
||||||
return Configuration(
|
return Configuration(
|
||||||
uri,
|
uri,
|
||||||
credentials.userName, authtoken,
|
credentials.userName,
|
||||||
|
null,
|
||||||
|
authtoken,
|
||||||
userInfo,
|
userInfo,
|
||||||
exception
|
exception
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun findInitialConfigurationEtebase(): Configuration {
|
||||||
|
var exception: Throwable? = null
|
||||||
|
|
||||||
|
val uri = credentials.uri
|
||||||
|
|
||||||
|
var etebaseSession: String? = null
|
||||||
|
try {
|
||||||
|
val client = Client.create(httpClient, uri.toString())
|
||||||
|
val etebase = Account.login(client, credentials.userName, credentials.password)
|
||||||
|
etebaseSession = etebase.save(null)
|
||||||
|
} catch (e: EtebaseException) {
|
||||||
|
exception = e
|
||||||
|
}
|
||||||
|
|
||||||
|
return Configuration(
|
||||||
|
uri,
|
||||||
|
credentials.userName,
|
||||||
|
etebaseSession,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
exception
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun findInitialConfiguration(): Configuration {
|
||||||
|
try {
|
||||||
|
if (isServerEtebase()) {
|
||||||
|
return findInitialConfigurationEtebase()
|
||||||
|
} else {
|
||||||
|
return findInitialConfigurationLegacy()
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
return Configuration(
|
||||||
|
credentials.uri,
|
||||||
|
credentials.userName,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
e
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// data classes
|
// data classes
|
||||||
|
|
||||||
class Configuration
|
class Configuration
|
||||||
// We have to use URI here because HttpUrl is not serializable!
|
// We have to use URI here because HttpUrl is not serializable!
|
||||||
|
|
||||||
(val url: URI, val userName: String, val authtoken: String?, var userInfo: UserInfoManager.UserInfo?, var error: Throwable?) : Serializable {
|
(val url: URI?, val userName: String, val etebaseSession: String?, val authtoken: String?, var userInfo: UserInfoManager.UserInfo?, var error: Throwable?) : Serializable {
|
||||||
var rawPassword: String? = null
|
var rawPassword: String? = null
|
||||||
var password: String? = null
|
var password: String? = null
|
||||||
var keyPair: Crypto.AsymmetricKeyPair? = null
|
var keyPair: Crypto.AsymmetricKeyPair? = null
|
||||||
@ -76,6 +143,9 @@ class BaseConfigurationFinder(protected val context: Context, protected val cred
|
|||||||
val isFailed: Boolean
|
val isFailed: Boolean
|
||||||
get() = this.error != null
|
get() = this.error != null
|
||||||
|
|
||||||
|
val isLegacy: Boolean
|
||||||
|
get() = this.authtoken != null
|
||||||
|
|
||||||
class ServiceInfo : Serializable {
|
class ServiceInfo : Serializable {
|
||||||
val collections: Map<String, CollectionInfo> = HashMap()
|
val collections: Map<String, CollectionInfo> = HashMap()
|
||||||
|
|
||||||
|
@ -0,0 +1,111 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2013 – 2016 Ricki Hirner (bitfire web engineering).
|
||||||
|
* All rights reserved. This program and the accompanying materials
|
||||||
|
* are made available under the terms of the GNU Public License v3.0
|
||||||
|
* which accompanies this distribution, and is available at
|
||||||
|
* http://www.gnu.org/licenses/gpl.html
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.etesync.syncadapter.ui.setup
|
||||||
|
|
||||||
|
import android.accounts.Account
|
||||||
|
import android.accounts.AccountManager
|
||||||
|
import android.app.Activity
|
||||||
|
import android.app.Dialog
|
||||||
|
import android.app.ProgressDialog
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.AsyncTask
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.provider.CalendarContract
|
||||||
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import androidx.fragment.app.DialogFragment
|
||||||
|
import at.bitfire.ical4android.TaskProvider.Companion.OPENTASK_PROVIDERS
|
||||||
|
import com.etesync.journalmanager.Crypto
|
||||||
|
import com.etesync.journalmanager.Exceptions
|
||||||
|
import com.etesync.syncadapter.*
|
||||||
|
import com.etesync.syncadapter.log.Logger
|
||||||
|
import com.etesync.syncadapter.model.CollectionInfo
|
||||||
|
import com.etesync.syncadapter.model.JournalEntity
|
||||||
|
import com.etesync.syncadapter.model.ServiceEntity
|
||||||
|
import com.etesync.syncadapter.ui.setup.BaseConfigurationFinder.Configuration
|
||||||
|
import com.etesync.syncadapter.utils.AndroidCompat
|
||||||
|
import com.etesync.syncadapter.utils.TaskProviderHandling
|
||||||
|
import java.util.logging.Level
|
||||||
|
|
||||||
|
class CreateAccountFragment : DialogFragment() {
|
||||||
|
|
||||||
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
|
val progress = ProgressDialog(activity)
|
||||||
|
progress.setTitle(R.string.login_encryption_setup_title)
|
||||||
|
progress.setMessage(getString(R.string.login_encryption_setup))
|
||||||
|
progress.isIndeterminate = true
|
||||||
|
progress.setCanceledOnTouchOutside(false)
|
||||||
|
isCancelable = false
|
||||||
|
return progress
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
val config = requireArguments().getSerializable(KEY_CONFIG) as Configuration
|
||||||
|
|
||||||
|
val activity = requireActivity()
|
||||||
|
if (createAccount(config.userName, config)) {
|
||||||
|
activity.setResult(Activity.RESULT_OK)
|
||||||
|
activity.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(InvalidAccountException::class)
|
||||||
|
protected fun createAccount(accountName: String, config: Configuration): Boolean {
|
||||||
|
val account = Account(accountName, App.accountType)
|
||||||
|
|
||||||
|
// create Android account
|
||||||
|
Logger.log.log(Level.INFO, "Creating Android account with initial config", arrayOf(account, config.userName, config.url))
|
||||||
|
|
||||||
|
val accountManager = AccountManager.get(context)
|
||||||
|
if (!accountManager.addAccountExplicitly(account, config.password, null))
|
||||||
|
return false
|
||||||
|
|
||||||
|
AccountSettings.setUserData(accountManager, account, config.url, config.userName)
|
||||||
|
|
||||||
|
// add entries for account to service DB
|
||||||
|
Logger.log.log(Level.INFO, "Writing account configuration to database", config)
|
||||||
|
try {
|
||||||
|
val settings = AccountSettings(requireContext(), account)
|
||||||
|
|
||||||
|
settings.etebaseSession = config.etebaseSession
|
||||||
|
|
||||||
|
// contact sync is automatically enabled by isAlwaysSyncable="true" in res/xml/sync_contacts.xml
|
||||||
|
settings.setSyncInterval(App.addressBooksAuthority, Constants.DEFAULT_SYNC_INTERVAL.toLong())
|
||||||
|
|
||||||
|
// calendar sync is automatically enabled by isAlwaysSyncable="true" in res/xml/sync_contacts.xml
|
||||||
|
settings.setSyncInterval(CalendarContract.AUTHORITY, Constants.DEFAULT_SYNC_INTERVAL.toLong())
|
||||||
|
|
||||||
|
OPENTASK_PROVIDERS.forEach {
|
||||||
|
// enable task sync if OpenTasks is installed
|
||||||
|
// further changes will be handled by PackageChangedReceiver
|
||||||
|
TaskProviderHandling.updateTaskSync(requireContext(), it)
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (e: InvalidAccountException) {
|
||||||
|
Logger.log.log(Level.SEVERE, "Couldn't access account settings", e)
|
||||||
|
AndroidCompat.removeAccount(accountManager, account)
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val KEY_CONFIG = "config"
|
||||||
|
|
||||||
|
fun newInstance(config: Configuration): CreateAccountFragment {
|
||||||
|
val frag = CreateAccountFragment()
|
||||||
|
val args = Bundle(1)
|
||||||
|
args.putSerializable(KEY_CONFIG, config)
|
||||||
|
frag.arguments = args
|
||||||
|
return frag
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -13,6 +13,7 @@ import android.app.ProgressDialog
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.widget.Toast
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.fragment.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
import androidx.loader.app.LoaderManager
|
import androidx.loader.app.LoaderManager
|
||||||
@ -47,17 +48,23 @@ class DetectConfigurationFragment : DialogFragment(), LoaderManager.LoaderCallba
|
|||||||
|
|
||||||
override fun onLoadFinished(loader: Loader<Configuration>, data: Configuration?) {
|
override fun onLoadFinished(loader: Loader<Configuration>, data: Configuration?) {
|
||||||
if (data != null) {
|
if (data != null) {
|
||||||
if (data.isFailed)
|
if (data.isFailed) {
|
||||||
// no service found: show error message
|
// no service found: show error message
|
||||||
requireFragmentManager().beginTransaction()
|
requireFragmentManager().beginTransaction()
|
||||||
.add(NothingDetectedFragment.newInstance(data.error!!.localizedMessage), null)
|
.add(NothingDetectedFragment.newInstance(data.error!!.localizedMessage), null)
|
||||||
.commitAllowingStateLoss()
|
.commitAllowingStateLoss()
|
||||||
else
|
} else if (data.isLegacy) {
|
||||||
// service found: continue
|
// legacy service found: continue
|
||||||
requireFragmentManager().beginTransaction()
|
requireFragmentManager().beginTransaction()
|
||||||
.replace(android.R.id.content, EncryptionDetailsFragment.newInstance(data))
|
.replace(android.R.id.content, EncryptionDetailsFragment.newInstance(data))
|
||||||
.addToBackStack(null)
|
.addToBackStack(null)
|
||||||
.commitAllowingStateLoss()
|
.commitAllowingStateLoss()
|
||||||
|
} else {
|
||||||
|
requireFragmentManager().beginTransaction()
|
||||||
|
.replace(android.R.id.content, CreateAccountFragment.newInstance(data))
|
||||||
|
.addToBackStack(null)
|
||||||
|
.commitAllowingStateLoss()
|
||||||
|
}
|
||||||
} else
|
} else
|
||||||
Logger.log.severe("Configuration detection failed")
|
Logger.log.severe("Configuration detection failed")
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user