Unverified Commit ae33779b authored by Rafael Kellermann Streit's avatar Rafael Kellermann Streit Committed by GitHub

Merge pull request #1334 from RocketChat/ux/smart-lock

[IMPROVEMENT] Google Smart Lock feature refactor
parents f6ad8e18 0802e41d
......@@ -55,7 +55,6 @@ class LoginPresenter @Inject constructor(
private lateinit var credentialSecret: String
private lateinit var deepLinkUserId: String
private lateinit var deepLinkToken: String
private var loginCredentials: Credential? = null
fun setupView() {
setupConnectionInfo(currentServer)
......@@ -330,10 +329,7 @@ class LoginPresenter @Inject constructor(
saveToken(token)
registerPushToken()
if (loginType == TYPE_LOGIN_USER_EMAIL) {
loginCredentials = Credential.Builder(usernameOrEmail)
.setPassword(password)
.build()
view.saveSmartLockCredentials(loginCredentials)
view.saveSmartLockCredentials(usernameOrEmail, password)
}
navigator.toChatList()
} else if (loginType == TYPE_LOGIN_OAUTH) {
......
......@@ -226,7 +226,7 @@ interface LoginView : LoadingView, MessageView {
fun alertWrongPassword()
/**
* Save credentials via google smart lock
* Saves Google Smart Lock credentials.
*/
fun saveSmartLockCredentials(loginCredential: Credential?)
fun saveSmartLockCredentials(id: String, password: String)
}
\ No newline at end of file
......@@ -15,7 +15,6 @@ import chat.rocket.core.internal.rest.login
import chat.rocket.core.internal.rest.me
import chat.rocket.core.internal.rest.signup
import chat.rocket.core.model.Myself
import com.google.android.gms.auth.api.credentials.Credential
import javax.inject.Inject
class SignupPresenter @Inject constructor(
......@@ -64,10 +63,7 @@ class SignupPresenter @Inject constructor(
localRepository.save(LocalRepository.CURRENT_USERNAME_KEY, me.username)
saveAccount(me)
registerPushToken()
val loginCredentials = Credential.Builder(email)
.setPassword(password)
.build()
view.saveSmartLockCredentials(loginCredentials)
view.saveSmartLockCredentials(username, password)
navigator.toChatList()
} catch (exception: RocketChatException) {
exception.message?.let {
......
......@@ -27,7 +27,7 @@ interface SignupView : LoadingView, MessageView {
fun alertBlankEmail()
/**
* Save credentials via google smart lock
* Saves Google Smart Lock credentials.
*/
fun saveSmartLockCredentials(loginCredential: Credential)
fun saveSmartLockCredentials(id: String, password: String)
}
\ No newline at end of file
package chat.rocket.android.authentication.signup.ui
import DrawableHelper
import android.app.Activity.RESULT_OK
import android.app.Activity
import android.content.Intent
import android.os.Build
import android.os.Bundle
......@@ -11,30 +11,24 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.ViewTreeObserver
import android.widget.Toast
import chat.rocket.android.R
import chat.rocket.android.authentication.login.ui.googleApiClient
import chat.rocket.android.R.string.message_credentials_saved_successfully
import chat.rocket.android.authentication.signup.presentation.SignupPresenter
import chat.rocket.android.authentication.signup.presentation.SignupView
import chat.rocket.android.helper.KeyboardHelper
import chat.rocket.android.helper.SmartLockHelper
import chat.rocket.android.helper.TextHelper
import chat.rocket.android.util.extensions.*
import com.google.android.gms.auth.api.Auth
import com.google.android.gms.auth.api.credentials.Credential
import com.google.android.gms.common.api.ResolvingResultCallbacks
import com.google.android.gms.common.api.Status
import com.google.android.gms.auth.api.credentials.Credentials
import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_authentication_sign_up.*
import timber.log.Timber
import javax.inject.Inject
internal const val SAVE_CREDENTIALS = 1
class SignupFragment : Fragment(), SignupView {
@Inject
lateinit var presenter: SignupPresenter
private lateinit var credentialsToBeSaved: Credential
private val layoutListener = ViewTreeObserver.OnGlobalLayoutListener {
if (KeyboardHelper.isSoftKeyboardShown(relative_layout.rootView)) {
bottom_container.setVisible(false)
......@@ -120,44 +114,16 @@ class SignupFragment : Fragment(), SignupView {
}
}
override fun saveSmartLockCredentials(loginCredential: Credential) {
credentialsToBeSaved = loginCredential
googleApiClient.let {
if (it.isConnected) {
saveCredentials()
}
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == SAVE_CREDENTIALS) {
if (resultCode == RESULT_OK) {
Toast.makeText(
context,
getString(R.string.message_credentials_saved_successfully),
Toast.LENGTH_SHORT
).show()
} else {
Timber.e("ERROR: Cancelled by user")
if (resultCode == Activity.RESULT_OK) {
if (data != null) {
if (requestCode == SAVE_CREDENTIALS) {
showMessage(getString(message_credentials_saved_successfully))
}
}
}
}
private fun saveCredentials() {
activity?.let {
Auth.CredentialsApi.save(googleApiClient, credentialsToBeSaved).setResultCallback(
object : ResolvingResultCallbacks<Status>(it, SAVE_CREDENTIALS) {
override fun onSuccess(status: Status) {
Timber.d("save:SUCCESS:$status")
}
override fun onUnresolvableFailure(status: Status) {
Timber.e("save:FAILURE:$status")
}
})
}
}
override fun showLoading() {
ui {
enableUserInput(false)
......@@ -188,6 +154,12 @@ class SignupFragment : Fragment(), SignupView {
showMessage(getString(R.string.msg_generic_error))
}
override fun saveSmartLockCredentials(id: String, password: String) {
activity?.let {
SmartLockHelper.save(Credentials.getClient(it), it, id, password)
}
}
private fun tintEditTextDrawableStart() {
ui {
val personDrawable =
......
package chat.rocket.android.helper
import android.app.Activity
import android.content.IntentSender
import android.support.v4.app.FragmentActivity
import com.google.android.gms.auth.api.credentials.*
import com.google.android.gms.common.api.CommonStatusCodes
import com.google.android.gms.common.api.ResolvableApiException
import timber.log.Timber
const val REQUEST_CODE_FOR_SIGN_IN_REQUIRED = 1
const val REQUEST_CODE_FOR_MULTIPLE_ACCOUNTS_RESOLUTION = 2
const val REQUEST_CODE_FOR_SAVE_RESOLUTION = 3
/**
* This class handles some cases of Google Smart Lock for passwords like the request to retrieve
* credentials, to retrieve sign-in hints and to store the credentials.
*
* See https://developers.google.com/identity/smartlock-passwords/android/overview for futher
* information.
*/
object SmartLockHelper {
/**
* Requests for stored Google Smart Lock credentials.
* Note that in case of exception it will try to start a sign in
* ([REQUEST_CODE_FOR_SIGN_IN_REQUIRED]) or "multiple account"
* ([REQUEST_CODE_FOR_MULTIPLE_ACCOUNTS_RESOLUTION]) resolution.
*
* @param credentialsClient The credential client.
* @param activity The activity.
* @return null or the [Credential] result.
*/
fun requestStoredCredentials(
credentialsClient: CredentialsClient,
activity: Activity
): Credential? {
var credential: Credential? = null
val credentialRequest = CredentialRequest.Builder()
.setPasswordLoginSupported(true)
.build()
credentialsClient.request(credentialRequest)
.addOnCompleteListener {
when {
it.isSuccessful -> {
credential = it.result.credential
}
it.exception is ResolvableApiException -> {
val resolvableApiException = (it.exception as ResolvableApiException)
if (resolvableApiException.statusCode == CommonStatusCodes.SIGN_IN_REQUIRED) {
provideSignInHint(credentialsClient, activity)
} else {
// This is most likely the case where the user has multiple saved
// credentials and needs to pick one. This requires showing UI to
// resolve the read request.
resolveResult(
resolvableApiException,
REQUEST_CODE_FOR_MULTIPLE_ACCOUNTS_RESOLUTION,
activity
)
}
}
}
}
return credential
}
/**
* Saves a user credential to Google Smart Lock.
* Note that in case of exception it will try to start a save resolution,
* so the activity/fragment should expected for a request code
* ([REQUEST_CODE_FOR_SAVE_RESOLUTION]) on onActivityResult call.
*
* @param credentialsClient The credential client.
* @param activity The activity.
* @param id The user id credential.
* @param password The user password credential.
*/
fun save(
credentialsClient: CredentialsClient,
activity: FragmentActivity,
id: String,
password: String
) {
val credential = Credential.Builder(id)
.setPassword(password)
.build()
credentialsClient.save(credential)
.addOnCompleteListener {
val exception = it.exception
if (exception is ResolvableApiException) {
// Try to resolve the save request. This will prompt the user if
// the credential is new.
try {
exception.startResolutionForResult(
activity,
REQUEST_CODE_FOR_SAVE_RESOLUTION
)
} catch (e: IntentSender.SendIntentException) {
Timber.e("Failed to send resolution. Exception is: $e")
}
}
}
}
private fun provideSignInHint(credentialsClient: CredentialsClient, activity: Activity) {
val hintRequest = HintRequest.Builder()
.setHintPickerConfig(
CredentialPickerConfig.Builder()
.setShowCancelButton(true)
.build()
)
.setEmailAddressIdentifierSupported(true)
.build()
try {
val intent = credentialsClient.getHintPickerIntent(hintRequest)
activity.startIntentSenderForResult(
intent.intentSender,
REQUEST_CODE_FOR_SIGN_IN_REQUIRED,
null,
0,
0,
0,
null
)
} catch (e: IntentSender.SendIntentException) {
Timber.e("Could not start hint picker Intent. Exception is: $e")
}
}
private fun resolveResult(
exception: ResolvableApiException,
requestCode: Int,
activity: Activity
) {
try {
exception.startResolutionForResult(activity, requestCode)
} catch (e: IntentSender.SendIntentException) {
Timber.e("Failed to send resolution. Exception is: $e")
}
}
}
\ No newline at end of file
......@@ -32,6 +32,18 @@
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_headline" />
<ImageView
android:id="@+id/image_key"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
android:src="@drawable/ic_vpn_key_black_24dp"
android:tint="@color/colorDrawableTintGrey"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@+id/text_username_or_email"
app:layout_constraintEnd_toEndOf="@+id/text_username_or_email"
app:layout_constraintTop_toTopOf="@+id/text_username_or_email" />
<EditText
android:id="@+id/text_password"
style="@style/Authentication.EditText"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment