mirror of
https://github.com/etesync/android
synced 2025-01-10 15:51:08 +00:00
Signup: add a signup fragment so people can sign up from the app
This commit is contained in:
parent
44503715a8
commit
2eeee1214f
@ -0,0 +1,226 @@
|
||||
/*
|
||||
* 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.etebase
|
||||
|
||||
import android.app.Dialog
|
||||
import android.app.ProgressDialog
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Button
|
||||
import android.widget.CheckedTextView
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.commit
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.observe
|
||||
import com.etebase.client.Account
|
||||
import com.etebase.client.Client
|
||||
import com.etebase.client.User
|
||||
import com.etebase.client.exceptions.EtebaseException
|
||||
import com.etesync.syncadapter.Constants
|
||||
import com.etesync.syncadapter.HttpClient
|
||||
import com.etesync.syncadapter.R
|
||||
import com.etesync.syncadapter.ui.setup.BaseConfigurationFinder
|
||||
import com.etesync.syncadapter.ui.setup.CreateAccountFragment
|
||||
import com.etesync.syncadapter.ui.setup.DetectConfigurationFragment
|
||||
import com.etesync.syncadapter.ui.setup.LoginCredentialsFragment
|
||||
import com.google.android.material.textfield.TextInputEditText
|
||||
import com.google.android.material.textfield.TextInputLayout
|
||||
import net.cachapa.expandablelayout.ExpandableLayout
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
||||
import org.jetbrains.anko.doAsync
|
||||
import org.jetbrains.anko.uiThread
|
||||
import java.net.URI
|
||||
import java.util.concurrent.Future
|
||||
|
||||
class SignupFragment(private val initialUsername: String?, private val initialPassword: String?) : Fragment() {
|
||||
internal lateinit var editUserName: TextInputLayout
|
||||
internal lateinit var editEmail: TextInputLayout
|
||||
internal lateinit var editPassword: TextInputLayout
|
||||
|
||||
internal lateinit var showAdvanced: CheckedTextView
|
||||
internal lateinit var customServer: TextInputEditText
|
||||
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
val v = inflater.inflate(R.layout.signup_fragment, container, false)
|
||||
|
||||
editUserName = v.findViewById(R.id.user_name)
|
||||
editEmail = v.findViewById(R.id.email)
|
||||
editPassword = v.findViewById(R.id.url_password)
|
||||
showAdvanced = v.findViewById(R.id.show_advanced)
|
||||
customServer = v.findViewById(R.id.custom_server)
|
||||
|
||||
if (savedInstanceState == null) {
|
||||
editUserName.editText?.setText(initialUsername ?: "")
|
||||
editPassword.editText?.setText(initialPassword ?: "")
|
||||
}
|
||||
|
||||
val login = v.findViewById<Button>(R.id.login)
|
||||
login.setOnClickListener {
|
||||
parentFragmentManager.commit {
|
||||
replace(android.R.id.content, LoginCredentialsFragment(editUserName.editText?.text.toString(), editPassword.editText?.text.toString()))
|
||||
}
|
||||
}
|
||||
|
||||
val createAccount = v.findViewById<Button>(R.id.create_account)
|
||||
createAccount.setOnClickListener {
|
||||
val credentials = validateData()
|
||||
if (credentials != null) {
|
||||
SignupDoFragment(credentials).show(fragmentManager!!, null)
|
||||
}
|
||||
}
|
||||
|
||||
val advancedLayout = v.findViewById<View>(R.id.advanced_layout) as ExpandableLayout
|
||||
|
||||
showAdvanced.setOnClickListener {
|
||||
if (showAdvanced.isChecked) {
|
||||
showAdvanced.isChecked = false
|
||||
advancedLayout.collapse()
|
||||
} else {
|
||||
showAdvanced.isChecked = true
|
||||
advancedLayout.expand()
|
||||
}
|
||||
}
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
protected fun validateData(): SignupCredentials? {
|
||||
var valid = true
|
||||
|
||||
val userName = editUserName.editText?.text.toString()
|
||||
if (userName.isEmpty()) {
|
||||
editUserName.error = getString(R.string.login_username_error)
|
||||
valid = false
|
||||
} else {
|
||||
editUserName.error = null
|
||||
}
|
||||
|
||||
val email = editEmail.editText?.text.toString()
|
||||
if (email.isEmpty()) {
|
||||
editEmail.error = getString(R.string.login_email_address_error)
|
||||
valid = false
|
||||
} else {
|
||||
editEmail.error = null
|
||||
}
|
||||
|
||||
val password = editPassword.editText?.text.toString()
|
||||
if (password.isEmpty()) {
|
||||
editPassword.error = getString(R.string.signup_password_restrictions)
|
||||
valid = false
|
||||
} else {
|
||||
editPassword.error = null
|
||||
}
|
||||
|
||||
var uri: URI? = null
|
||||
if (showAdvanced.isChecked) {
|
||||
val server = customServer.text.toString()
|
||||
// If this field is null, just use the default
|
||||
if (!server.isEmpty()) {
|
||||
val url = server.toHttpUrlOrNull()
|
||||
if (url != null) {
|
||||
uri = url.toUri()
|
||||
customServer.error = null
|
||||
} else {
|
||||
customServer.error = getString(R.string.login_custom_server_error)
|
||||
valid = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return if (valid) SignupCredentials(uri, userName, email, password) else null
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
class SignupDoFragment(private val signupCredentials: SignupCredentials) : DialogFragment() {
|
||||
private val model: ConfigurationViewModel by viewModels()
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
val progress = ProgressDialog(activity)
|
||||
progress.setTitle(R.string.login_configuration_detection)
|
||||
progress.setMessage(getString(R.string.login_querying_server))
|
||||
progress.isIndeterminate = true
|
||||
progress.setCanceledOnTouchOutside(false)
|
||||
isCancelable = false
|
||||
return progress
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
if (savedInstanceState == null) {
|
||||
model.signup(requireContext(), signupCredentials)
|
||||
model.observe(this) {
|
||||
if (it.isFailed) {
|
||||
// no service found: show error message
|
||||
requireFragmentManager().beginTransaction()
|
||||
.add(DetectConfigurationFragment.NothingDetectedFragment.newInstance(it.error!!.localizedMessage), null)
|
||||
.commitAllowingStateLoss()
|
||||
} else {
|
||||
requireFragmentManager().beginTransaction()
|
||||
.replace(android.R.id.content, CreateAccountFragment.newInstance(it))
|
||||
.addToBackStack(null)
|
||||
.commitAllowingStateLoss()
|
||||
}
|
||||
dismissAllowingStateLoss()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ConfigurationViewModel : ViewModel() {
|
||||
private val account = MutableLiveData<BaseConfigurationFinder.Configuration>()
|
||||
private var asyncTask: Future<Unit>? = null
|
||||
|
||||
fun signup(context: Context, credentials: SignupCredentials) {
|
||||
asyncTask = doAsync {
|
||||
val httpClient = HttpClient.Builder(context).build().okHttpClient
|
||||
val uri = credentials.uri ?: URI(Constants.etebaseServiceUrl)
|
||||
var etebaseSession: String? = null
|
||||
var exception: Throwable? = null
|
||||
try {
|
||||
val client = Client.create(httpClient, uri.toString())
|
||||
val user = User(credentials.userName, credentials.email)
|
||||
val etebase = Account.signup(client, user, credentials.password)
|
||||
etebaseSession = etebase.save(null)
|
||||
} catch (e: EtebaseException) {
|
||||
exception = e
|
||||
}
|
||||
|
||||
uiThread {
|
||||
account.value = BaseConfigurationFinder.Configuration(
|
||||
uri,
|
||||
credentials.userName,
|
||||
etebaseSession,
|
||||
null,
|
||||
null,
|
||||
exception
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun cancelLoad() {
|
||||
asyncTask?.cancel(true)
|
||||
}
|
||||
|
||||
fun observe(owner: LifecycleOwner, observer: (BaseConfigurationFinder.Configuration) -> Unit) =
|
||||
account.observe(owner, observer)
|
||||
}
|
||||
|
||||
data class SignupCredentials(val uri: URI?, val userName: String, val email: String, val password: String)
|
@ -30,7 +30,7 @@ class LoginActivity : BaseActivity() {
|
||||
if (savedInstanceState == null)
|
||||
// first call, add fragment
|
||||
supportFragmentManager.beginTransaction()
|
||||
.replace(android.R.id.content, LoginCredentialsFragment())
|
||||
.replace(android.R.id.content, LoginCredentialsFragment(null, null))
|
||||
.commit()
|
||||
|
||||
}
|
||||
@ -43,16 +43,4 @@ class LoginActivity : BaseActivity() {
|
||||
fun showHelp(item: MenuItem) {
|
||||
WebViewActivity.openUrl(this, Constants.helpUri)
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* When set, and [.EXTRA_PASSWORD] is set too, the user name field will be set to this value.
|
||||
*/
|
||||
val EXTRA_USERNAME = "username"
|
||||
|
||||
/**
|
||||
* When set, the password field will be set to this value.
|
||||
*/
|
||||
val EXTRA_PASSWORD = "password"
|
||||
}
|
||||
}
|
||||
|
@ -17,16 +17,19 @@ import android.widget.CheckedTextView
|
||||
import android.widget.EditText
|
||||
import android.widget.TextView
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.commit
|
||||
import androidx.fragment.app.replace
|
||||
import com.etesync.syncadapter.Constants
|
||||
import com.etesync.syncadapter.R
|
||||
import com.etesync.syncadapter.ui.WebViewActivity
|
||||
import com.etesync.syncadapter.ui.etebase.SignupFragment
|
||||
import com.google.android.material.textfield.TextInputEditText
|
||||
import com.google.android.material.textfield.TextInputLayout
|
||||
import net.cachapa.expandablelayout.ExpandableLayout
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
||||
import java.net.URI
|
||||
|
||||
class LoginCredentialsFragment : Fragment() {
|
||||
class LoginCredentialsFragment(private val initialUsername: String?, private val initialPassword: String?) : Fragment() {
|
||||
internal lateinit var editUserName: EditText
|
||||
internal lateinit var editUrlPassword: TextInputLayout
|
||||
|
||||
@ -43,22 +46,15 @@ class LoginCredentialsFragment : Fragment() {
|
||||
customServer = v.findViewById<TextInputEditText>(R.id.custom_server)
|
||||
|
||||
if (savedInstanceState == null) {
|
||||
val activity = activity
|
||||
val intent = activity?.intent
|
||||
if (intent != null) {
|
||||
// we've got initial login data
|
||||
val username = intent.getStringExtra(LoginActivity.EXTRA_USERNAME)
|
||||
val password = intent.getStringExtra(LoginActivity.EXTRA_PASSWORD)
|
||||
|
||||
editUserName.setText(username)
|
||||
editUrlPassword.editText?.setText(password)
|
||||
}
|
||||
editUserName.setText(initialUsername ?: "")
|
||||
editUrlPassword.editText?.setText(initialPassword ?: "")
|
||||
}
|
||||
|
||||
val createAccount = v.findViewById<View>(R.id.create_account) as Button
|
||||
createAccount.setOnClickListener {
|
||||
val createUri = Constants.registrationUrl.buildUpon().appendQueryParameter("email", editUserName.text.toString()).build()
|
||||
WebViewActivity.openUrl(context!!, createUri)
|
||||
parentFragmentManager.commit {
|
||||
replace(android.R.id.content, SignupFragment(editUserName.text.toString(), editUrlPassword.editText?.text.toString()))
|
||||
}
|
||||
}
|
||||
|
||||
val login = v.findViewById<View>(R.id.login) as Button
|
||||
|
134
app/src/main/res/layout/signup_fragment.xml
Normal file
134
app/src/main/res/layout/signup_fragment.xml
Normal file
@ -0,0 +1,134 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ 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
|
||||
-->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ScrollView android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_margin="@dimen/activity_margin">
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/login_type_headline"
|
||||
android:text="@string/signup_title"
|
||||
android:layout_marginBottom="14dp"/>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/user_name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="14dp">
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/login_username"
|
||||
android:autofillHints="emailAddress"
|
||||
android:inputType="textEmailAddress"/>
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/email"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="14dp">
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/login_email_address"
|
||||
android:autofillHints="emailAddress"
|
||||
android:inputType="textEmailAddress"/>
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/url_password"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:passwordToggleEnabled="true">
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="monospace"
|
||||
android:autofillHints="password"
|
||||
android:inputType="textPassword"
|
||||
android:hint="@string/login_password"/>
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/login_encryption_check_password"/>
|
||||
|
||||
<CheckedTextView
|
||||
android:id="@+id/show_advanced"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="14dp"
|
||||
android:layout_marginLeft="4dp"
|
||||
android:textSize="18sp"
|
||||
android:gravity="center_vertical"
|
||||
android:checkMark="?android:attr/listChoiceIndicatorMultiple"
|
||||
android:text="@string/login_toggle_advanced" />
|
||||
|
||||
<net.cachapa.expandablelayout.ExpandableLayout
|
||||
android:id="@+id/advanced_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/custom_server"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/login_custom_server"
|
||||
android:inputType="textUri"/>
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
</net.cachapa.expandablelayout.ExpandableLayout>
|
||||
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/stepper_nav_bar">
|
||||
|
||||
<Button
|
||||
android:id="@+id/login"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/login_login"
|
||||
style="@style/stepper_nav_button"/>
|
||||
|
||||
<Space
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
style="@style/stepper_nav_button"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/create_account"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/login_signup"
|
||||
style="@style/stepper_nav_button"/>
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
@ -211,6 +211,7 @@
|
||||
<!-- AddAccountActivity -->
|
||||
<string name="login_title">Add account</string>
|
||||
<string name="login_username">Username</string>
|
||||
<string name="login_username_error">Valid username required</string>
|
||||
<string name="login_email_address">Email</string>
|
||||
<string name="login_email_address_error">Valid email required</string>
|
||||
<string name="login_password">Password</string>
|
||||
@ -220,7 +221,7 @@
|
||||
<string name="login_encryption_password">Encryption Password</string>
|
||||
<string name="login_encryption_set_new_password">Please set your encryption password below, and make sure you got it right, as it *can\'t* be recovered if lost!</string>
|
||||
<string name="login_encryption_enter_password">You are logged in as \"%s\". Please enter your encryption password to continue, or log out from the side menu.</string>
|
||||
<string name="login_encryption_check_password">* Please double-check the password, as it can\'t be recovered if wrong!</string>
|
||||
<string name="login_encryption_check_password">* Please make sure you remember your password, as it can\'t be recovered if lost!</string>
|
||||
<string name="login_encryption_extra_info">* This password is used to encrypt your data, unlike the previous one, which is used to log into the service.\nYou are asked to choose a separate encryption password for security reasons. For more information, please refer to the FAQ at: %s</string>
|
||||
<string name="login_password_required">Password required</string>
|
||||
<string name="login_login">Log In</string>
|
||||
@ -245,6 +246,9 @@
|
||||
<string name="wrong_encryption_password">Wrong encryption password</string>
|
||||
<string name="wrong_encryption_password_content">Got an integrity error while accessing your account, which most likely means you put in the wrong encryption password.\nPlease note that the username is case sensitive, so please also try different capitalizations, for example make the first character uppercase.\n\nError: %s</string>
|
||||
|
||||
<string name="signup_title">Enter Signup Details</string>
|
||||
<string name="signup_password_restrictions">Password should be at least 8 characters long</string>
|
||||
|
||||
<!-- ChangeEncryptionPasswordActivity -->
|
||||
<string name="change_encryption_password_title">Change Encryption Password</string>
|
||||
<string name="change_encryption_password_extra_info">Please don\'t use this tool if you believe your encryption password has been compromised. Contact support instead.</string>
|
||||
|
Loading…
Reference in New Issue
Block a user