Commit bee45fd3 authored by Filipe de Lima Brito's avatar Filipe de Lima Brito

Improve authentication flow.

parent dbcbe54c
...@@ -41,7 +41,12 @@ ...@@ -41,7 +41,12 @@
android:theme="@style/AppTheme" /> android:theme="@style/AppTheme" />
<activity <activity
android:name=".webview.WebViewActivity" android:name=".webview.ui.WebViewActivity"
android:windowSoftInputMode="adjustResize|stateAlwaysHidden"
android:theme="@style/AppTheme" />
<activity
android:name=".webview.cas.ui.CasWebViewActivity"
android:windowSoftInputMode="adjustResize|stateAlwaysHidden" android:windowSoftInputMode="adjustResize|stateAlwaysHidden"
android:theme="@style/AppTheme" /> android:theme="@style/AppTheme" />
......
...@@ -13,7 +13,6 @@ import chat.rocket.android.util.extensions.isEmailValid ...@@ -13,7 +13,6 @@ import chat.rocket.android.util.extensions.isEmailValid
import chat.rocket.android.util.extensions.launchUI import chat.rocket.android.util.extensions.launchUI
import chat.rocket.common.RocketChatException import chat.rocket.common.RocketChatException
import chat.rocket.common.RocketChatTwoFactorException import chat.rocket.common.RocketChatTwoFactorException
import chat.rocket.common.model.Token
import chat.rocket.common.util.ifNull import chat.rocket.common.util.ifNull
import chat.rocket.core.RocketChatClient import chat.rocket.core.RocketChatClient
import chat.rocket.core.internal.rest.* import chat.rocket.core.internal.rest.*
...@@ -30,67 +29,67 @@ class LoginPresenter @Inject constructor(private val view: LoginView, ...@@ -30,67 +29,67 @@ class LoginPresenter @Inject constructor(private val view: LoginView,
// TODO - we should validate the current server when opening the app, and have a nonnull get() // TODO - we should validate the current server when opening the app, and have a nonnull get()
private val client: RocketChatClient = factory.create(serverInteractor.get()!!) private val client: RocketChatClient = factory.create(serverInteractor.get()!!)
fun setup() { fun setupView() {
val server = serverInteractor.get() val server = serverInteractor.get()
if (server == null) { if (server == null) {
navigator.toServerScreen() navigator.toServerScreen()
return return
} }
val settings = settingsInteractor.get(server) val settings = settingsInteractor.get(server)
if (settings == null) {
navigator.toServerScreen() if (settings.isLoginFormEnabled()) {
return view.showFormView()
view.setupLoginButtonListener()
view.setupGlobalListener()
} else {
view.hideFormView()
} }
if (settings.casEnabled()) { if (settings.isRegistrationEnabledForNewUsers()) {
// Hiding the views in order to show the WebView instead. view.showSignUpView()
view.hideUsernameOrEmailView() view.setupSignUpView()
view.hidePasswordView() }
view.hideSignUpView()
view.hideOauthView()
view.hideLoginButton()
view.showLoading() if (settings.isCasAuthenticationEnabled()) {
val token = generateRandomString(17) val token = generateRandomString(17)
view.showCasView(UrlHelper.getCasUrl(settings.casLoginUrl(), server, token), token) view.setupCasButtonListener(UrlHelper.getCasUrl(settings.casLoginUrl(), server, token), token)
} else { view.showCasButton()
if (settings.registrationEnabled()){
view.showSignUpView()
} }
var hasSocial = false var totalSocialAccountsEnabled = 0
if (settings.facebookEnabled()) { if (settings.isFacebookAuthenticationEnabled()) {
view.enableLoginByFacebook() view.enableLoginByFacebook()
hasSocial = true totalSocialAccountsEnabled++
} }
if (settings.githubEnabled()) { if (settings.isGithubAuthenticationEnabled()) {
view.enableLoginByGithub() view.enableLoginByGithub()
hasSocial = true totalSocialAccountsEnabled++
} }
if (settings.googleEnabled()) { if (settings.isGoogleAuthenticationEnabled()) {
view.enableLoginByGoogle() view.enableLoginByGoogle()
hasSocial = true totalSocialAccountsEnabled++
} }
if (settings.linkedinEnabled()) { if (settings.isLinkedinAuthenticationEnabled()) {
view.enableLoginByLinkedin() view.enableLoginByLinkedin()
hasSocial = true totalSocialAccountsEnabled++
} }
if (settings.meteorEnabled()) { if (settings.isMeteorAuthenticationEnabled()) {
view.enableLoginByMeteor() view.enableLoginByMeteor()
hasSocial = true totalSocialAccountsEnabled++
} }
if (settings.twitterEnabled()) { if (settings.isTwitterAuthenticationEnabled()) {
view.enableLoginByTwitter() view.enableLoginByTwitter()
hasSocial = true totalSocialAccountsEnabled++
} }
if (settings.gitlabEnabled()) { if (settings.isGitlabAuthenticationEnabled()) {
view.enableLoginByGitlab() view.enableLoginByGitlab()
hasSocial = true totalSocialAccountsEnabled++
} }
if (hasSocial) { if (totalSocialAccountsEnabled > 0) {
view.showOauthView() view.showOauthView()
if (totalSocialAccountsEnabled > 3) {
view.setupFabListener()
} }
} }
} }
...@@ -110,32 +109,23 @@ class LoginPresenter @Inject constructor(private val view: LoginView, ...@@ -110,32 +109,23 @@ class LoginPresenter @Inject constructor(private val view: LoginView,
else -> { else -> {
launchUI(strategy) { launchUI(strategy) {
if (NetworkHelper.hasInternetAccess()) { if (NetworkHelper.hasInternetAccess()) {
view.disableUserInput()
view.showLoading() view.showLoading()
try { try {
var token: Token? = null val token = if (usernameOrEmail.isEmailValid()) {
if (usernameOrEmail.isEmailValid()) { client.loginWithEmail(usernameOrEmail, password)
token = client.loginWithEmail(usernameOrEmail, password)
} else { } else {
val settings = settingsInteractor.get(server) val settings = settingsInteractor.get(server)
if (settings != null) { if (settings.isLdapAuthenticationEnabled()) {
token = if (settings.ldapEnabled()) {
client.loginWithLdap(usernameOrEmail, password) client.loginWithLdap(usernameOrEmail, password)
} else { } else {
client.login(usernameOrEmail, password) client.login(usernameOrEmail, password)
} }
} else {
navigator.toServerScreen()
}
} }
if (token != null) {
saveToken(server, TokenModel(token.userId, token.authToken), client.me().username) saveToken(server, TokenModel(token.userId, token.authToken), client.me().username)
registerPushToken() registerPushToken()
navigator.toChatList() navigator.toChatList()
} else {
view.showGenericErrorMessage()
}
} catch (exception: RocketChatException) { } catch (exception: RocketChatException) {
when (exception) { when (exception) {
is RocketChatTwoFactorException -> { is RocketChatTwoFactorException -> {
...@@ -151,6 +141,7 @@ class LoginPresenter @Inject constructor(private val view: LoginView, ...@@ -151,6 +141,7 @@ class LoginPresenter @Inject constructor(private val view: LoginView,
} }
} finally { } finally {
view.hideLoading() view.hideLoading()
view.enableUserInput()
} }
} else { } else {
view.showNoInternetConnection() view.showNoInternetConnection()
...@@ -160,14 +151,15 @@ class LoginPresenter @Inject constructor(private val view: LoginView, ...@@ -160,14 +151,15 @@ class LoginPresenter @Inject constructor(private val view: LoginView,
} }
} }
fun authenticateWithCas(casCredential: String) { fun authenticateWithCas(casToken: String) {
launchUI(strategy) { launchUI(strategy) {
if (NetworkHelper.hasInternetAccess()) { if (NetworkHelper.hasInternetAccess()) {
view.disableUserInput()
view.showLoading() view.showLoading()
try { try {
val server = serverInteractor.get() val server = serverInteractor.get()
if (server != null) { if (server != null) {
val token = client.loginWithCas(casCredential) val token = client.loginWithCas(casToken)
saveToken(server, TokenModel(token.userId, token.authToken), client.me().username) saveToken(server, TokenModel(token.userId, token.authToken), client.me().username)
registerPushToken() registerPushToken()
navigator.toChatList() navigator.toChatList()
...@@ -182,6 +174,7 @@ class LoginPresenter @Inject constructor(private val view: LoginView, ...@@ -182,6 +174,7 @@ class LoginPresenter @Inject constructor(private val view: LoginView,
} }
} finally { } finally {
view.hideLoading() view.hideLoading()
view.enableUserInput()
} }
} else { } else {
view.showNoInternetConnection() view.showNoInternetConnection()
...@@ -189,14 +182,14 @@ class LoginPresenter @Inject constructor(private val view: LoginView, ...@@ -189,14 +182,14 @@ class LoginPresenter @Inject constructor(private val view: LoginView,
} }
} }
fun signup() = navigator.toSignUp()
private suspend fun saveToken(server: String, tokenModel: TokenModel, username: String?) { private suspend fun saveToken(server: String, tokenModel: TokenModel, username: String?) {
multiServerRepository.save(server, tokenModel) multiServerRepository.save(server, tokenModel)
localRepository.save(LocalRepository.USERNAME_KEY, username) localRepository.save(LocalRepository.USERNAME_KEY, username)
registerPushToken() registerPushToken()
} }
fun signup() = navigator.toSignUp()
private suspend fun registerPushToken() { private suspend fun registerPushToken() {
localRepository.get(LocalRepository.KEY_PUSH_TOKEN)?.let { localRepository.get(LocalRepository.KEY_PUSH_TOKEN)?.let {
client.registerPushToken(it) client.registerPushToken(it)
......
...@@ -7,64 +7,126 @@ import chat.rocket.android.core.behaviours.MessageView ...@@ -7,64 +7,126 @@ import chat.rocket.android.core.behaviours.MessageView
interface LoginView : LoadingView, MessageView, InternetView { interface LoginView : LoadingView, MessageView, InternetView {
/** /**
* Shows the CAS view if the server settings allow the sign in/sign out via CAS protocol. * Shows the form view (i.e the username/email and password fields) if it is enabled by the server settings.
*
* REMARK: We must set up the login button listener [setupLoginButtonListener].
* Remember to enable [enableUserInput] or disable [disableUserInput] the view interaction for the user when submitting the form.
*/
fun showFormView()
/**
* Hides the form view.
*/
fun hideFormView()
/**
* Setups the login button when tapped.
*/
fun setupLoginButtonListener()
/**
* Enables the view interactions for the user.
*/
fun enableUserInput()
/**
* Disables the view interactions for the user.
*/
fun disableUserInput()
/**
* Shows the CAS button if the sign in/sign out via CAS protocol is enabled by the server settings.
*
* REMARK: We must set up the CAS button listener [setupCasButtonListener].
*/
fun showCasButton()
/**
* Hides the CAS button.
*/
fun hideCasButton()
/**
* Setups the CAS button when tapped.
*
* @param casUrl The CAS URL to login/sign up with. * @param casUrl The CAS URL to login/sign up with.
* @param requestedToken The requested Token sent to the CAS server. * @param casToken The requested Token sent to the CAS server.
*/ */
fun showCasView(casUrl: String, requestedToken: String) fun setupCasButtonListener(casUrl: String, casToken: String)
/** /**
* Shows the sign up view if the server settings allow the new users registration. * Shows the sign up view if the new users registration is enabled by the server settings.
*
* REMARK: We must set up the sign up view listener [setupSignUpView].
*/ */
fun showSignUpView() fun showSignUpView()
/** /**
* Shows the oauth view if the server settings allow the login via social accounts. * Setups the sign up view when tapped.
*/
fun setupSignUpView()
/**
* Hides the sign up view.
*/
fun hideSignUpView()
/**
* Shows the oauth view if the login via social accounts is enabled by the server settings.
* *
* REMARK: we must show at maximum *three* social accounts views ([enableLoginByFacebook], [enableLoginByGithub], [enableLoginByGoogle], * REMARK: We must show at maximum *three* social accounts views ([enableLoginByFacebook], [enableLoginByGithub], [enableLoginByGoogle],
* [enableLoginByLinkedin], [enableLoginByMeteor], [enableLoginByTwitter] or [enableLoginByGitlab]) for the oauth view. * [enableLoginByLinkedin], [enableLoginByMeteor], [enableLoginByTwitter] or [enableLoginByGitlab]) for the oauth view.
* If the possibility of login via social accounts exceeds 3 different ways we should set up the FAB ([setupFabListener]) to show the remaining view(s). * If the possibility of login via social accounts exceeds 3 different ways we should set up the FAB ([setupFabListener]) to show the remaining view(s).
*/ */
fun showOauthView() fun showOauthView()
/**
* Hides the oauth view.
*/
fun hideOauthView()
/** /**
* Shows the login button. * Shows the login button.
*/ */
fun showLoginButton() fun showLoginButton()
/** /**
* Shows the login by Facebook view if the server settings allow it. * Hides the login button.
*/
fun hideLoginButton()
/**
* Shows the "login by Facebook view if it is enabled by the server settings.
*/ */
fun enableLoginByFacebook() fun enableLoginByFacebook()
/** /**
* Shows the login by Github view if the server settings allow it. * Shows the "login by Github" view if it is enabled by the server settings.
*/ */
fun enableLoginByGithub() fun enableLoginByGithub()
/** /**
* Shows the login by Google view if the server settings allow it. * Shows the "login by Google" view if it is enabled by the server settings.
*/ */
fun enableLoginByGoogle() fun enableLoginByGoogle()
/** /**
* Shows the login by Linkedin view if the server settings allow it. * Shows the "login by Linkedin" view if it is enabled by the server settings.
*/ */
fun enableLoginByLinkedin() fun enableLoginByLinkedin()
/** /**
* Shows the login by Meteor view if the server settings allow it. * Shows the "login by Meteor" view if it is enabled by the server settings.
*/ */
fun enableLoginByMeteor() fun enableLoginByMeteor()
/** /**
* Shows the login by Twitter view if the server settings allow it. * Shows the "login by Twitter" view if it is enabled by the server settings.
*/ */
fun enableLoginByTwitter() fun enableLoginByTwitter()
/** /**
* Shows the login by Gitlab view if the server settings allow it. * Shows the "login by Gitlab" view if it is enabled by the server settings.
*/ */
fun enableLoginByGitlab() fun enableLoginByGitlab()
...@@ -73,30 +135,8 @@ interface LoginView : LoadingView, MessageView, InternetView { ...@@ -73,30 +135,8 @@ interface LoginView : LoadingView, MessageView, InternetView {
*/ */
fun setupFabListener() fun setupFabListener()
/** fun setupGlobalListener()
* Hides the username/e-mail view.
*/
fun hideUsernameOrEmailView()
/**
* Hides the password view.
*/
fun hidePasswordView()
/**
* Hides the sign up view if the server settings does not allow the new users registration.
*/
fun hideSignUpView()
/**
* Hides the oauth view.
*/
fun hideOauthView()
/**
* Hides the login button.
*/
fun hideLoginButton()
/** /**
* Alerts the user about a wrong inputted username or email. * Alerts the user about a wrong inputted username or email.
*/ */
......
...@@ -9,7 +9,7 @@ import chat.rocket.android.authentication.twofactor.ui.TwoFAFragment ...@@ -9,7 +9,7 @@ import chat.rocket.android.authentication.twofactor.ui.TwoFAFragment
import chat.rocket.android.authentication.ui.AuthenticationActivity import chat.rocket.android.authentication.ui.AuthenticationActivity
import chat.rocket.android.main.ui.MainActivity import chat.rocket.android.main.ui.MainActivity
import chat.rocket.android.util.extensions.addFragmentBackStack import chat.rocket.android.util.extensions.addFragmentBackStack
import chat.rocket.android.webview.webViewIntent import chat.rocket.android.webview.ui.webViewIntent
class AuthenticationNavigator(internal val activity: AuthenticationActivity, internal val context: Context) { class AuthenticationNavigator(internal val activity: AuthenticationActivity, internal val context: Context) {
...@@ -37,10 +37,7 @@ class AuthenticationNavigator(internal val activity: AuthenticationActivity, int ...@@ -37,10 +37,7 @@ class AuthenticationNavigator(internal val activity: AuthenticationActivity, int
} }
fun toChatList() { fun toChatList() {
val chatList = Intent(activity, MainActivity::class.java).apply { activity.startActivity(Intent(activity, MainActivity::class.java))
//TODO any parameter to pass
}
activity.startActivity(chatList)
activity.finish() activity.finish()
} }
......
...@@ -17,16 +17,14 @@ class ServerPresenter @Inject constructor(private val view: ServerView, ...@@ -17,16 +17,14 @@ class ServerPresenter @Inject constructor(private val view: ServerView,
private val refreshSettingsInteractor: RefreshSettingsInteractor) { private val refreshSettingsInteractor: RefreshSettingsInteractor) {
fun connect(server: String) { fun connect(server: String) {
if (!UrlHelper.isValidUrl(server)) { if (!UrlHelper.isValidUrl(server)) {
view.showInvalidServerUrl() view.showInvalidServerUrlMessage()
} else { } else {
launchUI(strategy) { launchUI(strategy) {
if (NetworkHelper.hasInternetAccess()) { if (NetworkHelper.hasInternetAccess()) {
view.showLoading() view.showLoading()
try { try {
refreshSettingsInteractor.refresh(server) refreshSettingsInteractor.refresh(server)
serverInteractor.save(server) serverInteractor.save(server)
navigator.toLogin() navigator.toLogin()
} catch (ex: Exception) { } catch (ex: Exception) {
ex.message?.let { ex.message?.let {
......
...@@ -7,7 +7,7 @@ import chat.rocket.android.core.behaviours.MessageView ...@@ -7,7 +7,7 @@ import chat.rocket.android.core.behaviours.MessageView
interface ServerView : LoadingView, MessageView, InternetView { interface ServerView : LoadingView, MessageView, InternetView {
/** /**
* Notifies the user about an invalid inputted server URL. * Shows an invalid server URL message.
*/ */
fun showInvalidServerUrl() fun showInvalidServerUrlMessage()
} }
\ No newline at end of file
...@@ -30,7 +30,8 @@ class ServerFragment : Fragment(), ServerView { ...@@ -30,7 +30,8 @@ class ServerFragment : Fragment(), ServerView {
AndroidSupportInjection.inject(this) AndroidSupportInjection.inject(this)
} }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? = container?.inflate(R.layout.fragment_authentication_server) override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? =
container?.inflate(R.layout.fragment_authentication_server)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
...@@ -43,7 +44,7 @@ class ServerFragment : Fragment(), ServerView { ...@@ -43,7 +44,7 @@ class ServerFragment : Fragment(), ServerView {
relative_layout.viewTreeObserver.removeOnGlobalLayoutListener(layoutListener) relative_layout.viewTreeObserver.removeOnGlobalLayoutListener(layoutListener)
} }
override fun showInvalidServerUrl() = showMessage(getString(R.string.msg_invalid_server_url)) override fun showInvalidServerUrlMessage() = showMessage(getString(R.string.msg_invalid_server_url))
override fun showLoading() { override fun showLoading() {
enableUserInput(false) enableUserInput(false)
......
package chat.rocket.android.authentication.signup.ui package chat.rocket.android.authentication.signup.ui
import DrawableHelper import DrawableHelper
import android.content.Context
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.support.v4.app.Fragment import android.support.v4.app.Fragment
...@@ -11,19 +10,15 @@ import android.widget.Toast ...@@ -11,19 +10,15 @@ import android.widget.Toast
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.authentication.signup.presentation.SignupPresenter import chat.rocket.android.authentication.signup.presentation.SignupPresenter
import chat.rocket.android.authentication.signup.presentation.SignupView import chat.rocket.android.authentication.signup.presentation.SignupView
import chat.rocket.android.helper.AnimationHelper
import chat.rocket.android.helper.KeyboardHelper import chat.rocket.android.helper.KeyboardHelper
import chat.rocket.android.helper.TextHelper import chat.rocket.android.helper.TextHelper
import chat.rocket.android.util.extensions.setVisible import chat.rocket.android.util.extensions.*
import chat.rocket.android.util.extensions.textContent
import chat.rocket.android.util.extensions.showToast
import dagger.android.support.AndroidSupportInjection import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_authentication_sign_up.* import kotlinx.android.synthetic.main.fragment_authentication_sign_up.*
import javax.inject.Inject import javax.inject.Inject
class SignupFragment : Fragment(), SignupView { class SignupFragment : Fragment(), SignupView {
@Inject lateinit var presenter: SignupPresenter @Inject lateinit var presenter: SignupPresenter
@Inject lateinit var appContext: Context
private val layoutListener = ViewTreeObserver.OnGlobalLayoutListener { private val layoutListener = ViewTreeObserver.OnGlobalLayoutListener {
if (KeyboardHelper.isSoftKeyboardShown(constraint_layout.rootView)) { if (KeyboardHelper.isSoftKeyboardShown(constraint_layout.rootView)) {
...@@ -68,26 +63,26 @@ class SignupFragment : Fragment(), SignupView { ...@@ -68,26 +63,26 @@ class SignupFragment : Fragment(), SignupView {
} }
override fun alertBlankName() { override fun alertBlankName() {
AnimationHelper.vibrateSmartPhone(appContext) vibrateSmartPhone()
AnimationHelper.shakeView(text_name) text_name.shake()
text_name.requestFocus() text_name.requestFocus()
} }
override fun alertBlankUsername() { override fun alertBlankUsername() {
AnimationHelper.vibrateSmartPhone(appContext) vibrateSmartPhone()
AnimationHelper.shakeView(text_username) text_username.shake()
text_username.requestFocus() text_username.requestFocus()
} }
override fun alertEmptyPassword() { override fun alertEmptyPassword() {
AnimationHelper.vibrateSmartPhone(appContext) vibrateSmartPhone()
AnimationHelper.shakeView(text_password) text_password.shake()
text_password.requestFocus() text_password.requestFocus()
} }
override fun alertBlankEmail() { override fun alertBlankEmail() {
AnimationHelper.vibrateSmartPhone(appContext) vibrateSmartPhone()
AnimationHelper.shakeView(text_email) text_email.shake()
text_email.requestFocus() text_email.requestFocus()
} }
...@@ -114,16 +109,18 @@ class SignupFragment : Fragment(), SignupView { ...@@ -114,16 +109,18 @@ class SignupFragment : Fragment(), SignupView {
} }
private fun tintEditTextDrawableStart() { private fun tintEditTextDrawableStart() {
val personDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_person_black_24dp, appContext) activity?.apply {
val atDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_at_black_24dp, appContext) val personDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_person_black_24dp, this)
val lockDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_lock_black_24dp, appContext) val atDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_at_black_24dp, this)
val emailDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_email_black_24dp, appContext) val lockDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_lock_black_24dp, this)
val emailDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_email_black_24dp, this)
val drawables = arrayOf(personDrawable, atDrawable, lockDrawable, emailDrawable) val drawables = arrayOf(personDrawable, atDrawable, lockDrawable, emailDrawable)
DrawableHelper.wrapDrawables(drawables) DrawableHelper.wrapDrawables(drawables)
DrawableHelper.tintDrawables(drawables, appContext, R.color.colorDrawableTintGrey) DrawableHelper.tintDrawables(drawables, this, R.color.colorDrawableTintGrey)
DrawableHelper.compoundDrawables(arrayOf(text_name, text_username, text_password, text_email), drawables) DrawableHelper.compoundDrawables(arrayOf(text_name, text_username, text_password, text_email), drawables)
} }
}
private fun setUpNewUserAgreementListener() { private fun setUpNewUserAgreementListener() {
val termsOfService = getString(R.string.action_terms_of_service) val termsOfService = getString(R.string.action_terms_of_service)
......
...@@ -12,10 +12,7 @@ import android.view.inputmethod.InputMethodManager ...@@ -12,10 +12,7 @@ import android.view.inputmethod.InputMethodManager
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.authentication.twofactor.presentation.TwoFAPresenter import chat.rocket.android.authentication.twofactor.presentation.TwoFAPresenter
import chat.rocket.android.authentication.twofactor.presentation.TwoFAView import chat.rocket.android.authentication.twofactor.presentation.TwoFAView
import chat.rocket.android.helper.AnimationHelper import chat.rocket.android.util.extensions.*
import chat.rocket.android.util.extensions.setVisible
import chat.rocket.android.util.extensions.textContent
import chat.rocket.android.util.extensions.showToast
import dagger.android.support.AndroidSupportInjection import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_authentication_two_fa.* import kotlinx.android.synthetic.main.fragment_authentication_two_fa.*
import javax.inject.Inject import javax.inject.Inject
...@@ -65,10 +62,8 @@ class TwoFAFragment : Fragment(), TwoFAView { ...@@ -65,10 +62,8 @@ class TwoFAFragment : Fragment(), TwoFAView {
} }
override fun alertBlankTwoFactorAuthenticationCode() { override fun alertBlankTwoFactorAuthenticationCode() {
activity?.let { vibrateSmartPhone()
AnimationHelper.vibrateSmartPhone(it) text_two_factor_auth.shake()
AnimationHelper.shakeView(text_two_factor_auth)
}
} }
override fun alertInvalidTwoFactorAuthenticationCode() = showMessage(getString(R.string.msg_invalid_2fa_code)) override fun alertInvalidTwoFactorAuthenticationCode() = showMessage(getString(R.string.msg_invalid_2fa_code))
......
package chat.rocket.android.helper
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.AnimatorSet
import android.animation.ObjectAnimator
import android.content.Context
import android.os.Build
import android.os.VibrationEffect
import android.os.Vibrator
import android.view.View
object AnimationHelper {
/**
* Shakes a view.
*/
fun shakeView(viewToShake: View, x: Float = 2F, num: Int = 0) {
if (num == 6) {
viewToShake.translationX = 0.toFloat()
return
}
val animatorSet = AnimatorSet()
animatorSet.playTogether(ObjectAnimator.ofFloat(viewToShake, "translationX", dp(viewToShake.context, x)))
animatorSet.duration = 50
animatorSet.addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
shakeView(viewToShake, if (num == 5) 0.toFloat() else -x, num + 1)
}
})
animatorSet.start()
}
/**
* Vibrates the smart phone.
*/
fun vibrateSmartPhone(context: Context) {
val vibrator = context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
if (Build.VERSION.SDK_INT >= 26) {
vibrator.vibrate(VibrationEffect.createOneShot(200, VibrationEffect.DEFAULT_AMPLITUDE))
} else {
vibrator.vibrate(200)
}
}
private fun dp(context: Context, value: Float): Float {
val density = context.resources.displayMetrics.density
val result = Math.ceil(density.times(value.toDouble()))
return result.toFloat()
}
}
\ No newline at end of file
...@@ -11,12 +11,16 @@ class RefreshSettingsInteractor @Inject constructor(private val factory: RocketC ...@@ -11,12 +11,16 @@ class RefreshSettingsInteractor @Inject constructor(private val factory: RocketC
private val repository: SettingsRepository) { private val repository: SettingsRepository) {
private var settingsFilter = arrayOf( private var settingsFilter = arrayOf(
LDAP_ENABLE, CAS_ENABLE, CAS_LOGIN_URL,
ACCOUNT_REGISTRATION, ACCOUNT_LOGIN_FORM, ACCOUNT_PASSWORD_RESET, ACCOUNT_CUSTOM_FIELDS,
ACCOUNT_GOOGLE, ACCOUNT_FACEBOOK, ACCOUNT_GITHUB, ACCOUNT_LINKEDIN, ACCOUNT_METEOR,
ACCOUNT_TWITTER, ACCOUNT_WORDPRESS, ACCOUNT_GITLAB,
SITE_URL, SITE_NAME, FAVICON_512, USE_REALNAME, ALLOW_ROOM_NAME_SPECIAL_CHARS, SITE_URL, SITE_NAME, FAVICON_512, USE_REALNAME, ALLOW_ROOM_NAME_SPECIAL_CHARS,
FAVORITE_ROOMS, ACCOUNT_LOGIN_FORM, ACCOUNT_GOOGLE, ACCOUNT_FACEBOOK, ACCOUNT_GITHUB, FAVORITE_ROOMS, UPLOAD_STORAGE_TYPE, UPLOAD_MAX_FILE_SIZE, UPLOAD_WHITELIST_MIMETYPES,
ACCOUNT_GITLAB, ACCOUNT_LINKEDIN, ACCOUNT_METEOR, ACCOUNT_TWITTER, ACCOUNT_WORDPRESS, HIDE_USER_JOIN, HIDE_USER_LEAVE,
LDAP_ENABLE, CAS_ENABLE, CAS_LOGIN_URL, ACCOUNT_REGISTRATION, UPLOAD_STORAGE_TYPE, HIDE_TYPE_AU, HIDE_MUTE_UNMUTE, HIDE_TYPE_RU, ALLOW_MESSAGE_DELETING,
UPLOAD_MAX_FILE_SIZE, UPLOAD_WHITELIST_MIMETYPES, HIDE_USER_JOIN, HIDE_USER_LEAVE,
HIDE_TYPE_AU, HIDE_MUTE_UNMUTE, HIDE_TYPE_RU, ACCOUNT_CUSTOM_FIELDS, ALLOW_MESSAGE_DELETING,
ALLOW_MESSAGE_EDITING, ALLOW_MESSAGE_PINNING, SHOW_DELETED_STATUS, SHOW_EDITED_STATUS) ALLOW_MESSAGE_EDITING, ALLOW_MESSAGE_PINNING, SHOW_DELETED_STATUS, SHOW_EDITED_STATUS)
suspend fun refresh(server: String) { suspend fun refresh(server: String) {
......
...@@ -7,20 +7,25 @@ typealias PublicSettings = Map<String, Value<Any>> ...@@ -7,20 +7,25 @@ typealias PublicSettings = Map<String, Value<Any>>
interface SettingsRepository { interface SettingsRepository {
fun save(url: String, settings: PublicSettings) fun save(url: String, settings: PublicSettings)
fun get(url: String): PublicSettings? fun get(url: String): PublicSettings
} }
// Authentication methods.
const val LDAP_ENABLE = "LDAP_Enable"
const val CAS_ENABLE = "CAS_enabled"
const val CAS_LOGIN_URL = "CAS_login_url"
const val ACCOUNT_REGISTRATION = "Accounts_RegistrationForm"
const val ACCOUNT_LOGIN_FORM = "Accounts_ShowFormLogin"
const val ACCOUNT_PASSWORD_RESET = "Accounts_PasswordReset"
const val ACCOUNT_CUSTOM_FIELDS = "Accounts_CustomFields"
const val ACCOUNT_GOOGLE = "Accounts_OAuth_Google"
const val ACCOUNT_FACEBOOK = "Accounts_OAuth_Facebook" const val ACCOUNT_FACEBOOK = "Accounts_OAuth_Facebook"
const val ACCOUNT_GITHUB = "Accounts_OAuth_Github" const val ACCOUNT_GITHUB = "Accounts_OAuth_Github"
const val ACCOUNT_GITLAB = "Accounts_OAuth_Gitlab"
const val ACCOUNT_GOOGLE = "Accounts_OAuth_Google"
const val ACCOUNT_LINKEDIN = "Accounts_OAuth_Linkedin" const val ACCOUNT_LINKEDIN = "Accounts_OAuth_Linkedin"
const val ACCOUNT_METEOR = "Accounts_OAuth_Meteor" const val ACCOUNT_METEOR = "Accounts_OAuth_Meteor"
const val ACCOUNT_TWITTER = "Accounts_OAuth_Twitter" const val ACCOUNT_TWITTER = "Accounts_OAuth_Twitter"
const val ACCOUNT_WORDPRESS = "Accounts_OAuth_Wordpress" const val ACCOUNT_WORDPRESS = "Accounts_OAuth_Wordpress"
const val ACCOUNT_REGISTRATION = "Accounts_RegistrationForm" const val ACCOUNT_GITLAB = "Accounts_OAuth_Gitlab"
const val ACCOUNT_LOGIN_FORM = "Accounts_ShowFormLogin"
const val ACCOUNT_CUSTOM_FIELDS = "Accounts_CustomFields"
const val SITE_URL = "Site_Url" const val SITE_URL = "Site_Url"
const val SITE_NAME = "Site_Name" const val SITE_NAME = "Site_Name"
...@@ -28,9 +33,6 @@ const val FAVICON_512 = "Assets_favicon_512" ...@@ -28,9 +33,6 @@ const val FAVICON_512 = "Assets_favicon_512"
const val USE_REALNAME = "UI_Use_Real_Name" const val USE_REALNAME = "UI_Use_Real_Name"
const val ALLOW_ROOM_NAME_SPECIAL_CHARS = "UI_Allow_room_names_with_special_chars" const val ALLOW_ROOM_NAME_SPECIAL_CHARS = "UI_Allow_room_names_with_special_chars"
const val FAVORITE_ROOMS = "Favorite_Rooms" const val FAVORITE_ROOMS = "Favorite_Rooms"
const val LDAP_ENABLE = "LDAP_Enable"
const val CAS_ENABLE = "CAS_enabled"
const val CAS_LOGIN_URL = "CAS_login_url"
const val UPLOAD_STORAGE_TYPE = "FileUpload_Storage_Type" const val UPLOAD_STORAGE_TYPE = "FileUpload_Storage_Type"
const val UPLOAD_MAX_FILE_SIZE = "FileUpload_MaxFileSize" const val UPLOAD_MAX_FILE_SIZE = "FileUpload_MaxFileSize"
const val UPLOAD_WHITELIST_MIMETYPES = "FileUpload_MediaTypeWhiteList" const val UPLOAD_WHITELIST_MIMETYPES = "FileUpload_MediaTypeWhiteList"
...@@ -44,25 +46,29 @@ const val ALLOW_MESSAGE_EDITING = "Message_AllowEditing" ...@@ -44,25 +46,29 @@ const val ALLOW_MESSAGE_EDITING = "Message_AllowEditing"
const val SHOW_DELETED_STATUS = "Message_ShowDeletedStatus" const val SHOW_DELETED_STATUS = "Message_ShowDeletedStatus"
const val SHOW_EDITED_STATUS = "Message_ShowEditedStatus" const val SHOW_EDITED_STATUS = "Message_ShowEditedStatus"
const val ALLOW_MESSAGE_PINNING = "Message_AllowPinning" const val ALLOW_MESSAGE_PINNING = "Message_AllowPinning"
/* /*
* Extension functions for Public Settings. * Extension functions for Public Settings.
* *
* If you need to access a Setting, add a const val key above, add it to the filter on * If you need to access a Setting, add a const val key above, add it to the filter on
* ServerPresenter.kt and a extension function to access it * ServerPresenter.kt and a extension function to access it
*/ */
fun PublicSettings.googleEnabled(): Boolean = this[ACCOUNT_GOOGLE]?.value == true fun PublicSettings.isLdapAuthenticationEnabled(): Boolean = this[LDAP_ENABLE]?.value == true
fun PublicSettings.facebookEnabled(): Boolean = this[ACCOUNT_FACEBOOK]?.value == true fun PublicSettings.isCasAuthenticationEnabled(): Boolean = this[CAS_ENABLE]?.value == true
fun PublicSettings.githubEnabled(): Boolean = this[ACCOUNT_GITHUB]?.value == true fun PublicSettings.casLoginUrl(): String = this[CAS_LOGIN_URL]?.value.toString()
fun PublicSettings.linkedinEnabled(): Boolean = this[ACCOUNT_LINKEDIN]?.value == true fun PublicSettings.isRegistrationEnabledForNewUsers(): Boolean = this[ACCOUNT_REGISTRATION]?.value == "Public"
fun PublicSettings.meteorEnabled(): Boolean = this[ACCOUNT_METEOR]?.value == true fun PublicSettings.isLoginFormEnabled(): Boolean = this[ACCOUNT_LOGIN_FORM]?.value == true
fun PublicSettings.twitterEnabled(): Boolean = this[ACCOUNT_TWITTER]?.value == true fun PublicSettings.isPasswordResetEnabled(): Boolean = this[ACCOUNT_PASSWORD_RESET]?.value == true
fun PublicSettings.gitlabEnabled(): Boolean = this[ACCOUNT_GITLAB]?.value == true fun PublicSettings.isGoogleAuthenticationEnabled(): Boolean = this[ACCOUNT_GOOGLE]?.value == true
fun PublicSettings.wordpressEnabled(): Boolean = this[ACCOUNT_WORDPRESS]?.value == true fun PublicSettings.isFacebookAuthenticationEnabled(): Boolean = this[ACCOUNT_FACEBOOK]?.value == true
fun PublicSettings.isGithubAuthenticationEnabled(): Boolean = this[ACCOUNT_GITHUB]?.value == true
fun PublicSettings.isLinkedinAuthenticationEnabled(): Boolean = this[ACCOUNT_LINKEDIN]?.value == true
fun PublicSettings.isMeteorAuthenticationEnabled(): Boolean = this[ACCOUNT_METEOR]?.value == true
fun PublicSettings.isTwitterAuthenticationEnabled(): Boolean = this[ACCOUNT_TWITTER]?.value == true
fun PublicSettings.isGitlabAuthenticationEnabled(): Boolean = this[ACCOUNT_GITLAB]?.value == true
fun PublicSettings.isWordpressAuthenticationEnabled(): Boolean = this[ACCOUNT_WORDPRESS]?.value == true
fun PublicSettings.useRealName(): Boolean = this[USE_REALNAME]?.value == true fun PublicSettings.useRealName(): Boolean = this[USE_REALNAME]?.value == true
fun PublicSettings.ldapEnabled(): Boolean = this[LDAP_ENABLE]?.value == true
fun PublicSettings.casEnabled(): Boolean = this[CAS_ENABLE]?.value == true
fun PublicSettings.casLoginUrl(): String = this[CAS_LOGIN_URL]?.value.toString()
// Message settings // Message settings
fun PublicSettings.showDeletedStatus(): Boolean = this[SHOW_DELETED_STATUS]?.value == true fun PublicSettings.showDeletedStatus(): Boolean = this[SHOW_DELETED_STATUS]?.value == true
...@@ -71,11 +77,6 @@ fun PublicSettings.allowedMessagePinning(): Boolean = this[ALLOW_MESSAGE_PINNING ...@@ -71,11 +77,6 @@ fun PublicSettings.allowedMessagePinning(): Boolean = this[ALLOW_MESSAGE_PINNING
fun PublicSettings.allowedMessageEditing(): Boolean = this[ALLOW_MESSAGE_EDITING]?.value == true fun PublicSettings.allowedMessageEditing(): Boolean = this[ALLOW_MESSAGE_EDITING]?.value == true
fun PublicSettings.allowedMessageDeleting(): Boolean = this[ALLOW_MESSAGE_DELETING]?.value == true fun PublicSettings.allowedMessageDeleting(): Boolean = this[ALLOW_MESSAGE_DELETING]?.value == true
fun PublicSettings.registrationEnabled(): Boolean {
val value = this[ACCOUNT_REGISTRATION]
return value?.value == "Public"
}
fun PublicSettings.uploadMimeTypeFilter(): Array<String> { fun PublicSettings.uploadMimeTypeFilter(): Array<String> {
val values = this[UPLOAD_WHITELIST_MIMETYPES]?.value val values = this[UPLOAD_WHITELIST_MIMETYPES]?.value
values?.let { it as String }?.split(",")?.let { values?.let { it as String }?.split(",")?.let {
......
...@@ -14,12 +14,10 @@ class SharedPreferencesSettingsRepository(private val localRepository: LocalRepo ...@@ -14,12 +14,10 @@ class SharedPreferencesSettingsRepository(private val localRepository: LocalRepo
localRepository.save("$SETTINGS_KEY$url", adapter.toJson(settings)) localRepository.save("$SETTINGS_KEY$url", adapter.toJson(settings))
} }
override fun get(url: String): PublicSettings? { override fun get(url: String): PublicSettings {
val settings = localRepository.get("$SETTINGS_KEY$url") val settings = localRepository.get("$SETTINGS_KEY$url")!!
settings?.let { settings.let {
return adapter.fromJson(it) return adapter.fromJson(it)!!
} }
return null
} }
} }
\ No newline at end of file
package chat.rocket.android.util.extensions package chat.rocket.android.util.extensions
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.AnimatorSet
import android.animation.ObjectAnimator
import android.content.Context
import android.os.Build
import android.os.VibrationEffect
import android.os.Vibrator
import android.support.v4.app.Fragment
import android.view.View import android.view.View
import android.view.ViewAnimationUtils import android.view.ViewAnimationUtils
import android.view.animation.AccelerateInterpolator import android.view.animation.AccelerateInterpolator
...@@ -64,3 +73,36 @@ fun View.circularRevealOrUnreveal(centerX: Int, centerY: Int, startRadius: Float ...@@ -64,3 +73,36 @@ fun View.circularRevealOrUnreveal(centerX: Int, centerY: Int, startRadius: Float
anim.start() anim.start()
} }
fun View.shake(x: Float = 2F, num: Int = 0){
if (num == 6) {
this.translationX = 0.toFloat()
return
}
val animatorSet = AnimatorSet()
animatorSet.playTogether(ObjectAnimator.ofFloat(this, "translationX", this.context.dp(x)))
animatorSet.duration = 50
animatorSet.addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
shake(if (num == 5) 0.toFloat() else -x, num + 1)
}
})
animatorSet.start()
}
fun Context.dp(value: Float): Float {
val density = this.resources.displayMetrics.density
val result = Math.ceil(density.times(value.toDouble()))
return result.toFloat()
}
fun Fragment.vibrateSmartPhone() {
val vibrator = context?.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
if (Build.VERSION.SDK_INT >= 26) {
vibrator.vibrate(VibrationEffect.createOneShot(200, VibrationEffect.DEFAULT_AMPLITUDE))
} else {
vibrator.vibrate(200)
}
}
\ No newline at end of file
package chat.rocket.android.webview.cas.ui
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.webkit.WebView
import android.webkit.WebViewClient
import chat.rocket.android.R
import kotlinx.android.synthetic.main.activity_web_view.*
import kotlinx.android.synthetic.main.app_bar.*
fun Context.webViewIntent(webPageUrl: String, casToken: String): Intent {
return Intent(this, CasWebViewActivity::class.java).apply {
putExtra(INTENT_WEB_PAGE_URL, webPageUrl)
putExtra(INTENT_CAS_TOKEN, casToken)
}
}
private const val INTENT_WEB_PAGE_URL = "web_page_url"
private const val INTENT_CAS_TOKEN = "cas_token"
class CasWebViewActivity : AppCompatActivity() {
private lateinit var webPageUrl: String
private lateinit var casToken: String
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_web_view)
webPageUrl = intent.getStringExtra(INTENT_WEB_PAGE_URL)
requireNotNull(webPageUrl) { "no web_page_url provided in Intent extras" }
casToken = intent.getStringExtra(INTENT_CAS_TOKEN)
requireNotNull(casToken) { "no cas_token provided in Intent extras" }
setupToolbar()
}
override fun onResume() {
super.onResume()
setupWebView()
}
override fun onBackPressed() {
if (web_view.canGoBack()) {
web_view.goBack()
} else {
finishActivity(false)
}
}
private fun setupToolbar() {
toolbar.title = getString(R.string.title_authentication)
toolbar.setNavigationIcon(R.drawable.ic_close_white_24dp)
toolbar.setNavigationOnClickListener { finishActivity(false) }
}
@SuppressLint("SetJavaScriptEnabled")
private fun setupWebView() {
web_view.settings.javaScriptEnabled = true
web_view.webViewClient = object : WebViewClient() {
override fun onPageStarted(view: WebView, url: String, favicon: Bitmap?) {
// The user can be already logged in the CAS, so check if the URL contains the "ticket" word
// (that means he/she is successful authenticated and we don't need to wait until the page is finished.
if (url.contains("ticket")) {
finishActivity(true)
}
}
override fun onPageFinished(view: WebView, url: String) {
if (url.contains("ticket")) {
finishActivity(true)
} else {
view_loading.hide()
}
}
}
web_view.loadUrl(webPageUrl)
}
private fun finishActivity(setResultOk: Boolean) {
if (setResultOk) {
setResult(Activity.RESULT_OK, Intent().putExtra(INTENT_CAS_TOKEN, casToken))
finish()
} else {
super.onBackPressed()
}
overridePendingTransition(R.anim.hold, R.anim.slide_down)
}
}
\ No newline at end of file
package chat.rocket.android.webview package chat.rocket.android.webview.ui
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Context import android.content.Context
...@@ -19,6 +19,7 @@ fun Context.webViewIntent(webPageUrl: String): Intent { ...@@ -19,6 +19,7 @@ fun Context.webViewIntent(webPageUrl: String): Intent {
private const val INTENT_WEB_PAGE_URL = "web_page_url" private const val INTENT_WEB_PAGE_URL = "web_page_url"
// Simple WebView to load URL.
class WebViewActivity : AppCompatActivity() { class WebViewActivity : AppCompatActivity() {
private lateinit var webPageUrl: String private lateinit var webPageUrl: String
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
tools:context=".webview.WebViewActivity"> tools:context=".webview.ui.WebViewActivity">
<include <include
android:id="@+id/layout_app_bar" android:id="@+id/layout_app_bar"
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
<android.support.constraint.ConstraintLayout <android.support.constraint.ConstraintLayout
android:id="@+id/middle_container" android:id="@+id/middle_container"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="wrap_content">
<TextView <TextView
android:id="@+id/text_headline" android:id="@+id/text_headline"
...@@ -21,21 +21,6 @@ ...@@ -21,21 +21,6 @@
app:layout_constraintRight_toRightOf="parent" app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
<WebView
android:id="@+id/web_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="22dp"
android:layout_marginEnd="@dimen/screen_edge_left_and_right_margins"
android:layout_marginStart="@dimen/screen_edge_left_and_right_margins"
android:layout_marginTop="64dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_headline"
tools:visibility="visible" />
<EditText <EditText
android:id="@+id/text_username_or_email" android:id="@+id/text_username_or_email"
style="@style/Authentication.EditText" style="@style/Authentication.EditText"
...@@ -60,6 +45,19 @@ ...@@ -60,6 +45,19 @@
app:layout_constraintRight_toRightOf="parent" app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_username_or_email" /> app:layout_constraintTop_toBottomOf="@+id/text_username_or_email" />
<Button
android:id="@+id/button_cas"
style="@style/Authentication.Button"
android:layout_marginEnd="@dimen/screen_edge_left_and_right_margins"
android:layout_marginStart="@dimen/screen_edge_left_and_right_margins"
android:layout_marginTop="16dp"
android:text="@string/action_login_or_sign_up"
android:visibility="gone"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_password"
tools:visibility="visible" />
<TextView <TextView
android:id="@+id/text_new_to_rocket_chat" android:id="@+id/text_new_to_rocket_chat"
android:layout_width="wrap_content" android:layout_width="wrap_content"
...@@ -72,7 +70,7 @@ ...@@ -72,7 +70,7 @@
android:visibility="gone" android:visibility="gone"
app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_password" app:layout_constraintTop_toBottomOf="@+id/button_cas"
tools:visibility="visible" /> tools:visibility="visible" />
<com.wang.avi.AVLoadingIndicatorView <com.wang.avi.AVLoadingIndicatorView
...@@ -84,7 +82,7 @@ ...@@ -84,7 +82,7 @@
app:indicatorName="BallPulseIndicator" app:indicatorName="BallPulseIndicator"
app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_password" app:layout_constraintTop_toBottomOf="@+id/button_cas"
tools:visibility="visible" /> tools:visibility="visible" />
<LinearLayout <LinearLayout
...@@ -210,9 +208,8 @@ ...@@ -210,9 +208,8 @@
android:id="@+id/button_log_in" android:id="@+id/button_log_in"
style="@style/Authentication.Button" style="@style/Authentication.Button"
android:text="@string/title_log_in" android:text="@string/title_log_in"
android:visibility="visible" android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent" />
tools:visibility="gone" />
</android.support.constraint.ConstraintLayout> </android.support.constraint.ConstraintLayout>
</ScrollView> </ScrollView>
\ No newline at end of file
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
<string name="title_sign_in_your_server">Faça login no seu servidor</string> <string name="title_sign_in_your_server">Faça login no seu servidor</string>
<string name="title_log_in">Entrar</string> <string name="title_log_in">Entrar</string>
<string name="title_sign_up">Inscreva-se</string> <string name="title_sign_up">Inscreva-se</string>
<string name="title_log_in_or_sign_up">Entrar ou Inscrever-se</string> <string name="title_authentication">Autenticação</string>
<string name="title_legal_terms">Termos Legais</string> <string name="title_legal_terms">Termos Legais</string>
<string name="title_chats">Chats</string> <string name="title_chats">Chats</string>
<string name="title_profile">Perfil</string> <string name="title_profile">Perfil</string>
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
<!-- Actions --> <!-- Actions -->
<string name="action_connect">Conectar</string> <string name="action_connect">Conectar</string>
<string name="action_login_or_sign_up">Toque este botão para fazer login ou criar uma conta</string>
<string name="action_terms_of_service">Termos de Serviço</string> <string name="action_terms_of_service">Termos de Serviço</string>
<string name="action_privacy_policy">Política de Privacidade</string> <string name="action_privacy_policy">Política de Privacidade</string>
<string name="action_search">Pesquisar</string> <string name="action_search">Pesquisar</string>
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
<string name="title_sign_in_your_server">Sign in your server</string> <string name="title_sign_in_your_server">Sign in your server</string>
<string name="title_log_in">Log in</string> <string name="title_log_in">Log in</string>
<string name="title_sign_up">Sign up</string> <string name="title_sign_up">Sign up</string>
<string name="title_log_in_or_sign_up">Log in or Sign up</string> <string name="title_authentication">Authentication</string>
<string name="title_legal_terms">Legal Terms</string> <string name="title_legal_terms">Legal Terms</string>
<string name="title_chats">Chats</string> <string name="title_chats">Chats</string>
<string name="title_profile">Profile</string> <string name="title_profile">Profile</string>
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
<!-- Actions --> <!-- Actions -->
<string name="action_connect">Connect</string> <string name="action_connect">Connect</string>
<string name="action_login_or_sign_up">Tap this button to log in or create an account</string>
<string name="action_terms_of_service">Terms of Service</string> <string name="action_terms_of_service">Terms of Service</string>
<string name="action_privacy_policy">Privacy Policy</string> <string name="action_privacy_policy">Privacy Policy</string>
<string name="action_search">Search</string> <string name="action_search">Search</string>
......
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