Unverified Commit f14f8777 authored by Lucio Maciel's avatar Lucio Maciel Committed by GitHub

Merge branch 'develop' into fix-collapsing-quote

parents da692e15 c89473be
......@@ -9,24 +9,25 @@
<application
android:name=".app.RocketChatApplication"
tools:replace="android:name"
android:allowBackup="true"
android:fullBackupContent="@xml/backup_config"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:networkSecurityConfig="@xml/network_security_config"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true">
android:supportsRtl="true"
tools:replace="android:name">
<activity
android:name=".authentication.ui.AuthenticationActivity"
android:configChanges="orientation"
android:screenOrientation="portrait"
android:theme="@style/AuthenticationTheme"
android:theme="@style/AppTheme"
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
......@@ -40,6 +41,7 @@
<data
android:host="auth"
android:scheme="rocketchat" />
<data
android:host="go.rocket.chat"
android:path="/auth"
......@@ -49,7 +51,7 @@
<activity
android:name=".server.ui.ChangeServerActivity"
android:theme="@style/AuthenticationTheme" />
android:theme="@style/AppTheme" />
<activity
android:name=".main.ui.MainActivity"
......
......@@ -2,13 +2,22 @@ package chat.rocket.android.analytics.event
sealed class ScreenViewEvent(val screenName: String) {
// Authentication
object OnBoarding : ScreenViewEvent("OnBoardingFragment")
object Server : ScreenViewEvent("ServerFragment")
object LoginOptions : ScreenViewEvent("LoginOptionsFragment")
object Login : ScreenViewEvent("LoginFragment")
object TwoFa : ScreenViewEvent("TwoFAFragment")
object SignUp : ScreenViewEvent("SignupFragment")
object RegisterUsername : ScreenViewEvent("RegisterUsernameFragment")
object ResetPassword : ScreenViewEvent("ResetPasswordFragment")
object About : ScreenViewEvent("AboutFragment")
object ChatRoom : ScreenViewEvent("ChatRoomFragment")
object ChatRooms : ScreenViewEvent("ChatRoomsFragment")
object CreateChannel : ScreenViewEvent("CreateChannelFragment")
object FavoriteMessages : ScreenViewEvent("FavoriteMessagesFragment")
object Files : ScreenViewEvent("FilesFragment")
object Login : ScreenViewEvent("LoginFragment")
object MemberBottomSheet : ScreenViewEvent("MemberBottomSheetFragment")
object Members : ScreenViewEvent("MembersFragment")
object Mentions : ScreenViewEvent("MentionsFragment")
......@@ -17,10 +26,5 @@ sealed class ScreenViewEvent(val screenName: String) {
object PinnedMessages : ScreenViewEvent("PinnedMessagesFragment")
object Preferences : ScreenViewEvent("PreferencesFragment")
object Profile : ScreenViewEvent("ProfileFragment")
object RegisterUsername : ScreenViewEvent("RegisterUsernameFragment")
object ResetPassword : ScreenViewEvent("ResetPasswordFragment")
object Server : ScreenViewEvent("ServerFragment")
object Settings : ScreenViewEvent("SettingsFragment")
object SignUp : ScreenViewEvent("SignupFragment")
object TwoFa : ScreenViewEvent("TwoFAFragment")
}
package chat.rocket.android.authentication.di
import androidx.lifecycle.LifecycleOwner
import chat.rocket.android.authentication.presentation.AuthenticationNavigator
import chat.rocket.android.authentication.ui.AuthenticationActivity
import chat.rocket.android.dagger.qualifier.ForAuthentication
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.dagger.scope.PerActivity
import dagger.Module
import dagger.Provides
import kotlinx.coroutines.experimental.Job
@Module
class AuthenticationModule {
@Provides
@PerActivity
fun provideAuthenticationNavigator(activity: AuthenticationActivity) = AuthenticationNavigator(activity)
fun provideAuthenticationNavigator(activity: AuthenticationActivity) =
AuthenticationNavigator(activity)
@Provides
@PerActivity
fun provideJob() = Job()
@Provides
@PerActivity
fun provideLifecycleOwner(activity: AuthenticationActivity): LifecycleOwner = activity
@Provides
@PerActivity
fun provideCancelStrategy(owner: LifecycleOwner, jobs: Job): CancelStrategy =
CancelStrategy(owner, jobs)
}
\ No newline at end of file
......@@ -3,34 +3,18 @@ package chat.rocket.android.authentication.login.di
import androidx.lifecycle.LifecycleOwner
import chat.rocket.android.authentication.login.presentation.LoginView
import chat.rocket.android.authentication.login.ui.LoginFragment
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.dagger.scope.PerFragment
import dagger.Module
import dagger.Provides
import kotlinx.coroutines.experimental.Job
@Module
class LoginFragmentModule {
@Provides
@PerFragment
fun provideJob() = Job()
fun loginView(frag: LoginFragment): LoginView = frag
@Provides
@PerFragment
fun loginView(frag: LoginFragment): LoginView {
return frag
}
@Provides
@PerFragment
fun provideLifecycleOwner(frag: LoginFragment): LifecycleOwner {
return frag
}
@Provides
@PerFragment
fun provideCancelStrategy(owner: LifecycleOwner, jobs: Job): CancelStrategy {
return CancelStrategy(owner, jobs)
}
fun provideLifecycleOwner(frag: LoginFragment): LifecycleOwner = frag
}
\ No newline at end of file
......@@ -2,10 +2,8 @@ package chat.rocket.android.authentication.login.presentation
import chat.rocket.android.analytics.AnalyticsManager
import chat.rocket.android.analytics.event.AuthenticationEvent
import chat.rocket.android.authentication.domain.model.LoginDeepLinkInfo
import chat.rocket.android.authentication.presentation.AuthenticationNavigator
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.helper.OauthHelper
import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.server.domain.GetConnectingServerInteractor
import chat.rocket.android.server.domain.GetSettingsInteractor
......@@ -13,35 +11,17 @@ import chat.rocket.android.server.domain.PublicSettings
import chat.rocket.android.server.domain.SaveAccountInteractor
import chat.rocket.android.server.domain.SaveCurrentServerInteractor
import chat.rocket.android.server.domain.TokenRepository
import chat.rocket.android.server.domain.casLoginUrl
import chat.rocket.android.server.domain.favicon
import chat.rocket.android.server.domain.gitlabUrl
import chat.rocket.android.server.domain.isCasAuthenticationEnabled
import chat.rocket.android.server.domain.isFacebookAuthenticationEnabled
import chat.rocket.android.server.domain.isGithubAuthenticationEnabled
import chat.rocket.android.server.domain.isGitlabAuthenticationEnabled
import chat.rocket.android.server.domain.isGoogleAuthenticationEnabled
import chat.rocket.android.server.domain.isLdapAuthenticationEnabled
import chat.rocket.android.server.domain.isLinkedinAuthenticationEnabled
import chat.rocket.android.server.domain.isLoginFormEnabled
import chat.rocket.android.server.domain.isPasswordResetEnabled
import chat.rocket.android.server.domain.isRegistrationEnabledForNewUsers
import chat.rocket.android.server.domain.isWordpressAuthenticationEnabled
import chat.rocket.android.server.domain.model.Account
import chat.rocket.android.server.domain.wideTile
import chat.rocket.android.server.domain.wordpressUrl
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.extensions.avatarUrl
import chat.rocket.android.util.extensions.casUrl
import chat.rocket.android.util.extensions.encodeToBase64
import chat.rocket.android.util.extensions.generateRandomString
import chat.rocket.android.util.extensions.isEmail
import chat.rocket.android.util.extensions.parseColor
import chat.rocket.android.util.extensions.samlUrl
import chat.rocket.android.util.extensions.serverLogoUrl
import chat.rocket.android.util.retryIO
import chat.rocket.common.RocketChatAuthException
import chat.rocket.common.RocketChatException
import chat.rocket.common.RocketChatTwoFactorException
import chat.rocket.common.model.Email
......@@ -50,30 +30,11 @@ import chat.rocket.common.model.User
import chat.rocket.common.util.ifNull
import chat.rocket.core.RocketChatClient
import chat.rocket.core.internal.rest.login
import chat.rocket.core.internal.rest.loginWithCas
import chat.rocket.core.internal.rest.loginWithEmail
import chat.rocket.core.internal.rest.loginWithLdap
import chat.rocket.core.internal.rest.loginWithOauth
import chat.rocket.core.internal.rest.loginWithSaml
import chat.rocket.core.internal.rest.me
import chat.rocket.core.internal.rest.settingsOauth
import kotlinx.coroutines.experimental.delay
import timber.log.Timber
import java.util.concurrent.TimeUnit
import javax.inject.Inject
private const val TYPE_LOGIN_USER_EMAIL = 0
private const val TYPE_LOGIN_CAS = 1
private const val TYPE_LOGIN_SAML = 2
private const val TYPE_LOGIN_OAUTH = 3
private const val TYPE_LOGIN_DEEP_LINK = 4
private const val SERVICE_NAME_FACEBOOK = "facebook"
private const val SERVICE_NAME_GITHUB = "github"
private const val SERVICE_NAME_GOOGLE = "google"
private const val SERVICE_NAME_LINKEDIN = "linkedin"
private const val SERVICE_NAME_GILAB = "gitlab"
private const val SERVICE_NAME_WORDPRESS = "wordpress"
class LoginPresenter @Inject constructor(
private val view: LoginView,
private val strategy: CancelStrategy,
......@@ -82,382 +43,45 @@ class LoginPresenter @Inject constructor(
private val localRepository: LocalRepository,
private val settingsInteractor: GetSettingsInteractor,
private val analyticsManager: AnalyticsManager,
serverInteractor: GetConnectingServerInteractor,
private val saveCurrentServer: SaveCurrentServerInteractor,
private val saveAccountInteractor: SaveAccountInteractor,
private val factory: RocketChatClientFactory
private val factory: RocketChatClientFactory,
val serverInteractor: GetConnectingServerInteractor
) {
// TODO - we should validate the current server when opening the app, and have a nonnull get()
private var currentServer = serverInteractor.get()!!
private lateinit var client: RocketChatClient
private lateinit var settings: PublicSettings
private lateinit var usernameOrEmail: String
private lateinit var password: String
private lateinit var credentialToken: String
private lateinit var credentialSecret: String
private lateinit var deepLinkUserId: String
private lateinit var deepLinkToken: String
private lateinit var loginMethod: AuthenticationEvent
fun setupView() {
setupConnectionInfo(currentServer)
setupLoginView()
setupUserRegistrationView()
setupForgotPasswordView()
setupCasView()
setupOauthServicesView()
}
fun authenticateWithUserAndPassword(usernameOrEmail: String, password: String) {
when {
usernameOrEmail.isBlank() -> {
view.alertWrongUsernameOrEmail()
}
password.isEmpty() -> {
view.alertWrongPassword()
}
else -> {
this.usernameOrEmail = usernameOrEmail
this.password = password
loginMethod = AuthenticationEvent.AuthenticationWithUserAndPassword
doAuthentication(TYPE_LOGIN_USER_EMAIL)
}
}
}
fun authenticateWithCas(casToken: String) {
credentialToken = casToken
loginMethod = AuthenticationEvent.AuthenticationWithCas
doAuthentication(TYPE_LOGIN_CAS)
}
fun authenticateWithSaml(samlToken: String) {
credentialToken = samlToken
loginMethod = AuthenticationEvent.AuthenticationWithSaml
doAuthentication(TYPE_LOGIN_SAML)
}
fun authenticateWithOauth(oauthToken: String, oauthSecret: String) {
credentialToken = oauthToken
credentialSecret = oauthSecret
loginMethod = AuthenticationEvent.AuthenticationWithOauth
doAuthentication(TYPE_LOGIN_OAUTH)
}
fun authenticateWithDeepLink(deepLinkInfo: LoginDeepLinkInfo) {
val serverUrl = deepLinkInfo.url
setupConnectionInfo(serverUrl)
if (deepLinkInfo.userId != null && deepLinkInfo.token != null) {
deepLinkUserId = deepLinkInfo.userId
deepLinkToken = deepLinkInfo.token
tokenRepository.save(serverUrl, Token(deepLinkUserId, deepLinkToken))
loginMethod = AuthenticationEvent.AuthenticationWithDeeplink
doAuthentication(TYPE_LOGIN_DEEP_LINK)
} else {
// If we don't have the login credentials, just go through normal setup and user input.
setupView()
}
}
private fun setupConnectionInfo(serverUrl: String) {
currentServer = serverUrl
client = factory.create(serverUrl)
settings = settingsInteractor.get(serverUrl)
}
fun signup() = navigator.toSignUp()
fun forgotPassword() = navigator.toForgotPassword()
private fun setupLoginView() {
if (settings.isLoginFormEnabled()) {
view.showFormView()
view.setupLoginButtonListener()
view.setupGlobalListener()
} else {
view.hideFormView()
}
}
private fun setupCasView() {
if (settings.isCasAuthenticationEnabled()) {
val casToken = generateRandomString(17)
view.setupCasButtonListener(
settings.casLoginUrl().casUrl(currentServer, casToken),
casToken
)
view.showCasButton()
}
}
private fun setupUserRegistrationView() {
if (settings.isRegistrationEnabledForNewUsers() && settings.isLoginFormEnabled()) {
view.setupSignUpView()
view.showSignUpView()
}
client = factory.create(currentServer)
settings = settingsInteractor.get(currentServer)
}
private fun setupForgotPasswordView() {
if (settings.isPasswordResetEnabled()) {
view.setupForgotPasswordView()
view.showForgotPasswordView()
}
}
private fun setupOauthServicesView() {
launchUI(strategy) {
try {
val services = retryIO("settingsOauth()") {
client.settingsOauth().services
}
if (services.isNotEmpty()) {
val state =
"{\"loginStyle\":\"popup\",\"credentialToken\":\"${generateRandomString(40)}\",\"isCordova\":true}".encodeToBase64()
var totalSocialAccountsEnabled = 0
if (settings.isFacebookAuthenticationEnabled()) {
getServiceMap(services, SERVICE_NAME_FACEBOOK)?.let { serviceMap ->
getOauthClientId(serviceMap)?.let { clientId ->
view.setupFacebookButtonListener(
OauthHelper.getFacebookOauthUrl(
clientId,
currentServer,
state
), state
)
view.enableLoginByFacebook()
totalSocialAccountsEnabled++
}
}
}
if (settings.isGithubAuthenticationEnabled()) {
getServiceMap(services, SERVICE_NAME_GITHUB)?.let { serviceMap ->
getOauthClientId(serviceMap)?.let { clientId ->
view.setupGithubButtonListener(
OauthHelper.getGithubOauthUrl(
clientId,
state
), state
)
view.enableLoginByGithub()
totalSocialAccountsEnabled++
}
}
}
if (settings.isGoogleAuthenticationEnabled()) {
getServiceMap(services, SERVICE_NAME_GOOGLE)?.let { serviceMap ->
getOauthClientId(serviceMap)?.let { clientId ->
view.setupGoogleButtonListener(
OauthHelper.getGoogleOauthUrl(
clientId,
currentServer,
state
), state
)
view.enableLoginByGoogle()
totalSocialAccountsEnabled++
}
}
}
if (settings.isLinkedinAuthenticationEnabled()) {
getServiceMap(services, SERVICE_NAME_LINKEDIN)?.let { serviceMap ->
getOauthClientId(serviceMap)?.let { clientId ->
view.setupLinkedinButtonListener(
OauthHelper.getLinkedinOauthUrl(
clientId,
currentServer,
state
), state
)
view.enableLoginByLinkedin()
totalSocialAccountsEnabled++
}
}
}
if (settings.isGitlabAuthenticationEnabled()) {
getServiceMap(services, SERVICE_NAME_GILAB)?.let { serviceMap ->
getOauthClientId(serviceMap)?.let { clientId ->
val gitlabOauthUrl = if (settings.gitlabUrl() != null) {
OauthHelper.getGitlabOauthUrl(
host = settings.gitlabUrl(),
clientId = clientId,
serverUrl = currentServer,
state = state
)
} else {
OauthHelper.getGitlabOauthUrl(
clientId = clientId,
serverUrl = currentServer,
state = state
)
}
view.setupGitlabButtonListener(gitlabOauthUrl, state)
view.enableLoginByGitlab()
totalSocialAccountsEnabled++
}
}
}
if (settings.isWordpressAuthenticationEnabled()) {
getServiceMap(services, SERVICE_NAME_WORDPRESS)?.let { serviceMap ->
getOauthClientId(serviceMap)?.let { clientId ->
val wordpressOauthUrl =
if (settings.wordpressUrl().isNullOrEmpty()) {
OauthHelper.getWordpressComOauthUrl(
clientId,
currentServer,
state
)
} else {
OauthHelper.getWordpressCustomOauthUrl(
getCustomOauthHost(serviceMap)
?: "https://public-api.wordpress.com",
getCustomOauthAuthorizePath(serviceMap)
?: "/oauth/authorize",
clientId,
currentServer,
SERVICE_NAME_WORDPRESS,
state,
getCustomOauthScope(serviceMap) ?: "openid"
)
}
wordpressOauthUrl.let {
view.setupWordpressButtonListener(it, state)
view.enableLoginByWordpress()
totalSocialAccountsEnabled++
}
}
}
}
getCustomOauthServices(services).let {
for (serviceMap in it) {
val serviceName = getCustomOauthServiceName(serviceMap)
val host = getCustomOauthHost(serviceMap)
val authorizePath = getCustomOauthAuthorizePath(serviceMap)
val clientId = getOauthClientId(serviceMap)
val scope = getCustomOauthScope(serviceMap)
val textColor = getServiceNameColorForCustomOauthOrSaml(serviceMap)
val buttonColor = getServiceButtonColor(serviceMap)
if (serviceName != null &&
host != null &&
authorizePath != null &&
clientId != null &&
scope != null &&
textColor != null &&
buttonColor != null
) {
val customOauthUrl = OauthHelper.getCustomOauthUrl(
host,
authorizePath,
clientId,
currentServer,
serviceName,
state,
scope
)
view.addCustomOauthServiceButton(
customOauthUrl,
state,
serviceName,
textColor,
buttonColor
)
totalSocialAccountsEnabled++
}
}
}
getSamlServices(services).let {
val samlToken = generateRandomString(17)
for (serviceMap in it) {
val provider = getSamlProvider(serviceMap)
val serviceName = getSamlServiceName(serviceMap)
val textColor = getServiceNameColorForCustomOauthOrSaml(serviceMap)
val buttonColor = getServiceButtonColor(serviceMap)
if (provider != null &&
serviceName != null &&
textColor != null &&
buttonColor != null
) {
view.addSamlServiceButton(
currentServer.samlUrl(provider, samlToken),
samlToken,
serviceName,
textColor,
buttonColor
)
totalSocialAccountsEnabled++
}
}
}
if (totalSocialAccountsEnabled > 0) {
view.enableOauthView()
if (totalSocialAccountsEnabled > 3) {
view.setupFabListener()
}
} else {
view.disableOauthView()
}
} else {
view.disableOauthView()
}
} catch (exception: RocketChatException) {
Timber.e(exception)
view.disableOauthView()
}
}
}
private fun doAuthentication(loginType: Int) {
fun authenticateWithUserAndPassword(usernameOrEmail: String, password: String) {
launchUI(strategy) {
view.disableUserInput()
view.showLoading()
try {
val token = retryIO("login") {
when (loginType) {
TYPE_LOGIN_USER_EMAIL -> {
when {
settings.isLdapAuthenticationEnabled() ->
client.loginWithLdap(usernameOrEmail, password)
usernameOrEmail.isEmail() ->
client.loginWithEmail(usernameOrEmail, password)
else ->
client.login(usernameOrEmail, password)
}
}
TYPE_LOGIN_CAS -> {
delay(3, TimeUnit.SECONDS)
client.loginWithCas(credentialToken)
}
TYPE_LOGIN_SAML -> {
delay(3, TimeUnit.SECONDS)
client.loginWithSaml(credentialToken)
}
TYPE_LOGIN_OAUTH -> {
client.loginWithOauth(credentialToken, credentialSecret)
}
TYPE_LOGIN_DEEP_LINK -> {
val myself = client.me() // Just checking if the credentials worked.
if (myself.id == deepLinkUserId) {
Token(deepLinkUserId, deepLinkToken)
} else {
throw RocketChatAuthException("Invalid AuthenticationEvent Deep Link Credentials...")
}
}
else -> {
throw IllegalStateException("Expected TYPE_LOGIN_USER_EMAIL, TYPE_LOGIN_CAS,TYPE_LOGIN_SAML, TYPE_LOGIN_OAUTH or TYPE_LOGIN_DEEP_LINK")
}
when {
settings.isLdapAuthenticationEnabled() ->
client.loginWithLdap(usernameOrEmail, password)
usernameOrEmail.isEmail() ->
client.loginWithEmail(usernameOrEmail, password)
else ->
client.login(usernameOrEmail, password)
}
}
val myself = retryIO("me()") { client.me() }
......@@ -476,13 +100,12 @@ class LoginPresenter @Inject constructor(
localRepository.save(LocalRepository.CURRENT_USERNAME_KEY, myself.username)
saveAccount(myself.username!!)
saveToken(token)
analyticsManager.logLogin(loginMethod, true)
if (loginType == TYPE_LOGIN_USER_EMAIL) {
view.saveSmartLockCredentials(usernameOrEmail, password)
}
analyticsManager.logLogin(
AuthenticationEvent.AuthenticationWithUserAndPassword,
true
)
view.saveSmartLockCredentials(usernameOrEmail, password)
navigator.toChatList()
} else if (loginType == TYPE_LOGIN_OAUTH) {
navigator.toRegisterUsername(token.userId, token.authToken)
}
} catch (exception: RocketChatException) {
when (exception) {
......@@ -490,7 +113,10 @@ class LoginPresenter @Inject constructor(
navigator.toTwoFA(usernameOrEmail, password)
}
else -> {
analyticsManager.logLogin(loginMethod, false)
analyticsManager.logLogin(
AuthenticationEvent.AuthenticationWithUserAndPassword,
false
)
exception.message?.let {
view.showMessage(it)
}.ifNull {
......@@ -500,119 +126,12 @@ class LoginPresenter @Inject constructor(
}
} finally {
view.hideLoading()
view.enableUserInput()
}
}
}
/**
* Returns an OAuth service map given a [serviceName].
*
* @param listMap The list of [Map] to get the service from.
* @param serviceName The service name to get in the [listMap]
* @return The OAuth service map or null otherwise.
*/
private fun getServiceMap(
listMap: List<Map<String, Any>>,
serviceName: String
): Map<String, Any>? = listMap.find { map -> map.containsValue(serviceName) }
/**
* Returns the OAuth client ID of a [serviceMap].
* REMARK: This function works for common OAuth providers (Google, Facebook, Github and so on)
* as well as custom OAuth.
*
* @param serviceMap The service map to get the OAuth client ID.
* @return The OAuth client ID or null otherwise.
*/
private fun getOauthClientId(serviceMap: Map<String, Any>): String? =
serviceMap["clientId"] as? String ?: serviceMap["appId"] as? String
/**
* Returns a custom OAuth service list.
*
* @return A custom OAuth service list, otherwise an empty list if there is no custom OAuth service.
*/
private fun getCustomOauthServices(listMap: List<Map<String, Any>>): List<Map<String, Any>> =
listMap.filter { map -> map["custom"] == true }
/** Returns the custom OAuth service host.
*
* @param serviceMap The service map to get the custom OAuth service host.
* @return The custom OAuth service host, otherwise null.
*/
private fun getCustomOauthHost(serviceMap: Map<String, Any>): String? =
serviceMap["serverURL"] as? String
/** Returns the custom OAuth service authorize path.
*
* @param serviceMap The service map to get the custom OAuth service authorize path.
* @return The custom OAuth service authorize path, otherwise null.
*/
private fun getCustomOauthAuthorizePath(serviceMap: Map<String, Any>): String? =
serviceMap["authorizePath"] as? String
/** Returns the custom OAuth service scope.
*
* @param serviceMap The service map to get the custom OAuth service scope.
* @return The custom OAuth service scope, otherwise null.
*/
private fun getCustomOauthScope(serviceMap: Map<String, Any>): String? =
serviceMap["scope"] as? String
/** Returns the text of the custom OAuth service.
*
* @param serviceMap The service map to get the text of the custom OAuth service.
* @return The text of the custom OAuth service, otherwise null.
*/
private fun getCustomOauthServiceName(serviceMap: Map<String, Any>): String? =
serviceMap["service"] as? String
/**
* Returns a SAML OAuth service list.
*
* @return A SAML service list, otherwise an empty list if there is no SAML OAuth service.
*/
private fun getSamlServices(listMap: List<Map<String, Any>>): List<Map<String, Any>> =
listMap.filter { map -> map["service"] == "saml" }
/**
* Returns the SAML provider.
*
* @param serviceMap The service map to provider from.
* @return The SAML provider, otherwise null.
*/
private fun getSamlProvider(serviceMap: Map<String, Any>): String? =
(serviceMap["clientConfig"] as Map<*, *>)["provider"] as? String
/**
* Returns the text of the SAML service.
*
* @param serviceMap The service map to get the text of the SAML service.
* @return The text of the SAML service, otherwise null.
*/
private fun getSamlServiceName(serviceMap: Map<String, Any>): String? =
serviceMap["buttonLabelText"] as? String
/**
* Returns the text color of the service name.
* REMARK: This can be used for custom OAuth or SAML.
*
* @param serviceMap The service map to get the text color from.
* @return The text color of the service (custom OAuth or SAML), otherwise null.
*/
private fun getServiceNameColorForCustomOauthOrSaml(serviceMap: Map<String, Any>): Int? =
(serviceMap["buttonLabelColor"] as? String)?.parseColor()
}
/**
* Returns the button color of the service name.
* REMARK: This can be used for custom OAuth or SAML.
*
* @param serviceMap The service map to get the button color from.
* @return The button color of the service (custom OAuth or SAML), otherwise null.
*/
private fun getServiceButtonColor(serviceMap: Map<String, Any>): Int? =
(serviceMap["buttonColor"] as? String)?.parseColor()
fun forgotPassword() = navigator.toForgotPassword()
private suspend fun saveAccount(username: String) {
val icon = settings.favicon()?.let {
......@@ -626,7 +145,5 @@ class LoginPresenter @Inject constructor(
saveAccountInteractor.save(account)
}
private fun saveToken(token: Token) {
tokenRepository.save(currentServer, token)
}
private fun saveToken(token: Token) = tokenRepository.save(currentServer, token)
}
\ No newline at end of file
......@@ -5,259 +5,34 @@ import chat.rocket.android.core.behaviours.MessageView
interface LoginView : LoadingView, MessageView {
/**
* 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 before showing it [setupCasButtonListener].
*/
fun showCasButton()
/**
* Hides the CAS button.
*/
fun hideCasButton()
/**
* Setups the CAS button when tapped.
*
* @param casUrl The CAS URL to authenticate with.
* @param casToken The requested token to be sent to the CAS server.
*/
fun setupCasButtonListener(casUrl: String, casToken: String)
/**
* 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()
/**
* Setups the sign up view when tapped.
*/
fun setupSignUpView()
/**
* Shows the forgot password view if enabled by the server settings.
*
* REMARK: We must set up the forgot password view listener [setupForgotPasswordView].
*/
fun showForgotPasswordView()
/**
* Setups the forgot password view when tapped.
*/
fun setupForgotPasswordView()
/**
* Hides the sign up view.
*/
fun hideSignUpView()
/**
* Enables and shows the oauth view if there is login via social accounts enabled by the server settings.
*
* REMARK: We must show at maximum *three* social accounts views ([enableLoginByFacebook], [enableLoginByGithub], [enableLoginByGoogle],
* [enableLoginByLinkedin], [enableLoginByMeteor], [enableLoginByTwitter], [enableLoginByGitlab], [addCustomOauthServiceButton] or [addSamlServiceButton]) 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).
*/
fun enableOauthView()
/**
* Disables and hides the Oauth view if there is not login via social accounts enabled by the server settings.
*/
fun disableOauthView()
/**
* Shows the login button.
*/
fun showLoginButton()
/**
* Hides the login button.
*/
fun hideLoginButton()
/**
* Shows the "login by Facebook view if it is enable by the server settings.
*/
fun enableLoginByFacebook()
/**
* Shows the "login by Github" view if it is enable by the server settings.
*
* REMARK: We must set up the Github button listener before enabling it [setupGithubButtonListener].
*/
fun enableLoginByGithub()
/**
* Setups the Github button when tapped.
*
* @param githubUrl The Github OAuth URL to authenticate with.
* @param state A random string generated by the app, which you'll verify later (to protect against forgery attacks).
*/
fun setupGithubButtonListener(githubUrl: String, state: String)
/**
* Shows the "login by Google" view if it is enable by the server settings.
*
* REMARK: We must set up the Google button listener before enabling it [setupGoogleButtonListener].
*/
fun enableLoginByGoogle()
/**
* Setups the Google button when tapped.
*
* @param googleUrl The Google OAuth URL to authenticate with.
* @param state A random string generated by the app, which you'll verify later (to protect against forgery attacks).
*/
fun setupGoogleButtonListener(googleUrl: String, state: String)
/**
* Shows the "login by Linkedin" view if it is enable by the server settings.
*
* REMARK: We must set up the Linkedin button listener before enabling it [setupLinkedinButtonListener].
*/
fun enableLoginByLinkedin()
/**
* Setups the Linkedin button when tapped.
*
* @param linkedinUrl The Linkedin OAuth URL to authenticate with.
* @param state A random string generated by the app, which you'll verify later (to protect against forgery attacks).
*/
fun setupLinkedinButtonListener(linkedinUrl: String, state: String)
/**
* Setups the Facebook button when tapped.
*
* @param facebookOauthUrl The Facebook OAuth URL to authenticate with.
* @param state A random string generated by the app, which you'll verify later (to protect against forgery attacks).
*/
fun setupFacebookButtonListener(facebookOauthUrl: String, state: String)
/**
* Shows the "login by Meteor" view if it is enable by the server settings.
*/
fun enableLoginByMeteor()
/**
* Shows the "login by Twitter" view if it is enable by the server settings.
*/
fun enableLoginByTwitter()
/**
* Shows the "login by Gitlab" view if it is enable by the server settings.
*
* REMARK: We must set up the Gitlab button listener before enabling it [setupGitlabButtonListener].
*/
fun enableLoginByGitlab()
/**
* Setups the Gitlab button when tapped.
*
* @param gitlabUrl The Gitlab OAuth URL to authenticate with.
* @param state A random string generated by the app, which you'll verify later (to protect against forgery attacks).
*/
fun setupGitlabButtonListener(gitlabUrl: String, state: String)
/**
* Shows the "login by WordPress" view if it is enable by the server settings.
*
* REMARK: We must set up the Gitlab button listener before enabling it [setupWordpressButtonListener].
*/
fun enableLoginByWordpress()
/**
* Setups the WordPress button when tapped.
*
* @param wordpressUrl The WordPress OAuth URL to authenticate with.
* @param state A random string generated by the app, which you'll verify later (to protect against forgery attacks).
*/
fun setupWordpressButtonListener(wordpressUrl: String, state: String)
/**
* Adds a custom OAuth button in the oauth view.
*
* @customOauthUrl The custom OAuth url to sets up the button (the listener).
* @state A random string generated by the app, which you'll verify later (to protect against forgery attacks).
* @serviceName The custom OAuth service name.
* @serviceNameColor The custom OAuth service name color (just stylizing).
* @buttonColor The custom OAuth button color (just stylizing).
* @see [enableOauthView]
*/
fun addCustomOauthServiceButton(
customOauthUrl: String,
state: String,
serviceName: String,
serviceNameColor: Int,
buttonColor: Int
)
/**
* Adds a SAML button in the oauth view.
*
* @samlUrl The SAML url to sets up the button (the listener).
* @serviceName The SAML service name.
* @serviceNameColor The SAML service name color (just stylizing).
* @buttonColor The SAML button color (just stylizing).
* @see [enableOauthView]
* Saves Google Smart Lock credentials.
*/
fun addSamlServiceButton(
samlUrl: String,
samlToken: String,
serviceName: String,
serviceNameColor: Int,
buttonColor: Int
)
fun saveSmartLockCredentials(id: String, password: String)
/**
* Setups the FloatingActionButton to show more social accounts views (expanding the oauth view interface to show the remaining view(s)).
* Enables the button to login when the user inputs an username and a password.
*/
fun setupFabListener()
fun setupGlobalListener()
fun enableButtonLogin()
/**
* Alerts the user about a wrong inputted username or email.
* Disables the button to login when there is not an entered/not blank username or password by
* the user (i.e. The fields are empty/blank)
*/
fun alertWrongUsernameOrEmail()
fun disableButtonLogin()
/**
* Alerts the user about a wrong inputted password.
* Enables the forget password button after requesting a processing to log in.
*/
fun alertWrongPassword()
fun enableButtonForgetPassword()
/**
* Saves Google Smart Lock credentials.
* Disables the forget password button when requesting a processing to log in.
*/
fun saveSmartLockCredentials(id: String, password: String)
}
\ No newline at end of file
fun disableButtonForgetPassword()
}
package chat.rocket.android.authentication.login.ui
import DrawableHelper
import android.app.Activity
import android.content.Intent
import android.graphics.PorterDuff
import android.os.Build
import android.os.Bundle
import android.text.style.ClickableSpan
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.ViewTreeObserver
import android.widget.Button
import android.widget.ImageButton
import android.widget.LinearLayout
import android.widget.ScrollView
import androidx.core.content.ContextCompat
import androidx.core.view.ViewCompat
import androidx.core.view.isVisible
import androidx.core.view.postDelayed
import androidx.fragment.app.Fragment
import chat.rocket.android.R
import chat.rocket.android.analytics.AnalyticsManager
import chat.rocket.android.analytics.event.ScreenViewEvent
import chat.rocket.android.authentication.domain.model.LoginDeepLinkInfo
import chat.rocket.android.authentication.login.presentation.LoginPresenter
import chat.rocket.android.authentication.login.presentation.LoginView
import chat.rocket.android.helper.KeyboardHelper
import chat.rocket.android.helper.TextHelper
import chat.rocket.android.authentication.ui.AuthenticationActivity
import chat.rocket.android.helper.getCredentials
import chat.rocket.android.helper.hasCredentialsSupport
import chat.rocket.android.helper.requestStoredCredentials
import chat.rocket.android.helper.saveCredentials
import chat.rocket.android.util.extension.asObservable
import chat.rocket.android.util.extensions.clearLightStatusBar
import chat.rocket.android.util.extensions.inflate
import chat.rocket.android.util.extensions.shake
import chat.rocket.android.util.extensions.showToast
import chat.rocket.android.util.extensions.textContent
import chat.rocket.android.util.extensions.ui
import chat.rocket.android.util.extensions.vibrateSmartPhone
import chat.rocket.android.webview.oauth.ui.INTENT_OAUTH_CREDENTIAL_SECRET
import chat.rocket.android.webview.oauth.ui.INTENT_OAUTH_CREDENTIAL_TOKEN
import chat.rocket.android.webview.oauth.ui.oauthWebViewIntent
import chat.rocket.android.webview.sso.ui.INTENT_SSO_TOKEN
import chat.rocket.android.webview.sso.ui.ssoWebViewIntent
import chat.rocket.common.util.ifNull
import dagger.android.support.AndroidSupportInjection
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.Observables
import kotlinx.android.synthetic.main.app_bar.*
import kotlinx.android.synthetic.main.fragment_authentication_log_in.*
import javax.inject.Inject
internal const val TAG_LOGIN_FRAGMENT = "LoginFragment"
private const val SERVER_NAME = "server_name"
internal const val REQUEST_CODE_FOR_SIGN_IN_REQUIRED = 1
internal const val REQUEST_CODE_FOR_MULTIPLE_ACCOUNTS_RESOLUTION = 2
internal const val REQUEST_CODE_FOR_SAVE_RESOLUTION = 3
internal const val REQUEST_CODE_FOR_CAS = 4
internal const val REQUEST_CODE_FOR_SAML = 5
internal const val REQUEST_CODE_FOR_OAUTH = 6
fun newInstance(serverName: String): Fragment {
return LoginFragment().apply {
arguments = Bundle(1).apply {
putString(SERVER_NAME, serverName)
}
}
}
class LoginFragment : Fragment(), LoginView {
@Inject
lateinit var presenter: LoginPresenter
@Inject
lateinit var analyticsManager: AnalyticsManager
private var isOauthViewEnable = false
private val layoutListener = ViewTreeObserver.OnGlobalLayoutListener {
areLoginOptionsNeeded()
}
private var isGlobalLayoutListenerSetUp = false
private var deepLinkInfo: LoginDeepLinkInfo? = null
companion object {
private const val DEEP_LINK_INFO = "DeepLinkInfo"
fun newInstance(deepLinkInfo: LoginDeepLinkInfo? = null) = LoginFragment().apply {
arguments = Bundle().apply {
putParcelable(DEEP_LINK_INFO, deepLinkInfo)
}
}
}
private var serverName: String? = null
private val editTextsDisposable = CompositeDisposable()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
AndroidSupportInjection.inject(this)
deepLinkInfo = arguments?.getParcelable(DEEP_LINK_INFO)
val bundle = arguments
if (bundle != null) {
serverName = bundle.getString(SERVER_NAME)
}
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? =
container?.inflate(R.layout.fragment_authentication_log_in)
): View? = container?.inflate(R.layout.fragment_authentication_log_in)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) {
tintEditTextDrawableStart()
}
deepLinkInfo?.let {
presenter.authenticateWithDeepLink(it)
}.ifNull {
presenter.setupView()
}
if (!hasCredentialsSupport()) {
image_key.isVisible = false
}
setupToolbar()
presenter.setupView()
subscribeEditTexts()
setupOnClickListener()
analyticsManager.logScreenView(ScreenViewEvent.Login)
}
override fun onDestroyView() {
super.onDestroyView()
if (isGlobalLayoutListenerSetUp) {
scroll_view.viewTreeObserver.removeOnGlobalLayoutListener(layoutListener)
isGlobalLayoutListenerSetUp = false
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (resultCode == Activity.RESULT_OK) {
if (data != null) {
when (requestCode) {
REQUEST_CODE_FOR_MULTIPLE_ACCOUNTS_RESOLUTION -> {
REQUEST_CODE_FOR_MULTIPLE_ACCOUNTS_RESOLUTION ->
getCredentials(data)?.let {
onCredentialRetrieved(it.first, it.second)
}
}
REQUEST_CODE_FOR_SIGN_IN_REQUIRED -> {
REQUEST_CODE_FOR_SIGN_IN_REQUIRED ->
getCredentials(data)?.let { credential ->
text_username_or_email.setText(credential.first)
text_password.setText(credential.second)
}
}
REQUEST_CODE_FOR_SAVE_RESOLUTION -> {
showMessage(getString(R.string.message_credentials_saved_successfully))
}
REQUEST_CODE_FOR_CAS -> {
presenter.authenticateWithCas(data.getStringExtra(INTENT_SSO_TOKEN))
}
REQUEST_CODE_FOR_SAML -> data.apply {
presenter.authenticateWithSaml(getStringExtra(INTENT_SSO_TOKEN))
}
REQUEST_CODE_FOR_OAUTH -> {
presenter.authenticateWithOauth(
data.getStringExtra(INTENT_OAUTH_CREDENTIAL_TOKEN),
data.getStringExtra(INTENT_OAUTH_CREDENTIAL_SECRET)
)
}
REQUEST_CODE_FOR_SAVE_RESOLUTION -> showMessage(getString(R.string.message_credentials_saved_successfully))
}
}
}
......@@ -154,48 +101,29 @@ class LoginFragment : Fragment(), LoginView {
override fun onResume() {
super.onResume()
image_key.setOnClickListener {
requestStoredCredentials()
image_key.isVisible = false
if (hasCredentialsSupport()) {
// NOTE: Disabling the GLS feature for now. Needs to improve its behaviour on the app.
// requestStoredCredentials()
}
}
private fun tintEditTextDrawableStart() {
ui {
val personDrawable =
DrawableHelper.getDrawableFromId(R.drawable.ic_assignment_ind_black_24dp, it)
val lockDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_lock_black_24dp, it)
val drawables = arrayOf(personDrawable, lockDrawable)
DrawableHelper.wrapDrawables(drawables)
DrawableHelper.tintDrawables(drawables, it, R.color.colorDrawableTintGrey)
DrawableHelper.compoundDrawables(
arrayOf(text_username_or_email, text_password),
drawables
)
}
override fun onDestroyView() {
super.onDestroyView()
unsubscribeEditTexts()
}
private fun requestStoredCredentials() {
activity?.let {
it.requestStoredCredentials()?.let { credentials ->
onCredentialRetrieved(credentials.first, credentials.second)
}
private fun setupToolbar() {
with(activity as AuthenticationActivity) {
this.clearLightStatusBar()
toolbar.isVisible = true
toolbar.title = serverName?.replace(getString(R.string.default_protocol), "")
}
}
private fun onCredentialRetrieved(id: String, password: String) {
presenter.authenticateWithUserAndPassword(id, password)
}
override fun saveSmartLockCredentials(id: String, password: String) {
activity?.let {
it.saveCredentials(id, password)
}
}
override fun showLoading() {
ui {
disableUserInput()
view_loading.isVisible = true
}
}
......@@ -203,6 +131,7 @@ class LoginFragment : Fragment(), LoginView {
override fun hideLoading() {
ui {
view_loading.isVisible = false
enableUserInput()
}
}
......@@ -218,27 +147,10 @@ class LoginFragment : Fragment(), LoginView {
}
}
override fun showGenericErrorMessage() {
showMessage(R.string.msg_generic_error)
}
override fun showFormView() {
ui {
text_username_or_email.isVisible = true
text_password.isVisible = true
image_key.isVisible = true
}
}
override fun hideFormView() {
ui {
text_username_or_email.isVisible = false
text_password.isVisible = false
}
}
override fun showGenericErrorMessage() = showMessage(R.string.msg_generic_error)
override fun setupLoginButtonListener() {
ui {
private fun setupOnClickListener() =
ui { _ ->
button_log_in.setOnClickListener {
presenter.authenticateWithUserAndPassword(
text_username_or_email.textContent,
......@@ -246,425 +158,102 @@ class LoginFragment : Fragment(), LoginView {
)
}
}
}
override fun enableUserInput() {
ui {
button_log_in.isEnabled = true
text_username_or_email.isEnabled = true
text_password.isEnabled = true
}
}
override fun disableUserInput() {
ui {
button_log_in.isEnabled = false
text_username_or_email.isEnabled = false
text_password.isEnabled = false
}
}
override fun showCasButton() {
ui {
button_cas.isVisible = true
}
}
override fun hideCasButton() {
ui {
button_cas.isVisible = false
}
}
override fun setupCasButtonListener(casUrl: String, casToken: String) {
ui { activity ->
button_cas.setOnClickListener {
startActivityForResult(
activity.ssoWebViewIntent(casUrl, casToken),
REQUEST_CODE_FOR_CAS
)
activity.overridePendingTransition(R.anim.slide_up, R.anim.hold)
}
}
}
override fun showSignUpView() {
ui {
text_new_to_rocket_chat.isVisible = true
}
}
override fun setupSignUpView() {
ui {
val signUp = getString(R.string.title_sign_up)
val newToRocketChat = String.format(getString(R.string.msg_new_user), signUp)
text_new_to_rocket_chat.text = newToRocketChat
val signUpListener = object : ClickableSpan() {
override fun onClick(view: View) = presenter.signup()
}
TextHelper.addLink(text_new_to_rocket_chat, arrayOf(signUp), arrayOf(signUpListener))
}
}
override fun showForgotPasswordView() {
ui {
text_forgot_your_password.isVisible = true
}
}
override fun setupForgotPasswordView() {
ui {
val reset = getString(R.string.msg_reset)
val forgotPassword = String.format(getString(R.string.msg_forgot_password), reset)
text_forgot_your_password.text = forgotPassword
ui { _ ->
button_forgot_your_password.isVisible = true
button_forgot_your_password.setOnClickListener { presenter.forgotPassword() }
val resetListener = object : ClickableSpan() {
override fun onClick(view: View) = presenter.forgotPassword()
}
TextHelper.addLink(text_forgot_your_password, arrayOf(reset), arrayOf(resetListener))
}
}
override fun hideSignUpView() {
ui {
text_new_to_rocket_chat.isVisible = false
}
}
override fun enableOauthView() {
ui {
isOauthViewEnable = true
showThreeSocialAccountsMethods()
social_accounts_container.isVisible = true
}
}
override fun disableOauthView() {
ui {
isOauthViewEnable = false
social_accounts_container.isVisible = false
}
}
override fun showLoginButton() {
ui {
button_log_in.isVisible = true
}
}
override fun hideLoginButton() {
ui {
button_log_in.isVisible = false
}
}
override fun enableLoginByFacebook() {
ui {
button_facebook.isClickable = true
}
}
override fun setupFacebookButtonListener(facebookOauthUrl: String, state: String) {
ui { activity ->
button_facebook.setOnClickListener {
startActivityForResult(
activity.oauthWebViewIntent(facebookOauthUrl, state),
REQUEST_CODE_FOR_OAUTH
)
activity.overridePendingTransition(R.anim.slide_up, R.anim.hold)
}
}
}
override fun enableLoginByGithub() {
ui {
button_github.isClickable = true
}
}
override fun setupGithubButtonListener(githubUrl: String, state: String) {
ui { activity ->
button_github.setOnClickListener {
startActivityForResult(
activity.oauthWebViewIntent(githubUrl, state),
REQUEST_CODE_FOR_OAUTH
)
activity.overridePendingTransition(R.anim.slide_up, R.anim.hold)
}
}
}
override fun enableLoginByGoogle() {
ui {
button_google.isClickable = true
}
}
// TODO: Use custom tabs instead of web view.
// See https://github.com/RocketChat/Rocket.Chat.Android/issues/968
override fun setupGoogleButtonListener(googleUrl: String, state: String) {
ui { activity ->
button_google.setOnClickListener {
startActivityForResult(
activity.oauthWebViewIntent(googleUrl, state),
REQUEST_CODE_FOR_OAUTH
)
activity.overridePendingTransition(R.anim.slide_up, R.anim.hold)
}
}
}
override fun enableLoginByLinkedin() {
ui {
button_linkedin.isClickable = true
}
}
override fun setupLinkedinButtonListener(linkedinUrl: String, state: String) {
ui { activity ->
button_linkedin.setOnClickListener {
startActivityForResult(
activity.oauthWebViewIntent(linkedinUrl, state),
REQUEST_CODE_FOR_OAUTH
)
activity.overridePendingTransition(R.anim.slide_up, R.anim.hold)
}
}
}
override fun enableLoginByMeteor() {
ui {
button_meteor.isClickable = true
override fun enableButtonLogin() {
context?.let {
ViewCompat.setBackgroundTintList(
button_log_in, ContextCompat.getColorStateList(it, R.color.colorAccent)
)
button_log_in.isEnabled = true
}
}
override fun enableLoginByTwitter() {
ui {
button_twitter.isClickable = true
}
}
override fun enableLoginByGitlab() {
ui {
button_gitlab.isClickable = true
override fun disableButtonLogin() {
context?.let {
ViewCompat.setBackgroundTintList(
button_log_in,
ContextCompat.getColorStateList(it, R.color.colorAuthenticationButtonDisabled)
)
button_log_in.isEnabled = false
}
}
override fun setupGitlabButtonListener(gitlabUrl: String, state: String) {
ui { activity ->
button_gitlab.setOnClickListener {
startActivityForResult(
activity.oauthWebViewIntent(gitlabUrl, state),
REQUEST_CODE_FOR_OAUTH
)
activity.overridePendingTransition(R.anim.slide_up, R.anim.hold)
}
override fun enableButtonForgetPassword() {
context?.let {
button_forgot_your_password.isEnabled = true
button_forgot_your_password.setTextColor(
ContextCompat.getColorStateList(it, R.color.colorAccent)
)
}
}
override fun enableLoginByWordpress() {
ui {
button_wordpress.isClickable = true
override fun disableButtonForgetPassword() {
context?.let {
button_forgot_your_password.isEnabled = false
button_forgot_your_password.setTextColor(
ContextCompat.getColorStateList(it, R.color.colorAuthenticationButtonDisabled)
)
}
}
override fun setupWordpressButtonListener(wordpressUrl: String, state: String) {
ui { activity ->
button_wordpress.setOnClickListener {
startActivityForResult(
activity.oauthWebViewIntent(wordpressUrl, state),
REQUEST_CODE_FOR_OAUTH
)
activity.overridePendingTransition(R.anim.slide_up, R.anim.hold)
}
private fun requestStoredCredentials() {
activity?.requestStoredCredentials()?.let { credentials ->
onCredentialRetrieved(credentials.first, credentials.second)
}
}
override fun addCustomOauthServiceButton(
customOauthUrl: String,
state: String,
serviceName: String,
serviceNameColor: Int,
buttonColor: Int
) {
ui { activity ->
val button = getCustomServiceButton(serviceName, serviceNameColor, buttonColor)
social_accounts_container.addView(button)
button.setOnClickListener {
startActivityForResult(
activity.oauthWebViewIntent(customOauthUrl, state),
REQUEST_CODE_FOR_OAUTH
)
activity.overridePendingTransition(R.anim.slide_up, R.anim.hold)
}
}
private fun onCredentialRetrieved(id: String, password: String) {
presenter.authenticateWithUserAndPassword(id, password)
}
override fun addSamlServiceButton(
samlUrl: String,
samlToken: String,
serviceName: String,
serviceNameColor: Int,
buttonColor: Int
) {
ui { activity ->
val button = getCustomServiceButton(serviceName, serviceNameColor, buttonColor)
social_accounts_container.addView(button)
button.setOnClickListener {
startActivityForResult(
activity.ssoWebViewIntent(samlUrl, samlToken),
REQUEST_CODE_FOR_SAML
)
activity.overridePendingTransition(R.anim.slide_up, R.anim.hold)
}
}
override fun saveSmartLockCredentials(id: String, password: String) {
activity?.saveCredentials(id, password)
}
override fun setupFabListener() {
ui {
button_fab.isVisible = true
button_fab.setOnClickListener {
button_fab.hide()
showRemainingSocialAccountsView()
scrollToBottom()
}
}
private fun subscribeEditTexts() {
editTextsDisposable.add(
Observables.combineLatest(
text_username_or_email.asObservable(),
text_password.asObservable()
) { text_username_or_email, text_password ->
return@combineLatest (
text_username_or_email.isNotBlank() && text_password.isNotBlank()
)
}.subscribe { isValid ->
if (isValid) {
enableButtonLogin()
} else {
disableButtonLogin()
}
})
}
override fun setupGlobalListener() {
// We need to setup the layout to hide and show the oauth interface when the soft keyboard
// is shown (which means that the user has touched the text_username_or_email or
// text_password EditText to fill that respective fields).
if (!isGlobalLayoutListenerSetUp) {
scroll_view.viewTreeObserver.addOnGlobalLayoutListener(layoutListener)
isGlobalLayoutListenerSetUp = true
}
}
private fun unsubscribeEditTexts() = editTextsDisposable.clear()
override fun alertWrongUsernameOrEmail() {
private fun enableUserInput() {
ui {
vibrateSmartPhone()
text_username_or_email.shake()
text_username_or_email.requestFocus()
enableButtonLogin()
enableButtonForgetPassword()
text_username_or_email.isEnabled = true
text_password.isEnabled = true
}
}
override fun alertWrongPassword() {
private fun disableUserInput() {
ui {
vibrateSmartPhone()
text_password.shake()
text_password.requestFocus()
}
}
private fun showRemainingSocialAccountsView() {
social_accounts_container.postDelayed(300) {
ui {
(0..social_accounts_container.childCount)
.mapNotNull { social_accounts_container.getChildAt(it) as? ImageButton }
.filter { it.isClickable }
.forEach { it.isVisible = true }
}
}
}
// Scrolling to the bottom of the screen.
private fun scrollToBottom() {
scroll_view.postDelayed(1250) {
ui {
scroll_view.fullScroll(ScrollView.FOCUS_DOWN)
}
}
}
private fun areLoginOptionsNeeded() {
if (!isEditTextEmpty() || KeyboardHelper.isSoftKeyboardShown(scroll_view.rootView)) {
hideSignUpView()
hideOauthView()
showLoginButton()
} else {
showSignUpView()
showOauthView()
hideLoginButton()
}
}
// Returns true if *all* EditTexts are empty.
private fun isEditTextEmpty(): Boolean {
return text_username_or_email.textContent.isBlank() && text_password.textContent.isEmpty()
}
private fun showThreeSocialAccountsMethods() {
(0..social_accounts_container.childCount)
.mapNotNull { social_accounts_container.getChildAt(it) as? ImageButton }
.filter { it.isClickable }
.take(3)
.forEach { it.isVisible = true }
}
private fun showOauthView() {
if (isOauthViewEnable) {
social_accounts_container.isVisible = true
if (enabledSocialAccounts() > 3) {
button_fab.isVisible = true
}
}
}
private fun hideOauthView() {
if (isOauthViewEnable) {
social_accounts_container.isVisible = false
button_fab.isVisible = false
disableButtonLogin()
disableButtonForgetPassword()
text_username_or_email.isEnabled = false
text_password.isEnabled = false
}
}
private fun enabledSocialAccounts(): Int {
return enabledOauthAccountsImageButtons() + enabledServicesAccountsButtons()
}
private fun enabledOauthAccountsImageButtons(): Int {
return (0..social_accounts_container.childCount)
.mapNotNull { social_accounts_container.getChildAt(it) as? ImageButton }
.filter { it.isClickable }
.size
}
private fun enabledServicesAccountsButtons(): Int {
return (0..social_accounts_container.childCount)
.mapNotNull { social_accounts_container.getChildAt(it) as? Button }
.size
}
/**
* Gets a stylized custom service button.
*/
private fun getCustomServiceButton(
buttonText: String,
buttonTextColor: Int,
buttonBgColor: Int
): Button {
val params: LinearLayout.LayoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT
)
val margin = resources.getDimensionPixelSize(R.dimen.screen_edge_left_and_right_margins)
params.setMargins(margin, margin, margin, 0)
val button = Button(context)
button.layoutParams = params
button.text = buttonText
button.setTextColor(buttonTextColor)
button.background.setColorFilter(buttonBgColor, PorterDuff.Mode.MULTIPLY)
return button
}
}
\ No newline at end of file
}
package chat.rocket.android.authentication.loginoptions.di
import androidx.lifecycle.LifecycleOwner
import chat.rocket.android.authentication.loginoptions.presentation.LoginOptionsView
import chat.rocket.android.authentication.loginoptions.ui.LoginOptionsFragment
import chat.rocket.android.dagger.scope.PerFragment
import dagger.Module
import dagger.Provides
@Module
class LoginOptionsFragmentModule {
@Provides
@PerFragment
fun loginOptionsView(frag: LoginOptionsFragment): LoginOptionsView = frag
@Provides
@PerFragment
fun provideLifecycleOwner(frag: LoginOptionsFragment): LifecycleOwner = frag
}
\ No newline at end of file
package chat.rocket.android.authentication.loginoptions.di
import chat.rocket.android.authentication.loginoptions.ui.LoginOptionsFragment
import chat.rocket.android.dagger.scope.PerFragment
import dagger.Module
import dagger.android.ContributesAndroidInjector
@Module abstract class LoginOptionsFragmentProvider {
@ContributesAndroidInjector(modules = [LoginOptionsFragmentModule::class])
@PerFragment
abstract fun providesLoginOptionFragment(): LoginOptionsFragment
}
\ No newline at end of file
package chat.rocket.android.authentication.loginoptions.presentation
import chat.rocket.android.analytics.AnalyticsManager
import chat.rocket.android.analytics.event.AuthenticationEvent
import chat.rocket.android.authentication.domain.model.LoginDeepLinkInfo
import chat.rocket.android.authentication.presentation.AuthenticationNavigator
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.server.domain.GetConnectingServerInteractor
import chat.rocket.android.server.domain.GetSettingsInteractor
import chat.rocket.android.server.domain.PublicSettings
import chat.rocket.android.server.domain.SaveAccountInteractor
import chat.rocket.android.server.domain.SaveCurrentServerInteractor
import chat.rocket.android.server.domain.TokenRepository
import chat.rocket.android.server.domain.favicon
import chat.rocket.android.server.domain.model.Account
import chat.rocket.android.server.domain.wideTile
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.extensions.avatarUrl
import chat.rocket.android.util.extensions.serverLogoUrl
import chat.rocket.android.util.retryIO
import chat.rocket.common.RocketChatAuthException
import chat.rocket.common.RocketChatException
import chat.rocket.common.model.Email
import chat.rocket.common.model.Token
import chat.rocket.common.model.User
import chat.rocket.common.util.ifNull
import chat.rocket.core.RocketChatClient
import chat.rocket.core.internal.rest.loginWithCas
import chat.rocket.core.internal.rest.loginWithOauth
import chat.rocket.core.internal.rest.loginWithSaml
import chat.rocket.core.internal.rest.me
import kotlinx.coroutines.experimental.delay
import java.util.concurrent.TimeUnit
import javax.inject.Inject
private const val TYPE_LOGIN_OAUTH = 1
private const val TYPE_LOGIN_CAS = 2
private const val TYPE_LOGIN_SAML = 3
private const val TYPE_LOGIN_DEEP_LINK = 4
class LoginOptionsPresenter @Inject constructor(
private val view: LoginOptionsView,
private val strategy: CancelStrategy,
private val factory: RocketChatClientFactory,
private val navigator: AuthenticationNavigator,
private val settingsInteractor: GetSettingsInteractor,
private val localRepository: LocalRepository,
private val saveCurrentServer: SaveCurrentServerInteractor,
private val saveAccountInteractor: SaveAccountInteractor,
private val analyticsManager: AnalyticsManager,
private val tokenRepository: TokenRepository,
serverInteractor: GetConnectingServerInteractor
) {
// TODO - we should validate the current server when opening the app, and have a nonnull get()
private var currentServer = serverInteractor.get()!!
private lateinit var client: RocketChatClient
private lateinit var settings: PublicSettings
private lateinit var credentialToken: String
private lateinit var credentialSecret: String
private lateinit var deepLinkUserId: String
private lateinit var deepLinkToken: String
private lateinit var loginMethod: AuthenticationEvent
fun toCreateAccount() = navigator.toCreateAccount()
fun toLoginWithEmail() = navigator.toLogin(currentServer)
fun authenticateWithOauth(oauthToken: String, oauthSecret: String) {
credentialToken = oauthToken
credentialSecret = oauthSecret
loginMethod = AuthenticationEvent.AuthenticationWithOauth
doAuthentication(TYPE_LOGIN_OAUTH)
}
fun authenticateWithCas(casToken: String) {
credentialToken = casToken
loginMethod = AuthenticationEvent.AuthenticationWithCas
doAuthentication(TYPE_LOGIN_CAS)
}
fun authenticateWithSaml(samlToken: String) {
credentialToken = samlToken
loginMethod = AuthenticationEvent.AuthenticationWithSaml
doAuthentication(TYPE_LOGIN_SAML)
}
fun authenticateWithDeepLink(deepLinkInfo: LoginDeepLinkInfo) {
val serverUrl = deepLinkInfo.url
setupConnectionInfo(serverUrl)
if (deepLinkInfo.userId != null && deepLinkInfo.token != null) {
deepLinkUserId = deepLinkInfo.userId
deepLinkToken = deepLinkInfo.token
tokenRepository.save(serverUrl, Token(deepLinkUserId, deepLinkToken))
loginMethod = AuthenticationEvent.AuthenticationWithDeeplink
doAuthentication(TYPE_LOGIN_DEEP_LINK)
}
}
private fun doAuthentication(loginType: Int) {
launchUI(strategy) {
view.showLoading()
try {
val token = retryIO("login") {
when (loginType) {
TYPE_LOGIN_OAUTH -> client.loginWithOauth(credentialToken, credentialSecret)
TYPE_LOGIN_CAS -> {
delay(3, TimeUnit.SECONDS)
client.loginWithCas(credentialToken)
}
TYPE_LOGIN_SAML -> {
delay(3, TimeUnit.SECONDS)
client.loginWithSaml(credentialToken)
}
TYPE_LOGIN_DEEP_LINK -> {
val myself = client.me() // Just checking if the credentials worked.
if (myself.id == deepLinkUserId) {
Token(deepLinkUserId, deepLinkToken)
} else {
throw RocketChatAuthException("Invalid AuthenticationEvent Deep Link Credentials...")
}
}
else -> {
throw IllegalStateException(
"Expected TYPE_LOGIN_USER_EMAIL, " +
"TYPE_LOGIN_CAS,TYPE_LOGIN_SAML, TYPE_LOGIN_OAUTH or " +
"TYPE_LOGIN_DEEP_LINK"
)
}
}
}
val myself = retryIO("me()") { client.me() }
myself.username?.let { username ->
val user = User(
id = myself.id,
roles = myself.roles,
status = myself.status,
name = myself.name,
emails = myself.emails?.map { Email(it.address ?: "", it.verified) },
username = myself.username,
utcOffset = myself.utcOffset
)
localRepository.saveCurrentUser(url = currentServer, user = user)
saveCurrentServer.save(currentServer)
saveAccount(username)
saveToken(token)
analyticsManager.logLogin(loginMethod, true)
navigator.toChatList()
}.ifNull {
if (loginType == TYPE_LOGIN_OAUTH) {
navigator.toRegisterUsername(token.userId, token.authToken)
}
}
} catch (exception: RocketChatException) {
analyticsManager.logLogin(loginMethod, false)
exception.message?.let {
view.showMessage(it)
}.ifNull {
view.showGenericErrorMessage()
}
} finally {
view.hideLoading()
}
}
}
private fun setupConnectionInfo(serverUrl: String) {
currentServer = serverUrl
client = factory.create(currentServer)
settings = settingsInteractor.get(currentServer)
}
private suspend fun saveAccount(username: String) {
val icon = settings.favicon()?.let {
currentServer.serverLogoUrl(it)
}
val logo = settings.wideTile()?.let {
currentServer.serverLogoUrl(it)
}
val thumb = currentServer.avatarUrl(username)
val account = Account(currentServer, icon, logo, username, thumb)
saveAccountInteractor.save(account)
}
private fun saveToken(token: Token) = tokenRepository.save(currentServer, token)
}
\ No newline at end of file
package chat.rocket.android.authentication.loginoptions.presentation
import chat.rocket.android.core.behaviours.LoadingView
import chat.rocket.android.core.behaviours.MessageView
interface LoginOptionsView : LoadingView, MessageView {
// OAuth accounts.
/**
* Shows the "login by Facebook" view if it is enabled by the server settings.
*
* REMARK: We must set up the Facebook button listener before enabling it
* [setupFacebookButtonListener].
* @see [showAccountsView]
*/
fun enableLoginByFacebook()
/**
* Setups the Facebook button.
*
* @param facebookOauthUrl The Facebook OAuth URL to authenticate with.
* @param state A random string generated by the app, which you'll verify later
* (to protect against forgery attacks).
*/
fun setupFacebookButtonListener(facebookOauthUrl: String, state: String)
/**
* Shows the "login by Github" view if it is enabled by the server settings.
*
* REMARK: We must set up the Github button listener before enabling it
* [setupGithubButtonListener].
* @see [showAccountsView]
*/
fun enableLoginByGithub()
/**
* Setups the Github button.
*
* @param githubUrl The Github OAuth URL to authenticate with.
* @param state A random string generated by the app, which you'll verify later
* (to protect against forgery attacks).
*/
fun setupGithubButtonListener(githubUrl: String, state: String)
/**
* Shows the "login by Google" view if it is enabled by the server settings.
*
* REMARK: We must set up the Google button listener before enabling it
* [setupGoogleButtonListener].
* @see [showAccountsView]
*/
fun enableLoginByGoogle()
/**
* Setups the Google button.
*
* @param googleUrl The Google OAuth URL to authenticate with.
* @param state A random string generated by the app, which you'll verify later
* (to protect against forgery attacks).
*/
fun setupGoogleButtonListener(googleUrl: String, state: String)
/**
* Shows the "login by Linkedin" view if it is enabled by the server settings.
*
* REMARK: We must set up the Linkedin button listener before enabling it
* [setupLinkedinButtonListener].
* @see [showAccountsView]
*/
fun enableLoginByLinkedin()
/**
* Setups the Linkedin button.
*
* @param linkedinUrl The Linkedin OAuth URL to authenticate with.
* @param state A random string generated by the app, which you'll verify later
* (to protect against forgery attacks).
*/
fun setupLinkedinButtonListener(linkedinUrl: String, state: String)
/**
* Shows the "login by Gitlab" view if it is enabled by the server settings.
*
* REMARK: We must set up the Gitlab button listener before enabling it
* [setupGitlabButtonListener].
* @see [showAccountsView]
*/
fun enableLoginByGitlab()
/**
* Setups the Gitlab button.
*
* @param gitlabUrl The Gitlab OAuth URL to authenticate with.
* @param state A random string generated by the app, which you'll verify later
* (to protect against forgery attacks).
*/
fun setupGitlabButtonListener(gitlabUrl: String, state: String)
/**
* Shows the "login by WordPress" view if it is enabled by the server settings.
*
* REMARK: We must set up the Gitlab button listener before enabling it [setupWordpressButtonListener].
*/
fun enableLoginByWordpress()
/**
* Setups the WordPress button when tapped.
*
* @param wordpressUrl The WordPress OAuth URL to authenticate with.
* @param state A random string generated by the app, which you'll verify later (to protect against forgery attacks).
*/
fun setupWordpressButtonListener(wordpressUrl: String, state: String)
// CAS account.
/**
* 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 before showing it [setupCasButtonListener].
* @see [showAccountsView]
*/
fun enableLoginByCas()
/**
* Setups the CAS button.
*
* @param casUrl The CAS URL to authenticate with.
* @param casToken The requested token to be sent to the CAS server.
*/
fun setupCasButtonListener(casUrl: String, casToken: String)
// Custom OAuth account.
/**
* Adds a custom OAuth button in the accounts container.
*
* @customOauthUrl The custom OAuth url.
* @state A random string generated by the app, which you'll verify later
* (to protect against forgery attacks).
* @serviceName The custom OAuth service name.
* @serviceNameColor The custom OAuth service name color (just stylizing).
* @buttonColor The custom OAuth button color (just stylizing).
* @see [showAccountsView]
*/
fun addCustomOauthButton(
customOauthUrl: String,
state: String,
serviceName: String,
serviceNameColor: Int,
buttonColor: Int
)
// SAML account.
/**
* Adds a SAML button in the accounts container.
*
* @samlUrl The SAML url.
* @serviceName The SAML service name.
* @serviceNameColor The SAML service name color (just stylizing).
* @buttonColor The SAML button color (just stylizing).
* @see [showAccountsView]
*/
fun addSamlButton(
samlUrl: String,
samlToken: String,
serviceName: String,
serviceNameColor: Int,
buttonColor: Int
)
/**
* Shows the accounts container view if there is at least a login via
* OAuth/Custom OAuth/CAS/SAML account enabled by the server settings.
*
* REMARK: We must show at maximum *three* accounts views ([enableLoginByFacebook],
* [enableLoginByGithub], [enableLoginByGoogle], [enableLoginByLinkedin], [enableLoginByGitlab],
* [enableLoginByCas], [addCustomOauthButton] or [addSamlButton]) for the accounts container view.
* If the enabled accounts exceeds 3 we must set up the [setupExpandAccountsView] to show the
* remaining view(s).
*/
fun showAccountsView()
/**
* Setups the expand accounts view to show more accounts views (expanding the accounts view
* interface to show the remaining view(s)).
*/
fun setupExpandAccountsView()
/**
* Shows the "login with e-mail" view if it is enabled by the server settings.
*/
fun showLoginWithEmailButton()
/**
* Shows the "Create new account" view if it is enabled by the server settings.
*/
fun showCreateNewAccountButton()
}
package chat.rocket.android.authentication.loginoptions.ui
import android.app.Activity
import android.content.Intent
import android.graphics.PorterDuff
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.LinearLayout
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import chat.rocket.android.R
import chat.rocket.android.analytics.AnalyticsManager
import chat.rocket.android.analytics.event.ScreenViewEvent
import chat.rocket.android.authentication.domain.model.LoginDeepLinkInfo
import chat.rocket.android.authentication.loginoptions.presentation.LoginOptionsPresenter
import chat.rocket.android.authentication.loginoptions.presentation.LoginOptionsView
import chat.rocket.android.authentication.ui.AuthenticationActivity
import chat.rocket.android.util.extensions.clearLightStatusBar
import chat.rocket.android.util.extensions.inflate
import chat.rocket.android.util.extensions.rotateBy
import chat.rocket.android.util.extensions.showToast
import chat.rocket.android.util.extensions.ui
import chat.rocket.android.webview.oauth.ui.INTENT_OAUTH_CREDENTIAL_SECRET
import chat.rocket.android.webview.oauth.ui.INTENT_OAUTH_CREDENTIAL_TOKEN
import chat.rocket.android.webview.oauth.ui.oauthWebViewIntent
import chat.rocket.android.webview.sso.ui.INTENT_SSO_TOKEN
import chat.rocket.android.webview.sso.ui.ssoWebViewIntent
import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.app_bar.*
import kotlinx.android.synthetic.main.fragment_authentication_login_options.*
import javax.inject.Inject
private const val SERVER_NAME = "server_name"
private const val STATE = "state"
private const val FACEBOOK_OAUTH_URL = "facebook_oauth_url"
private const val GITHUB_OAUTH_URL = "github_oauth_url"
private const val GOOGLE_OAUTH_URL = "google_oauth_url"
private const val LINKEDIN_OAUTH_URL = "linkedin_oauth_url"
private const val GITLAB_OAUTH_URL = "gitlab_oauth_url"
private const val WORDPRESS_OAUTH_URL = "wordpress_oauth_url"
private const val CAS_LOGIN_URL = "cas_login_url"
private const val CAS_TOKEN = "cas_token"
private const val CUSTOM_OAUTH_URL = "custom_oauth_url"
private const val CUSTOM_OAUTH_SERVICE_NAME = "custom_oauth_service_name"
private const val CUSTOM_OAUTH_SERVICE_NAME_TEXT_COLOR = "custom_oauth_service_name_text_color"
private const val CUSTOM_OAUTH_SERVICE_BUTTON_COLOR = "custom_oauth_service_button_color"
private const val SAML_URL = "saml_url"
private const val SAML_TOKEN = "saml_token"
private const val SAML_SERVICE_NAME = "saml_service_name"
private const val SAML_SERVICE_NAME_TEXT_COLOR = "saml_service_name_text_color"
private const val SAML_SERVICE_BUTTON_COLOR = "saml_service_button_color"
private const val TOTAL_SOCIAL_ACCOUNTS = "total_social_accounts"
private const val IS_LOGIN_FORM_ENABLED = "is_login_form_enabled"
private const val IS_NEW_ACCOUNT_CREATION_ENABLED = "is_new_account_creation_enabled"
private const val DEEP_LINK_INFO = "deep-link-info"
internal const val REQUEST_CODE_FOR_OAUTH = 1
internal const val REQUEST_CODE_FOR_CAS = 2
internal const val REQUEST_CODE_FOR_SAML = 3
fun newInstance(
serverName: String,
state: String? = null,
facebookOauthUrl: String? = null,
githubOauthUrl: String? = null,
googleOauthUrl: String? = null,
linkedinOauthUrl: String? = null,
gitlabOauthUrl: String? = null,
wordpressOauthUrl: String? = null,
casLoginUrl: String? = null,
casToken: String? = null,
customOauthUrl: String? = null,
customOauthServiceName: String? = null,
customOauthServiceNameTextColor: Int = 0,
customOauthServiceButtonColor: Int = 0,
samlUrl: String? = null,
samlToken: String? = null,
samlServiceName: String? = null,
samlServiceNameTextColor: Int = 0,
samlServiceButtonColor: Int = 0,
totalSocialAccountsEnabled: Int = 0,
isLoginFormEnabled: Boolean,
isNewAccountCreationEnabled: Boolean,
deepLinkInfo: LoginDeepLinkInfo? = null
): Fragment {
return LoginOptionsFragment().apply {
arguments = Bundle(23).apply {
putString(SERVER_NAME, serverName)
putString(STATE, state)
putString(FACEBOOK_OAUTH_URL, facebookOauthUrl)
putString(GITHUB_OAUTH_URL, githubOauthUrl)
putString(GOOGLE_OAUTH_URL, googleOauthUrl)
putString(LINKEDIN_OAUTH_URL, linkedinOauthUrl)
putString(GITLAB_OAUTH_URL, gitlabOauthUrl)
putString(WORDPRESS_OAUTH_URL, wordpressOauthUrl)
putString(CAS_LOGIN_URL, casLoginUrl)
putString(CAS_TOKEN, casToken)
putString(CUSTOM_OAUTH_URL, customOauthUrl)
putString(CUSTOM_OAUTH_SERVICE_NAME, customOauthServiceName)
putInt(CUSTOM_OAUTH_SERVICE_NAME_TEXT_COLOR, customOauthServiceNameTextColor)
putInt(CUSTOM_OAUTH_SERVICE_BUTTON_COLOR, customOauthServiceButtonColor)
putString(SAML_URL, samlUrl)
putString(SAML_TOKEN, samlToken)
putString(SAML_SERVICE_NAME, samlServiceName)
putInt(SAML_SERVICE_NAME_TEXT_COLOR, samlServiceNameTextColor)
putInt(SAML_SERVICE_BUTTON_COLOR, samlServiceButtonColor)
putInt(TOTAL_SOCIAL_ACCOUNTS, totalSocialAccountsEnabled)
putBoolean(IS_LOGIN_FORM_ENABLED, isLoginFormEnabled)
putBoolean(IS_NEW_ACCOUNT_CREATION_ENABLED, isNewAccountCreationEnabled)
putParcelable(DEEP_LINK_INFO, deepLinkInfo)
}
}
}
class LoginOptionsFragment : Fragment(), LoginOptionsView {
@Inject
lateinit var presenter: LoginOptionsPresenter
@Inject
lateinit var analyticsManager: AnalyticsManager
private var serverName: String? = null
private var state: String? = null
private var facebookOauthUrl: String? = null
private var githubOauthUrl: String? = null
private var googleOauthUrl: String? = null
private var linkedinOauthUrl: String? = null
private var gitlabOauthUrl: String? = null
private var wordpressOauthUrl: String? = null
private var casLoginUrl: String? = null
private var casToken: String? = null
private var customOauthUrl: String? = null
private var customOauthServiceName: String? = null
private var customOauthServiceTextColor: Int = 0
private var customOauthServiceButtonColor: Int = 0
private var samlUrl: String? = null
private var samlToken: String? = null
private var samlServiceName: String? = null
private var samlServiceTextColor: Int = 0
private var samlServiceButtonColor: Int = 0
private var totalSocialAccountsEnabled = 0
private var isLoginFormEnabled = false
private var isNewAccountCreationEnabled = false
private var deepLinkInfo: LoginDeepLinkInfo? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
AndroidSupportInjection.inject(this)
val bundle = arguments
if (bundle != null) {
serverName = bundle.getString(SERVER_NAME)
state = bundle.getString(STATE)
facebookOauthUrl = bundle.getString(FACEBOOK_OAUTH_URL)
githubOauthUrl = bundle.getString(GITHUB_OAUTH_URL)
googleOauthUrl = bundle.getString(GOOGLE_OAUTH_URL)
linkedinOauthUrl = bundle.getString(LINKEDIN_OAUTH_URL)
gitlabOauthUrl = bundle.getString(GITLAB_OAUTH_URL)
wordpressOauthUrl = bundle.getString(WORDPRESS_OAUTH_URL)
casLoginUrl = bundle.getString(CAS_LOGIN_URL)
casToken = bundle.getString(CAS_TOKEN)
customOauthUrl = bundle.getString(CUSTOM_OAUTH_URL)
customOauthServiceName = bundle.getString(CUSTOM_OAUTH_SERVICE_NAME)
customOauthServiceTextColor = bundle.getInt(CUSTOM_OAUTH_SERVICE_NAME_TEXT_COLOR)
customOauthServiceButtonColor = bundle.getInt(CUSTOM_OAUTH_SERVICE_BUTTON_COLOR)
samlUrl = bundle.getString(SAML_URL)
samlToken = bundle.getString(SAML_TOKEN)
samlServiceName = bundle.getString(SAML_SERVICE_NAME)
samlServiceTextColor = bundle.getInt(SAML_SERVICE_NAME_TEXT_COLOR)
samlServiceButtonColor = bundle.getInt(SAML_SERVICE_BUTTON_COLOR)
totalSocialAccountsEnabled = bundle.getInt(TOTAL_SOCIAL_ACCOUNTS)
isLoginFormEnabled = bundle.getBoolean(IS_LOGIN_FORM_ENABLED)
isNewAccountCreationEnabled = bundle.getBoolean(IS_NEW_ACCOUNT_CREATION_ENABLED)
deepLinkInfo = bundle.getParcelable(DEEP_LINK_INFO)
}
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? = container?.inflate(R.layout.fragment_authentication_login_options)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setupToolbar()
setupAccounts()
analyticsManager.logScreenView(ScreenViewEvent.LoginOptions)
deepLinkInfo?.let { presenter.authenticateWithDeepLink(it) }
}
private fun setupToolbar() {
with(activity as AuthenticationActivity) {
this.clearLightStatusBar()
toolbar.isVisible = true
toolbar.title = serverName?.replace(getString(R.string.default_protocol), "")
}
}
private fun setupAccounts() {
setupSocialAccounts()
setupCas()
setupCustomOauth()
setupSaml()
setupLoginWithEmailView()
setupCreateNewAccountView()
}
private fun setupSocialAccounts() {
if (facebookOauthUrl != null && state != null) {
setupFacebookButtonListener(facebookOauthUrl.toString(), state.toString())
enableLoginByFacebook()
}
if (githubOauthUrl != null && state != null) {
setupGithubButtonListener(githubOauthUrl.toString(), state.toString())
enableLoginByGithub()
}
if (googleOauthUrl != null && state != null) {
setupGoogleButtonListener(googleOauthUrl.toString(), state.toString())
enableLoginByGoogle()
}
if (linkedinOauthUrl != null && state != null) {
setupLinkedinButtonListener(linkedinOauthUrl.toString(), state.toString())
enableLoginByLinkedin()
}
if (gitlabOauthUrl != null && state != null) {
setupGitlabButtonListener(gitlabOauthUrl.toString(), state.toString())
enableLoginByGitlab()
}
if (wordpressOauthUrl != null && state != null) {
setupWordpressButtonListener(wordpressOauthUrl.toString(), state.toString())
enableLoginByWordpress()
}
if (totalSocialAccountsEnabled > 0) {
showAccountsView()
if (totalSocialAccountsEnabled > 3) {
setupExpandAccountsView()
}
}
}
private fun setupCas() {
if (casLoginUrl != null && casToken != null) {
setupCasButtonListener(casLoginUrl.toString(), casToken.toString())
enableLoginByCas()
}
}
private fun setupCustomOauth() {
if (customOauthUrl != null && state != null && customOauthServiceName != null) {
addCustomOauthButton(
customOauthUrl.toString(),
state.toString(),
customOauthServiceName.toString(),
customOauthServiceTextColor,
customOauthServiceButtonColor
)
}
}
private fun setupSaml() {
if (samlUrl != null && samlToken != null && samlServiceName != null) {
addSamlButton(
samlUrl.toString(),
samlToken.toString(),
samlServiceName.toString(),
samlServiceTextColor,
samlServiceButtonColor
)
}
}
private fun setupLoginWithEmailView() {
if (isLoginFormEnabled) {
showLoginWithEmailButton()
}
}
private fun setupCreateNewAccountView() {
if (isNewAccountCreationEnabled) {
showCreateNewAccountButton()
}
}
// OAuth Accounts.
override fun enableLoginByFacebook() = enableAccountButton(button_facebook)
override fun setupFacebookButtonListener(facebookOauthUrl: String, state: String) =
setupButtonListener(button_facebook, facebookOauthUrl, state, REQUEST_CODE_FOR_OAUTH)
override fun enableLoginByGithub() = enableAccountButton(button_github)
override fun setupGithubButtonListener(githubUrl: String, state: String) =
setupButtonListener(button_github, githubUrl, state, REQUEST_CODE_FOR_OAUTH)
override fun enableLoginByGoogle() = enableAccountButton(button_google)
override fun setupGoogleButtonListener(googleUrl: String, state: String) =
setupButtonListener(button_google, googleUrl, state, REQUEST_CODE_FOR_OAUTH)
override fun enableLoginByLinkedin() = enableAccountButton(button_linkedin)
override fun setupLinkedinButtonListener(linkedinUrl: String, state: String) =
setupButtonListener(button_linkedin, linkedinUrl, state, REQUEST_CODE_FOR_OAUTH)
override fun enableLoginByGitlab() = enableAccountButton(button_gitlab)
override fun setupGitlabButtonListener(gitlabUrl: String, state: String) =
setupButtonListener(button_gitlab, gitlabUrl, state, REQUEST_CODE_FOR_OAUTH)
override fun enableLoginByWordpress() = enableAccountButton(button_wordpress)
override fun setupWordpressButtonListener(wordpressUrl: String, state: String) =
setupButtonListener(button_wordpress, wordpressUrl, state, REQUEST_CODE_FOR_OAUTH)
// CAS service account.
override fun enableLoginByCas() = enableAccountButton(button_cas)
override fun setupCasButtonListener(casUrl: String, casToken: String) =
setupButtonListener(button_cas, casUrl, casToken, REQUEST_CODE_FOR_CAS)
// Custom OAuth account.
override fun addCustomOauthButton(
customOauthUrl: String,
state: String,
serviceName: String,
serviceNameColor: Int,
buttonColor: Int
) {
val button = getCustomServiceButton(serviceName, serviceNameColor, buttonColor)
accounts_container.addView(button)
setupButtonListener(button, customOauthUrl, state, REQUEST_CODE_FOR_OAUTH)
}
// SAML account.
override fun addSamlButton(
samlUrl: String,
samlToken: String,
serviceName: String,
serviceNameColor: Int,
buttonColor: Int
) {
val button = getCustomServiceButton(serviceName, serviceNameColor, buttonColor)
accounts_container.addView(button)
setupButtonListener(button, samlUrl, samlToken, REQUEST_CODE_FOR_SAML)
}
override fun showAccountsView() {
ui {
showThreeAccountsMethods()
accounts_container.isVisible = true
}
}
override fun setupExpandAccountsView() {
ui { _ ->
expand_more_accounts_container.isVisible = true
var isAccountsCollapsed = true
button_expand_collapse_accounts.setOnClickListener {
isAccountsCollapsed = if (isAccountsCollapsed) {
button_expand_collapse_accounts.rotateBy(180F, 400)
expandAccountsView()
false
} else {
button_expand_collapse_accounts.rotateBy(180F, 400)
collapseAccountsView()
true
}
}
}
}
override fun showLoginWithEmailButton() {
ui { _ ->
button_login_with_email.setOnClickListener { presenter.toLoginWithEmail() }
button_login_with_email.isVisible = true
}
}
override fun showCreateNewAccountButton() {
ui { _ ->
button_create_an_account.setOnClickListener { presenter.toCreateAccount() }
button_create_an_account.isVisible = true
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (resultCode == Activity.RESULT_OK && data != null) {
when (requestCode) {
REQUEST_CODE_FOR_OAUTH -> {
presenter.authenticateWithOauth(
data.getStringExtra(INTENT_OAUTH_CREDENTIAL_TOKEN),
data.getStringExtra(INTENT_OAUTH_CREDENTIAL_SECRET)
)
}
REQUEST_CODE_FOR_CAS -> presenter.authenticateWithCas(
data.getStringExtra(INTENT_SSO_TOKEN)
)
REQUEST_CODE_FOR_SAML -> data.apply {
presenter.authenticateWithSaml(getStringExtra(INTENT_SSO_TOKEN))
}
}
}
}
override fun showLoading() {
ui {
view_loading.isVisible = true
}
}
override fun hideLoading() {
ui {
view_loading.isVisible = false
}
}
override fun showMessage(resId: Int) {
ui {
showToast(resId)
}
}
override fun showMessage(message: String) {
ui {
showToast(message)
}
}
override fun showGenericErrorMessage() {
showMessage(R.string.msg_generic_error)
}
private fun enableAccountButton(button: Button) {
ui {
button.isClickable = true
}
}
private fun setupButtonListener(
button: Button,
accountUrl: String,
argument: String,
requestCode: Int
) {
ui { activity ->
button.setOnClickListener {
when (requestCode) {
REQUEST_CODE_FOR_OAUTH -> startActivityForResult(
activity.oauthWebViewIntent(accountUrl, argument), REQUEST_CODE_FOR_OAUTH
)
REQUEST_CODE_FOR_CAS -> startActivityForResult(
activity.ssoWebViewIntent(accountUrl, argument), REQUEST_CODE_FOR_CAS
)
REQUEST_CODE_FOR_SAML -> startActivityForResult(
activity.ssoWebViewIntent(accountUrl, argument), REQUEST_CODE_FOR_SAML
)
}
activity.overridePendingTransition(R.anim.slide_up, R.anim.hold)
}
}
}
/**
* Gets a stylized custom service button.
*/
private fun getCustomServiceButton(
buttonText: String,
buttonTextColor: Int,
buttonBgColor: Int
): Button {
val params: LinearLayout.LayoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT
)
val marginTop = resources.getDimensionPixelSize(R.dimen.button_account_margin_top)
params.setMargins(0, marginTop, 0, 0)
val button = Button(context)
button.layoutParams = params
button.text = buttonText
button.setTextColor(buttonTextColor)
button.background.setColorFilter(buttonBgColor, PorterDuff.Mode.MULTIPLY)
return button
}
private fun showThreeAccountsMethods() {
(0..accounts_container.childCount)
.mapNotNull { accounts_container.getChildAt(it) as? Button }
.filter { it.isClickable }
.take(3)
.forEach { it.isVisible = true }
}
private fun expandAccountsView() {
(0..accounts_container.childCount)
.mapNotNull { accounts_container.getChildAt(it) as? Button }
.filter { it.isClickable && !it.isVisible }
.forEach { it.isVisible = true }
}
private fun collapseAccountsView() {
(0..accounts_container.childCount)
.mapNotNull { accounts_container.getChildAt(it) as? Button }
.filter { it.isClickable && it.isVisible }
.drop(3)
.forEach { it.isVisible = false }
}
}
package chat.rocket.android.authentication.onboarding.di
import androidx.lifecycle.LifecycleOwner
import chat.rocket.android.authentication.onboarding.presentation.OnBoardingView
import chat.rocket.android.authentication.onboarding.ui.OnBoardingFragment
import chat.rocket.android.dagger.scope.PerFragment
import dagger.Module
import dagger.Provides
@Module
class OnBoardingFragmentModule {
@Provides
@PerFragment
fun onBoardingView(frag: OnBoardingFragment): OnBoardingView = frag
@Provides
@PerFragment
fun provideLifecycleOwner(frag: OnBoardingFragment): LifecycleOwner = frag
}
\ No newline at end of file
package chat.rocket.android.authentication.onboarding.di
import chat.rocket.android.authentication.onboarding.ui.OnBoardingFragment
import chat.rocket.android.dagger.scope.PerFragment
import dagger.Module
import dagger.android.ContributesAndroidInjector
@Module
abstract class OnBoardingFragmentProvider {
@ContributesAndroidInjector(modules = [OnBoardingFragmentModule::class])
@PerFragment
abstract fun provideOnBoardingFragment(): OnBoardingFragment
}
\ No newline at end of file
package chat.rocket.android.authentication.onboarding.presentation
import chat.rocket.android.authentication.presentation.AuthenticationNavigator
import chat.rocket.android.core.behaviours.showMessage
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.server.domain.GetAccountsInteractor
import chat.rocket.android.server.domain.GetSettingsInteractor
import chat.rocket.android.server.domain.RefreshSettingsInteractor
import chat.rocket.android.server.domain.SaveConnectingServerInteractor
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.server.presentation.CheckServerPresenter
import chat.rocket.android.util.extension.launchUI
import kotlinx.coroutines.experimental.DefaultDispatcher
import kotlinx.coroutines.experimental.withContext
import javax.inject.Inject
class OnBoardingPresenter @Inject constructor(
private val view: OnBoardingView,
private val strategy: CancelStrategy,
private val navigator: AuthenticationNavigator,
private val serverInteractor: SaveConnectingServerInteractor,
private val refreshSettingsInteractor: RefreshSettingsInteractor,
private val getAccountsInteractor: GetAccountsInteractor,
val settingsInteractor: GetSettingsInteractor,
val factory: RocketChatClientFactory
) : CheckServerPresenter(strategy, factory, settingsInteractor) {
fun toSignInToYourServer() = navigator.toSignInToYourServer()
fun connectToCommunityServer(communityServerUrl: String) {
connectToServer(communityServerUrl) {
if (totalSocialAccountsEnabled == 0 && !isNewAccountCreationEnabled) {
navigator.toLogin(communityServerUrl)
} else {
navigator.toLoginOptions(
communityServerUrl,
state,
facebookOauthUrl,
githubOauthUrl,
googleOauthUrl,
linkedinOauthUrl,
gitlabOauthUrl,
wordpressOauthUrl,
casLoginUrl,
casToken,
customOauthUrl,
customOauthServiceName,
customOauthServiceNameTextColor,
customOauthServiceButtonColor,
samlUrl,
samlToken,
samlServiceName,
samlServiceNameTextColor,
samlServiceButtonColor,
totalSocialAccountsEnabled,
isLoginFormEnabled,
isNewAccountCreationEnabled
)
}
}
}
fun toCreateANewServer(createServerUrl: String) = navigator.toWebPage(createServerUrl)
private fun connectToServer(serverUrl: String, block: () -> Unit) {
launchUI(strategy) {
// Check if we already have an account for this server...
val account = getAccountsInteractor.get().firstOrNull { it.serverUrl == serverUrl }
if (account != null) {
navigator.toChatList(serverUrl)
return@launchUI
}
view.showLoading()
try {
withContext(DefaultDispatcher) {
refreshSettingsInteractor.refresh(serverUrl)
setupConnectionInfo(serverUrl)
// preparing next fragment before showing it
checkEnabledAccounts(serverUrl)
checkIfLoginFormIsEnabled()
checkIfCreateNewAccountIsEnabled()
serverInteractor.save(serverUrl)
block()
}
} catch (ex: Exception) {
view.showMessage(ex)
} finally {
view.hideLoading()
}
}
}
}
\ No newline at end of file
package chat.rocket.android.authentication.onboarding.presentation
import chat.rocket.android.core.behaviours.LoadingView
import chat.rocket.android.core.behaviours.MessageView
interface OnBoardingView : LoadingView, MessageView
\ No newline at end of file
package chat.rocket.android.authentication.onboarding.ui
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import chat.rocket.android.R
import chat.rocket.android.analytics.AnalyticsManager
import chat.rocket.android.analytics.event.ScreenViewEvent
import chat.rocket.android.authentication.onboarding.presentation.OnBoardingPresenter
import chat.rocket.android.authentication.onboarding.presentation.OnBoardingView
import chat.rocket.android.authentication.ui.AuthenticationActivity
import chat.rocket.android.util.extensions.inflate
import chat.rocket.android.util.extensions.setLightStatusBar
import chat.rocket.android.util.extensions.showToast
import chat.rocket.android.util.extensions.ui
import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.app_bar.*
import kotlinx.android.synthetic.main.fragment_authentication_on_boarding.*
import javax.inject.Inject
fun newInstance() = OnBoardingFragment()
class OnBoardingFragment : Fragment(), OnBoardingView {
@Inject
lateinit var presenter: OnBoardingPresenter
@Inject
lateinit var analyticsManager: AnalyticsManager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
AndroidSupportInjection.inject(this)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? = container?.inflate(R.layout.fragment_authentication_on_boarding)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setupToolbar()
setupOnClickListener()
analyticsManager.logScreenView(ScreenViewEvent.OnBoarding)
}
private fun setupToolbar() {
with(activity as AuthenticationActivity) {
view?.let { this.setLightStatusBar(it) }
toolbar.isVisible = false
}
}
private fun setupOnClickListener() {
connect_with_a_server_container.setOnClickListener { signInToYourServer() }
join_community_container.setOnClickListener { joinInTheCommunity() }
create_server_container.setOnClickListener { createANewServer() }
}
override fun showLoading() {
ui {
view_loading.isVisible = true
}
}
override fun hideLoading() {
ui {
view_loading.isVisible = false
}
}
override fun showMessage(resId: Int) {
ui {
showToast(resId)
}
}
override fun showMessage(message: String) {
ui {
showToast(message)
}
}
override fun showGenericErrorMessage() = showMessage(getString(R.string.msg_generic_error))
private fun signInToYourServer() = ui {
presenter.toSignInToYourServer()
}
private fun joinInTheCommunity() = ui {
presenter.connectToCommunityServer(
getString(R.string.default_protocol) + getString(R.string.community_server_url)
)
}
private fun createANewServer() = ui {
presenter.toCreateANewServer(
getString(R.string.default_protocol) + getString(R.string.create_server_url)
)
}
}
......@@ -2,19 +2,9 @@ package chat.rocket.android.authentication.presentation
import android.content.Intent
import chat.rocket.android.R
import chat.rocket.android.analytics.event.ScreenViewEvent
import chat.rocket.android.authentication.domain.model.LoginDeepLinkInfo
import chat.rocket.android.authentication.login.ui.LoginFragment
import chat.rocket.android.authentication.login.ui.TAG_LOGIN_FRAGMENT
import chat.rocket.android.authentication.registerusername.ui.RegisterUsernameFragment
import chat.rocket.android.authentication.registerusername.ui.TAG_REGISTER_USERNAME_FRAGMENT
import chat.rocket.android.authentication.resetpassword.ui.ResetPasswordFragment
import chat.rocket.android.authentication.resetpassword.ui.TAG_RESET_PASSWORD_FRAGMENT
import chat.rocket.android.authentication.signup.ui.SignupFragment
import chat.rocket.android.authentication.signup.ui.TAG_SIGNUP_FRAGMENT
import chat.rocket.android.authentication.twofactor.ui.TAG_TWO_FA_FRAGMENT
import chat.rocket.android.authentication.twofactor.ui.TwoFAFragment
import chat.rocket.android.authentication.ui.AuthenticationActivity
import chat.rocket.android.authentication.ui.newServerIntent
import chat.rocket.android.main.ui.MainActivity
import chat.rocket.android.server.ui.changeServerIntent
import chat.rocket.android.util.extensions.addFragmentBackStack
......@@ -23,51 +13,114 @@ import chat.rocket.android.webview.ui.webViewIntent
class AuthenticationNavigator(internal val activity: AuthenticationActivity) {
fun toLogin() {
activity.addFragmentBackStack(TAG_LOGIN_FRAGMENT, R.id.fragment_container) {
LoginFragment.newInstance()
fun toSignInToYourServer() {
activity.addFragmentBackStack(ScreenViewEvent.Server.screenName, R.id.fragment_container) {
chat.rocket.android.authentication.server.ui.newInstance()
}
}
fun toLogin(deepLinkInfo: LoginDeepLinkInfo) {
activity.addFragmentBackStack(TAG_LOGIN_FRAGMENT, R.id.fragment_container) {
LoginFragment.newInstance(deepLinkInfo)
fun toLoginOptions(
serverUrl: String,
state: String? = null,
facebookOauthUrl: String? = null,
githubOauthUrl: String? = null,
googleOauthUrl: String? = null,
linkedinOauthUrl: String? = null,
gitlabOauthUrl: String? = null,
wordpressOauthUrl: String? = null,
casLoginUrl: String? = null,
casToken: String? = null,
customOauthUrl: String? = null,
customOauthServiceName: String? = null,
customOauthServiceNameTextColor: Int = 0,
customOauthServiceButtonColor: Int = 0,
samlUrl: String? = null,
samlToken: String? = null,
samlServiceName: String? = null,
samlServiceNameTextColor: Int = 0,
samlServiceButtonColor: Int = 0,
totalSocialAccountsEnabled: Int = 0,
isLoginFormEnabled: Boolean = true,
isNewAccountCreationEnabled: Boolean = true,
deepLinkInfo: LoginDeepLinkInfo? = null
) {
activity.addFragmentBackStack(
ScreenViewEvent.LoginOptions.screenName,
R.id.fragment_container
) {
chat.rocket.android.authentication.loginoptions.ui.newInstance(
serverUrl,
state,
facebookOauthUrl,
githubOauthUrl,
googleOauthUrl,
linkedinOauthUrl,
gitlabOauthUrl,
wordpressOauthUrl,
casLoginUrl,
casToken,
customOauthUrl,
customOauthServiceName,
customOauthServiceNameTextColor,
customOauthServiceButtonColor,
samlUrl,
samlToken,
samlServiceName,
samlServiceNameTextColor,
samlServiceButtonColor,
totalSocialAccountsEnabled,
isLoginFormEnabled,
isNewAccountCreationEnabled,
deepLinkInfo
)
}
}
fun toPreviousView() {
activity.toPreviousView()
fun toTwoFA(username: String, password: String) {
activity.addFragmentBackStack(ScreenViewEvent.TwoFa.screenName, R.id.fragment_container) {
chat.rocket.android.authentication.twofactor.ui.newInstance(username, password)
}
}
fun toTwoFA(username: String, password: String) {
activity.addFragmentBackStack(TAG_TWO_FA_FRAGMENT, R.id.fragment_container) {
TwoFAFragment.newInstance(username, password)
fun toCreateAccount() {
activity.addFragmentBackStack(ScreenViewEvent.SignUp.screenName, R.id.fragment_container) {
chat.rocket.android.authentication.signup.ui.newInstance()
}
}
fun toSignUp() {
activity.addFragmentBackStack(TAG_SIGNUP_FRAGMENT, R.id.fragment_container) {
SignupFragment.newInstance()
fun toLogin(serverUrl: String) {
activity.addFragmentBackStack(ScreenViewEvent.Login.screenName, R.id.fragment_container) {
chat.rocket.android.authentication.login.ui.newInstance(serverUrl)
}
}
fun toForgotPassword() {
activity.addFragmentBackStack(TAG_RESET_PASSWORD_FRAGMENT, R.id.fragment_container) {
ResetPasswordFragment.newInstance()
activity.addFragmentBackStack(
ScreenViewEvent.ResetPassword.screenName,
R.id.fragment_container
) {
chat.rocket.android.authentication.resetpassword.ui.newInstance()
}
}
fun toWebPage(url: String) {
activity.startActivity(activity.webViewIntent(url))
activity.overridePendingTransition(R.anim.slide_up, R.anim.hold)
fun toPreviousView() {
activity.toPreviousView()
}
fun toRegisterUsername(userId: String, authToken: String) {
activity.addFragmentBackStack(TAG_REGISTER_USERNAME_FRAGMENT, R.id.fragment_container) {
RegisterUsernameFragment.newInstance(userId, authToken)
activity.addFragmentBackStack(
ScreenViewEvent.RegisterUsername.screenName,
R.id.fragment_container
) {
chat.rocket.android.authentication.registerusername.ui.newInstance(userId, authToken)
}
}
fun toWebPage(url: String, toolbarTitle: String? = null) {
activity.startActivity(activity.webViewIntent(url, toolbarTitle))
activity.overridePendingTransition(R.anim.slide_up, R.anim.hold)
}
fun toChatList() {
activity.startActivity(Intent(activity, MainActivity::class.java))
activity.finish()
......@@ -77,9 +130,4 @@ class AuthenticationNavigator(internal val activity: AuthenticationActivity) {
activity.startActivity(activity.changeServerIntent(serverUrl))
activity.finish()
}
fun toServerScreen() {
activity.startActivity(activity.newServerIntent())
activity.finish()
}
}
\ No newline at end of file
}
package chat.rocket.android.authentication.presentation
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.server.domain.GetAccountInteractor
import chat.rocket.android.server.domain.GetConnectingServerInteractor
import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.domain.SettingsRepository
import chat.rocket.android.server.domain.TokenRepository
import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.extensions.privacyPolicyUrl
import chat.rocket.android.util.extensions.termsOfServiceUrl
import javax.inject.Inject
class AuthenticationPresenter @Inject constructor(
private val strategy: CancelStrategy,
private val navigator: AuthenticationNavigator,
private val getCurrentServerInteractor: GetCurrentServerInteractor,
private val getAccountInteractor: GetAccountInteractor,
private val settingsRepository: SettingsRepository,
private val localRepository: LocalRepository,
private val tokenRepository: TokenRepository
private val tokenRepository: TokenRepository,
private val serverInteractor: GetConnectingServerInteractor
) {
suspend fun loadCredentials(newServer: Boolean, callback: (authenticated: Boolean) -> Unit) {
val currentServer = getCurrentServerInteractor.get()
val serverToken = currentServer?.let { tokenRepository.get(currentServer) }
val settings = currentServer?.let { settingsRepository.get(currentServer) }
val account = currentServer?.let { getAccountInteractor.get(currentServer) }
account?.let {
localRepository.save(LocalRepository.CURRENT_USERNAME_KEY, account.userName)
}
if (newServer || currentServer == null || serverToken == null || settings == null || account?.userName == null) {
callback(false)
} else {
callback(true)
navigator.toChatList()
fun loadCredentials(newServer: Boolean, callback: (isAuthenticated: Boolean) -> Unit) {
launchUI(strategy) {
val currentServer = getCurrentServerInteractor.get()
val serverToken = currentServer?.let { tokenRepository.get(currentServer) }
val settings = currentServer?.let { settingsRepository.get(currentServer) }
val account = currentServer?.let { getAccountInteractor.get(currentServer) }
account?.let {
localRepository.save(LocalRepository.CURRENT_USERNAME_KEY, account.userName)
}
if (newServer || currentServer == null ||
serverToken == null ||
settings == null ||
account?.userName == null
) {
callback(false)
} else {
callback(true)
}
}
}
fun termsOfService(toolbarTitle: String) =
serverInteractor.get()?.let { navigator.toWebPage(it.termsOfServiceUrl(), toolbarTitle) }
fun privacyPolicy(toolbarTitle: String) =
serverInteractor.get()?.let { navigator.toWebPage(it.privacyPolicyUrl(), toolbarTitle) }
fun toChatList() = navigator.toChatList()
}
\ No newline at end of file
......@@ -3,34 +3,18 @@ package chat.rocket.android.authentication.registerusername.di
import androidx.lifecycle.LifecycleOwner
import chat.rocket.android.authentication.registerusername.presentation.RegisterUsernameView
import chat.rocket.android.authentication.registerusername.ui.RegisterUsernameFragment
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.dagger.scope.PerFragment
import dagger.Module
import dagger.Provides
import kotlinx.coroutines.experimental.Job
@Module
class RegisterUsernameFragmentModule {
@Provides
@PerFragment
fun provideJob() = Job()
fun registerUsernameView(frag: RegisterUsernameFragment): RegisterUsernameView = frag
@Provides
@PerFragment
fun registerUsernameView(frag: RegisterUsernameFragment): RegisterUsernameView {
return frag
}
@Provides
@PerFragment
fun provideLifecycleOwner(frag: RegisterUsernameFragment): LifecycleOwner {
return frag
}
@Provides
@PerFragment
fun provideCancelStrategy(owner: LifecycleOwner, jobs: Job): CancelStrategy {
return CancelStrategy(owner, jobs)
}
fun provideLifecycleOwner(frag: RegisterUsernameFragment): LifecycleOwner = frag
}
\ No newline at end of file
......@@ -30,48 +30,44 @@ class RegisterUsernamePresenter @Inject constructor(
private val strategy: CancelStrategy,
private val navigator: AuthenticationNavigator,
private val tokenRepository: TokenRepository,
factory: RocketChatClientFactory,
private val saveAccountInteractor: SaveAccountInteractor,
private val analyticsManager: AnalyticsManager,
serverInteractor: GetConnectingServerInteractor,
private val saveCurrentServer: SaveCurrentServerInteractor,
settingsInteractor: GetSettingsInteractor
val serverInteractor: GetConnectingServerInteractor,
val factory: RocketChatClientFactory,
val settingsInteractor: GetSettingsInteractor
) {
private val currentServer = serverInteractor.get()!!
private val client: RocketChatClient = factory.create(currentServer)
private var settings: PublicSettings = settingsInteractor.get(serverInteractor.get()!!)
fun registerUsername(username: String, userId: String, authToken: String) {
if (username.isBlank()) {
view.alertBlankUsername()
} else {
launchUI(strategy) {
view.showLoading()
try {
val me = retryIO("updateOwnBasicInformation(username = $username)") {
client.updateOwnBasicInformation(username = username)
}
val registeredUsername = me.username
if (registeredUsername != null) {
saveAccount(registeredUsername)
saveCurrentServer.save(currentServer)
tokenRepository.save(currentServer, Token(userId, authToken))
analyticsManager.logSignUp(
AuthenticationEvent.AuthenticationWithOauth,
true
)
navigator.toChatList()
}
} catch (exception: RocketChatException) {
analyticsManager.logSignUp(AuthenticationEvent.AuthenticationWithOauth, false)
exception.message?.let {
view.showMessage(it)
}.ifNull {
view.showGenericErrorMessage()
}
} finally {
view.hideLoading()
launchUI(strategy) {
view.showLoading()
try {
val me = retryIO("updateOwnBasicInformation(username = $username)") {
client.updateOwnBasicInformation(username = username)
}
val registeredUsername = me.username
if (registeredUsername != null) {
saveAccount(registeredUsername)
saveCurrentServer.save(currentServer)
tokenRepository.save(currentServer, Token(userId, authToken))
analyticsManager.logSignUp(
AuthenticationEvent.AuthenticationWithOauth,
true
)
navigator.toChatList()
}
} catch (exception: RocketChatException) {
analyticsManager.logSignUp(AuthenticationEvent.AuthenticationWithOauth, false)
exception.message?.let {
view.showMessage(it)
}.ifNull {
view.showGenericErrorMessage()
}
} finally {
view.hideLoading()
}
}
}
......
......@@ -6,7 +6,12 @@ import chat.rocket.android.core.behaviours.MessageView
interface RegisterUsernameView : LoadingView, MessageView {
/**
* Alerts the user about a blank username.
* Enables the button to set the username if the user entered at least one character.
*/
fun alertBlankUsername()
fun enableButtonUseThisUsername()
/**
* Disables the button to set the username when there is no character entered by the user.
*/
fun disableButtonUseThisUsername()
}
\ No newline at end of file
......@@ -6,25 +6,39 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.core.view.ViewCompat
import androidx.fragment.app.Fragment
import chat.rocket.android.R
import chat.rocket.android.analytics.AnalyticsManager
import chat.rocket.android.analytics.event.ScreenViewEvent
import chat.rocket.android.authentication.registerusername.presentation.RegisterUsernamePresenter
import chat.rocket.android.authentication.registerusername.presentation.RegisterUsernameView
import chat.rocket.android.util.extension.asObservable
import chat.rocket.android.util.extensions.inflate
import chat.rocket.android.util.extensions.setVisible
import chat.rocket.android.util.extensions.shake
import chat.rocket.android.util.extensions.showKeyboard
import chat.rocket.android.util.extensions.showToast
import chat.rocket.android.util.extensions.textContent
import chat.rocket.android.util.extensions.ui
import chat.rocket.android.util.extensions.vibrateSmartPhone
import dagger.android.support.AndroidSupportInjection
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import kotlinx.android.synthetic.main.fragment_authentication_register_username.*
import java.util.concurrent.TimeUnit
import javax.inject.Inject
internal const val TAG_REGISTER_USERNAME_FRAGMENT = "RegisterUsernameFragment"
private const val BUNDLE_USER_ID = "user_id"
private const val BUNDLE_AUTH_TOKEN = "auth_token"
fun newInstance(userId: String, authToken: String): Fragment {
return RegisterUsernameFragment().apply {
arguments = Bundle(2).apply {
putString(BUNDLE_USER_ID, userId)
putString(BUNDLE_AUTH_TOKEN, authToken)
}
}
}
class RegisterUsernameFragment : Fragment(), RegisterUsernameView {
@Inject
......@@ -33,26 +47,19 @@ class RegisterUsernameFragment : Fragment(), RegisterUsernameView {
lateinit var analyticsManager: AnalyticsManager
private lateinit var userId: String
private lateinit var authToken: String
companion object {
private const val USER_ID = "user_id"
private const val AUTH_TOKEN = "auth_token"
fun newInstance(userId: String, authToken: String) = RegisterUsernameFragment().apply {
arguments = Bundle(1).apply {
putString(USER_ID, userId)
putString(AUTH_TOKEN, authToken)
}
}
}
private lateinit var usernameDisposable: Disposable
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
AndroidSupportInjection.inject(this)
// TODO - research a better way to initialize parameters on fragments.
userId = arguments?.getString(USER_ID) ?: ""
authToken = arguments?.getString(AUTH_TOKEN) ?: ""
val bundle = arguments
if (bundle != null) {
userId = bundle.getString(BUNDLE_USER_ID)
authToken = bundle.getString(BUNDLE_AUTH_TOKEN)
} else {
requireNotNull(bundle) { "no arguments supplied when the fragment was instantiated" }
}
}
override fun onCreateView(
......@@ -74,14 +81,32 @@ class RegisterUsernameFragment : Fragment(), RegisterUsernameView {
}
setupOnClickListener()
subscribeEditText()
analyticsManager.logScreenView(ScreenViewEvent.RegisterUsername)
}
override fun alertBlankUsername() {
ui {
vibrateSmartPhone()
text_username.shake()
override fun onDestroyView() {
super.onDestroyView()
unsubscribeEditText()
}
override fun enableButtonUseThisUsername() {
context?.let {
ViewCompat.setBackgroundTintList(
button_use_this_username, ContextCompat.getColorStateList(it, R.color.colorAccent)
)
button_use_this_username.isEnabled = true
}
}
override fun disableButtonUseThisUsername() {
context?.let {
ViewCompat.setBackgroundTintList(
button_use_this_username,
ContextCompat.getColorStateList(it, R.color.colorAuthenticationButtonDisabled)
)
button_use_this_username.isEnabled = false
}
}
......@@ -117,7 +142,7 @@ class RegisterUsernameFragment : Fragment(), RegisterUsernameView {
private fun tintEditTextDrawableStart() {
ui {
val atDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_at_black_24dp, it)
val atDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_at_black_20dp, it)
DrawableHelper.wrapDrawable(atDrawable)
DrawableHelper.tintDrawable(atDrawable, it, R.color.colorDrawableTintGrey)
DrawableHelper.compoundDrawable(text_username, atDrawable)
......@@ -125,12 +150,12 @@ class RegisterUsernameFragment : Fragment(), RegisterUsernameView {
}
private fun enableUserInput() {
button_use_this_username.isEnabled = true
enableButtonUseThisUsername()
text_username.isEnabled = true
}
private fun disableUserInput() {
button_use_this_username.isEnabled = false
disableButtonUseThisUsername()
text_username.isEnabled = true
}
......@@ -139,4 +164,18 @@ class RegisterUsernameFragment : Fragment(), RegisterUsernameView {
presenter.registerUsername(text_username.textContent, userId, authToken)
}
}
private fun subscribeEditText() {
usernameDisposable = text_username.asObservable()
.debounce(300, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread())
.subscribe {
if (it.isNotBlank()) {
enableButtonUseThisUsername()
} else {
disableButtonUseThisUsername()
}
}
}
private fun unsubscribeEditText() = usernameDisposable.dispose()
}
\ No newline at end of file
......@@ -3,34 +3,18 @@ package chat.rocket.android.authentication.resetpassword.di
import androidx.lifecycle.LifecycleOwner
import chat.rocket.android.authentication.resetpassword.presentation.ResetPasswordView
import chat.rocket.android.authentication.resetpassword.ui.ResetPasswordFragment
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.dagger.scope.PerFragment
import dagger.Module
import dagger.Provides
import kotlinx.coroutines.experimental.Job
@Module
class ResetPasswordFragmentModule {
@Provides
@PerFragment
fun provideJob() = Job()
fun resetPasswordView(frag: ResetPasswordFragment): ResetPasswordView = frag
@Provides
@PerFragment
fun resetPasswordView(frag: ResetPasswordFragment): ResetPasswordView {
return frag
}
@Provides
@PerFragment
fun provideLifecycleOwner(frag: ResetPasswordFragment): LifecycleOwner {
return frag
}
@Provides
@PerFragment
fun provideCancelStrategy(owner: LifecycleOwner, jobs: Job): CancelStrategy {
return CancelStrategy(owner, jobs)
}
fun provideLifecycleOwner(frag: ResetPasswordFragment): LifecycleOwner = frag
}
\ No newline at end of file
......@@ -4,7 +4,6 @@ import chat.rocket.android.authentication.presentation.AuthenticationNavigator
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.server.domain.GetConnectingServerInteractor
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.extensions.isEmail
import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.retryIO
import chat.rocket.common.RocketChatException
......@@ -25,30 +24,26 @@ class ResetPasswordPresenter @Inject constructor(
private val client: RocketChatClient = factory.create(currentServer)
fun resetPassword(email: String) {
when {
email.isBlank() -> view.alertBlankEmail()
!email.isEmail() -> view.alertInvalidEmail()
else -> launchUI(strategy) {
view.showLoading()
try {
retryIO("forgotPassword(email = $email)") {
client.forgotPassword(email)
}
navigator.toPreviousView()
view.emailSent()
} catch (exception: RocketChatException) {
if (exception is RocketChatInvalidResponseException) {
view.updateYourServerVersion()
} else {
exception.message?.let {
view.showMessage(it)
}.ifNull {
view.showGenericErrorMessage()
}
launchUI(strategy) {
view.showLoading()
try {
retryIO("forgotPassword(email = $email)") {
client.forgotPassword(email)
}
navigator.toPreviousView()
view.emailSent()
} catch (exception: RocketChatException) {
if (exception is RocketChatInvalidResponseException) {
view.updateYourServerVersion()
} else {
exception.message?.let {
view.showMessage(it)
}.ifNull {
view.showGenericErrorMessage()
}
} finally {
view.hideLoading()
}
} finally {
view.hideLoading()
}
}
}
......
......@@ -6,22 +6,22 @@ import chat.rocket.android.core.behaviours.MessageView
interface ResetPasswordView : LoadingView, MessageView {
/**
* Alerts the user about a blank email.
* Shows a successful email sent message.
*/
fun alertBlankEmail()
fun emailSent()
/**
* Alerts the user about a invalid email.
* Shows a message to update the server version in order to use an app feature.
*/
fun alertInvalidEmail()
fun updateYourServerVersion()
/**
* Shows a successful email sent message.
* Enables the button to reset the password when the user inputs a valid email address.
*/
fun emailSent()
fun enableButtonConnect()
/**
* Shows a message to update the server version in order to use an app feature.
* Disables the button to reset the password when the user entered an invalid email address
*/
fun updateYourServerVersion()
fun disableButtonConnect()
}
\ No newline at end of file
package chat.rocket.android.authentication.resetpassword.ui
import DrawableHelper
import android.os.Build
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.core.content.ContextCompat
import androidx.core.view.ViewCompat
import androidx.fragment.app.Fragment
import chat.rocket.android.R
import chat.rocket.android.analytics.AnalyticsManager
import chat.rocket.android.analytics.event.ScreenViewEvent
import chat.rocket.android.authentication.resetpassword.presentation.ResetPasswordPresenter
import chat.rocket.android.authentication.resetpassword.presentation.ResetPasswordView
import chat.rocket.android.util.extension.asObservable
import chat.rocket.android.util.extensions.inflate
import chat.rocket.android.util.extensions.isEmail
import chat.rocket.android.util.extensions.setVisible
import chat.rocket.android.util.extensions.shake
import chat.rocket.android.util.extensions.showKeyboard
import chat.rocket.android.util.extensions.showToast
import chat.rocket.android.util.extensions.textContent
import chat.rocket.android.util.extensions.ui
import chat.rocket.android.util.extensions.vibrateSmartPhone
import dagger.android.support.AndroidSupportInjection
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import kotlinx.android.synthetic.main.fragment_authentication_reset_password.*
import java.util.concurrent.TimeUnit
import javax.inject.Inject
internal const val TAG_RESET_PASSWORD_FRAGMENT = "ResetPasswordFragment"
fun newInstance(): Fragment = ResetPasswordFragment()
class ResetPasswordFragment : Fragment(), ResetPasswordView {
@Inject
lateinit var presenter: ResetPasswordPresenter
@Inject
lateinit var analyticsManager: AnalyticsManager
private lateinit var emailAddressDisposable: Disposable
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
......@@ -52,34 +55,39 @@ class ResetPasswordFragment : Fragment(), ResetPasswordView {
showKeyboard(text_email)
}
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) {
tintEditTextDrawableStart()
}
setupOnClickListener()
subscribeEditText()
analyticsManager.logScreenView(ScreenViewEvent.ResetPassword)
}
override fun alertBlankEmail() {
ui {
vibrateShakeAndRequestFocusForTextEmail()
}
override fun onDestroyView() {
super.onDestroyView()
unsubscribeEditText()
}
override fun alertInvalidEmail() {
ui {
vibrateShakeAndRequestFocusForTextEmail()
showMessage(R.string.msg_invalid_email)
}
}
override fun emailSent() = showMessage(R.string.msg_check_your_email_to_reset_your_password)
override fun updateYourServerVersion() =
showMessage(R.string.msg_update_app_version_in_order_to_continue)
override fun emailSent() {
showToast(R.string.msg_check_your_email_to_reset_your_password, Toast.LENGTH_LONG)
override fun enableButtonConnect() {
context?.let {
ViewCompat.setBackgroundTintList(
button_reset_password, ContextCompat.getColorStateList(it, R.color.colorAccent)
)
button_reset_password.isEnabled = true
}
}
override fun updateYourServerVersion() {
showMessage(R.string.msg_update_app_version_in_order_to_continue)
override fun disableButtonConnect() {
context?.let {
ViewCompat.setBackgroundTintList(
button_reset_password,
ContextCompat.getColorStateList(it, R.color.colorAuthenticationButtonDisabled)
)
button_reset_password.isEnabled = false
}
}
override fun showLoading() {
......@@ -108,42 +116,35 @@ class ResetPasswordFragment : Fragment(), ResetPasswordView {
}
}
override fun showGenericErrorMessage() {
showMessage(getString(R.string.msg_generic_error))
}
private fun tintEditTextDrawableStart() {
ui {
val emailDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_email_black_24dp, it)
DrawableHelper.wrapDrawable(emailDrawable)
DrawableHelper.tintDrawable(emailDrawable, it, R.color.colorDrawableTintGrey)
DrawableHelper.compoundDrawable(text_email, emailDrawable)
}
}
override fun showGenericErrorMessage() = showMessage(getString(R.string.msg_generic_error))
private fun enableUserInput() {
button_reset_password.isEnabled = true
enableButtonConnect()
text_email.isEnabled = true
}
private fun disableUserInput() {
button_reset_password.isEnabled = false
text_email.isEnabled = true
}
private fun vibrateShakeAndRequestFocusForTextEmail() {
vibrateSmartPhone()
text_email.shake()
text_email.requestFocus()
disableButtonConnect()
text_email.isEnabled = false
}
private fun setupOnClickListener() {
private fun setupOnClickListener() =
button_reset_password.setOnClickListener {
presenter.resetPassword(text_email.textContent)
}
}
companion object {
fun newInstance() = ResetPasswordFragment()
private fun subscribeEditText() {
emailAddressDisposable = text_email.asObservable()
.debounce(300, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread())
.filter { it.isNotBlank() }
.subscribe {
if (it.toString().isEmail()) {
enableButtonConnect()
} else {
disableButtonConnect()
}
}
}
private fun unsubscribeEditText() = emailAddressDisposable.dispose()
}
\ No newline at end of file
......@@ -3,34 +3,18 @@ package chat.rocket.android.authentication.server.di
import androidx.lifecycle.LifecycleOwner
import chat.rocket.android.authentication.server.presentation.ServerView
import chat.rocket.android.authentication.server.ui.ServerFragment
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.dagger.scope.PerFragment
import dagger.Module
import dagger.Provides
import kotlinx.coroutines.experimental.Job
@Module
class ServerFragmentModule {
@Provides
@PerFragment
fun provideJob() = Job()
fun serverView(frag: ServerFragment): ServerView = frag
@Provides
@PerFragment
fun serverView(frag: ServerFragment): ServerView {
return frag
}
@Provides
@PerFragment
fun provideLifecycleOwner(frag: ServerFragment): LifecycleOwner {
return frag
}
@Provides
@PerFragment
fun provideCancelStrategy(owner: LifecycleOwner, jobs: Job): CancelStrategy {
return CancelStrategy(owner, jobs)
}
fun provideLifecycleOwner(frag: ServerFragment): LifecycleOwner = frag
}
\ No newline at end of file
......@@ -5,12 +5,15 @@ import chat.rocket.android.authentication.presentation.AuthenticationNavigator
import chat.rocket.android.core.behaviours.showMessage
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.server.domain.GetAccountsInteractor
import chat.rocket.android.server.domain.GetSettingsInteractor
import chat.rocket.android.server.domain.RefreshSettingsInteractor
import chat.rocket.android.server.domain.SaveConnectingServerInteractor
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.server.presentation.CheckServerPresenter
import chat.rocket.android.util.extensions.isValidUrl
import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.extensions.isValidUrl
import kotlinx.coroutines.experimental.DefaultDispatcher
import kotlinx.coroutines.experimental.withContext
import javax.inject.Inject
class ServerPresenter @Inject constructor(
......@@ -20,42 +23,86 @@ class ServerPresenter @Inject constructor(
private val serverInteractor: SaveConnectingServerInteractor,
private val refreshSettingsInteractor: RefreshSettingsInteractor,
private val getAccountsInteractor: GetAccountsInteractor,
factory: RocketChatClientFactory
) : CheckServerPresenter(strategy, factory, view) {
val settingsInteractor: GetSettingsInteractor,
val factory: RocketChatClientFactory
) : CheckServerPresenter(strategy, factory, settingsInteractor, view) {
fun checkServer(server: String) {
if (!server.isValidUrl()) {
view.showInvalidServerUrlMessage()
} else {
view.showLoading()
setupConnectionInfo(server)
checkServerInfo(server)
}
}
fun connect(server: String) {
//code that leads to login screen (smart lock will be implemented after this)
connectToServer(server) {
navigator.toLogin()
fun connect(serverUrl: String) {
connectToServer(serverUrl) {
if (totalSocialAccountsEnabled == 0 && !isNewAccountCreationEnabled) {
navigator.toLogin(serverUrl)
} else {
navigator.toLoginOptions(
serverUrl,
state,
facebookOauthUrl,
githubOauthUrl,
googleOauthUrl,
linkedinOauthUrl,
gitlabOauthUrl,
wordpressOauthUrl,
casLoginUrl,
casToken,
customOauthUrl,
customOauthServiceName,
customOauthServiceNameTextColor,
customOauthServiceButtonColor,
samlUrl,
samlToken,
samlServiceName,
samlServiceNameTextColor,
samlServiceButtonColor,
totalSocialAccountsEnabled,
isLoginFormEnabled,
isNewAccountCreationEnabled
)
}
}
}
private fun connectToServer(server: String, block: () -> Unit) {
if (!server.isValidUrl()) {
fun deepLink(deepLinkInfo: LoginDeepLinkInfo) {
connectToServer(deepLinkInfo.url) {
navigator.toLoginOptions(deepLinkInfo.url, deepLinkInfo = deepLinkInfo)
}
}
private fun connectToServer(serverUrl: String, block: () -> Unit) {
if (!serverUrl.isValidUrl()) {
view.showInvalidServerUrlMessage()
} else {
launchUI(strategy) {
// Check if we already have an account for this server...
val account = getAccountsInteractor.get().firstOrNull { it.serverUrl == server }
val account = getAccountsInteractor.get().firstOrNull { it.serverUrl == serverUrl }
if (account != null) {
navigator.toChatList(server)
navigator.toChatList(serverUrl)
return@launchUI
}
view.showLoading()
try {
refreshSettingsInteractor.refresh(server)
serverInteractor.save(server)
block()
withContext(DefaultDispatcher) {
refreshSettingsInteractor.refresh(serverUrl)
setupConnectionInfo(serverUrl)
// preparing next fragment before showing it
checkEnabledAccounts(serverUrl)
checkIfLoginFormIsEnabled()
checkIfCreateNewAccountIsEnabled()
serverInteractor.save(serverUrl)
block()
}
} catch (ex: Exception) {
view.showMessage(ex)
} finally {
......@@ -65,10 +112,4 @@ class ServerPresenter @Inject constructor(
}
}
fun deepLink(deepLinkInfo: LoginDeepLinkInfo) {
//code that leads to login screen (smart lock will be implemented after this)
connectToServer(deepLinkInfo.url) {
navigator.toLogin(deepLinkInfo)
}
}
}
\ No newline at end of file
......@@ -9,4 +9,15 @@ interface ServerView : LoadingView, MessageView, VersionCheckView {
* Shows an invalid server URL message.
*/
fun showInvalidServerUrlMessage()
/**
* Enables the button to connect to the server when the user inputs a valid url.
*/
fun enableButtonConnect()
/**
* Disables the button to connect to the server when the server address entered by the user
* is not a valid url.
*/
fun disableButtonConnect()
}
\ No newline at end of file
......@@ -3,6 +3,7 @@ package chat.rocket.android.authentication.server.presentation
import okhttp3.HttpUrl
interface VersionCheckView {
/**
* Alerts the user about the server version not meeting the recommended server version.
*/
......
package chat.rocket.android.authentication.server.ui
import android.app.AlertDialog
import android.net.Uri
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
......@@ -9,6 +7,11 @@ import android.view.ViewGroup
import android.view.ViewTreeObserver
import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.Toast
import androidx.core.content.ContextCompat
import androidx.core.net.toUri
import androidx.core.view.ViewCompat
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import chat.rocket.android.BuildConfig
import chat.rocket.android.R
......@@ -17,22 +20,28 @@ import chat.rocket.android.analytics.event.ScreenViewEvent
import chat.rocket.android.authentication.domain.model.LoginDeepLinkInfo
import chat.rocket.android.authentication.server.presentation.ServerPresenter
import chat.rocket.android.authentication.server.presentation.ServerView
import chat.rocket.android.authentication.ui.AuthenticationActivity
import chat.rocket.android.helper.KeyboardHelper
import chat.rocket.android.util.extension.asObservable
import chat.rocket.android.util.extensions.hintContent
import chat.rocket.android.util.extensions.ifEmpty
import chat.rocket.android.util.extensions.inflate
import chat.rocket.android.util.extensions.isValidUrl
import chat.rocket.android.util.extensions.sanitize
import chat.rocket.android.util.extensions.setVisible
import chat.rocket.android.util.extensions.setLightStatusBar
import chat.rocket.android.util.extensions.showToast
import chat.rocket.android.util.extensions.textContent
import chat.rocket.android.util.extensions.ui
import chat.rocket.common.util.ifNull
import dagger.android.support.AndroidSupportInjection
import io.reactivex.disposables.Disposable
import kotlinx.android.synthetic.main.app_bar_chat_room.*
import kotlinx.android.synthetic.main.fragment_authentication_server.*
import okhttp3.HttpUrl
import javax.inject.Inject
internal const val TAG_SERVER_FRAGMENT = "ServerFragment"
fun newInstance() = ServerFragment()
private const val DEEP_LINK_INFO = "DeepLinkInfo"
class ServerFragment : Fragment(), ServerView {
@Inject
......@@ -40,92 +49,124 @@ class ServerFragment : Fragment(), ServerView {
@Inject
lateinit var analyticsManager: AnalyticsManager
private var deepLinkInfo: LoginDeepLinkInfo? = null
private var protocol = "https://"
private lateinit var serverUrlDisposable: Disposable
private val layoutListener = ViewTreeObserver.OnGlobalLayoutListener {
text_server_url.isCursorVisible = KeyboardHelper.isSoftKeyboardShown(relative_layout.rootView)
text_server_url.isCursorVisible =
KeyboardHelper.isSoftKeyboardShown(scroll_view.rootView)
}
private var protocol = "https://"
private var ignoreChange = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
AndroidSupportInjection.inject(this)
deepLinkInfo = arguments?.getParcelable(DEEP_LINK_INFO)
}
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?) {
super.onViewCreated(view, savedInstanceState)
relative_layout.viewTreeObserver.addOnGlobalLayoutListener(layoutListener)
scroll_view.viewTreeObserver.addOnGlobalLayoutListener(layoutListener)
setupToolbar()
setupSpinner()
setupOnClickListener()
subscribeEditText()
deepLinkInfo?.let {
val uri = Uri.parse(it.url)
uri?.let { text_server_url.hintContent = it.host }
it.url.toUri().host?.let { host -> text_server_url.hintContent = host }
presenter.deepLink(it)
}
text_server_protocol.adapter = ArrayAdapter<String>(activity,
android.R.layout.simple_dropdown_item_1line, arrayOf("https://", "http://"))
text_server_protocol.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
when(position) {
0 -> {
protocol = "https://"
}
1 -> {
if (ignoreChange) {
protocol = "http://"
} else {
ui {
AlertDialog.Builder(it)
.setTitle(R.string.msg_warning)
.setMessage(R.string.msg_http_insecure)
.setPositiveButton(R.string.msg_proceed) { _, _ ->
protocol = "http://"
}
.setNegativeButton(R.string.msg_cancel) { _, _ ->
text_server_protocol.setSelection(0)
}
.setCancelable(false)
.create()
.show()
analyticsManager.logScreenView(ScreenViewEvent.Server)
}
override fun onDestroyView() {
super.onDestroyView()
scroll_view.viewTreeObserver.removeOnGlobalLayoutListener(layoutListener)
// Reset deep link info, so user can come back and log to another server...
deepLinkInfo = null
unsubscribeEditText()
}
private fun setupToolbar() {
with(activity as AuthenticationActivity) {
view?.let { setLightStatusBar(it) }
toolbar.isVisible = false
}
}
private fun setupSpinner() {
context?.let {
spinner_server_protocol.adapter = ArrayAdapter<String>(
it,
android.R.layout.simple_dropdown_item_1line, arrayOf("https://", "http://")
)
spinner_server_protocol.onItemSelectedListener =
object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(
parent: AdapterView<*>?, view: View?, position: Int,
id: Long
) {
when (position) {
0 -> protocol = "https://"
1 -> {
protocol = "http://"
showToast(R.string.msg_http_insecure, Toast.LENGTH_LONG)
}
}
}
override fun onNothingSelected(parent: AdapterView<*>?) {}
}
}
ignoreChange = false
}
override fun onNothingSelected(parent: AdapterView<*>?) {
}
}
private fun setupOnClickListener() =
ui { _ ->
button_connect.setOnClickListener {
presenter.checkServer("$protocol${text_server_url.textContent.sanitize()}")
}
}
analyticsManager.logScreenView(ScreenViewEvent.Server)
}
override fun showInvalidServerUrlMessage() =
showMessage(getString(R.string.msg_invalid_server_url))
override fun onDestroyView() {
super.onDestroyView()
// reset deep link info, so user can come back and log to another server...
deepLinkInfo = null
relative_layout.viewTreeObserver.removeOnGlobalLayoutListener(layoutListener)
override fun enableButtonConnect() {
context?.let {
ViewCompat.setBackgroundTintList(
button_connect, ContextCompat.getColorStateList(it, R.color.colorAccent)
)
button_connect.isEnabled = true
}
}
override fun showInvalidServerUrlMessage() = showMessage(getString(R.string.msg_invalid_server_url))
override fun disableButtonConnect() {
context?.let {
ViewCompat.setBackgroundTintList(
button_connect,
ContextCompat.getColorStateList(it, R.color.colorAuthenticationButtonDisabled)
)
button_connect.isEnabled = false
}
}
override fun showLoading() {
ui {
enableUserInput(false)
view_loading.setVisible(true)
disableUserInput()
view_loading.isVisible = true
}
}
override fun hideLoading() {
ui {
view_loading.setVisible(false)
enableUserInput(true)
view_loading.isVisible = false
enableUserInput()
}
}
......@@ -141,41 +182,28 @@ class ServerFragment : Fragment(), ServerView {
}
}
override fun showGenericErrorMessage() {
showMessage(getString(R.string.msg_generic_error))
}
override fun showGenericErrorMessage() = showMessage(getString(R.string.msg_generic_error))
override fun alertNotRecommendedVersion() {
ui {
hideLoading()
AlertDialog.Builder(it)
.setMessage(getString(R.string.msg_ver_not_recommended, BuildConfig.RECOMMENDED_SERVER_VERSION))
.setPositiveButton(R.string.msg_ok) { _, _ ->
performConnect()
}
.create()
.show()
showToast(
getString(R.string.msg_ver_not_recommended, BuildConfig.RECOMMENDED_SERVER_VERSION)
)
performConnect()
}
}
override fun blockAndAlertNotRequiredVersion() {
ui {
hideLoading()
AlertDialog.Builder(it)
.setMessage(getString(R.string.msg_ver_not_minimum, BuildConfig.REQUIRED_SERVER_VERSION))
.setPositiveButton(R.string.msg_ok, null)
.setOnDismissListener {
// reset the deeplink info, so the user can log to another server...
deepLinkInfo = null
}
.create()
.show()
showToast(getString(R.string.msg_ver_not_minimum, BuildConfig.REQUIRED_SERVER_VERSION))
// reset the deeplink info, so the user can log to another server...
deepLinkInfo = null
}
}
override fun versionOk() {
performConnect()
}
override fun versionOk() = performConnect()
override fun errorCheckingServerVersion() {
hideLoading()
......@@ -188,47 +216,49 @@ class ServerFragment : Fragment(), ServerView {
}
override fun updateServerUrl(url: HttpUrl) {
if (activity != null && view != null) {
if (url.scheme() == "https") text_server_protocol.setSelection(0) else text_server_protocol.setSelection(1)
protocol = "${url.scheme()}://"
ui {
if (url.scheme() == "https") {
spinner_server_protocol.setSelection(0)
} else {
spinner_server_protocol.setSelection(1)
}
val serverUrl = url.toString().removePrefix("${url.scheme()}://")
text_server_url.textContent = serverUrl
protocol = "${url.scheme()}://"
text_server_url.textContent = url.toString().removePrefix(protocol)
}
}
private fun performConnect() {
ui {
deepLinkInfo?.let {
presenter.deepLink(it)
deepLinkInfo?.let { loginDeepLinkInfo ->
presenter.deepLink(loginDeepLinkInfo)
}.ifNull {
val url = text_server_url.textContent.ifEmpty(text_server_url.hintContent)
presenter.connect("$protocol${url.sanitize()}")
presenter.connect("$protocol${text_server_url.textContent.sanitize()}")
}
}
}
private fun enableUserInput(value: Boolean) {
button_connect.isEnabled = value
text_server_url.isEnabled = value
}
private fun setupOnClickListener() {
ui {
button_connect.setOnClickListener {
val url = text_server_url.textContent.ifEmpty(text_server_url.hintContent)
presenter.checkServer("${protocol}${url.sanitize()}")
private fun subscribeEditText() {
serverUrlDisposable = text_server_url.asObservable()
.filter { it.isNotBlank() }
.subscribe {
if (it.toString().isValidUrl()) {
enableButtonConnect()
} else {
disableButtonConnect()
}
}
}
}
companion object {
private const val DEEP_LINK_INFO = "DeepLinkInfo"
private fun unsubscribeEditText() = serverUrlDisposable.dispose()
fun newInstance(deepLinkInfo: LoginDeepLinkInfo?) = ServerFragment().apply {
arguments = Bundle().apply {
putParcelable(DEEP_LINK_INFO, deepLinkInfo)
}
}
private fun enableUserInput() {
enableButtonConnect()
text_server_url.isEnabled = true
}
private fun disableUserInput() {
disableButtonConnect()
text_server_url.isEnabled = false
}
}
\ No newline at end of file
}
......@@ -3,34 +3,18 @@ package chat.rocket.android.authentication.signup.di
import androidx.lifecycle.LifecycleOwner
import chat.rocket.android.authentication.signup.presentation.SignupView
import chat.rocket.android.authentication.signup.ui.SignupFragment
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.dagger.scope.PerFragment
import dagger.Module
import dagger.Provides
import kotlinx.coroutines.experimental.Job
@Module
class SignupFragmentModule {
@Provides
@PerFragment
fun provideJob() = Job()
fun signupView(frag: SignupFragment): SignupView = frag
@Provides
@PerFragment
fun signupView(frag: SignupFragment): SignupView {
return frag
}
@Provides
@PerFragment
fun provideLifecycleOwner(frag: SignupFragment): LifecycleOwner {
return frag
}
@Provides
@PerFragment
fun provideCancelStrategy(owner: LifecycleOwner, jobs: Job): CancelStrategy {
return CancelStrategy(owner, jobs)
}
fun provideLifecycleOwner(frag: SignupFragment): LifecycleOwner = frag
}
\ No newline at end of file
......@@ -44,57 +44,37 @@ class SignupPresenter @Inject constructor(
private var settings: PublicSettings = settingsInteractor.get(serverInteractor.get()!!)
fun signup(name: String, username: String, password: String, email: String) {
val server = serverInteractor.get()
when {
server == null -> {
navigator.toServerScreen()
}
name.isBlank() -> {
view.alertBlankName()
}
username.isBlank() -> {
view.alertBlankUsername()
}
password.isEmpty() -> {
view.alertEmptyPassword()
}
email.isBlank() -> {
view.alertBlankEmail()
}
else -> {
val client = factory.create(server)
launchUI(strategy) {
view.showLoading()
try {
// TODO This function returns a user so should we save it?
retryIO("signup") { client.signup(email, name, username, password) }
// TODO This function returns a user token so should we save it?
retryIO("login") { client.login(username, password) }
val me = retryIO("me") { client.me() }
saveCurrentServerInteractor.save(currentServer)
localRepository.save(LocalRepository.CURRENT_USERNAME_KEY, me.username)
saveAccount(me)
analyticsManager.logSignUp(
AuthenticationEvent.AuthenticationWithUserAndPassword,
true
)
view.saveSmartLockCredentials(username, password)
navigator.toChatList()
} catch (exception: RocketChatException) {
analyticsManager.logSignUp(
AuthenticationEvent.AuthenticationWithUserAndPassword,
false
)
exception.message?.let {
view.showMessage(it)
}.ifNull {
view.showGenericErrorMessage()
}
} finally {
view.hideLoading()
}
val client = factory.create(currentServer)
launchUI(strategy) {
view.showLoading()
try {
// TODO This function returns a user so should we save it?
retryIO("signup") { client.signup(email, name, username, password) }
// TODO This function returns a user token so should we save it?
retryIO("login") { client.login(username, password) }
val me = retryIO("me") { client.me() }
saveCurrentServerInteractor.save(currentServer)
localRepository.save(LocalRepository.CURRENT_USERNAME_KEY, me.username)
saveAccount(me)
analyticsManager.logSignUp(
AuthenticationEvent.AuthenticationWithUserAndPassword,
true
)
view.saveSmartLockCredentials(username, password)
navigator.toChatList()
} catch (exception: RocketChatException) {
analyticsManager.logSignUp(
AuthenticationEvent.AuthenticationWithUserAndPassword,
false
)
exception.message?.let {
view.showMessage(it)
}.ifNull {
view.showGenericErrorMessage()
}
} finally {
view.hideLoading()
}
}
}
......
......@@ -6,24 +6,15 @@ import chat.rocket.android.core.behaviours.MessageView
interface SignupView : LoadingView, MessageView {
/**
* Alerts the user about a blank name.
* Enables the button to register when the user enters all the required fields.
*/
fun alertBlankName()
fun enableButtonRegister()
/**
* Alerts the user about a blank username.
* Disables the button to register when the user doesn't enter all the required fields.
*/
fun alertBlankUsername()
fun disableButtonRegister()
/**
* Alerts the user about a empty password.
*/
fun alertEmptyPassword()
/**
* Alerts the user about a blank email.
*/
fun alertBlankEmail()
/**
* Saves Google Smart Lock credentials.
......
package chat.rocket.android.authentication.signup.ui
import DrawableHelper
import android.app.Activity
import android.content.Intent
import android.os.Build
import android.os.Bundle
import android.text.style.ClickableSpan
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.ViewTreeObserver
import androidx.core.content.ContextCompat
import androidx.core.view.ViewCompat
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import chat.rocket.android.R
import chat.rocket.android.R.string.message_credentials_saved_successfully
......@@ -17,20 +16,21 @@ import chat.rocket.android.analytics.AnalyticsManager
import chat.rocket.android.analytics.event.ScreenViewEvent
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.TextHelper
import chat.rocket.android.helper.saveCredentials
import chat.rocket.android.util.extensions.setVisible
import chat.rocket.android.util.extensions.shake
import chat.rocket.android.util.extension.asObservable
import chat.rocket.android.util.extensions.inflate
import chat.rocket.android.util.extensions.isEmail
import chat.rocket.android.util.extensions.showToast
import chat.rocket.android.util.extensions.textContent
import chat.rocket.android.util.extensions.ui
import chat.rocket.android.util.extensions.vibrateSmartPhone
import dagger.android.support.AndroidSupportInjection
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.Observables
import kotlinx.android.synthetic.main.fragment_authentication_sign_up.*
import javax.inject.Inject
internal const val TAG_SIGNUP_FRAGMENT = "SignupFragment"
fun newInstance() = SignupFragment()
internal const val SAVE_CREDENTIALS = 1
class SignupFragment : Fragment(), SignupView {
......@@ -38,17 +38,7 @@ class SignupFragment : Fragment(), SignupView {
lateinit var presenter: SignupPresenter
@Inject
lateinit var analyticsManager: AnalyticsManager
private val layoutListener = ViewTreeObserver.OnGlobalLayoutListener {
if (KeyboardHelper.isSoftKeyboardShown(relative_layout.rootView)) {
bottom_container.setVisible(false)
} else {
bottom_container.apply {
postDelayed({
ui { setVisible(true) }
}, 3)
}
}
}
private val editTextsDisposable = CompositeDisposable()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
......@@ -59,89 +49,76 @@ class SignupFragment : Fragment(), SignupView {
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? = inflater.inflate(R.layout.fragment_authentication_sign_up, container, false)
): View? = container?.inflate(R.layout.fragment_authentication_sign_up)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) {
tintEditTextDrawableStart()
}
relative_layout.viewTreeObserver.addOnGlobalLayoutListener(layoutListener)
subscribeEditTexts()
setupOnClickListener()
setUpNewUserAgreementListener()
analyticsManager.logScreenView(ScreenViewEvent.SignUp)
}
button_sign_up.setOnClickListener {
presenter.signup(
text_username.textContent,
text_username.textContent,
text_password.textContent,
text_email.textContent
)
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (resultCode == Activity.RESULT_OK) {
if (data != null) {
if (requestCode == SAVE_CREDENTIALS) {
showMessage(getString(message_credentials_saved_successfully))
}
}
}
analyticsManager.logScreenView(ScreenViewEvent.SignUp)
}
override fun onDestroyView() {
relative_layout.viewTreeObserver.removeOnGlobalLayoutListener(layoutListener)
super.onDestroyView()
}
override fun alertBlankName() {
ui {
vibrateSmartPhone()
text_name.shake()
text_name.requestFocus()
unsubscribeEditTexts()
}
private fun setupOnClickListener() =
ui { _ ->
button_register.setOnClickListener {
presenter.signup(
text_username.textContent,
text_username.textContent,
text_password.textContent,
text_email.textContent
)
}
}
}
override fun alertBlankUsername() {
ui {
vibrateSmartPhone()
text_username.shake()
text_username.requestFocus()
override fun enableButtonRegister() {
context?.let {
ViewCompat.setBackgroundTintList(
button_register, ContextCompat.getColorStateList(it, R.color.colorAccent)
)
button_register.isEnabled = true
}
}
override fun alertEmptyPassword() {
ui {
vibrateSmartPhone()
text_password.shake()
text_password.requestFocus()
}
}
override fun alertBlankEmail() {
ui {
vibrateSmartPhone()
text_email.shake()
text_email.requestFocus()
override fun disableButtonRegister() {
context?.let {
ViewCompat.setBackgroundTintList(
button_register,
ContextCompat.getColorStateList(it, R.color.colorAuthenticationButtonDisabled)
)
button_register.isEnabled = false
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (resultCode == Activity.RESULT_OK) {
if (data != null) {
if (requestCode == SAVE_CREDENTIALS) {
showMessage(getString(message_credentials_saved_successfully))
}
}
}
}
override fun showLoading() {
ui {
enableUserInput(false)
view_loading.setVisible(true)
disableUserInput()
view_loading.isVisible = true
}
}
override fun hideLoading() {
ui {
view_loading.setVisible(false)
enableUserInput(true)
view_loading.isVisible = false
enableUserInput()
}
}
......@@ -157,72 +134,51 @@ class SignupFragment : Fragment(), SignupView {
}
}
override fun showGenericErrorMessage() {
showMessage(getString(R.string.msg_generic_error))
}
override fun showGenericErrorMessage() = showMessage(getString(R.string.msg_generic_error))
override fun saveSmartLockCredentials(id: String, password: String) {
activity?.saveCredentials(id, password)
}
private fun tintEditTextDrawableStart() {
ui {
val personDrawable =
DrawableHelper.getDrawableFromId(R.drawable.ic_person_black_24dp, it)
val atDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_at_black_24dp, it)
val lockDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_lock_black_24dp, it)
val emailDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_email_black_24dp, it)
val drawables = arrayOf(personDrawable, atDrawable, lockDrawable, emailDrawable)
DrawableHelper.wrapDrawables(drawables)
DrawableHelper.tintDrawables(drawables, it, R.color.colorDrawableTintGrey)
DrawableHelper.compoundDrawables(
arrayOf(
text_name,
text_username,
text_password,
text_email
), drawables
)
}
private fun subscribeEditTexts() {
editTextsDisposable.add(
Observables.combineLatest(
text_name.asObservable(),
text_username.asObservable(),
text_password.asObservable(),
text_email.asObservable()
) { text_name, text_username, text_password, text_email ->
return@combineLatest (
text_name.isNotBlank() &&
text_username.isNotBlank() &&
text_password.isNotBlank() &&
text_email.isNotBlank() &&
text_email.toString().isEmail()
)
}.subscribe { isValid ->
if (isValid) {
enableButtonRegister()
} else {
disableButtonRegister()
}
})
}
private fun setUpNewUserAgreementListener() {
val termsOfService = getString(R.string.action_terms_of_service)
val privacyPolicy = getString(R.string.action_privacy_policy)
val newUserAgreement =
String.format(getString(R.string.msg_new_user_agreement), termsOfService, privacyPolicy)
text_new_user_agreement.text = newUserAgreement
val termsOfServiceListener = object : ClickableSpan() {
override fun onClick(view: View) {
presenter.termsOfService()
}
}
val privacyPolicyListener = object : ClickableSpan() {
override fun onClick(view: View) {
presenter.privacyPolicy()
}
}
TextHelper.addLink(
text_new_user_agreement,
arrayOf(termsOfService, privacyPolicy),
arrayOf(termsOfServiceListener, privacyPolicyListener)
)
}
private fun unsubscribeEditTexts() = editTextsDisposable.clear()
private fun enableUserInput(value: Boolean) {
button_sign_up.isEnabled = value
text_username.isEnabled = value
text_username.isEnabled = value
text_password.isEnabled = value
text_email.isEnabled = value
private fun enableUserInput() {
text_name.isEnabled = true
text_username.isEnabled = true
text_password.isEnabled = true
text_email.isEnabled = true
enableButtonRegister()
}
companion object {
fun newInstance() = SignupFragment()
private fun disableUserInput() {
disableButtonRegister()
text_name.isEnabled = false
text_username.isEnabled = false
text_password.isEnabled = false
text_email.isEnabled = false
}
}
......@@ -3,34 +3,18 @@ package chat.rocket.android.authentication.twofactor.di
import androidx.lifecycle.LifecycleOwner
import chat.rocket.android.authentication.twofactor.presentation.TwoFAView
import chat.rocket.android.authentication.twofactor.ui.TwoFAFragment
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.dagger.scope.PerFragment
import dagger.Module
import dagger.Provides
import kotlinx.coroutines.experimental.Job
@Module
class TwoFAFragmentModule {
@Provides
@PerFragment
fun provideJob() = Job()
fun loginView(frag: TwoFAFragment): TwoFAView = frag
@Provides
@PerFragment
fun loginView(frag: TwoFAFragment): TwoFAView {
return frag
}
@Provides
@PerFragment
fun provideLifecycleOwner(frag: TwoFAFragment): LifecycleOwner {
return frag
}
@Provides
@PerFragment
fun provideCancelStrategy(owner: LifecycleOwner, jobs: Job): CancelStrategy {
return CancelStrategy(owner, jobs)
}
fun provideLifecycleOwner(frag: TwoFAFragment): LifecycleOwner = frag
}
......@@ -35,77 +35,65 @@ class TwoFAPresenter @Inject constructor(
private val navigator: AuthenticationNavigator,
private val tokenRepository: TokenRepository,
private val localRepository: LocalRepository,
private val serverInteractor: GetConnectingServerInteractor,
private val saveCurrentServerInteractor: SaveCurrentServerInteractor,
private val analyticsManager: AnalyticsManager,
private val factory: RocketChatClientFactory,
private val saveAccountInteractor: SaveAccountInteractor,
settingsInteractor: GetSettingsInteractor
val serverInteractor: GetConnectingServerInteractor,
val settingsInteractor: GetSettingsInteractor
) {
private val currentServer = serverInteractor.get()!!
private var settings: PublicSettings = settingsInteractor.get(serverInteractor.get()!!)
// TODO: If the usernameOrEmail and password was informed by the user on the previous screen, then we should pass only the pin, like this: fun authenticate(pin: EditText)
fun authenticate(
usernameOrEmail: String,
password: String,
twoFactorAuthenticationCode: String
) {
val server = serverInteractor.get()
when {
server == null -> {
navigator.toServerScreen()
}
twoFactorAuthenticationCode.isBlank() -> {
view.alertBlankTwoFactorAuthenticationCode()
}
else -> {
launchUI(strategy) {
val client = factory.create(server)
view.showLoading()
try {
// The token is saved via the client TokenProvider
val token = retryIO("login") {
if (usernameOrEmail.isEmail()) {
client.loginWithEmail(usernameOrEmail, password, twoFactorAuthenticationCode)
} else {
client.login(usernameOrEmail, password, twoFactorAuthenticationCode)
}
}
val me = retryIO("me") { client.me() }
saveAccount(me)
saveCurrentServerInteractor.save(currentServer)
tokenRepository.save(server, token)
localRepository.save(LocalRepository.CURRENT_USERNAME_KEY, me.username)
analyticsManager.logLogin(
AuthenticationEvent.AuthenticationWithUserAndPassword,
true
launchUI(strategy) {
val client = factory.create(currentServer)
view.showLoading()
try {
// The token is saved via the client TokenProvider
val token = retryIO("login") {
if (usernameOrEmail.isEmail()) {
client.loginWithEmail(
usernameOrEmail,
password,
twoFactorAuthenticationCode
)
navigator.toChatList()
} catch (exception: RocketChatException) {
if (exception is RocketChatAuthException) {
view.alertInvalidTwoFactorAuthenticationCode()
} else {
analyticsManager.logLogin(
AuthenticationEvent.AuthenticationWithUserAndPassword,
false
)
exception.message?.let {
view.showMessage(it)
}.ifNull {
view.showGenericErrorMessage()
}
}
} finally {
view.hideLoading()
} else {
client.login(usernameOrEmail, password, twoFactorAuthenticationCode)
}
}
val me = retryIO("me") { client.me() }
saveAccount(me)
saveCurrentServerInteractor.save(currentServer)
tokenRepository.save(currentServer, token)
localRepository.save(LocalRepository.CURRENT_USERNAME_KEY, me.username)
analyticsManager.logLogin(
AuthenticationEvent.AuthenticationWithUserAndPassword, true
)
navigator.toChatList()
} catch (exception: RocketChatException) {
if (exception is RocketChatAuthException) {
view.alertInvalidTwoFactorAuthenticationCode()
} else {
analyticsManager.logLogin(
AuthenticationEvent.AuthenticationWithUserAndPassword, false
)
exception.message?.let {
view.showMessage(it)
}.ifNull {
view.showGenericErrorMessage()
}
}
} finally {
view.hideLoading()
}
}
}
fun signup() = navigator.toSignUp()
private suspend fun saveAccount(me: Myself) {
val icon = settings.favicon()?.let {
currentServer.serverLogoUrl(it)
......
......@@ -6,9 +6,14 @@ import chat.rocket.android.core.behaviours.MessageView
interface TwoFAView : LoadingView, MessageView {
/**
* Alerts the user about a blank Two Factor Authentication code.
* Enables the button to set the username if the user entered at least one character.
*/
fun alertBlankTwoFactorAuthenticationCode()
fun enableButtonConfirm()
/**
* Disables the button to set the username when there is no character entered by the user.
*/
fun disableButtonConfirm()
/**
* Alerts the user about an invalid inputted Two Factor Authentication code.
......
package chat.rocket.android.authentication.twofactor.ui
import DrawableHelper
import android.content.Context
import android.os.Build
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.InputMethodManager
import androidx.core.content.ContextCompat
import androidx.core.view.ViewCompat
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import chat.rocket.android.R
import chat.rocket.android.analytics.AnalyticsManager
import chat.rocket.android.analytics.event.ScreenViewEvent
import chat.rocket.android.authentication.twofactor.presentation.TwoFAPresenter
import chat.rocket.android.authentication.twofactor.presentation.TwoFAView
import chat.rocket.android.util.extension.asObservable
import chat.rocket.android.util.extensions.inflate
import chat.rocket.android.util.extensions.setVisible
import chat.rocket.android.util.extensions.shake
import chat.rocket.android.util.extensions.showToast
import chat.rocket.android.util.extensions.textContent
import chat.rocket.android.util.extensions.ui
import chat.rocket.android.util.extensions.vibrateSmartPhone
import dagger.android.support.AndroidSupportInjection
import io.reactivex.disposables.Disposable
import kotlinx.android.synthetic.main.fragment_authentication_two_fa.*
import javax.inject.Inject
internal const val TAG_TWO_FA_FRAGMENT = "TwoFAFragment"
fun newInstance(username: String, password: String): Fragment {
return TwoFAFragment().apply {
arguments = Bundle(2).apply {
putString(BUNDLE_USERNAME, username)
putString(BUNDLE_PASSWORD, password)
}
}
}
private const val BUNDLE_USERNAME = "username"
private const val BUNDLE_PASSWORD = "password"
class TwoFAFragment : Fragment(), TwoFAView {
@Inject
lateinit var presenter: TwoFAPresenter
@Inject
lateinit var analyticsManager: AnalyticsManager
lateinit var username: String
lateinit var password: String
private lateinit var username: String
private lateinit var password: String
private lateinit var twoFaCodeDisposable: Disposable
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
AndroidSupportInjection.inject(this)
// TODO - research a better way to initialize parameters on fragments.
username = arguments?.getString(USERNAME) ?: ""
password = arguments?.getString(PASSWORD) ?: ""
val bundle = arguments
if (bundle != null) {
username = bundle.getString(BUNDLE_USERNAME)
password = bundle.getString(BUNDLE_PASSWORD)
} else {
requireNotNull(bundle) { "no arguments supplied when the fragment was instantiated" }
}
}
override fun onCreateView(
......@@ -54,42 +69,58 @@ class TwoFAFragment : Fragment(), TwoFAView {
super.onViewCreated(view, savedInstanceState)
activity?.apply {
text_two_factor_auth.requestFocus()
text_two_factor_authentication_code.requestFocus()
val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.showSoftInput(text_two_factor_auth, InputMethodManager.RESULT_UNCHANGED_SHOWN)
}
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) {
tintEditTextDrawableStart()
imm.showSoftInput(
text_two_factor_authentication_code,
InputMethodManager.RESULT_UNCHANGED_SHOWN
)
}
setupOnClickListener()
subscribeEditText()
analyticsManager.logScreenView(ScreenViewEvent.TwoFa)
}
override fun alertBlankTwoFactorAuthenticationCode() {
ui {
vibrateSmartPhone()
text_two_factor_auth.shake()
override fun onDestroyView() {
super.onDestroyView()
unsubscribeEditText()
}
override fun enableButtonConfirm() {
context?.let {
ViewCompat.setBackgroundTintList(
button_confirm, ContextCompat.getColorStateList(it, R.color.colorAccent)
)
button_confirm.isEnabled = true
}
}
override fun alertInvalidTwoFactorAuthenticationCode() {
showMessage(getString(R.string.msg_invalid_2fa_code))
override fun disableButtonConfirm() {
context?.let {
ViewCompat.setBackgroundTintList(
button_confirm,
ContextCompat.getColorStateList(it, R.color.colorAuthenticationButtonDisabled)
)
button_confirm.isEnabled = false
}
}
override fun alertInvalidTwoFactorAuthenticationCode() =
showMessage(R.string.msg_invalid_2fa_code)
override fun showLoading() {
ui {
enableUserInput(false)
view_loading.setVisible(true)
disableUserInput()
view_loading.isVisible = true
}
}
override fun hideLoading() {
ui {
view_loading.setVisible(false)
enableUserInput(true)
view_loading.isVisible = false
enableUserInput()
}
}
......@@ -105,39 +136,38 @@ class TwoFAFragment : Fragment(), TwoFAView {
}
}
override fun showGenericErrorMessage() = showMessage(getString(R.string.msg_generic_error))
override fun showGenericErrorMessage() = showMessage(R.string.msg_generic_error)
private fun tintEditTextDrawableStart() {
ui {
val lockDrawable =
DrawableHelper.getDrawableFromId(R.drawable.ic_vpn_key_black_24dp, it)
DrawableHelper.wrapDrawable(lockDrawable)
DrawableHelper.tintDrawable(lockDrawable, it, R.color.colorDrawableTintGrey)
DrawableHelper.compoundDrawable(text_two_factor_auth, lockDrawable)
}
private fun enableUserInput() {
enableButtonConfirm()
text_two_factor_authentication_code.isEnabled = true
}
private fun enableUserInput(value: Boolean) {
button_log_in.isEnabled = value
text_two_factor_auth.isEnabled = value
private fun disableUserInput() {
disableButtonConfirm()
text_two_factor_authentication_code.isEnabled = false
}
private fun setupOnClickListener() {
button_log_in.setOnClickListener {
presenter.authenticate(username, password, text_two_factor_auth.textContent)
button_confirm.setOnClickListener {
presenter.authenticate(
username,
password,
text_two_factor_authentication_code.textContent
)
}
}
// TODO - we could create an in memory repository to save username and password.
companion object {
private const val USERNAME = "username"
private const val PASSWORD = "password"
fun newInstance(username: String, password: String) = TwoFAFragment().apply {
arguments = Bundle(2).apply {
putString(USERNAME, username)
putString(PASSWORD, password)
private fun subscribeEditText() {
twoFaCodeDisposable = text_two_factor_authentication_code.asObservable()
.subscribe {
if (it.isNotBlank()) {
enableButtonConfirm()
} else {
disableButtonConfirm()
}
}
}
}
private fun unsubscribeEditText() = twoFaCodeDisposable.dispose()
}
......@@ -3,22 +3,23 @@ package chat.rocket.android.authentication.ui
import android.content.Context
import android.content.Intent
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.Menu
import android.view.MenuItem
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import chat.rocket.android.R
import chat.rocket.android.analytics.event.ScreenViewEvent
import chat.rocket.android.authentication.domain.model.LoginDeepLinkInfo
import chat.rocket.android.authentication.domain.model.getLoginDeepLinkInfo
import chat.rocket.android.authentication.presentation.AuthenticationPresenter
import chat.rocket.android.authentication.server.ui.ServerFragment
import chat.rocket.android.authentication.server.ui.TAG_SERVER_FRAGMENT
import chat.rocket.android.util.extensions.addFragment
import chat.rocket.common.util.ifNull
import dagger.android.AndroidInjection
import dagger.android.AndroidInjector
import dagger.android.DispatchingAndroidInjector
import dagger.android.support.HasSupportFragmentInjector
import kotlinx.android.synthetic.main.app_bar.*
import kotlinx.coroutines.experimental.Job
import kotlinx.coroutines.experimental.android.UI
import kotlinx.coroutines.experimental.launch
import javax.inject.Inject
class AuthenticationActivity : AppCompatActivity(), HasSupportFragmentInjector {
......@@ -30,28 +31,19 @@ class AuthenticationActivity : AppCompatActivity(), HasSupportFragmentInjector {
override fun onCreate(savedInstanceState: Bundle?) {
AndroidInjection.inject(this)
setContentView(R.layout.activity_authentication)
setTheme(R.style.AuthenticationTheme)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_authentication)
setupToolbar()
loadCredentials()
}
override fun onStart() {
super.onStart()
val deepLinkInfo = intent.getLoginDeepLinkInfo()
launch(UI + job) {
val newServer = intent.getBooleanExtra(INTENT_ADD_NEW_SERVER, false)
// if we got authenticateWithDeepLink information, pass true to newServer also
presenter.loadCredentials(newServer || deepLinkInfo != null) { authenticated ->
if (!authenticated) {
showServerInput(deepLinkInfo)
}
}
private fun setupToolbar() {
with(toolbar) {
setSupportActionBar(this)
setNavigationIcon(R.drawable.ic_arrow_back_white_24dp)
setNavigationOnClickListener { onBackPressed() }
}
}
override fun onStop() {
job.cancel()
super.onStop()
supportActionBar?.setDisplayShowTitleEnabled(false)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
......@@ -60,15 +52,58 @@ class AuthenticationActivity : AppCompatActivity(), HasSupportFragmentInjector {
currentFragment?.onActivityResult(requestCode, resultCode, data)
}
override fun supportFragmentInjector(): AndroidInjector<Fragment> {
return fragmentDispatchingAndroidInjector
override fun supportFragmentInjector(): AndroidInjector<Fragment> =
fragmentDispatchingAndroidInjector
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.legal, menu)
return true
}
fun showServerInput(deepLinkInfo: LoginDeepLinkInfo?) {
addFragment(TAG_SERVER_FRAGMENT, R.id.fragment_container, allowStateLoss = true) {
ServerFragment.newInstance(deepLinkInfo)
override fun onOptionsItemSelected(item: MenuItem?): Boolean {
when (item?.itemId) {
R.id.action_terms_of_Service -> presenter.termsOfService(getString(R.string.action_terms_of_service))
R.id.action_privacy_policy -> presenter.privacyPolicy(getString(R.string.action_privacy_policy))
}
return super.onOptionsItemSelected(item)
}
private fun loadCredentials() {
intent.getLoginDeepLinkInfo()?.let {
showServerFragment(it)
}.ifNull {
val newServer = intent.getBooleanExtra(INTENT_ADD_NEW_SERVER, false)
presenter.loadCredentials(newServer) { isAuthenticated ->
if (isAuthenticated) {
showChatList()
} else {
showOnBoardingFragment()
}
}
}
}
private fun showOnBoardingFragment() {
addFragment(
ScreenViewEvent.OnBoarding.screenName,
R.id.fragment_container,
allowStateLoss = true
) {
chat.rocket.android.authentication.onboarding.ui.newInstance()
}
}
private fun showServerFragment(deepLinkInfo: LoginDeepLinkInfo) {
addFragment(
ScreenViewEvent.Server.screenName,
R.id.fragment_container,
allowStateLoss = true
) {
chat.rocket.android.authentication.server.ui.newInstance()
}
}
private fun showChatList() = presenter.toChatList()
}
const val INTENT_ADD_NEW_SERVER = "INTENT_ADD_NEW_SERVER"
......
......@@ -226,8 +226,10 @@ class CreateChannelFragment : Fragment(), CreateChannelView, ActionMode.Callback
private fun setupRecyclerView() {
ui {
recycler_view.layoutManager =
LinearLayoutManager(context, RecyclerView.VERTICAL, false)
recycler_view.addItemDecoration(DividerItemDecoration(it, DividerItemDecoration.HORIZONTAL))
LinearLayoutManager(context, RecyclerView.VERTICAL, false)
recycler_view.addItemDecoration(
DividerItemDecoration(it, DividerItemDecoration.HORIZONTAL)
)
recycler_view.adapter = adapter
}
}
......
......@@ -3,6 +3,8 @@ package chat.rocket.android.dagger.module
import chat.rocket.android.about.di.AboutFragmentProvider
import chat.rocket.android.authentication.di.AuthenticationModule
import chat.rocket.android.authentication.login.di.LoginFragmentProvider
import chat.rocket.android.authentication.loginoptions.di.LoginOptionsFragmentProvider
import chat.rocket.android.authentication.onboarding.di.OnBoardingFragmentProvider
import chat.rocket.android.authentication.registerusername.di.RegisterUsernameFragmentProvider
import chat.rocket.android.authentication.resetpassword.di.ResetPasswordFragmentProvider
import chat.rocket.android.authentication.server.di.ServerFragmentProvider
......@@ -41,14 +43,16 @@ abstract class ActivityBuilder {
@PerActivity
@ContributesAndroidInjector(
modules = [AuthenticationModule::class,
ServerFragmentProvider::class,
LoginFragmentProvider::class,
RegisterUsernameFragmentProvider::class,
ResetPasswordFragmentProvider::class,
SignupFragmentProvider::class,
TwoFAFragmentProvider::class
]
modules = [AuthenticationModule::class,
OnBoardingFragmentProvider::class,
ServerFragmentProvider::class,
LoginOptionsFragmentProvider::class,
LoginFragmentProvider::class,
RegisterUsernameFragmentProvider::class,
ResetPasswordFragmentProvider::class,
SignupFragmentProvider::class,
TwoFAFragmentProvider::class
]
)
abstract fun bindAuthenticationActivity(): AuthenticationActivity
......
package chat.rocket.android.helper
import chat.rocket.android.util.extensions.encodeToBase64
import chat.rocket.android.util.extensions.generateRandomString
import chat.rocket.android.util.extensions.removeTrailingSlash
object OauthHelper {
/**
* Returns an unguessable random string used to protect against forgery attacks.
*/
fun getState() =
("{\"loginStyle\":\"popup\"," +
"\"credentialToken\":\"${generateRandomString(40)}\"," +
"\"isCordova\":true}").encodeToBase64()
/**
* Returns the Github Oauth URL.
*
......
......@@ -27,7 +27,6 @@ import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.server.presentation.CheckServerPresenter
import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.extensions.adminPanelUrl
import chat.rocket.android.util.extensions.registerPushToken
import chat.rocket.android.util.extensions.serverLogoUrl
import chat.rocket.android.util.retryIO
import chat.rocket.common.RocketChatAuthException
......
......@@ -26,7 +26,7 @@ internal fun MainActivity.setupMenu(menu: Menu) {
R.id.menu_action_profile,
Menu.NONE,
R.string.title_profile
).setIcon(R.drawable.ic_person_black_24dp)
).setIcon(R.drawable.ic_person_black_20dp)
add(
R.id.menu_section_two,
......
......@@ -215,10 +215,10 @@ class ProfileFragment : Fragment(), ProfileView, ActionMode.Callback {
private fun tintEditTextDrawableStart() {
(activity as MainActivity).apply {
val personDrawable =
DrawableHelper.getDrawableFromId(R.drawable.ic_person_black_24dp, this)
val atDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_at_black_24dp, this)
DrawableHelper.getDrawableFromId(R.drawable.ic_person_black_20dp, this)
val atDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_at_black_20dp, this)
val emailDrawable =
DrawableHelper.getDrawableFromId(R.drawable.ic_email_black_24dp, this)
DrawableHelper.getDrawableFromId(R.drawable.ic_email_black_20dp, this)
val drawables = arrayOf(personDrawable, atDrawable, emailDrawable)
DrawableHelper.wrapDrawables(drawables)
......
......@@ -3,67 +3,416 @@ package chat.rocket.android.server.presentation
import chat.rocket.android.BuildConfig
import chat.rocket.android.authentication.server.presentation.VersionCheckView
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.helper.OauthHelper
import chat.rocket.android.server.domain.GetSettingsInteractor
import chat.rocket.android.server.domain.PublicSettings
import chat.rocket.android.server.domain.casLoginUrl
import chat.rocket.android.server.domain.gitlabUrl
import chat.rocket.android.server.domain.isCasAuthenticationEnabled
import chat.rocket.android.server.domain.isFacebookAuthenticationEnabled
import chat.rocket.android.server.domain.isGithubAuthenticationEnabled
import chat.rocket.android.server.domain.isGitlabAuthenticationEnabled
import chat.rocket.android.server.domain.isGoogleAuthenticationEnabled
import chat.rocket.android.server.domain.isLinkedinAuthenticationEnabled
import chat.rocket.android.server.domain.isLoginFormEnabled
import chat.rocket.android.server.domain.isRegistrationEnabledForNewUsers
import chat.rocket.android.server.domain.isWordpressAuthenticationEnabled
import chat.rocket.android.server.domain.wordpressUrl
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.VersionInfo
import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.extensions.casUrl
import chat.rocket.android.util.extensions.generateRandomString
import chat.rocket.android.util.extensions.parseColor
import chat.rocket.android.util.extensions.samlUrl
import chat.rocket.android.util.retryIO
import chat.rocket.common.RocketChatException
import chat.rocket.common.RocketChatInvalidProtocolException
import chat.rocket.common.model.ServerInfo
import chat.rocket.core.RocketChatClient
import chat.rocket.core.internal.rest.serverInfo
import chat.rocket.core.internal.rest.settingsOauth
import kotlinx.coroutines.experimental.Job
import timber.log.Timber
abstract class CheckServerPresenter constructor(private val strategy: CancelStrategy,
private val factory: RocketChatClientFactory,
private val view: VersionCheckView) {
private const val SERVICE_NAME_FACEBOOK = "facebook"
private const val SERVICE_NAME_GITHUB = "github"
private const val SERVICE_NAME_GOOGLE = "google"
private const val SERVICE_NAME_LINKEDIN = "linkedin"
private const val SERVICE_NAME_GILAB = "gitlab"
private const val SERVICE_NAME_WORDPRESS = "wordpress"
abstract class CheckServerPresenter constructor(
private val strategy: CancelStrategy,
private val factory: RocketChatClientFactory,
private val settingsInteractor: GetSettingsInteractor? = null,
private val view: VersionCheckView? = null
) {
private lateinit var currentServer: String
private lateinit var client: RocketChatClient
private lateinit var settings: PublicSettings
internal var state: String = ""
internal var facebookOauthUrl: String? = null
internal var githubOauthUrl: String? = null
internal var googleOauthUrl: String? = null
internal var linkedinOauthUrl: String? = null
internal var gitlabOauthUrl: String? = null
internal var wordpressOauthUrl: String? = null
internal var casLoginUrl: String? = null
internal var casToken: String? = null
internal var customOauthUrl: String? = null
internal var customOauthServiceName: String? = null
internal var customOauthServiceNameTextColor: Int = 0
internal var customOauthServiceButtonColor: Int = 0
internal var samlUrl: String? = null
internal var samlToken: String? = null
internal var samlServiceName: String? = null
internal var samlServiceNameTextColor: Int = 0
internal var samlServiceButtonColor: Int = 0
internal var totalSocialAccountsEnabled = 0
internal var isLoginFormEnabled = false
internal var isNewAccountCreationEnabled = false
internal fun setupConnectionInfo(serverUrl: String) {
settingsInteractor?.get(serverUrl)?.let {
settings = it
}
client = factory.create(serverUrl)
}
internal fun checkServerInfo(serverUrl: String): Job {
return launchUI(strategy) {
try {
currentServer = serverUrl
client = factory.create(currentServer)
val serverInfo = retryIO(description = "serverInfo", times = 5) {
client.serverInfo()
}
if (serverInfo.redirected) {
view.updateServerUrl(serverInfo.url)
view?.updateServerUrl(serverInfo.url)
}
val version = checkServerVersion(serverInfo)
when (version) {
is Version.VersionOk -> {
Timber.i("Your version is nice! (Requires: 0.62.0, Yours: ${version.version})")
view.versionOk()
view?.versionOk()
}
is Version.RecommendedVersionWarning -> {
Timber.i("Your server ${version.version} is bellow recommended version ${BuildConfig.RECOMMENDED_SERVER_VERSION}")
view.alertNotRecommendedVersion()
view?.alertNotRecommendedVersion()
}
is Version.OutOfDateError -> {
Timber.i("Oops. Looks like your server ${version.version} is out-of-date! Minimum server version required ${BuildConfig.REQUIRED_SERVER_VERSION}!")
view.blockAndAlertNotRequiredVersion()
view?.blockAndAlertNotRequiredVersion()
}
}
} catch (ex: Exception) {
Timber.d(ex, "Error getting server info")
when (ex) {
is RocketChatInvalidProtocolException -> {
view.errorInvalidProtocol()
is RocketChatInvalidProtocolException -> view?.errorInvalidProtocol()
else -> view?.errorCheckingServerVersion()
}
}
}
}
internal suspend fun checkEnabledAccounts(serverUrl: String) {
try {
val services = retryIO("settingsOauth()") {
client.settingsOauth().services
}
if (services.isNotEmpty()) {
state = OauthHelper.getState()
// OAuth accounts.
if (settings.isFacebookAuthenticationEnabled()) {
getServiceMap(services, SERVICE_NAME_FACEBOOK)?.let { serviceMap ->
getOauthClientId(serviceMap)?.let { clientId ->
facebookOauthUrl =
OauthHelper.getFacebookOauthUrl(clientId, serverUrl, state)
totalSocialAccountsEnabled++
}
}
}
if (settings.isGithubAuthenticationEnabled()) {
getServiceMap(services, SERVICE_NAME_GITHUB)?.let { serviceMap ->
getOauthClientId(serviceMap)?.let { clientId ->
githubOauthUrl =
OauthHelper.getGithubOauthUrl(clientId, state)
totalSocialAccountsEnabled++
}
}
}
if (settings.isGoogleAuthenticationEnabled()) {
getServiceMap(services, SERVICE_NAME_GOOGLE)?.let { serviceMap ->
getOauthClientId(serviceMap)?.let { clientId ->
googleOauthUrl =
OauthHelper.getGoogleOauthUrl(clientId, serverUrl, state)
totalSocialAccountsEnabled++
}
}
else -> {
view.errorCheckingServerVersion()
}
if (settings.isLinkedinAuthenticationEnabled()) {
getServiceMap(services, SERVICE_NAME_LINKEDIN)?.let { serviceMap ->
getOauthClientId(serviceMap)?.let { clientId ->
linkedinOauthUrl =
OauthHelper.getLinkedinOauthUrl(clientId, serverUrl, state)
totalSocialAccountsEnabled++
}
}
}
if (settings.isGitlabAuthenticationEnabled()) {
getServiceMap(services, SERVICE_NAME_GILAB)?.let { serviceMap ->
getOauthClientId(serviceMap)?.let { clientId ->
gitlabOauthUrl = if (settings.gitlabUrl() != null) {
OauthHelper.getGitlabOauthUrl(
host = settings.gitlabUrl(),
clientId = clientId,
serverUrl = serverUrl,
state = state
)
} else {
OauthHelper.getGitlabOauthUrl(
clientId = clientId,
serverUrl = serverUrl,
state = state
)
}
totalSocialAccountsEnabled++
}
}
}
if (settings.isWordpressAuthenticationEnabled()) {
getServiceMap(services, SERVICE_NAME_WORDPRESS)?.let { serviceMap ->
getOauthClientId(serviceMap)?.let { clientId ->
wordpressOauthUrl =
if (settings.wordpressUrl().isNullOrEmpty()) {
OauthHelper.getWordpressComOauthUrl(
clientId,
serverUrl,
state
)
} else {
OauthHelper.getWordpressCustomOauthUrl(
getCustomOauthHost(serviceMap)
?: "https://public-api.wordpress.com",
getCustomOauthAuthorizePath(serviceMap)
?: "/oauth/authorize",
clientId,
serverUrl,
SERVICE_NAME_WORDPRESS,
state,
getCustomOauthScope(serviceMap) ?: "openid"
)
}
totalSocialAccountsEnabled++
}
}
}
// CAS account.
if (settings.isCasAuthenticationEnabled()) {
casToken = generateRandomString(17)
casLoginUrl = settings.casLoginUrl().casUrl(serverUrl, casToken.toString())
totalSocialAccountsEnabled++
}
// Custom OAuth account.
getCustomOauthServices(services).let {
for (serviceMap in it) {
customOauthServiceName = getCustomOauthServiceName(serviceMap)
val host = getCustomOauthHost(serviceMap)
val authorizePath = getCustomOauthAuthorizePath(serviceMap)
val clientId = getOauthClientId(serviceMap)
val scope = getCustomOauthScope(serviceMap)
val serviceNameTextColor =
getServiceNameColorForCustomOauthOrSaml(serviceMap)
val serviceButtonColor = getServiceButtonColor(serviceMap)
if (customOauthServiceName != null &&
host != null &&
authorizePath != null &&
clientId != null &&
scope != null &&
serviceNameTextColor != null &&
serviceButtonColor != null
) {
customOauthUrl = OauthHelper.getCustomOauthUrl(
host,
authorizePath,
clientId,
serverUrl,
customOauthServiceName.toString(),
state,
scope
)
customOauthServiceNameTextColor = serviceNameTextColor
customOauthServiceButtonColor = serviceButtonColor
totalSocialAccountsEnabled++
}
}
}
// SAML account.
getSamlServices(services).let {
samlToken = generateRandomString(17)
for (serviceMap in it) {
val provider = getSamlProvider(serviceMap)
samlServiceName = getSamlServiceName(serviceMap)
val serviceNameTextColor =
getServiceNameColorForCustomOauthOrSaml(serviceMap)
val serviceButtonColor = getServiceButtonColor(serviceMap)
if (provider != null &&
samlServiceName != null &&
serviceNameTextColor != null &&
serviceButtonColor != null
) {
samlUrl = serverUrl.samlUrl(provider, samlToken.toString())
samlServiceNameTextColor = serviceNameTextColor
samlServiceButtonColor = serviceButtonColor
totalSocialAccountsEnabled++
}
}
}
}
} catch (exception: RocketChatException) {
Timber.e(exception)
}
}
internal fun checkIfLoginFormIsEnabled() {
if (settings.isLoginFormEnabled()) {
isLoginFormEnabled = true
}
}
internal fun checkIfCreateNewAccountIsEnabled() {
if (settings.isRegistrationEnabledForNewUsers() && settings.isLoginFormEnabled()) {
isNewAccountCreationEnabled = true
}
}
/** Returns an OAuth service map given a [serviceName].
*
* @param listMap The list of [Map] to get the service from.
* @param serviceName The service name to get in the [listMap]
* @return The OAuth service map or null otherwise.
*/
private fun getServiceMap(
listMap: List<Map<String, Any>>,
serviceName: String
): Map<String, Any>? = listMap.find { map -> map.containsValue(serviceName) }
/**
* Returns the OAuth client ID of a [serviceMap].
* REMARK: This function works for common OAuth providers (Google, Facebook, Github and so on)
* as well as custom OAuth.
*
* @param serviceMap The service map to get the OAuth client ID.
* @return The OAuth client ID or null otherwise.
*/
private fun getOauthClientId(serviceMap: Map<String, Any>): String? =
serviceMap["clientId"] as? String ?: serviceMap["appId"] as? String
/**
* Returns a custom OAuth service list.
*
* @return A custom OAuth service list, otherwise an empty list if there is no custom OAuth service.
*/
private fun getCustomOauthServices(listMap: List<Map<String, Any>>): List<Map<String, Any>> =
listMap.filter { map -> map["custom"] == true }
/** Returns the custom OAuth service host.
*
* @param serviceMap The service map to get the custom OAuth service host.
* @return The custom OAuth service host, otherwise null.
*/
private fun getCustomOauthHost(serviceMap: Map<String, Any>): String? =
serviceMap["serverURL"] as? String
/** Returns the custom OAuth service authorize path.
*
* @param serviceMap The service map to get the custom OAuth service authorize path.
* @return The custom OAuth service authorize path, otherwise null.
*/
private fun getCustomOauthAuthorizePath(serviceMap: Map<String, Any>): String? =
serviceMap["authorizePath"] as? String
/** Returns the custom OAuth service scope.
*
* @param serviceMap The service map to get the custom OAuth service scope.
* @return The custom OAuth service scope, otherwise null.
*/
private fun getCustomOauthScope(serviceMap: Map<String, Any>): String? =
serviceMap["scope"] as? String
/** Returns the text of the custom OAuth service.
*
* @param serviceMap The service map to get the text of the custom OAuth service.
* @return The text of the custom OAuth service, otherwise null.
*/
private fun getCustomOauthServiceName(serviceMap: Map<String, Any>): String? =
serviceMap["service"] as? String
/**
* Returns a SAML OAuth service list.
*
* @return A SAML service list, otherwise an empty list if there is no SAML OAuth service.
*/
private fun getSamlServices(listMap: List<Map<String, Any>>): List<Map<String, Any>> =
listMap.filter { map -> map["service"] == "saml" }
/**
* Returns the SAML provider.
*
* @param serviceMap The service map to provider from.
* @return The SAML provider, otherwise null.
*/
private fun getSamlProvider(serviceMap: Map<String, Any>): String? =
(serviceMap["clientConfig"] as Map<*, *>)["provider"] as? String
/**
* Returns the text of the SAML service.
*
* @param serviceMap The service map to get the text of the SAML service.
* @return The text of the SAML service, otherwise null.
*/
private fun getSamlServiceName(serviceMap: Map<String, Any>): String? =
serviceMap["buttonLabelText"] as? String
/**
* Returns the text color of the service name.
* REMARK: This can be used for custom OAuth or SAML.
*
* @param serviceMap The service map to get the text color from.
* @return The text color of the service (custom OAuth or SAML), otherwise null.
*/
private fun getServiceNameColorForCustomOauthOrSaml(serviceMap: Map<String, Any>): Int? =
(serviceMap["buttonLabelColor"] as? String)?.parseColor()
/**
* Returns the button color of the service name.
* REMARK: This can be used for custom OAuth or SAML.
*
* @param serviceMap The service map to get the button color from.
* @return The button color of the service (custom OAuth or SAML), otherwise null.
*/
private fun getServiceButtonColor(serviceMap: Map<String, Any>): Int? =
(serviceMap["buttonColor"] as? String)?.parseColor()
private fun checkServerVersion(serverInfo: ServerInfo): Version {
val thisServerVersion = serverInfo.version
val isRequiredVersion = isRequiredServerVersion(thisServerVersion)
val isRecommendedVersion = isRecommendedServerVersion(thisServerVersion)
return if (isRequiredVersion) {
if (isRecommendedVersion) {
Timber.i("Your version is nice! (Requires: 0.62.0, Yours: $thisServerVersion)")
......@@ -81,7 +430,10 @@ abstract class CheckServerPresenter constructor(private val strategy: CancelStra
}
private fun isRecommendedServerVersion(version: String): Boolean {
return isMinimumVersion(version, getVersionDistilled(BuildConfig.RECOMMENDED_SERVER_VERSION))
return isMinimumVersion(
version,
getVersionDistilled(BuildConfig.RECOMMENDED_SERVER_VERSION)
)
}
private fun isMinimumVersion(version: String, required: VersionInfo): Boolean {
......@@ -115,12 +467,14 @@ abstract class CheckServerPresenter constructor(private val strategy: CancelStra
val major = getVersionNumber(split, 0)
val minor = getVersionNumber(split, 1)
val update = getVersionNumber(split, 2)
return VersionInfo(
major = major,
minor = minor,
update = update,
release = release,
full = version)
major = major,
minor = minor,
update = update,
release = release,
full = version
)
}
private fun getVersionNumber(split: List<String>, index: Int): Int {
......@@ -133,7 +487,9 @@ abstract class CheckServerPresenter constructor(private val strategy: CancelStra
sealed class Version(val version: String) {
data class VersionOk(private val currentVersion: String) : Version(currentVersion)
data class RecommendedVersionWarning(private val currentVersion: String) : Version(currentVersion)
data class RecommendedVersionWarning(private val currentVersion: String) :
Version(currentVersion)
data class OutOfDateError(private val currentVersion: String) : Version(currentVersion)
}
}
\ No newline at end of file
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.view.View
import android.view.ViewAnimationUtils
import android.view.animation.AccelerateInterpolator
import android.view.animation.DecelerateInterpolator
import androidx.fragment.app.Fragment
import androidx.core.view.isVisible
fun View.rotateBy(value: Float, duration: Long = 100) {
animate()
......@@ -23,7 +15,7 @@ fun View.rotateBy(value: Float, duration: Long = 100) {
fun View.fadeIn(startValue: Float = 0f, finishValue: Float = 1f, duration: Long = 200) {
if (alpha == finishValue) {
setVisible(true)
isVisible = true
return
}
......@@ -35,15 +27,16 @@ fun View.fadeIn(startValue: Float = 0f, finishValue: Float = 1f, duration: Long
animate()
.alpha(finishValue)
.setDuration(duration / 2)
.setInterpolator(AccelerateInterpolator()).start()
.setInterpolator(AccelerateInterpolator())
.start()
}.start()
setVisible(true)
isVisible = true
}
fun View.fadeOut(startValue: Float = 1f, finishValue: Float = 0f, duration: Long = 200) {
if (alpha == finishValue) {
setVisible(false)
isVisible = false
return
}
......@@ -55,10 +48,11 @@ fun View.fadeOut(startValue: Float = 1f, finishValue: Float = 0f, duration: Long
animate()
.alpha(finishValue)
.setDuration(duration)
.setInterpolator(AccelerateInterpolator()).start()
.setInterpolator(AccelerateInterpolator())
.start()
}.start()
setVisible(false)
isVisible = false
}
fun View.circularRevealOrUnreveal(
......@@ -72,43 +66,7 @@ fun View.circularRevealOrUnreveal(
ViewAnimationUtils.createCircularReveal(this, centerX, centerY, startRadius, endRadius)
anim.duration = duration
if (startRadius < endRadius) {
setVisible(true)
} else {
setVisible(false)
}
isVisible = startRadius < endRadius
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
......@@ -3,6 +3,7 @@ package chat.rocket.android.util.extensions
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Context
import android.os.Build
import android.view.LayoutInflater
import android.view.Menu
import android.view.MenuItem
......@@ -16,9 +17,26 @@ import androidx.annotation.StringRes
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.view.SupportMenuInflater
import androidx.appcompat.view.menu.MenuBuilder
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import chat.rocket.android.R
fun FragmentActivity.setLightStatusBar(view: View) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
var flags = view.systemUiVisibility
flags = flags or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
view.systemUiVisibility = flags
window.statusBarColor = ContextCompat.getColor(this, R.color.colorWhite)
}
}
fun FragmentActivity.clearLightStatusBar() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
window.statusBarColor = ContextCompat.getColor(this, R.color.colorPrimary)
}
}
// TODO: Remove. Use KTX instead.
fun View.setVisible(visible: Boolean) {
visibility = if (visible) {
......@@ -35,11 +53,13 @@ fun View.isVisible(): Boolean {
fun ViewGroup.inflate(@LayoutRes resource: Int, attachToRoot: Boolean = false): View =
LayoutInflater.from(context).inflate(resource, this, attachToRoot)
fun AppCompatActivity.addFragment(tag: String, layoutId: Int, allowStateLoss: Boolean = false,
newInstance: () -> Fragment) {
fun AppCompatActivity.addFragment(
tag: String, layoutId: Int, allowStateLoss: Boolean = false,
newInstance: () -> Fragment
) {
val fragment = supportFragmentManager.findFragmentByTag(tag) ?: newInstance()
val transaction = supportFragmentManager.beginTransaction()
.replace(layoutId, fragment, tag)
.replace(layoutId, fragment, tag)
if (allowStateLoss) {
transaction.commitAllowingStateLoss()
} else {
......
......@@ -11,23 +11,27 @@ 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): Intent {
fun Context.webViewIntent(webPageUrl: String, toolbarTitle: String? = null): Intent {
return Intent(this, WebViewActivity::class.java).apply {
putExtra(INTENT_WEB_PAGE_URL, webPageUrl)
putExtra(TOOLBAR_TITLE, toolbarTitle)
}
}
private const val INTENT_WEB_PAGE_URL = "web_page_url"
private const val TOOLBAR_TITLE = "toolbar_title"
// Simple WebView to load URL.
class WebViewActivity : AppCompatActivity() {
private lateinit var webPageUrl: String
private var toolbarTitle: String? = ""
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_web_view)
webPageUrl = intent.getStringExtra(INTENT_WEB_PAGE_URL)
toolbarTitle = intent.getStringExtra(TOOLBAR_TITLE)
requireNotNull(webPageUrl) { "no web_page_url provided in Intent extras" }
setupToolbar()
......@@ -47,7 +51,7 @@ class WebViewActivity : AppCompatActivity() {
}
private fun setupToolbar() {
toolbar.title = getString(R.string.title_legal_terms)
toolbar.title = if(toolbarTitle != null) toolbarTitle else webPageUrl.replace("https://","")
toolbar.setNavigationIcon(R.drawable.ic_close_white_24dp)
toolbar.setNavigationOnClickListener {
finishActivity()
......
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="@color/colorAuthenticationChevronAndExpandIcon" />
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@color/colorPrimary">
<item
android:id="@android:id/background"
android:drawable="@color/colorAccent" />
</ripple>
\ No newline at end of file
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="@color/colorWhite"
android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
</vector>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M19,3h-4.18C14.4,1.84 13.3,1 12,1c-1.3,0 -2.4,0.84 -2.82,2L5,3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2L21,5c0,-1.1 -0.9,-2 -2,-2zM12,3c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zM12,7c1.66,0 3,1.34 3,3s-1.34,3 -3,3 -3,-1.34 -3,-3 1.34,-3 3,-3zM18,19L6,19v-1.4c0,-2 4,-3.1 6,-3.1s6,1.1 6,3.1L18,19z" />
</vector>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="20dp"
android:height="20dp"
android:viewportWidth="20"
android:viewportHeight="20">
<path
android:fillType="evenOdd"
android:pathData="M0 0h20v20H0z" />
<path
android:fillType="evenOdd"
android:strokeColor="#2F343D"
android:strokeWidth="1.5"
android:pathData="M 10 7 C 11.6568542495 7 13 8.34314575051 13 10 C 13 11.6568542495 11.6568542495 13 10 13 C 8.34314575051 13 7 11.6568542495 7 10 C 7 8.34314575051 8.34314575051 7 10 7 Z" />
<path
android:fillType="evenOdd"
android:strokeColor="#2F343D"
android:strokeWidth="1.5"
android:pathData="M12.68 16.469A7 7 0 1 1 17 10v0.542" />
<path
android:fillType="evenOdd"
android:strokeColor="#2F343D"
android:strokeWidth="1.5"
android:pathData="M17 10.49c0 1.387-0.781 2.51-2 2.51-1.219 0-2-1.112-2-2.51V6" />
</vector>
\ No newline at end of file
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M23.983,11.3C23.877,13.492 23.269,15.232 22.159,16.52C21.049,17.808 19.556,18.453 17.682,18.453C16.855,18.453 16.142,18.294 15.541,17.978C14.94,17.661 14.486,17.207 14.179,16.616C13.217,17.815 11.963,18.414 10.415,18.414C9.012,18.414 7.926,17.888 7.157,16.835C6.388,15.782 6.1,14.391 6.292,12.661C6.465,11.249 6.866,9.993 7.496,8.892C8.125,7.792 8.921,6.947 9.882,6.356C10.843,5.766 11.881,5.47 12.996,5.47C14.371,5.47 15.543,5.753 16.514,6.318L17.423,6.87L16.687,14.292C16.591,14.968 16.675,15.486 16.94,15.846C17.204,16.205 17.624,16.385 18.201,16.385C19.085,16.385 19.826,15.925 20.421,15.005C21.017,14.085 21.344,12.88 21.402,11.39C21.565,8.402 20.89,6.106 19.376,4.501C17.862,2.896 15.62,2.093 12.65,2.093C10.795,2.093 9.142,2.513 7.69,3.352C6.239,4.19 5.095,5.383 4.259,6.928C3.423,8.473 2.952,10.243 2.846,12.238C2.692,15.268 3.375,17.612 4.893,19.268C6.412,20.925 8.685,21.753 11.713,21.753C12.511,21.753 13.347,21.674 14.222,21.515C15.096,21.357 15.851,21.145 16.485,20.88L17.033,22.857C16.447,23.2 15.656,23.476 14.662,23.685C13.667,23.895 12.665,24 11.655,24C9.118,24 6.955,23.544 5.167,22.632C3.379,21.721 2.046,20.377 1.166,18.6C0.287,16.824 -0.095,14.703 0.02,12.238C0.135,9.875 0.741,7.756 1.837,5.881C2.932,4.006 4.425,2.558 6.314,1.535C8.202,0.511 10.334,0 12.708,0C15.12,0 17.197,0.46 18.936,1.38C20.676,2.301 21.979,3.617 22.844,5.329C23.709,7.041 24.088,9.032 23.983,11.3ZM9.493,12.661C9.387,13.791 9.497,14.65 9.824,15.236C10.151,15.822 10.68,16.116 11.41,16.116C11.881,16.116 12.328,15.932 12.751,15.563C13.174,15.195 13.525,14.665 13.804,13.971L14.409,7.897C14.034,7.786 13.65,7.73 13.256,7.73C12.15,7.73 11.293,8.15 10.682,8.989C10.072,9.828 9.675,11.052 9.493,12.661Z" />
</vector>
\ No newline at end of file
......@@ -5,5 +5,5 @@
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M16.59,8.59L12,13.17 7.41,8.59 6,10l6,6 6,-6z"/>
android:pathData="M10,6L8.59,7.41 13.17,12l-4.58,4.59L10,18l6,-6z"/>
</vector>
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="30dp"
android:height="30dp"
android:viewportWidth="30"
android:viewportHeight="30">
<path
android:pathData="M13.086 8.636l4.242-4.243a5.5 5.5 0 0 1 7.779 7.779l-4.243 4.242a5.5 5.5 0 0 1-7.778 0"
android:strokeWidth="1.5"
android:strokeColor="#FF000000"
android:strokeLineCap="round" />
<path
android:pathData="M15.914 21.364l-4.242 4.243a5.5 5.5 0 0 1-7.779-7.779l4.243-4.242a5.5 5.5 0 0 1 7.778 0"
android:strokeWidth="1.5"
android:strokeColor="#FF000000"
android:strokeLineCap="round" />
</vector>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="20dp"
android:height="20dp"
android:viewportWidth="20"
android:viewportHeight="20">
<path
android:fillType="evenOdd"
android:pathData="M0 0h20v20H0z" />
<path
android:fillType="evenOdd"
android:strokeColor="#2F343D"
android:strokeWidth="1.5"
android:pathData="M3 3.75c-0.69 0-1.25 0.56 -1.25 1.25v10c0 0.69 0.56 1.25 1.25 1.25h14c0.69 0 1.25-0.56 1.25-1.25V5c0-0.69-0.56-1.25-1.25-1.25H3z" />
<path
android:fillType="evenOdd"
android:strokeColor="#2F343D"
android:strokeWidth="1.5"
android:pathData="M16.074 7.064l-6.01 4.01-6.01-4.01" />
</vector>
\ No newline at end of file
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M20,4L4,4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,6c0,-1.1 -0.9,-2 -2,-2zM20,8l-8,5 -8,-5L4,6l8,5 8,-5v2z" />
</vector>
\ No newline at end of file
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
</vector>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="290dp"
android:height="40dp"
android:viewportHeight="40.0"
android:viewportWidth="290.0">
<path
android:fillColor="#2C5C9B"
android:fillType="evenOdd"
android:pathData="M2,0L288,0A2,2 0,0 1,290 2L290,38A2,2 0,0 1,288 40L2,40A2,2 0,0 1,0 38L0,2A2,2 0,0 1,2 0z"
android:strokeColor="#00000000"
android:strokeWidth="1" />
<path
android:fillColor="#FFFFFF"
android:fillType="evenOdd"
android:pathData="M122.76,21.33L117.61,21.33L117.61,26.75L115.38,26.75L115.38,13.95L123.51,13.95L123.51,15.75L117.61,15.75L117.61,19.55L122.76,19.55L122.76,21.33ZM132.25,26.75C132.15,26.57 132.07,26.27 132,25.86C131.32,26.57 130.49,26.93 129.51,26.93C128.55,26.93 127.77,26.65 127.17,26.11C126.56,25.56 126.26,24.89 126.26,24.09C126.26,23.07 126.64,22.3 127.39,21.75C128.15,21.21 129.22,20.94 130.62,20.94L131.93,20.94L131.93,20.32C131.93,19.82 131.79,19.43 131.52,19.13C131.24,18.84 130.82,18.69 130.26,18.69C129.78,18.69 129.38,18.81 129.07,19.06C128.76,19.3 128.6,19.61 128.6,19.98L126.47,19.98C126.47,19.46 126.64,18.97 126.98,18.52C127.33,18.06 127.8,17.71 128.39,17.45C128.99,17.19 129.65,17.06 130.39,17.06C131.5,17.06 132.39,17.34 133.05,17.9C133.71,18.46 134.05,19.25 134.07,20.26L134.07,24.55C134.07,25.41 134.19,26.09 134.43,26.6L134.43,26.75L132.25,26.75ZM129.9,25.21C130.32,25.21 130.72,25.11 131.09,24.9C131.46,24.7 131.74,24.42 131.93,24.08L131.93,22.29L130.78,22.29C129.99,22.29 129.39,22.42 129,22.7C128.6,22.97 128.4,23.36 128.4,23.87C128.4,24.28 128.54,24.6 128.81,24.85C129.08,25.09 129.44,25.21 129.9,25.21ZM142.01,25.22C142.54,25.22 142.99,25.07 143.34,24.75C143.69,24.44 143.88,24.06 143.9,23.6L145.91,23.6C145.89,24.2 145.71,24.75 145.36,25.26C145.01,25.77 144.55,26.18 143.95,26.48C143.36,26.78 142.72,26.93 142.04,26.93C140.71,26.93 139.65,26.5 138.87,25.63C138.09,24.77 137.7,23.58 137.7,22.07L137.7,21.85C137.7,20.4 138.09,19.24 138.86,18.37C139.64,17.5 140.69,17.06 142.03,17.06C143.16,17.06 144.08,17.39 144.79,18.05C145.5,18.71 145.88,19.58 145.91,20.65L143.9,20.65C143.88,20.11 143.69,19.66 143.34,19.31C142.99,18.95 142.55,18.78 142.01,18.78C141.32,18.78 140.79,19.03 140.41,19.53C140.04,20.03 139.85,20.79 139.84,21.81L139.84,22.15C139.84,23.18 140.03,23.95 140.4,24.46C140.77,24.97 141.31,25.22 142.01,25.22ZM153.51,26.93C152.16,26.93 151.06,26.5 150.22,25.65C149.38,24.79 148.96,23.66 148.96,22.24L148.96,21.98C148.96,21.03 149.14,20.18 149.51,19.43C149.88,18.69 150.39,18.1 151.05,17.69C151.72,17.27 152.45,17.06 153.27,17.06C154.56,17.06 155.56,17.48 156.27,18.3C156.98,19.13 157.33,20.3 157.33,21.81L157.33,22.67L151.12,22.67C151.18,23.46 151.44,24.08 151.9,24.54C152.36,24.99 152.94,25.22 153.64,25.22C154.62,25.22 155.41,24.83 156.03,24.03L157.18,25.13C156.8,25.7 156.29,26.14 155.65,26.46C155.02,26.77 154.31,26.93 153.51,26.93ZM153.26,18.78C152.67,18.78 152.2,18.98 151.84,19.39C151.48,19.8 151.25,20.37 151.15,21.11L155.22,21.11L155.22,20.95C155.17,20.23 154.98,19.69 154.65,19.33C154.31,18.96 153.85,18.78 153.26,18.78ZM169.13,22.09C169.13,23.57 168.8,24.74 168.14,25.62C167.48,26.49 166.58,26.93 165.42,26.93C164.31,26.93 163.44,26.52 162.82,25.72L162.71,26.75L160.78,26.75L160.78,13.25L162.92,13.25L162.92,18.15C163.53,17.43 164.36,17.06 165.4,17.06C166.56,17.06 167.48,17.5 168.14,18.36C168.8,19.22 169.13,20.42 169.13,21.97L169.13,22.09ZM167,21.91C167,20.88 166.81,20.1 166.45,19.59C166.09,19.07 165.56,18.81 164.87,18.81C163.94,18.81 163.29,19.22 162.92,20.03L162.92,23.95C163.3,24.77 163.95,25.19 164.89,25.19C165.55,25.19 166.07,24.94 166.43,24.44C166.8,23.94 166.98,23.19 167,22.18L167,21.91ZM172.32,21.91C172.32,20.98 172.5,20.14 172.87,19.39C173.24,18.64 173.76,18.07 174.43,17.67C175.1,17.27 175.86,17.06 176.73,17.06C178.01,17.06 179.06,17.48 179.86,18.3C180.66,19.13 181.09,20.23 181.15,21.59L181.16,22.09C181.16,23.03 180.98,23.87 180.62,24.61C180.26,25.34 179.74,25.92 179.07,26.32C178.4,26.72 177.63,26.93 176.75,26.93C175.41,26.93 174.33,26.48 173.53,25.59C172.72,24.69 172.32,23.5 172.32,22.01L172.32,21.91ZM174.46,22.09C174.46,23.07 174.66,23.84 175.06,24.39C175.47,24.94 176.03,25.22 176.75,25.22C177.47,25.22 178.03,24.94 178.43,24.38C178.83,23.81 179.03,22.99 179.03,21.91C179.03,20.95 178.83,20.18 178.41,19.62C178,19.06 177.44,18.78 176.73,18.78C176.03,18.78 175.48,19.06 175.07,19.61C174.66,20.16 174.46,20.99 174.46,22.09ZM184.37,21.91C184.37,20.98 184.55,20.14 184.92,19.39C185.29,18.64 185.81,18.07 186.48,17.67C187.14,17.27 187.91,17.06 188.78,17.06C190.06,17.06 191.1,17.48 191.9,18.3C192.7,19.13 193.14,20.23 193.2,21.59L193.21,22.09C193.21,23.03 193.03,23.87 192.67,24.61C192.31,25.34 191.79,25.92 191.12,26.32C190.45,26.72 189.68,26.93 188.8,26.93C187.46,26.93 186.38,26.48 185.58,25.59C184.77,24.69 184.37,23.5 184.37,22.01L184.37,21.91ZM186.5,22.09C186.5,23.07 186.71,23.84 187.11,24.39C187.51,24.94 188.08,25.22 188.8,25.22C189.52,25.22 190.08,24.94 190.48,24.38C190.88,23.81 191.08,22.99 191.08,21.91C191.08,20.95 190.88,20.18 190.46,19.62C190.05,19.06 189.49,18.78 188.78,18.78C188.08,18.78 187.53,19.06 187.12,19.61C186.71,20.16 186.5,20.99 186.5,22.09ZM199.9,22.67L198.96,23.65L198.96,26.75L196.82,26.75L196.82,13.25L198.96,13.25L198.96,21.04L199.62,20.2L202.25,17.24L204.82,17.24L201.28,21.2L205.2,26.75L202.73,26.75L199.9,22.67Z"
android:strokeColor="#00000000"
android:strokeWidth="1" />
<path
android:fillColor="#FFFFFF"
android:fillType="evenOdd"
android:pathData="M93.5,20.25m-10,0a10,10 0,1 1,20 0a10,10 0,1 1,-20 0"
android:strokeColor="#00000000"
android:strokeWidth="1" />
<path
android:fillColor="#2C5C9B"
android:fillType="nonZero"
android:pathData="M91.68,16.03C91.68,16.38 91.68,17.93 91.68,17.93L90.29,17.93L90.29,20.25L91.68,20.25L91.68,27.13L94.53,27.13L94.53,20.25L96.44,20.25C96.44,20.25 96.62,19.13 96.71,17.92C96.46,17.92 94.54,17.92 94.54,17.92C94.54,17.92 94.54,16.57 94.54,16.34C94.54,16.1 94.85,15.78 95.16,15.78C95.47,15.78 96.11,15.78 96.71,15.78C96.71,15.46 96.71,14.37 96.71,13.37C95.91,13.37 95,13.37 94.6,13.37C91.61,13.37 91.68,15.69 91.68,16.03Z"
android:strokeColor="#00000000"
android:strokeWidth="1" />
</vector>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FFF"
android:fillType="evenOdd"
android:pathData="M-31-124h360V885H-31z" />
<path
android:fillColor="#4267B2"
android:pathData="M22.676 0H1.324C0.593 0 0 0.593 0 1.324v21.352C0 23.407 0.593 24 1.324 24h11.504v-9.281H9.703v-3.633h3.125V8.412c0-3.099 1.895-4.787 4.659-4.787 0.931 -0.002 1.862 0.045 2.789 0.14 v3.24h-1.904c-1.506 0-1.8 0.712 -1.8 1.763v2.313h3.6l-0.467 3.633h-3.153V24h6.124c0.731 0 1.324-0.593 1.324-1.324V1.324C24 0.593 23.407 0 22.676 0z" />
<path
android:fillColor="#FFF"
android:pathData="M16.552 24v-9.281h3.132l0.468-3.633h-3.6V8.772c0-1.05 0.294 -1.762 1.8-1.762h1.924V3.766a26.566 26.566 0 0 0-2.793-0.141c-2.769 0-4.655 1.688-4.655 4.787v2.674H9.703v3.633h3.125V24h3.724z" />
</vector>
\ No newline at end of file
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="290dp"
android:height="40dp"
android:viewportHeight="40.0"
android:viewportWidth="290.0">
<path
android:fillColor="#4C4C4C"
android:fillType="evenOdd"
android:pathData="M2,0L288,0A2,2 0,0 1,290 2L290,38A2,2 0,0 1,288 40L2,40A2,2 0,0 1,0 38L0,2A2,2 0,0 1,2 0z"
android:strokeColor="#00000000"
android:strokeWidth="1" />
<path
android:fillColor="#FFFFFF"
android:fillType="evenOdd"
android:pathData="M139.73,25.09C139.27,25.69 138.62,26.15 137.8,26.46C136.98,26.77 136.05,26.93 135.01,26.93C133.94,26.93 132.99,26.68 132.16,26.2C131.33,25.71 130.7,25.01 130.25,24.11C129.8,23.2 129.57,22.15 129.55,20.94L129.55,19.94C129.55,18 130.01,16.49 130.94,15.41C131.86,14.32 133.16,13.78 134.81,13.78C136.24,13.78 137.37,14.13 138.21,14.83C139.05,15.54 139.55,16.55 139.72,17.87L137.54,17.87C137.29,16.33 136.4,15.56 134.86,15.56C133.86,15.56 133.11,15.92 132.59,16.64C132.07,17.36 131.8,18.41 131.78,19.8L131.78,20.78C131.78,22.17 132.07,23.24 132.66,24C133.24,24.77 134.05,25.15 135.09,25.15C136.22,25.15 137.03,24.89 137.51,24.38L137.51,21.87L134.88,21.87L134.88,20.18L139.73,20.18L139.73,25.09ZM146.04,26.75L143.9,26.75L143.9,17.24L146.04,17.24L146.04,26.75ZM143.77,14.77C143.77,14.44 143.87,14.17 144.08,13.95C144.29,13.74 144.59,13.63 144.97,13.63C145.36,13.63 145.66,13.74 145.87,13.95C146.08,14.17 146.19,14.44 146.19,14.77C146.19,15.09 146.08,15.36 145.87,15.57C145.66,15.79 145.36,15.9 144.97,15.9C144.59,15.9 144.29,15.79 144.08,15.57C143.87,15.36 143.77,15.09 143.77,14.77ZM152.84,14.93L152.84,17.24L154.52,17.24L154.52,18.82L152.84,18.82L152.84,24.13C152.84,24.49 152.91,24.76 153.05,24.92C153.2,25.08 153.45,25.16 153.82,25.16C154.07,25.16 154.32,25.13 154.57,25.07L154.57,26.72C154.08,26.86 153.61,26.93 153.16,26.93C151.52,26.93 150.7,26.02 150.7,24.21L150.7,18.82L149.14,18.82L149.14,17.24L150.7,17.24L150.7,14.93L152.84,14.93ZM160.05,18.28C160.75,17.47 161.63,17.06 162.7,17.06C164.72,17.06 165.75,18.22 165.78,20.54L165.78,26.75L163.65,26.75L163.65,20.62C163.65,19.96 163.5,19.49 163.22,19.22C162.94,18.95 162.52,18.81 161.97,18.81C161.11,18.81 160.47,19.19 160.05,19.96L160.05,26.75L157.92,26.75L157.92,13.25L160.05,13.25L160.05,18.28ZM175.5,25.82C174.87,26.56 173.98,26.93 172.83,26.93C171.8,26.93 171.02,26.62 170.49,26.02C169.96,25.42 169.69,24.54 169.69,23.4L169.69,17.24L171.83,17.24L171.83,23.38C171.83,24.58 172.33,25.19 173.33,25.19C174.37,25.19 175.07,24.81 175.43,24.07L175.43,17.24L177.57,17.24L177.57,26.75L175.55,26.75L175.5,25.82ZM189.89,22.09C189.89,23.57 189.56,24.74 188.9,25.62C188.24,26.49 187.33,26.93 186.18,26.93C185.06,26.93 184.2,26.52 183.58,25.72L183.47,26.75L181.54,26.75L181.54,13.25L183.67,13.25L183.67,18.15C184.29,17.43 185.12,17.06 186.16,17.06C187.32,17.06 188.23,17.5 188.89,18.36C189.56,19.22 189.89,20.42 189.89,21.97L189.89,22.09ZM187.75,21.91C187.75,20.88 187.57,20.1 187.21,19.59C186.84,19.07 186.32,18.81 185.62,18.81C184.7,18.81 184.05,19.22 183.67,20.03L183.67,23.95C184.05,24.77 184.71,25.19 185.64,25.19C186.31,25.19 186.83,24.94 187.19,24.44C187.55,23.94 187.74,23.19 187.75,22.18L187.75,21.91Z"
android:strokeColor="#00000000"
android:strokeWidth="1" />
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M115.79,15.15C114.91,13.65 113.72,12.46 112.22,11.58C110.72,10.7 109.08,10.27 107.3,10.27C105.52,10.27 103.88,10.7 102.38,11.58C100.88,12.46 99.69,13.65 98.81,15.15C97.94,16.65 97.5,18.29 97.5,20.07C97.5,22.2 98.12,24.12 99.37,25.83C100.62,27.53 102.23,28.71 104.2,29.37C104.43,29.41 104.6,29.38 104.71,29.28C104.82,29.18 104.88,29.05 104.88,28.9C104.88,28.87 104.87,28.64 104.87,28.21C104.86,27.77 104.86,27.4 104.86,27.07L104.57,27.12C104.38,27.16 104.15,27.17 103.86,27.17C103.58,27.16 103.28,27.13 102.97,27.08C102.67,27.02 102.38,26.9 102.12,26.7C101.86,26.5 101.67,26.23 101.56,25.91L101.43,25.62C101.35,25.42 101.21,25.2 101.03,24.97C100.85,24.73 100.66,24.57 100.47,24.48L100.38,24.42C100.32,24.38 100.27,24.32 100.22,24.26C100.17,24.21 100.13,24.15 100.1,24.09C100.08,24.03 100.1,23.98 100.17,23.94C100.24,23.9 100.36,23.88 100.54,23.88L100.79,23.92C100.96,23.95 101.17,24.06 101.42,24.23C101.67,24.4 101.88,24.62 102.04,24.89C102.24,25.24 102.47,25.5 102.75,25.69C103.03,25.87 103.31,25.96 103.59,25.96C103.87,25.96 104.11,25.94 104.31,25.9C104.52,25.86 104.71,25.79 104.89,25.71C104.96,25.14 105.17,24.7 105.51,24.39C105.03,24.34 104.59,24.26 104.21,24.16C103.82,24.06 103.42,23.89 103.01,23.66C102.59,23.43 102.25,23.15 101.98,22.81C101.71,22.47 101.48,22.02 101.31,21.47C101.13,20.92 101.05,20.28 101.05,19.56C101.05,18.53 101.38,17.65 102.06,16.93C101.74,16.15 101.77,15.29 102.14,14.32C102.39,14.25 102.76,14.3 103.24,14.5C103.73,14.69 104.08,14.85 104.31,14.99C104.53,15.12 104.71,15.24 104.85,15.33C105.64,15.11 106.46,15 107.3,15C108.14,15 108.96,15.11 109.75,15.33L110.24,15.03C110.57,14.82 110.96,14.63 111.41,14.46C111.86,14.29 112.2,14.25 112.44,14.32C112.83,15.29 112.86,16.15 112.55,16.93C113.22,17.65 113.55,18.53 113.55,19.56C113.55,20.28 113.47,20.92 113.29,21.48C113.12,22.03 112.89,22.48 112.62,22.82C112.34,23.15 111.99,23.44 111.58,23.66C111.17,23.89 110.77,24.06 110.38,24.16C110,24.26 109.56,24.34 109.07,24.39C109.52,24.78 109.74,25.38 109.74,26.2L109.74,28.9C109.74,29.05 109.79,29.18 109.9,29.28C110,29.38 110.17,29.41 110.4,29.37C112.38,28.71 113.99,27.53 115.23,25.83C116.48,24.12 117.1,22.2 117.1,20.07C117.1,18.29 116.66,16.65 115.79,15.15Z"
android:strokeColor="#00000000"
android:strokeWidth="1" />
</vector>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FFF"
android:fillType="evenOdd"
android:pathData="M-31-182h360V827H-31z" />
<path
android:fillColor="#161514"
android:fillType="evenOdd"
android:pathData="M11.999 0C5.373 0 0 5.373 0 12c0 5.302 3.438 9.8 8.207 11.386 0.6 0.11 0.819 -0.26 0.819 -0.578 0-0.284-0.01-1.04-0.017-2.04-3.337 0.724 -4.042-1.61-4.042-1.61-0.546-1.386-1.332-1.755-1.332-1.755-1.09-0.744 0.082 -0.73 0.082 -0.73 1.205 0.086 1.838 1.238 1.838 1.238 1.07 1.833 2.81 1.304 3.493 0.996 0.109-0.775 0.419 -1.304 0.762 -1.603C7.145 17 4.343 15.97 4.343 11.373c0-1.31 0.468 -2.382 1.236-3.22-0.124-0.304-0.536-1.524 0.118 -3.176 0 0 1.007-0.323 3.3 1.23 0.956 -0.266 1.983-0.4 3.003-0.404 1.02 0.005 2.046 0.138 3.005 0.404 2.29-1.553 3.296-1.23 3.296-1.23 0.655 1.652 0.243 2.872 0.12 3.176 0.77 0.838 1.233 1.91 1.233 3.22 0 4.61-2.806 5.624-5.478 5.921 0.43 0.37 0.814 1.103 0.814 2.223 0 1.603-0.015 2.898-0.015 3.291 0 0.321 0.217 0.695 0.825 0.578C20.565 21.796 24 17.3 24 12c0-6.627-5.373-12-12.001-12" />
</vector>
\ No newline at end of file
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="290dp"
android:height="40dp"
android:viewportHeight="40.0"
android:viewportWidth="290.0">
<path
android:fillColor="#383D47"
android:fillType="evenOdd"
android:pathData="M2,0L288,0A2,2 0,0 1,290 2L290,38A2,2 0,0 1,288 40L2,40A2,2 0,0 1,0 38L0,2A2,2 0,0 1,2 0z"
android:strokeColor="#00000000"
android:strokeWidth="1" />
<path
android:fillColor="#FFFFFF"
android:fillType="evenOdd"
android:pathData="M142.56,24.59C142.1,25.19 141.45,25.65 140.63,25.96C139.81,26.27 138.88,26.43 137.84,26.43C136.77,26.43 135.82,26.18 134.99,25.7C134.16,25.21 133.53,24.51 133.08,23.61C132.63,22.7 132.4,21.65 132.38,20.44L132.38,19.44C132.38,17.5 132.84,15.99 133.77,14.91C134.69,13.82 135.99,13.28 137.64,13.28C139.07,13.28 140.2,13.63 141.04,14.33C141.88,15.04 142.38,16.05 142.55,17.37L140.37,17.37C140.12,15.83 139.23,15.06 137.69,15.06C136.69,15.06 135.94,15.42 135.42,16.14C134.9,16.86 134.63,17.91 134.61,19.3L134.61,20.28C134.61,21.67 134.9,22.74 135.49,23.5C136.07,24.27 136.88,24.65 137.92,24.65C139.05,24.65 139.86,24.39 140.34,23.88L140.34,21.37L137.71,21.37L137.71,19.68L142.56,19.68L142.56,24.59ZM148.87,26.25L146.73,26.25L146.73,16.74L148.87,16.74L148.87,26.25ZM146.6,14.27C146.6,13.94 146.7,13.67 146.91,13.45C147.12,13.24 147.42,13.13 147.8,13.13C148.19,13.13 148.49,13.24 148.7,13.45C148.91,13.67 149.02,13.94 149.02,14.27C149.02,14.59 148.91,14.86 148.7,15.07C148.49,15.29 148.19,15.4 147.8,15.4C147.42,15.4 147.12,15.29 146.91,15.07C146.7,14.86 146.6,14.59 146.6,14.27ZM155.67,14.43L155.67,16.74L157.35,16.74L157.35,18.32L155.67,18.32L155.67,23.63C155.67,23.99 155.74,24.26 155.88,24.42C156.03,24.58 156.28,24.66 156.65,24.66C156.9,24.66 157.15,24.63 157.4,24.57L157.4,26.22C156.91,26.36 156.44,26.43 155.99,26.43C154.35,26.43 153.53,25.52 153.53,23.71L153.53,18.32L151.97,18.32L151.97,16.74L153.53,16.74L153.53,14.43L155.67,14.43ZM163.05,26.25L160.91,26.25L160.91,12.75L163.05,12.75L163.05,26.25ZM172.86,26.25C172.76,26.07 172.68,25.77 172.61,25.36C171.93,26.07 171.1,26.43 170.11,26.43C169.16,26.43 168.38,26.15 167.78,25.61C167.17,25.06 166.87,24.39 166.87,23.59C166.87,22.57 167.25,21.8 168,21.25C168.75,20.71 169.83,20.44 171.23,20.44L172.54,20.44L172.54,19.82C172.54,19.32 172.4,18.93 172.13,18.63C171.85,18.34 171.43,18.19 170.87,18.19C170.38,18.19 169.98,18.31 169.67,18.56C169.36,18.8 169.21,19.11 169.21,19.48L167.07,19.48C167.07,18.96 167.25,18.47 167.59,18.02C167.94,17.56 168.41,17.21 169,16.95C169.6,16.69 170.26,16.56 170.99,16.56C172.11,16.56 172.99,16.84 173.66,17.4C174.32,17.96 174.66,18.75 174.68,19.76L174.68,24.05C174.68,24.91 174.8,25.59 175.04,26.1L175.04,26.25L172.86,26.25ZM170.51,24.71C170.93,24.71 171.33,24.61 171.7,24.4C172.07,24.2 172.35,23.92 172.54,23.58L172.54,21.79L171.39,21.79C170.6,21.79 170,21.92 169.6,22.2C169.21,22.47 169.01,22.86 169.01,23.37C169.01,23.78 169.14,24.1 169.41,24.35C169.69,24.59 170.05,24.71 170.51,24.71ZM187.06,21.59C187.06,23.07 186.73,24.24 186.07,25.12C185.41,25.99 184.5,26.43 183.35,26.43C182.23,26.43 181.37,26.02 180.75,25.22L180.64,26.25L178.71,26.25L178.71,12.75L180.84,12.75L180.84,17.65C181.46,16.93 182.29,16.56 183.33,16.56C184.49,16.56 185.4,17 186.06,17.86C186.73,18.72 187.06,19.92 187.06,21.47L187.06,21.59ZM184.92,21.41C184.92,20.38 184.74,19.6 184.38,19.09C184.01,18.57 183.49,18.31 182.79,18.31C181.87,18.31 181.22,18.72 180.84,19.53L180.84,23.45C181.22,24.27 181.88,24.69 182.81,24.69C183.48,24.69 184,24.44 184.36,23.94C184.72,23.44 184.91,22.69 184.92,21.68L184.92,21.41Z"
android:strokeColor="#00000000"
android:strokeWidth="1" />
<path
android:fillAlpha="1"
android:fillColor="#ffffff"
android:pathData="M122.13,22.99L120.97,19.43C119.6,15.19 118.83,12.83 118.68,12.36C118.56,12 118.04,12 117.92,12.36C117.77,12.83 117,15.19 115.63,19.43L108,19.43C106.63,15.19 105.86,12.83 105.71,12.36C105.59,12 105.08,12 104.96,12.36C104.81,12.83 104.04,15.19 102.66,19.43C101.96,21.57 101.58,22.75 101.5,22.99C101.4,23.32 101.51,23.67 101.79,23.88C102.46,24.37 105.8,26.79 111.82,31.16C117.83,26.79 121.17,24.37 121.84,23.88C122.12,23.67 122.23,23.32 122.13,22.99" />
<path
android:fillAlpha="0"
android:fillColor="#FF000000"
android:pathData="M122.13,22.99L120.97,19.43C119.6,15.19 118.83,12.83 118.68,12.36C118.56,12 118.04,12 117.92,12.36C117.77,12.83 117,15.19 115.63,19.43L108,19.43C106.63,15.19 105.86,12.83 105.71,12.36C105.59,12 105.08,12 104.96,12.36C104.81,12.83 104.04,15.19 102.66,19.43C101.96,21.57 101.58,22.75 101.5,22.99C101.4,23.32 101.51,23.67 101.79,23.88C102.46,24.37 105.8,26.79 111.82,31.16C117.83,26.79 121.17,24.37 121.84,23.88C122.12,23.67 122.23,23.32 122.13,22.99"
android:strokeAlpha="0"
android:strokeColor="#000000"
android:strokeWidth="1" />
<path
android:fillAlpha="1"
android:fillColor="#ffffff"
android:pathData="M111.82,31.16L111.82,31.16L115.63,19.43L108.01,19.43L111.82,31.16Z" />
<path
android:fillAlpha="0"
android:fillColor="#FF000000"
android:pathData="M111.82,31.16L111.82,31.16L115.63,19.43L108.01,19.43L111.82,31.16Z"
android:strokeAlpha="0"
android:strokeColor="#000000"
android:strokeWidth="1" />
<path
android:fillAlpha="1"
android:fillColor="#ffffff"
android:pathData="M111.82,31.16L108.01,19.43L102.67,19.43L111.82,31.16Z" />
<path
android:fillAlpha="0"
android:fillColor="#FF000000"
android:pathData="M111.82,31.16L108.01,19.43L102.67,19.43L111.82,31.16Z"
android:strokeAlpha="0"
android:strokeColor="#000000"
android:strokeWidth="1" />
<path
android:fillAlpha="1"
android:fillColor="#ffffff"
android:pathData="M102.66,19.43C101.96,21.57 101.58,22.75 101.5,22.99C101.4,23.32 101.51,23.67 101.79,23.88C102.46,24.37 105.8,26.79 111.82,31.16L102.66,19.43L102.66,19.43L102.66,19.43Z" />
<path
android:fillAlpha="0"
android:fillColor="#FF000000"
android:pathData="M102.66,19.43C101.96,21.57 101.58,22.75 101.5,22.99C101.4,23.32 101.51,23.67 101.79,23.88C102.46,24.37 105.8,26.79 111.82,31.16L102.66,19.43L102.66,19.43L102.66,19.43Z"
android:strokeAlpha="0"
android:strokeColor="#000000"
android:strokeWidth="1" />
<path
android:fillAlpha="1"
android:fillColor="#ffffff"
android:pathData="M108,19.43C106.63,15.19 105.86,12.83 105.71,12.36C105.59,12 105.08,12 104.96,12.36C104.81,12.83 104.04,15.19 102.66,19.43L102.66,19.43L108,19.43Z" />
<path
android:fillAlpha="0"
android:fillColor="#FF000000"
android:pathData="M108,19.43C106.63,15.19 105.86,12.83 105.71,12.36C105.59,12 105.08,12 104.96,12.36C104.81,12.83 104.04,15.19 102.66,19.43L102.66,19.43L108,19.43Z"
android:strokeAlpha="0"
android:strokeColor="#000000"
android:strokeWidth="1" />
<path
android:fillAlpha="1"
android:fillColor="#ffffff"
android:pathData="M111.82,31.16L115.63,19.43L120.97,19.43L111.82,31.16Z" />
<path
android:fillAlpha="0"
android:fillColor="#FF000000"
android:pathData="M111.82,31.16L115.63,19.43L120.97,19.43L111.82,31.16Z"
android:strokeAlpha="0"
android:strokeColor="#000000"
android:strokeWidth="1" />
<path
android:fillAlpha="1"
android:fillColor="#ffffff"
android:pathData="M120.97,19.43C121.67,21.57 122.05,22.75 122.13,22.99C122.24,23.32 122.12,23.67 121.84,23.88C121.17,24.37 117.83,26.79 111.82,31.16L120.97,19.43L120.97,19.43L120.97,19.43Z" />
<path
android:fillAlpha="0"
android:fillColor="#FF000000"
android:pathData="M120.97,19.43C121.67,21.57 122.05,22.75 122.13,22.99C122.24,23.32 122.12,23.67 121.84,23.88C121.17,24.37 117.83,26.79 111.82,31.16L120.97,19.43L120.97,19.43L120.97,19.43Z"
android:strokeAlpha="0"
android:strokeColor="#000000"
android:strokeWidth="1" />
<path
android:fillAlpha="1"
android:fillColor="#ffffff"
android:pathData="M115.63,19.43C117,15.19 117.77,12.83 117.92,12.36C118.04,12 118.56,12 118.68,12.36C118.83,12.83 119.6,15.19 120.97,19.43L120.97,19.43L115.63,19.43Z" />
<path
android:fillAlpha="0"
android:fillColor="#FF000000"
android:pathData="M115.63,19.43C117,15.19 117.77,12.83 117.92,12.36C118.04,12 118.56,12 118.68,12.36C118.83,12.83 119.6,15.19 120.97,19.43L120.97,19.43L115.63,19.43Z"
android:strokeAlpha="0"
android:strokeColor="#000000"
android:strokeWidth="1" />
</vector>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FFF"
android:fillType="evenOdd"
android:pathData="M-31-240h360V769H-31z" />
<path
android:fillColor="#E24329"
android:pathData="M11.994 23.066l4.398-13.54H7.595z" />
<path
android:fillColor="#FC6D26"
android:pathData="M11.994 23.066l-4.4-13.54H1.43z" />
<path
android:fillColor="#FCA326"
android:pathData="M1.43 9.526L0.09 13.64a0.906 0.906 0 0 0 0.33 1.016l11.574 8.41L1.43 9.526z" />
<path
android:fillColor="#E24329"
android:pathData="M1.43 9.526h6.165L4.942 1.37a0.456 0.456 0 0 0-0.867 0L1.43 9.526z" />
<path
android:fillColor="#FC6D26"
android:pathData="M11.994 23.066l4.398-13.54h6.165z" />
<path
android:fillColor="#FCA326"
android:pathData="M22.557 9.526l1.34 4.114a0.906 0.906 0 0 1-0.33 1.016l-11.573 8.41 10.563-13.54z" />
<path
android:fillColor="#E24329"
android:pathData="M22.557 9.526h-6.165l2.653-8.157a0.456 0.456 0 0 1 0.867 0l2.645 8.157z" />
</vector>
\ No newline at end of file
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="290dp"
android:height="40dp"
android:viewportHeight="40.0"
android:viewportWidth="290.0">
<path
android:fillColor="#DF4F38"
android:fillType="evenOdd"
android:pathData="M2,0L288,0A2,2 0,0 1,290 2L290,38A2,2 0,0 1,288 40L2,40A2,2 0,0 1,0 38L0,2A2,2 0,0 1,2 0z"
android:strokeColor="#00000000"
android:strokeWidth="1" />
<path
android:fillColor="#FFFFFF"
android:fillType="evenOdd"
android:pathData="M137.11,25.09C136.64,25.69 136,26.15 135.18,26.46C134.36,26.77 133.43,26.93 132.39,26.93C131.31,26.93 130.36,26.68 129.54,26.2C128.71,25.71 128.07,25.01 127.63,24.11C127.18,23.2 126.95,22.15 126.93,20.94L126.93,19.94C126.93,18 127.39,16.49 128.32,15.41C129.24,14.32 130.53,13.78 132.19,13.78C133.62,13.78 134.75,14.13 135.58,14.83C136.42,15.54 136.93,16.55 137.1,17.87L134.92,17.87C134.67,16.33 133.78,15.56 132.24,15.56C131.24,15.56 130.48,15.92 129.96,16.64C129.45,17.36 129.18,18.41 129.16,19.8L129.16,20.78C129.16,22.17 129.45,23.24 130.03,24C130.62,24.77 131.43,25.15 132.46,25.15C133.6,25.15 134.41,24.89 134.89,24.38L134.89,21.87L132.25,21.87L132.25,20.18L137.11,20.18L137.11,25.09ZM140.74,21.91C140.74,20.98 140.93,20.14 141.3,19.39C141.67,18.64 142.18,18.07 142.85,17.67C143.52,17.27 144.29,17.06 145.15,17.06C146.44,17.06 147.48,17.48 148.28,18.3C149.08,19.13 149.51,20.23 149.58,21.59L149.58,22.09C149.58,23.03 149.4,23.87 149.04,24.61C148.68,25.34 148.17,25.92 147.5,26.32C146.83,26.72 146.05,26.93 145.17,26.93C143.83,26.93 142.76,26.48 141.95,25.59C141.15,24.69 140.74,23.5 140.74,22.01L140.74,21.91ZM142.88,22.09C142.88,23.07 143.08,23.84 143.48,24.39C143.89,24.94 144.45,25.22 145.17,25.22C145.89,25.22 146.45,24.94 146.86,24.38C147.26,23.81 147.46,22.99 147.46,21.91C147.46,20.95 147.25,20.18 146.84,19.62C146.42,19.06 145.86,18.78 145.15,18.78C144.46,18.78 143.9,19.06 143.49,19.61C143.08,20.16 142.88,20.99 142.88,22.09ZM152.79,21.91C152.79,20.98 152.97,20.14 153.34,19.39C153.71,18.64 154.23,18.07 154.9,17.67C155.57,17.27 156.34,17.06 157.2,17.06C158.49,17.06 159.53,17.48 160.33,18.3C161.13,19.13 161.56,20.23 161.62,21.59L161.63,22.09C161.63,23.03 161.45,23.87 161.09,24.61C160.73,25.34 160.22,25.92 159.54,26.32C158.87,26.72 158.1,26.93 157.22,26.93C155.88,26.93 154.8,26.48 154,25.59C153.19,24.69 152.79,23.5 152.79,22.01L152.79,21.91ZM154.93,22.09C154.93,23.07 155.13,23.84 155.53,24.39C155.94,24.94 156.5,25.22 157.22,25.22C157.94,25.22 158.5,24.94 158.9,24.38C159.3,23.81 159.51,22.99 159.51,21.91C159.51,20.95 159.3,20.18 158.89,19.62C158.47,19.06 157.91,18.78 157.2,18.78C156.51,18.78 155.95,19.06 155.54,19.61C155.13,20.16 154.93,20.99 154.93,22.09ZM164.86,21.92C164.86,20.45 165.21,19.27 165.91,18.39C166.6,17.51 167.52,17.06 168.67,17.06C169.75,17.06 170.61,17.44 171.23,18.2L171.32,17.24L173.25,17.24L173.25,26.46C173.25,27.71 172.86,28.69 172.08,29.41C171.31,30.13 170.26,30.49 168.94,30.49C168.25,30.49 167.56,30.35 166.9,30.06C166.23,29.77 165.73,29.39 165.38,28.92L166.39,27.64C167.05,28.42 167.86,28.81 168.82,28.81C169.53,28.81 170.09,28.61 170.5,28.23C170.91,27.85 171.11,27.28 171.11,26.54L171.11,25.9C170.5,26.58 169.68,26.93 168.65,26.93C167.54,26.93 166.63,26.48 165.92,25.6C165.22,24.71 164.86,23.49 164.86,21.92ZM166.99,22.11C166.99,23.06 167.19,23.82 167.58,24.36C167.97,24.91 168.51,25.19 169.2,25.19C170.06,25.19 170.7,24.82 171.11,24.08L171.11,19.89C170.71,19.17 170.08,18.81 169.22,18.81C168.51,18.81 167.97,19.09 167.58,19.65C167.19,20.21 166.99,21.03 166.99,22.11ZM179.51,26.75L177.38,26.75L177.38,13.25L179.51,13.25L179.51,26.75ZM187.83,26.93C186.47,26.93 185.38,26.5 184.54,25.65C183.69,24.79 183.27,23.66 183.27,22.24L183.27,21.98C183.27,21.03 183.46,20.18 183.82,19.43C184.19,18.69 184.7,18.1 185.37,17.69C186.03,17.27 186.77,17.06 187.58,17.06C188.88,17.06 189.88,17.48 190.58,18.3C191.29,19.13 191.64,20.3 191.64,21.81L191.64,22.67L185.43,22.67C185.49,23.46 185.75,24.08 186.21,24.54C186.67,24.99 187.25,25.22 187.95,25.22C188.93,25.22 189.73,24.83 190.34,24.03L191.49,25.13C191.11,25.7 190.6,26.14 189.97,26.46C189.33,26.77 188.62,26.93 187.83,26.93ZM187.57,18.78C186.99,18.78 186.51,18.98 186.15,19.39C185.79,19.8 185.56,20.37 185.46,21.11L189.53,21.11L189.53,20.95C189.49,20.23 189.29,19.69 188.96,19.33C188.63,18.96 188.16,18.78 187.57,18.78Z"
android:strokeColor="#00000000"
android:strokeWidth="1" />
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M99.43,22.32L98.73,24.92L96.19,24.97C95.43,23.56 95,21.95 95,20.24C95,18.58 95.4,17.02 96.12,15.65L96.12,15.65L98.38,16.06L99.37,18.31C99.16,18.91 99.05,19.56 99.05,20.24C99.05,20.97 99.18,21.67 99.43,22.32Z"
android:strokeColor="#00000000"
android:strokeWidth="1" />
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M114.8,18.37C114.92,18.98 114.98,19.6 114.98,20.24C114.98,20.95 114.9,21.65 114.76,22.32C114.27,24.61 113,26.61 111.24,28.03L111.24,28.03L108.39,27.88L107.99,25.36C109.16,24.68 110.07,23.61 110.55,22.32L105.21,22.32L105.21,18.37L110.63,18.37L114.8,18.37L114.8,18.37Z"
android:strokeColor="#00000000"
android:strokeWidth="1" />
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M111.24,28.03L111.24,28.03C109.53,29.4 107.36,30.23 104.99,30.23C101.18,30.23 97.88,28.1 96.19,24.97L99.43,22.32C100.27,24.57 102.44,26.18 104.99,26.18C106.08,26.18 107.11,25.88 107.99,25.36L111.24,28.03Z"
android:strokeColor="#00000000"
android:strokeWidth="1" />
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M111.36,12.55L108.13,15.2C107.22,14.63 106.14,14.3 104.99,14.3C102.39,14.3 100.17,15.98 99.37,18.31L96.12,15.65L96.12,15.65C97.78,12.44 101.13,10.25 104.99,10.25C107.41,10.25 109.63,11.11 111.36,12.55Z"
android:strokeColor="#00000000"
android:strokeWidth="1" />
</vector>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FFF"
android:fillType="evenOdd"
android:pathData="M-31-298h360V711H-31z" />
<path
android:fillColor="#4285F4"
android:fillType="evenOdd"
android:pathData="M22.36 10H12v4.255h5.92a5.06 5.06 0 0 1-2.195 3.32v2.76h3.555c2.08-1.915 3.28-4.735 3.28-8.085 0-0.78-0.07-1.53-0.2-2.25z" />
<path
android:fillColor="#34A853"
android:fillType="evenOdd"
android:pathData="M12 23c2.97 0 5.46-0.985 7.28-2.665l-3.555-2.76c-0.985 0.66 -2.245 1.05-3.725 1.05-2.865 0-5.29-1.935-6.155-4.535H2.17v2.85A10.996 10.996 0 0 0 12 23z" />
<path
android:fillColor="#FBBC05"
android:fillType="evenOdd"
android:pathData="M5.845 14.09A6.612 6.612 0 0 1 5.5 12c0-0.725 0.125 -1.43 0.345 -2.09V7.06H2.17A10.996 10.996 0 0 0 1 12c0 1.775 0.425 3.455 1.17 4.94l3.675-2.85z" />
<path
android:fillColor="#EA4335"
android:fillType="evenOdd"
android:pathData="M12 5.375c1.615 0 3.065 0.555 4.205 1.645l3.155-3.155C17.455 2.09 14.965 1 12 1 7.7 1 3.98 3.465 2.17 7.06l3.675 2.85C6.71 7.31 9.135 5.375 12 5.375z" />
</vector>
\ No newline at end of file
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,19h-2v-2h2v2zM15.07,11.25l-0.9,0.92C13.45,12.9 13,13.5 13,15h-2v-0.5c0,-1.1 0.45,-2.1 1.17,-2.83l1.24,-1.26c0.37,-0.36 0.59,-0.86 0.59,-1.41 0,-1.1 -0.9,-2 -2,-2s-2,0.9 -2,2L8,9c0,-2.21 1.79,-4 4,-4s4,1.79 4,4c0,0.88 -0.36,1.68 -0.93,2.25z"/>
</vector>
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="20dp"
android:height="20dp"
android:viewportWidth="20"
android:viewportHeight="20">
<path
android:fillColor="#00000000"
android:fillType="evenOdd"
android:pathData="M10.0002,5.5001C10.0002,6.3261 9.869,6.9474 9.6082,7.3639L14.4347,12.0381L15,13.8704L13.8695,15L12.0425,14.4382L11.6838,14.0804L11.6838,12.7898L10.3931,12.7898L9.8419,12.2385L9.8419,10.947L8.5504,10.947L7.1801,9.5715C6.6709,9.8576 6.11,10.0002 5.5001,10.0002C4.2845,10.0003 3.1205,9.5084 2.2732,8.6367C1.4258,7.765 0.9672,6.5876 1.0017,5.3724C1.0674,3.0239 3.0247,1.0674 5.3724,1.0017C6.5873,0.9675 7.7643,1.4261 8.6358,2.2732C9.5073,3.1203 9.9991,4.2839 9.9994,5.4992L10.0002,5.5001Z"
android:strokeWidth="1.5"
android:strokeColor="#FF2F343D" />
<path
android:fillColor="#FF2F343D"
android:fillType="nonZero"
android:pathData="M4.9375,4.9375m1.3125,0a1.3125,1.3125 0,1 0,-2.625 0a1.3125,1.3125 0,1 0,2.625 0"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="290dp"
android:height="40dp"
android:viewportHeight="40.0"
android:viewportWidth="290.0">
<path
android:fillColor="#2886BA"
android:fillType="evenOdd"
android:pathData="M2,0L288,0A2,2 0,0 1,290 2L290,38A2,2 0,0 1,288 40L2,40A2,2 0,0 1,0 38L0,2A2,2 0,0 1,2 0z"
android:strokeColor="#00000000"
android:strokeWidth="1" />
<path
android:fillColor="#FFFFFF"
android:fillType="evenOdd"
android:pathData="M122.33,24.6L128.14,24.6L128.14,26.38L120.1,26.38L120.1,13.58L122.33,13.58L122.33,24.6ZM133.71,26.38L131.57,26.38L131.57,16.87L133.71,16.87L133.71,26.38ZM131.44,14.4C131.44,14.07 131.54,13.79 131.75,13.58C131.96,13.36 132.26,13.25 132.64,13.25C133.03,13.25 133.33,13.36 133.54,13.58C133.75,13.79 133.86,14.07 133.86,14.4C133.86,14.72 133.75,14.99 133.54,15.2C133.33,15.41 133.03,15.52 132.64,15.52C132.26,15.52 131.96,15.41 131.75,15.2C131.54,14.99 131.44,14.72 131.44,14.4ZM139.82,16.87L139.88,17.96C140.58,17.11 141.5,16.69 142.65,16.69C144.63,16.69 145.63,17.82 145.67,20.09L145.67,26.38L143.53,26.38L143.53,20.21C143.53,19.61 143.4,19.16 143.14,18.87C142.88,18.58 142.45,18.44 141.86,18.44C141,18.44 140.36,18.83 139.94,19.61L139.94,26.38L137.8,26.38L137.8,16.87L139.82,16.87ZM152.73,22.3L151.78,23.27L151.78,26.38L149.65,26.38L149.65,12.88L151.78,12.88L151.78,20.66L152.45,19.83L155.08,16.87L157.65,16.87L154.11,20.83L158.02,26.38L155.55,26.38L152.73,22.3ZM164.85,26.55C163.5,26.55 162.4,26.12 161.56,25.27C160.72,24.42 160.3,23.28 160.3,21.87L160.3,21.6C160.3,20.65 160.48,19.81 160.85,19.06C161.21,18.31 161.73,17.73 162.39,17.31C163.05,16.9 163.79,16.69 164.61,16.69C165.9,16.69 166.9,17.1 167.61,17.93C168.31,18.75 168.67,19.92 168.67,21.44L168.67,22.3L162.45,22.3C162.52,23.08 162.78,23.7 163.24,24.16C163.7,24.62 164.28,24.85 164.97,24.85C165.95,24.85 166.75,24.45 167.37,23.66L168.52,24.76C168.14,25.33 167.63,25.77 166.99,26.08C166.36,26.39 165.64,26.55 164.85,26.55ZM164.6,18.4C164.01,18.4 163.54,18.61 163.18,19.02C162.82,19.43 162.59,20 162.49,20.73L166.56,20.73L166.56,20.57C166.51,19.86 166.32,19.32 165.99,18.95C165.65,18.59 165.19,18.4 164.6,18.4ZM171.72,21.55C171.72,20.08 172.06,18.91 172.74,18.02C173.42,17.13 174.33,16.69 175.48,16.69C176.48,16.69 177.3,17.04 177.92,17.74L177.92,12.88L180.05,12.88L180.05,26.38L178.12,26.38L178.02,25.39C177.38,26.16 176.52,26.55 175.46,26.55C174.34,26.55 173.44,26.1 172.76,25.21C172.07,24.31 171.72,23.09 171.72,21.55ZM173.86,21.73C173.86,22.7 174.04,23.46 174.42,24C174.79,24.54 175.32,24.81 176,24.81C176.88,24.81 177.51,24.42 177.92,23.64L177.92,19.58C177.53,18.82 176.89,18.44 176.02,18.44C175.33,18.44 174.8,18.71 174.42,19.26C174.05,19.81 173.86,20.63 173.86,21.73ZM186.35,26.38L184.22,26.38L184.22,16.87L186.35,16.87L186.35,26.38ZM184.09,14.4C184.09,14.07 184.19,13.79 184.4,13.58C184.61,13.36 184.9,13.25 185.29,13.25C185.68,13.25 185.98,13.36 186.19,13.58C186.4,13.79 186.5,14.07 186.5,14.4C186.5,14.72 186.4,14.99 186.19,15.2C185.98,15.41 185.68,15.52 185.29,15.52C184.9,15.52 184.61,15.41 184.4,15.2C184.19,14.99 184.09,14.72 184.09,14.4ZM192.46,16.87L192.52,17.96C193.23,17.11 194.15,16.69 195.29,16.69C197.27,16.69 198.28,17.82 198.32,20.09L198.32,26.38L196.18,26.38L196.18,20.21C196.18,19.61 196.05,19.16 195.79,18.87C195.53,18.58 195.1,18.44 194.51,18.44C193.65,18.44 193.01,18.83 192.58,19.61L192.58,26.38L190.45,26.38L190.45,16.87L192.46,16.87Z"
android:strokeColor="#00000000"
android:strokeWidth="1" />
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M108.88,22.13L108.88,29.85L104.4,29.85L104.4,22.65C104.4,20.84 103.76,19.61 102.14,19.61C100.9,19.61 100.16,20.44 99.84,21.24C99.72,21.53 99.69,21.93 99.69,22.34L99.69,29.85L95.22,29.85C95.22,29.85 95.28,17.66 95.22,16.39L99.69,16.39L99.69,18.3C99.68,18.31 99.67,18.33 99.66,18.34L99.69,18.34L99.69,18.3C100.29,17.38 101.35,16.08 103.73,16.08C106.67,16.08 108.88,18 108.88,22.13ZM90.53,9.9C89,9.9 88,10.91 88,12.23C88,13.52 88.97,14.55 90.47,14.55L90.5,14.55C92.06,14.55 93.04,13.52 93.04,12.23C93.01,10.91 92.06,9.9 90.53,9.9ZM88.27,29.85L92.74,29.85L92.74,16.39L88.27,16.39L88.27,29.85Z"
android:strokeColor="#00000000"
android:strokeWidth="1" />
</vector>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FFF"
android:fillType="evenOdd"
android:pathData="M-31-356h360V653H-31z" />
<path
android:fillColor="#0071A1"
android:fillType="evenOdd"
android:pathData="M22.224 0H1.77C0.793 0 0 0.775 0 1.73v20.538C0 23.224 0.793 24 1.77 24h20.454c0.979 0 1.775-0.776 1.775-1.732V1.731C24 0.775 23.203 0 22.224 0" />
<path
android:fillColor="#FFFFFE"
android:fillType="evenOdd"
android:pathData="M5.339 3.304a2.064 2.064 0 1 1-0.003 4.128 2.064 2.064 0 0 1 0.003-4.128zM3.556 20.451h3.563V8.997H3.556v11.454zM9.352 8.997h3.412v1.566h0.05c0.474-0.9 1.635-1.85 3.367-1.85 3.604 0 4.27 2.372 4.27 5.456v6.282h-3.559v-5.57c0-1.328-0.023-3.037-1.85-3.037-1.852 0-2.135 1.447-2.135 2.942v5.665H9.352V8.997" />
</vector>
\ No newline at end of file
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M18,8h-1L17,6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6v2L6,8c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,10c0,-1.1 -0.9,-2 -2,-2zM12,17c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2zM15.1,8L8.9,8L8.9,6c0,-1.71 1.39,-3.1 3.1,-3.1 1.71,0 3.1,1.39 3.1,3.1v2z" />
</vector>
\ No newline at end of file
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="13dp"
android:height="16dp"
android:viewportWidth="13.0"
android:viewportHeight="16.0">
<path
android:pathData="M6.095,12.19C6.933,12.19 7.619,11.505 7.619,10.667C7.619,9.829 6.933,9.143 6.095,9.143C5.257,9.143 4.571,9.829 4.571,10.667C4.571,11.505 5.257,12.19 6.095,12.19ZM10.667,5.333L9.905,5.333L9.905,3.81C9.905,1.707 8.198,0 6.095,0C3.992,0 2.286,1.707 2.286,3.81L2.286,5.333L1.524,5.333C0.686,5.333 0,6.019 0,6.857L0,14.476C0,15.314 0.686,16 1.524,16L10.667,16C11.505,16 12.19,15.314 12.19,14.476L12.19,6.857C12.19,6.019 11.505,5.333 10.667,5.333ZM3.733,3.81C3.733,2.507 4.792,1.448 6.095,1.448C7.398,1.448 8.457,2.507 8.457,3.81L8.457,5.333L3.733,5.333L3.733,3.81ZM10.667,14.476L1.524,14.476L1.524,6.857L10.667,6.857L10.667,14.476Z"
android:strokeColor="#00000000"
android:fillType="evenOdd"
android:fillColor="#000000"
android:strokeWidth="1"/>
</vector>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="12dp"
android:height="12dp"
android:viewportWidth="12"
android:viewportHeight="12">
<path
android:pathData="M1.5,5.5h9v6h-9z"
android:strokeWidth="1"
android:strokeColor="#DE000000"
android:fillType="evenOdd"/>
<path
android:pathData="M2.5,5.5L9.5,5.5L9.5,4C9.5,2.067 7.933,0.5 6,0.5C4.067,0.5 2.5,2.067 2.5,4L2.5,5.5Z"
android:strokeWidth="1"
android:strokeColor="#DE000000"
android:fillType="evenOdd"/>
</vector>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="290dp"
android:height="40dp"
android:viewportHeight="40.0"
android:viewportWidth="290.0">
<path
android:fillColor="#DC464F"
android:fillType="evenOdd"
android:pathData="M2,0L288,0A2,2 0,0 1,290 2L290,38A2,2 0,0 1,288 40L2,40A2,2 0,0 1,0 38L0,2A2,2 0,0 1,2 0z"
android:strokeColor="#00000000"
android:strokeWidth="1" />
<path
android:fillColor="#FFFFFF"
android:fillType="evenOdd"
android:pathData="M130.46,13.45L134.16,23.26L137.84,13.45L140.71,13.45L140.71,26.25L138.5,26.25L138.5,22.03L138.72,16.39L134.94,26.25L133.35,26.25L129.58,16.4L129.8,22.03L129.8,26.25L127.58,26.25L127.58,13.45L130.46,13.45ZM149.12,26.43C147.77,26.43 146.67,26 145.83,25.15C144.99,24.29 144.57,23.16 144.57,21.74L144.57,21.48C144.57,20.53 144.75,19.68 145.12,18.93C145.48,18.19 146,17.6 146.66,17.19C147.32,16.77 148.06,16.56 148.88,16.56C150.17,16.56 151.17,16.98 151.88,17.8C152.58,18.63 152.94,19.8 152.94,21.31L152.94,22.17L146.72,22.17C146.79,22.96 147.05,23.58 147.51,24.04C147.97,24.49 148.55,24.72 149.24,24.72C150.22,24.72 151.02,24.33 151.64,23.53L152.79,24.63C152.41,25.2 151.9,25.64 151.26,25.96C150.63,26.27 149.91,26.43 149.12,26.43ZM148.87,18.28C148.28,18.28 147.81,18.48 147.45,18.89C147.09,19.3 146.86,19.87 146.76,20.61L150.83,20.61L150.83,20.45C150.78,19.73 150.59,19.19 150.26,18.83C149.92,18.46 149.46,18.28 148.87,18.28ZM159.07,14.43L159.07,16.74L160.75,16.74L160.75,18.32L159.07,18.32L159.07,23.63C159.07,23.99 159.14,24.26 159.28,24.42C159.43,24.58 159.68,24.66 160.05,24.66C160.3,24.66 160.55,24.63 160.8,24.57L160.8,26.22C160.31,26.36 159.85,26.43 159.39,26.43C157.75,26.43 156.93,25.52 156.93,23.71L156.93,18.32L155.37,18.32L155.37,16.74L156.93,16.74L156.93,14.43L159.07,14.43ZM168.37,26.43C167.01,26.43 165.92,26 165.07,25.15C164.23,24.29 163.81,23.16 163.81,21.74L163.81,21.48C163.81,20.53 164,19.68 164.36,18.93C164.73,18.19 165.24,17.6 165.91,17.19C166.57,16.77 167.31,16.56 168.12,16.56C169.42,16.56 170.42,16.98 171.12,17.8C171.83,18.63 172.18,19.8 172.18,21.31L172.18,22.17L165.97,22.17C166.03,22.96 166.29,23.58 166.75,24.04C167.21,24.49 167.79,24.72 168.49,24.72C169.47,24.72 170.26,24.33 170.88,23.53L172.03,24.63C171.65,25.2 171.14,25.64 170.51,25.96C169.87,26.27 169.16,26.43 168.37,26.43ZM168.11,18.28C167.53,18.28 167.05,18.48 166.69,18.89C166.33,19.3 166.1,19.87 166,20.61L170.07,20.61L170.07,20.45C170.02,19.73 169.83,19.19 169.5,18.83C169.17,18.46 168.7,18.28 168.11,18.28ZM175.24,21.41C175.24,20.48 175.42,19.64 175.79,18.89C176.16,18.14 176.68,17.57 177.35,17.17C178.01,16.77 178.78,16.56 179.65,16.56C180.93,16.56 181.97,16.98 182.77,17.8C183.57,18.63 184.01,19.73 184.07,21.09L184.08,21.59C184.08,22.53 183.9,23.37 183.54,24.11C183.18,24.84 182.66,25.42 181.99,25.82C181.32,26.22 180.55,26.43 179.67,26.43C178.33,26.43 177.25,25.98 176.45,25.09C175.64,24.19 175.24,23 175.24,21.51L175.24,21.41ZM177.37,21.59C177.37,22.57 177.58,23.34 177.98,23.89C178.38,24.44 178.95,24.72 179.67,24.72C180.39,24.72 180.95,24.44 181.35,23.88C181.75,23.31 181.95,22.49 181.95,21.41C181.95,20.45 181.75,19.68 181.33,19.12C180.92,18.56 180.36,18.28 179.65,18.28C178.95,18.28 178.4,18.56 177.99,19.11C177.58,19.66 177.37,20.49 177.37,21.59ZM192.66,18.69C192.38,18.64 192.09,18.62 191.79,18.62C190.82,18.62 190.16,19 189.82,19.75L189.82,26.25L187.68,26.25L187.68,16.74L189.72,16.74L189.77,17.8C190.29,16.98 191,16.56 191.92,16.56C192.22,16.56 192.47,16.61 192.67,16.69L192.66,18.69Z"
android:strokeColor="#00000000"
android:strokeWidth="1" />
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M95.75,9.88L112.99,28.14C112.99,28.14 113.58,28.56 114.03,28.08C114.48,27.59 114.13,27.11 114.13,27.11L95.75,9.88L95.75,9.88Z"
android:strokeColor="#00000000"
android:strokeWidth="1" />
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M101.21,11.6L114.34,25.76C114.34,25.76 114.93,26.18 115.38,25.69C115.83,25.21 115.48,24.73 115.48,24.73L101.21,11.6L101.21,11.6Z"
android:strokeColor="#00000000"
android:strokeWidth="1" />
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M97.37,15.3L110.5,29.46C110.5,29.46 111.09,29.87 111.54,29.39C111.99,28.9 111.64,28.42 111.64,28.42L97.37,15.3L97.37,15.3Z"
android:strokeColor="#00000000"
android:strokeWidth="1" />
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M105.82,13.09L115,22.98C115,22.98 115.41,23.27 115.72,22.93C116.04,22.59 115.8,22.26 115.8,22.26L105.82,13.09L105.82,13.09Z"
android:strokeColor="#00000000"
android:strokeWidth="1" />
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M98.63,19.58L107.81,29.47C107.81,29.47 108.22,29.76 108.53,29.42C108.85,29.09 108.61,28.75 108.61,28.75L98.63,19.58L98.63,19.58Z"
android:strokeColor="#00000000"
android:strokeWidth="1" />
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M110.54,15.23L114.7,19.73C114.7,19.73 114.9,19.86 115.06,19.7C115.21,19.54 115.09,19.39 115.09,19.39L110.54,15.23L110.54,15.23Z"
android:strokeColor="#00000000"
android:strokeWidth="1" />
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M100.93,24.14L105.09,28.64C105.09,28.64 105.29,28.77 105.45,28.61C105.6,28.45 105.48,28.3 105.48,28.3L100.93,24.14L100.93,24.14Z"
android:strokeColor="#00000000"
android:strokeWidth="1" />
</vector>
\ No newline at end of file
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="248dp"
android:height="196dp"
android:viewportWidth="248"
android:viewportHeight="196">
<path
android:fillColor="#E1E5E8"
android:fillType="nonZero"
android:pathData="M140.189,16.897C134.348,15.539 128.266,14.821 122.018,14.821C111.572,14.82 101.37,16.858 91.924,20.689C91.651,20.137 91.33,19.61 90.967,19.114C100.7,15.126 111.23,13 122.018,13C129.004,13 135.787,13.878 142.265,15.529C141.556,15.957 140.864,16.413 140.189,16.897ZM188.915,50.846C189.13,50.046 189.313,49.238 189.464,48.424C198.627,61.659 204,77.711 204,94.997C204,140.217 167.218,177 122,177C76.782,177 40,140.211 40,94.997C40,93.872 40.022,92.751 40.067,91.634C40.734,91.98 41.232,92.69 41.839,93.146C41.839,93.765 41.839,94.384 41.839,95.003C41.839,139.215 77.807,175.185 122.018,175.185C166.229,175.185 202.197,139.215 202.197,95.003C202.197,78.7 197.307,63.517 188.915,50.846ZM80.722,26.288C80.714,26.293 80.705,26.298 80.696,26.303C80.281,25.939 79.831,25.502 79.362,24.984C79.365,24.982 79.368,24.98 79.372,24.978C79.848,25.475 80.305,25.912 80.722,26.288ZM75.927,29.412C69.829,33.704 64.297,38.871 59.532,44.818L57.213,44.818C62.398,38.11 68.524,32.331 75.329,27.61C75.478,28.225 75.678,28.828 75.927,29.412Z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
<path
android:fillColor="#00000000"
android:fillType="evenOdd"
android:pathData="M24,86L18,86"
android:strokeWidth="3"
android:strokeColor="#E1E5E8"
android:strokeLineCap="round" />
<path
android:fillColor="#00000000"
android:fillType="evenOdd"
android:pathData="M22,83L22,89"
android:strokeWidth="3"
android:strokeColor="#E1E5E8"
android:strokeLineCap="round" />
<path
android:fillColor="#00000000"
android:fillType="evenOdd"
android:pathData="M58,29m-3,0a3,3 0,1 1,6 0a3,3 0,1 1,-6 0"
android:strokeWidth="3"
android:strokeColor="#E1E5E8" />
<path
android:fillColor="#00000000"
android:fillType="evenOdd"
android:pathData="M16,29L2,29"
android:strokeWidth="3"
android:strokeColor="#E1E5E8"
android:strokeLineCap="round" />
<path
android:fillColor="#00000000"
android:fillType="evenOdd"
android:pathData="M34.5,29L27,29"
android:strokeWidth="3"
android:strokeColor="#E1E5E8"
android:strokeLineCap="round" />
<path
android:fillColor="#00000000"
android:fillType="evenOdd"
android:pathData="M145.5,5L138,5"
android:strokeWidth="3"
android:strokeColor="#E1E5E8"
android:strokeLineCap="round" />
<path
android:fillColor="#00000000"
android:fillType="evenOdd"
android:pathData="M205.5,31L198,31"
android:strokeWidth="3"
android:strokeColor="#E1E5E8"
android:strokeLineCap="round" />
<path
android:fillColor="#00000000"
android:fillType="evenOdd"
android:pathData="M219.5,31L212,31"
android:strokeWidth="3"
android:strokeColor="#E1E5E8"
android:strokeLineCap="round" />
<path
android:fillColor="#00000000"
android:fillType="evenOdd"
android:pathData="M131.5,5L127,5"
android:strokeWidth="3"
android:strokeColor="#E1E5E8"
android:strokeLineCap="round" />
<path
android:fillColor="#00000000"
android:fillType="evenOdd"
android:pathData="M48.5,29L41,29"
android:strokeWidth="3"
android:strokeColor="#E1E5E8"
android:strokeLineCap="round" />
<path
android:fillColor="#00000000"
android:fillType="evenOdd"
android:pathData="M228,32m-3,0a3,3 0,1 1,6 0a3,3 0,1 1,-6 0"
android:strokeWidth="3"
android:strokeColor="#E1E5E8" />
<path
android:fillColor="#00000000"
android:fillType="evenOdd"
android:pathData="M169,84m-3,0a3,3 0,1 1,6 0a3,3 0,1 1,-6 0"
android:strokeWidth="2"
android:strokeColor="#E1E5E8" />
<path
android:fillColor="#00000000"
android:fillType="evenOdd"
android:pathData="M100,65m-3,0a3,3 0,1 1,6 0a3,3 0,1 1,-6 0"
android:strokeWidth="2"
android:strokeColor="#E1E5E8" />
<path
android:fillColor="#00000000"
android:fillType="evenOdd"
android:pathData="M62,132m-3,0a3,3 0,1 1,6 0a3,3 0,1 1,-6 0"
android:strokeWidth="2"
android:strokeColor="#E1E5E8" />
<path
android:fillColor="#00000000"
android:fillType="evenOdd"
android:pathData="M124,5L118,5"
android:strokeWidth="3"
android:strokeColor="#E1E5E8"
android:strokeLineCap="round" />
<path
android:fillColor="#00000000"
android:fillType="evenOdd"
android:pathData="M121,2L121,8"
android:strokeWidth="3"
android:strokeColor="#E1E5E8"
android:strokeLineCap="round" />
<path
android:fillColor="#00000000"
android:fillType="evenOdd"
android:pathData="M246,86L240,86"
android:strokeWidth="3"
android:strokeColor="#E1E5E8"
android:strokeLineCap="round" />
<path
android:fillColor="#00000000"
android:fillType="evenOdd"
android:pathData="M243,83L243,89"
android:strokeWidth="3"
android:strokeColor="#E1E5E8"
android:strokeLineCap="round" />
<path
android:fillColor="#00000000"
android:fillType="evenOdd"
android:pathData="M235,86L207,86"
android:strokeWidth="3"
android:strokeColor="#E1E5E8"
android:strokeLineCap="round" />
<path
android:fillColor="#00000000"
android:fillType="evenOdd"
android:pathData="M71,100C71,100 91.468,106.392 82.907,127"
android:strokeWidth="3"
android:strokeColor="#E1E5E8"
android:strokeLineCap="round" />
<path
android:fillColor="#00000000"
android:fillType="evenOdd"
android:pathData="M189,155.377C189,155.377 208.598,160.202 207.986,143"
android:strokeWidth="3"
android:strokeColor="#E1E5E8"
android:strokeLineCap="round" />
<path
android:fillColor="#00000000"
android:fillType="evenOdd"
android:pathData="M150,95.605C150,95.605 167.62,87.971 175,106"
android:strokeWidth="3"
android:strokeColor="#E1E5E8"
android:strokeLineCap="round" />
<path
android:fillColor="#00000000"
android:fillType="evenOdd"
android:pathData="M111.216,88C111.216,88 103.487,62.498 127,59"
android:strokeWidth="3"
android:strokeColor="#E1E5E8"
android:strokeLineCap="round" />
<path
android:fillColor="#00000000"
android:fillType="evenOdd"
android:pathData="M178,70C178,70 196.756,76.962 192.322,95"
android:strokeWidth="3"
android:strokeColor="#E1E5E8"
android:strokeLineCap="round" />
<path
android:fillColor="#00000000"
android:fillType="evenOdd"
android:pathData="M129.025,123C129.025,123 127.046,145.748 158,143.892"
android:strokeWidth="3"
android:strokeColor="#E1E5E8"
android:strokeLineCap="round" />
<path
android:fillColor="#00000000"
android:fillType="evenOdd"
android:pathData="M108.267,129C108.267,129 103.666,118.058 112,116"
android:strokeWidth="3"
android:strokeColor="#E1E5E8"
android:strokeLineCap="round" />
<path
android:fillColor="#00000000"
android:fillType="evenOdd"
android:pathData="M128,163.879C128,163.879 158.716,166.439 160,148"
android:strokeWidth="3"
android:strokeColor="#E1E5E8"
android:strokeLineCap="round" />
<path
android:fillColor="#00000000"
android:fillType="evenOdd"
android:pathData="M107,98C107,98 104.679,82.173 87,84.175"
android:strokeWidth="3"
android:strokeColor="#E1E5E8"
android:strokeLineCap="round" />
<path
android:fillColor="#00000000"
android:fillType="evenOdd"
android:pathData="M86,59C86,59 97.204,40.499 122,49.441"
android:strokeWidth="3"
android:strokeColor="#E1E5E8"
android:strokeLineCap="round" />
<path
android:fillColor="#00000000"
android:fillType="nonZero"
android:pathData="M82.892,28C82.892,28 69.268,19.328 73.998,3C73.998,3 86.199,7.079 87,25.571C86.994,25.571 83.495,26.574 82.892,28Z"
android:strokeWidth="2"
android:strokeColor="#175CC4"
android:strokeLineCap="round"
android:strokeLineJoin="round" />
<path
android:fillColor="#00000000"
android:fillType="evenOdd"
android:pathData="M79,16m-0.391,1.961a2,2 50.849,1 1,0.782 -3.923a2,2 50.849,1 1,-0.782 3.923"
android:strokeWidth="2"
android:strokeColor="#175CC4"
android:strokeLineCap="round"
android:strokeLineJoin="round" />
<path
android:fillColor="#F5455C"
android:fillType="nonZero"
android:pathData="M84.157,27.611C84.157,27.611 82.731,31.313 88.483,36C89.612,32.524 88.858,28.616 86.553,26L84.157,27.611Z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
<path
android:fillColor="#00000000"
android:fillType="nonZero"
android:pathData="M76.064,20C74.042,24.388 74.957,29.608 78.344,33C78.344,33 79.754,28.945 83,27.917C83,27.898 79.043,26.161 76.064,20Z"
android:strokeWidth="2"
android:strokeColor="#175CC4"
android:strokeLineCap="round"
android:strokeLineJoin="round" />
<path
android:fillColor="#00000000"
android:fillType="nonZero"
android:pathData="M85,15C89.556,16.284 92.768,20.224 93,24.811C93,24.811 89.097,23.291 86.187,25C86.181,25.018 87.533,21.052 85,15Z"
android:strokeWidth="2"
android:strokeColor="#175CC4"
android:strokeLineCap="round"
android:strokeLineJoin="round" />
<path
android:fillColor="#00000000"
android:fillType="evenOdd"
android:pathData="M74,13L77,11"
android:strokeWidth="2"
android:strokeColor="#175CC4"
android:strokeLineCap="round"
android:strokeLineJoin="round" />
<path
android:fillColor="#00000000"
android:fillType="evenOdd"
android:pathData="M87,30L82,21"
android:strokeWidth="2"
android:strokeColor="#175CC4"
android:strokeLineCap="round"
android:strokeLineJoin="round" />
<path
android:fillColor="#FFD21F"
android:fillType="nonZero"
android:pathData="M126,51C128.835,61.365 137.127,69.421 147.695,72.076C158.263,74.731 169.465,71.571 177,63.811C168.217,63.09 159.513,61.631 150.984,59.448C142.424,57.344 134.066,54.518 126,51Z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
<path
android:fillColor="#FFD21F"
android:fillType="nonZero"
android:pathData="M163.017,13.928C151.987,11.169 140.312,14.671 132.726,23.014C132.388,23.387 132.058,23.767 131.736,24.155C130.647,25.473 129.675,26.879 128.829,28.359C128.615,28.738 128.408,29.128 128.218,29.518C127.226,31.458 126.45,33.498 125.904,35.602C125.306,37.515 125.001,39.504 125,41.506C133.728,45.944 142.95,49.37 152.482,51.715C163.371,54.442 173.858,56.958 182.035,57L182.493,57C184.603,53.061 185.797,48.71 185.986,44.262C185.986,43.812 186.017,43.355 185.986,42.905C185.979,41.146 185.814,39.392 185.492,37.662C185.394,37.141 185.284,36.625 185.162,36.112C182.55,25.243 174.02,16.697 163.017,13.928Z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
<path
android:fillColor="#175CC4"
android:fillType="nonZero"
android:pathData="M120.113,43.868C111.679,38.773 107.377,33.678 108.341,29.926C109.305,26.173 115.516,23.74 125.385,23.278C126.557,23.222 127.762,23.194 128.999,23.194C130.933,23.194 132.946,23.26 135.024,23.392C135.341,22.992 135.667,22.613 136,22.253C132.448,21.99 128.883,21.932 125.324,22.079C114.739,22.571 108.293,25.25 107.172,29.626C106.052,34.002 110.426,39.42 119.493,44.887C122.681,46.786 125.98,48.493 129.373,50C129.24,49.512 129.12,49.019 129.011,48.519C125.961,47.135 122.99,45.582 120.113,43.868Z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
<path
android:fillColor="#175CC4"
android:fillType="nonZero"
android:pathData="M188.656,35C188.778,35.514 188.889,36.031 188.987,36.553C202.526,42.77 210.973,49.837 209.637,55.007C208.3,60.177 197.237,62.458 182.109,61.495C181.75,61.897 181.38,62.298 181,62.699C183.679,62.898 186.241,63 188.65,63C201.043,63 209.49,60.382 210.826,55.308C212.376,49.301 203.464,41.621 188.656,35Z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
<path
android:fillColor="#175CC4"
android:fillType="nonZero"
android:pathData="M154.908,58.202C146.019,55.939 137.348,52.859 129,49C129.11,49.516 129.232,50.031 129.366,50.543C137.516,54.238 145.961,57.206 154.609,59.414C163.227,61.709 172.021,63.243 180.895,64C181.274,63.584 181.642,63.167 182,62.751C172.853,62.077 163.786,60.555 154.908,58.202Z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
<path
android:fillColor="#175CC4"
android:fillType="nonZero"
android:pathData="M189.245,43.328C195.758,46.74 199.288,50.07 198.691,52.277C198.094,54.485 193.234,55.819 185.69,55.819C185.47,56.213 185.24,56.606 185,57L185.572,57C191.644,57 198.915,56.209 199.897,52.537C200.879,48.865 194.676,44.757 189.276,42C189.276,42.443 189.263,42.885 189.245,43.328Z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
<path
android:fillColor="#175CC4"
android:fillType="nonZero"
android:pathData="M117.099,31.569C116.252,34.995 120.899,39.107 128.276,43C128.276,42.557 128.276,42.109 128.276,41.66C121.341,37.888 117.688,34.273 118.276,31.866C118.864,29.458 123.405,28.185 130.765,28.185L131.412,28.185C131.606,27.791 131.8,27.396 132,27.014C123.823,26.845 117.94,28.173 117.099,31.569Z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
<path
android:fillColor="#175CC4"
android:fillType="nonZero"
android:pathData="M184.576,56.753C176.473,56.709 166.071,55.117 155.281,52.272C143.861,49.264 134.524,45.56 128,42C128,42.464 128,42.928 128,43.385C135.262,47.239 145.059,50.906 154.954,53.488C165.744,56.333 176.134,57.931 184.328,58C184.562,57.582 184.786,57.164 185,56.747L184.576,56.753Z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
<path
android:fillColor="#175CC4"
android:fillType="nonZero"
android:pathData="M128.854,50.192C136.999,53.802 145.439,56.702 154.082,58.861C162.695,61.103 171.484,62.602 180.353,63.342C172.744,71.311 161.433,74.556 150.762,71.829C140.09,69.103 131.717,60.83 128.854,50.186L128.854,50.192ZM127.024,48.006L127.695,50.503C130.672,61.574 139.381,70.178 150.48,73.013C161.579,75.848 173.342,72.474 181.256,64.184L183,62.31L180.426,62.115C171.628,61.374 162.909,59.885 154.363,57.664C145.796,55.518 137.429,52.64 129.354,49.062L127,48L127.024,48.006Z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
<path
android:fillColor="#175CC4"
android:fillType="nonZero"
android:pathData="M158.479,12.198C172.527,12.193 184.734,21.889 187.959,35.616C188.08,36.134 188.189,36.657 188.286,37.184C188.605,38.935 188.77,40.711 188.777,42.491C188.777,42.947 188.777,43.403 188.777,43.865C188.589,48.368 187.405,52.773 185.313,56.76L184.834,56.76C176.725,56.717 166.315,55.173 155.517,52.413C144.09,49.495 134.746,45.902 128.217,42.449C128.243,37.758 129.35,33.136 131.451,28.946C131.651,28.551 131.851,28.156 132.057,27.773C132.895,26.275 133.859,24.851 134.939,23.518C135.258,23.112 135.585,22.727 135.92,22.362C141.663,15.921 149.862,12.232 158.473,12.216L158.479,12.198ZM158.473,11C149.517,11.014 140.99,14.849 135.018,21.548C134.673,21.937 134.334,22.338 134.001,22.764C132.877,24.157 131.874,25.643 131.003,27.208C130.797,27.579 130.585,27.98 130.361,28.424C128.176,32.782 127.026,37.588 127,42.467L127,43.203L127.648,43.549C134.764,47.318 144.55,50.893 155.215,53.617C166.115,56.401 176.622,57.957 184.822,58L186.033,58L186.372,57.356C188.546,53.21 189.775,48.631 189.97,43.95C189.97,43.428 190,42.96 190,42.522C189.991,40.67 189.818,38.823 189.485,37.002C189.382,36.448 189.273,35.901 189.146,35.372C185.789,21.101 173.098,11.021 158.491,11.024L158.473,11Z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M137.415,35.634L132.628,40.421"
android:strokeWidth="2"
android:strokeColor="#175CC4"
android:strokeLineCap="round" />
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M132.628,35.634L137.415,40.421"
android:strokeWidth="2"
android:strokeColor="#175CC4"
android:strokeLineCap="round" />
<path
android:fillColor="#1D74F5"
android:fillType="nonZero"
android:pathData="M56.5,71.5m-26.274,3.45a26.5,26.5 123.365,1 1,52.549 -6.9a26.5,26.5 123.365,1 1,-52.549 6.9"
android:strokeWidth="1"
android:strokeColor="#00000000" />
<path
android:fillColor="#00000000"
android:fillType="evenOdd"
android:pathData="M59,73m-26.768,3.529a27,27 107.359,1 1,53.537 -7.058a27,27 107.359,1 1,-53.537 7.058"
android:strokeWidth="2"
android:strokeColor="#0229BC" />
<path
android:fillColor="#00000000"
android:fillType="evenOdd"
android:pathData="M56.561,64C54.181,64.439 52.089,65.396 50.606,67.416C47.278,71.938 49.327,77.078 54.857,78.098C56.703,78.437 59.601,77.078 60.179,79.018C60.935,81.551 62.332,84.177 61.68,87.049C61.311,88.674 62.141,90.07 62.99,91.287C63.839,92.504 64.897,93.919 66.804,92.208C69.21,90.058 71.861,88.254 71.917,84.485C71.96,81.143 74.673,79.049 76.34,76.059C73.743,74.76 71.845,72.382 71.148,69.554C73.535,69.338 76.131,71.975 77.792,69.084C78.591,67.694 76.85,66.879 76.561,65.687"
android:strokeWidth="2"
android:strokeColor="#0229BC"
android:strokeLineCap="round"
android:strokeLineJoin="round" />
<path
android:fillColor="#00000000"
android:fillType="evenOdd"
android:pathData="M56.688,63.961C59.932,63.961 62.537,66.352 65.957,65.956C67.174,65.816 68.154,65.637 69,64.603C68.391,62.692 66.748,62.055 64.697,62.033C62.172,62.033 59.646,61.943 57.126,62.061C55.909,62.122 55.544,62.759 56.706,63.569"
android:strokeWidth="2"
android:strokeColor="#0229BC"
android:strokeLineCap="round"
android:strokeLineJoin="round" />
<path
android:fillColor="#00000000"
android:fillType="evenOdd"
android:pathData="M33,70C35.388,70.472 34.669,72.288 34.713,73.716C34.776,75.784 34.644,77.582 37.264,78.795C38.434,79.279 39.338,80.212 39.753,81.364C40.168,82.517 40.056,83.783 39.446,84.853C38.752,86.258 39.283,87.632 39.052,89"
android:strokeWidth="2"
android:strokeColor="#0229BC"
android:strokeLineCap="round"
android:strokeLineJoin="round" />
<path
android:fillColor="#00000000"
android:fillType="evenOdd"
android:pathData="M68,47C67.28,48.364 66.524,49.754 67.655,50.988C66.742,53.063 64.897,51.495 63.747,52.52C63.142,53.069 61.376,53.137 61.388,53.415C61.551,58.027 56.18,57.736 55,61.077C55.448,61.632 55.889,62.151 56.682,61.959"
android:strokeWidth="2"
android:strokeColor="#0229BC"
android:strokeLineCap="round"
android:strokeLineJoin="round" />
<path
android:fillColor="#00000000"
android:fillType="evenOdd"
android:pathData="M44.549,50C45.942,53.353 43.922,55.033 41.054,55.77C38.516,56.314 36.325,57.845 35,60"
android:strokeWidth="2"
android:strokeColor="#0229BC"
android:strokeLineCap="round"
android:strokeLineJoin="round" />
<path
android:fillColor="#00000000"
android:fillType="evenOdd"
android:pathData="M51,50.994C54.201,51.126 56.125,49.108 58,47"
android:strokeWidth="2"
android:strokeColor="#0229BC"
android:strokeLineCap="round"
android:strokeLineJoin="round" />
<path
android:fillColor="#00000000"
android:fillType="evenOdd"
android:pathData="M51,47C50.845,50.739 47.673,51.106 45,52"
android:strokeWidth="2"
android:strokeColor="#0229BC"
android:strokeLineCap="round"
android:strokeLineJoin="round" />
<path
android:fillColor="#00000000"
android:fillType="evenOdd"
android:pathData="M62,98C58.455,96.594 54.731,96.964 51,97.358"
android:strokeWidth="2"
android:strokeColor="#0229BC"
android:strokeLineCap="round"
android:strokeLineJoin="round" />
<path
android:fillColor="#00000000"
android:fillType="evenOdd"
android:pathData="M84,65L84,70C80.806,68.165 79.828,64.394 76,66.161"
android:strokeWidth="2"
android:strokeColor="#0229BC"
android:strokeLineCap="round"
android:strokeLineJoin="round" />
<path
android:fillColor="#2DE0A5"
android:fillType="nonZero"
android:pathData="M37.238,55C37.766,54.526 38.384,54.159 39.058,53.919L41.027,53.22C41.32,53.116 41.592,52.959 41.828,52.759L43.399,51.428C43.65,51.215 43.822,50.927 43.89,50.609L43.952,50.323C44.052,49.89 44.415,49.561 44.865,49.497L45.728,49.394C46.022,49.359 46.311,49.285 46.585,49.175L48.597,48.367L49.268,48.203C49.625,48.117 49.988,48.058 50.355,48.027L50.92,47.978C51.575,47.92 52.209,47.728 52.783,47.414L54.702,46.363C55.075,46.159 55.405,45.889 55.677,45.567L57,44L55.721,44C52.247,44.342 48.889,45.406 45.871,47.122C40.735,50.031 39.66,51.599 37.039,54.8C36.884,54.964 37.238,55 37.238,55Z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
<path
android:fillColor="#2DE0A5"
android:fillType="nonZero"
android:pathData="M32.026,68.024C32.495,67.92 32.972,68.163 33.125,68.583C33.497,69.347 33.707,68.926 33.497,72.793C33.288,76.66 37.068,77.171 37.46,77.478C37.46,77.478 39.775,79.102 38.728,82.223C38.728,82.223 37.793,82.909 37.983,85.164L37.983,86C37.983,86 35.786,83.739 33.458,78.44C32.19,75.548 32,68.643 32,68.643L32.026,68.024Z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
<path
android:fillColor="#2DE0A5"
android:fillType="nonZero"
android:pathData="M50,95.752C50,95.752 57.663,93.503 61,96.785C61,96.785 56.396,97.701 50,95.752Z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
<path
android:fillColor="#2DE0A5"
android:fillType="nonZero"
android:pathData="M68.034,45.053C68.034,45.053 73.587,47.878 76.391,50.624C79.195,53.369 79.231,53.29 81.768,57.225C82.537,58.559 83.172,59.968 83.661,61.429C84.113,63.024 84.113,64.715 83.661,66.31C83.661,66.31 83.661,66.694 83.055,65.95C82.448,65.206 80.02,62.423 78.752,62.344C77.483,62.265 76.148,62.405 76.178,62.783C76.209,63.162 75.626,62.735 77.113,65.047C78.6,67.359 74.788,67.701 74.788,67.701C74.788,67.701 74.527,68.012 71.529,66.694C71.529,66.694 70.17,66.359 71.02,68.061C71.869,69.763 72.021,70.819 75.632,73.363C75.632,73.363 75.99,73.363 74.788,74.73C73.44,76.26 72.373,78.021 71.639,79.928C71.639,79.928 71.639,83.412 70.103,85.419C68.91,86.906 67.59,88.286 66.158,89.543C65.749,89.906 65.199,90.065 64.661,89.976C64.122,89.887 63.652,89.561 63.379,89.086C62.165,87.255 61.267,86.48 61.467,84.357C61.667,82.234 61.831,81.477 61.006,79.068C60.18,76.658 60.077,75.218 58.9,75.175C57.722,75.132 53.438,75.657 52.703,74.62C51.969,73.583 49.226,73.247 49.007,68.872C48.789,64.498 53.698,62.064 53.698,62.064C53.698,62.064 54.518,61.13 58.305,61.545C62.092,61.96 62.395,63.888 66.28,63.613C70.376,63.32 70.328,60.679 66.498,59.489C63.379,58.525 58.305,59.287 56.527,59.599C55.939,59.716 55.329,59.586 54.839,59.239C54.305,58.836 54.038,58.177 55.483,57.225C58.105,55.493 61.03,54.541 60.866,51.734C60.866,51.734 60.472,51.191 62.213,50.959C63.955,50.728 68.926,49.971 66.504,47.909C66.522,47.884 67.05,44.541 68.034,45.053Z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
<path
android:fillColor="#00000000"
android:fillType="evenOdd"
android:pathData="M73,57C73,57 90.458,51.299 87.706,35"
android:strokeWidth="3"
android:strokeColor="#E1E5E8"
android:strokeLineCap="round" />
<path
android:fillColor="#F5455C"
android:fillType="nonZero"
android:pathData="M196,121m-21.811,2.875a22,22 128.215,1 1,43.623 -5.751a22,22 128.215,1 1,-43.623 5.751"
android:strokeWidth="1"
android:strokeColor="#00000000" />
<path
android:fillColor="#00000000"
android:fillType="nonZero"
android:pathData="M197,119m-21.811,2.875a22,22 65.095,1 1,43.623 -5.751a22,22 65.095,1 1,-43.623 5.751"
android:strokeWidth="2"
android:strokeColor="#175CC4" />
<path
android:fillColor="#00000000"
android:fillType="evenOdd"
android:pathData="M178,129L190,129"
android:strokeWidth="2"
android:strokeColor="#175CC4"
android:strokeLineCap="round" />
<path
android:fillColor="#00000000"
android:fillType="evenOdd"
android:pathData="M177,126L184,126"
android:strokeWidth="2"
android:strokeColor="#175CC4"
android:strokeLineCap="round" />
<path
android:fillColor="#EB1221"
android:fillType="nonZero"
android:pathData="M201.5,135.5m-3.47,0.457a3.5,3.5 93.829,1 1,6.94 -0.915a3.5,3.5 93.829,1 1,-6.94 0.915"
android:strokeWidth="1"
android:strokeColor="#00000000" />
<path
android:fillColor="#00000000"
android:fillType="evenOdd"
android:pathData="M202,135m-3,0a3,3 0,1 1,6 0a3,3 0,1 1,-6 0"
android:strokeWidth="2"
android:strokeColor="#175CC4"
android:strokeLineCap="round" />
<path
android:fillColor="#F5455C"
android:fillType="nonZero"
android:pathData="M166.5,141.5m-6.444,0.85a6.5,6.5 61.837,1 1,12.888 -1.699a6.5,6.5 61.837,1 1,-12.888 1.699"
android:strokeWidth="1"
android:strokeColor="#00000000" />
<path
android:fillColor="#00000000"
android:fillType="nonZero"
android:pathData="M168.5,139.5m-6.444,0.85a6.5,6.5 111.735,1 1,12.888 -1.699a6.5,6.5 111.735,1 1,-12.888 1.699"
android:strokeWidth="2"
android:strokeColor="#175CC4" />
<path
android:fillColor="#F5455C"
android:fillType="nonZero"
android:pathData="M182,151m-3.966,0.523a4,4 127.79,1 1,7.931 -1.046a4,4 127.79,1 1,-7.931 1.046"
android:strokeWidth="1"
android:strokeColor="#00000000" />
<path
android:fillColor="#00000000"
android:fillType="nonZero"
android:pathData="M184,150m-3.966,0.523a4,4 99.849,1 1,7.931 -1.046a4,4 99.849,1 1,-7.931 1.046"
android:strokeWidth="2"
android:strokeColor="#175CC4" />
<path
android:fillColor="#9EA2A8"
android:fillType="nonZero"
android:pathData="M127.5,102.5m-3.597,18.147a18.5,18.5 54.442,1 1,7.193 -36.294a18.5,18.5 54.442,1 1,-7.193 36.294"
android:strokeWidth="1"
android:strokeColor="#00000000" />
<path
android:fillColor="#00000000"
android:fillType="nonZero"
android:pathData="M129.5,101.5m-3.597,18.147a18.5,18.5 53.68,1 1,7.193 -36.294a18.5,18.5 53.68,1 1,-7.193 36.294"
android:strokeWidth="2"
android:strokeColor="#175CC4" />
<path
android:fillColor="#61666E"
android:fillType="nonZero"
android:pathData="M126.5,90.5m-0.486,2.452a2.5,2.5 121.374,1 1,0.972 -4.905a2.5,2.5 121.374,1 1,-0.972 4.905"
android:strokeWidth="1"
android:strokeColor="#00000000" />
<path
android:fillColor="#61666E"
android:fillType="nonZero"
android:pathData="M142,104m-0.583,2.943a3,3 111.552,1 1,1.166 -5.886a3,3 111.552,1 1,-1.166 5.886"
android:strokeWidth="1"
android:strokeColor="#00000000" />
<path
android:fillColor="#61666E"
android:fillType="nonZero"
android:pathData="M133,110m-0.972,4.905a5,5 112.598,1 1,1.944 -9.809a5,5 112.598,1 1,-1.944 9.809"
android:strokeWidth="1"
android:strokeColor="#00000000" />
<path
android:fillColor="#61666E"
android:fillType="nonZero"
android:pathData="M133.274,85.116C133.043,85.073 132.817,85.037 132.586,85C132.369,85.415 132.212,85.859 132.117,86.318C131.839,87.539 132.061,88.821 132.734,89.876C133.407,90.931 134.475,91.672 135.697,91.931C137.999,92.326 139.936,91.573 140.689,89.357C138.464,87.504 136.116,85.66 133.274,85.116Z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
<path
android:fillColor="#00000000"
android:fillType="evenOdd"
android:pathData="M134,108m-0.972,4.905a5,5 112.598,1 1,1.944 -9.809a5,5 112.598,1 1,-1.944 9.809"
android:strokeWidth="2"
android:strokeColor="#175CC4" />
<path
android:fillColor="#00000000"
android:fillType="evenOdd"
android:pathData="M143,103m-0.583,2.943a3,3 111.552,1 1,1.166 -5.886a3,3 111.552,1 1,-1.166 5.886"
android:strokeWidth="2"
android:strokeColor="#175CC4" />
<path
android:fillColor="#00000000"
android:fillType="evenOdd"
android:pathData="M127,89m-0.389,1.962a2,2 101.648,1 1,0.778 -3.924a2,2 101.648,1 1,-0.778 3.924"
android:strokeWidth="2"
android:strokeColor="#175CC4" />
<path
android:fillColor="#00000000"
android:fillType="evenOdd"
android:pathData="M133.147,83.432C132.916,83.389 132.622,83.469 132.391,83.432C132.174,83.847 132.212,83.859 132.117,84.318C131.839,85.539 132.061,86.821 132.734,87.876C133.407,88.931 134.475,89.672 135.697,89.931C137.999,90.326 140.022,89.424 140.775,87.208C138.549,85.354 135.99,83.976 133.147,83.432Z"
android:strokeWidth="2"
android:strokeColor="#175CC4" />
<path
android:fillColor="#FFD21F"
android:fillType="nonZero"
android:pathData="M52.621,112.791L58.947,115.787A3,3 127.343,0 1,60.375 119.782L60.375,119.782A3,3 127.343,0 1,56.379 121.209L50.053,118.213A3,3 117.626,0 1,48.625 114.218L48.625,114.218A3,3 117.626,0 1,52.621 112.791z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
<path
android:fillColor="#FFD21F"
android:fillType="nonZero"
android:pathData="M25.621,100.791L31.947,103.787A3,3 114.062,0 1,33.375 107.782L33.375,107.782A3,3 114.062,0 1,29.379 109.209L23.053,106.213A3,3 63.668,0 1,21.625 102.218L21.625,102.218A3,3 63.668,0 1,25.621 100.791z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
<path
android:fillColor="#1D74F5"
android:fillType="nonZero"
android:pathData="M41.5,111.5m-1.926,4.067a4.5,4.5 81.105,1 1,3.852 -8.134a4.5,4.5 81.105,1 1,-3.852 8.134"
android:strokeWidth="1"
android:strokeColor="#00000000" />
<path
android:fillColor="#00000000"
android:fillType="evenOdd"
android:pathData="M35,106L39,108"
android:strokeWidth="2"
android:strokeColor="#175CC4" />
<path
android:fillColor="#00000000"
android:fillType="evenOdd"
android:pathData="M47,112L51,114"
android:strokeWidth="2"
android:strokeColor="#175CC4" />
<path
android:fillColor="#00000000"
android:fillType="evenOdd"
android:pathData="M54.621,111.791L60.947,114.787A3,3 117.626,0 1,62.375 118.782L62.375,118.782A3,3 117.626,0 1,58.379 120.209L52.053,117.213A3,3 127.343,0 1,50.625 113.218L50.625,113.218A3,3 127.343,0 1,54.621 111.791z"
android:strokeWidth="2"
android:strokeColor="#175CC4" />
<path
android:fillColor="#00000000"
android:fillType="evenOdd"
android:pathData="M27.621,99.791L33.947,102.787A3,3 97.846,0 1,35.375 106.782L35.375,106.782A3,3 97.846,0 1,31.379 108.209L25.053,105.213A3,3 63.668,0 1,23.625 101.218L23.625,101.218A3,3 63.668,0 1,27.621 99.791z"
android:strokeWidth="2"
android:strokeColor="#175CC4" />
<path
android:fillColor="#00000000"
android:fillType="evenOdd"
android:pathData="M42.5,109.5m-1.926,4.067a4.5,4.5 65.689,1 1,3.852 -8.134a4.5,4.5 65.689,1 1,-3.852 8.134"
android:strokeWidth="2"
android:strokeColor="#175CC4" />
<path
android:fillColor="#2DE0A5"
android:fillType="nonZero"
android:pathData="M91.5,163.5m-32.221,4.248a32.5,32.5 120.622,1 1,64.442 -8.495a32.5,32.5 120.622,1 1,-64.442 8.495"
android:strokeWidth="1"
android:strokeColor="#00000000" />
<path
android:fillColor="#00000000"
android:fillType="nonZero"
android:pathData="M93.5,161.5m-32.221,4.248a32.5,32.5 126.125,1 1,64.442 -8.495a32.5,32.5 126.125,1 1,-64.442 8.495"
android:strokeWidth="2"
android:strokeColor="#175CC4" />
<path
android:fillColor="#175CC4"
android:fillType="nonZero"
android:pathData="M92,143.003C92,141.345 90.691,140 89.077,140L69.243,140C68.881,140.422 68.528,140.845 68.182,141.267L89.077,141.267C89.969,141.323 90.665,142.082 90.665,143C90.665,143.918 89.969,144.677 89.077,144.733L65.734,144.733C65.481,145.151 65.234,145.575 65,146L89.052,146C89.831,146.007 90.58,145.694 91.133,145.132C91.686,144.569 91.998,143.803 92,143.003Z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
<path
android:fillColor="#175CC4"
android:fillType="nonZero"
android:pathData="M115,150.5C114.997,149.673 114.221,149.003 113.264,149L64.296,149C64.115,149.344 63.952,149.693 63.79,150.042L113.258,150.042C113.551,150.042 113.788,150.247 113.788,150.5C113.788,150.753 113.551,150.958 113.258,150.958L63.41,150.958C63.265,151.302 63.127,151.651 63,152L113.258,152C114.218,152 114.997,151.329 115,150.5Z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
<path
android:fillColor="#175CC4"
android:fillType="nonZero"
android:pathData="M111,160.003C111,158.345 109.675,157 108.041,157L71.825,157C70.244,157.073 69,158.394 69,160C69,161.606 70.244,162.927 71.825,163L108.041,163C109.673,163 110.997,161.659 111,160.003ZM70.09,160.003C70.09,159.534 70.273,159.085 70.6,158.753C70.926,158.422 71.369,158.236 71.831,158.236L108.047,158.236C108.693,158.197 109.306,158.525 109.641,159.086C109.975,159.648 109.975,160.352 109.641,160.914C109.306,161.475 108.693,161.803 108.047,161.764L71.825,161.764C70.868,161.761 70.093,160.974 70.09,160.003Z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
<path
android:fillColor="#175CC4"
android:fillType="nonZero"
android:pathData="M87.252,168.667C86.672,168.667 86.201,168.144 86.201,167.5C86.201,166.856 86.672,166.333 87.252,166.333L125.874,166.333C125.922,165.893 125.964,165.447 126,165L87.252,165C86.008,165 85,166.119 85,167.5C85,168.881 86.008,170 87.252,170L125.279,170C125.375,169.56 125.453,169.113 125.531,168.667L87.252,168.667Z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
<path
android:fillColor="#175CC4"
android:fillType="nonZero"
android:pathData="M102,183.5C101.997,181.568 100.517,180.004 98.69,180L66,180C66.273,180.424 66.552,180.848 66.837,181.273L98.69,181.273C99.853,181.273 100.796,182.27 100.796,183.5C100.796,184.73 99.853,185.727 98.69,185.727L70.526,185.727C70.955,186.152 71.394,186.576 71.844,187L98.69,187C100.517,186.996 101.997,185.432 102,183.5Z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
<path
android:fillColor="#175CC4"
android:fillType="nonZero"
android:pathData="M89.876,190C88.289,190.003 87.003,191.286 87,192.87C87.004,193.159 87.053,193.445 87.144,193.719C87.639,193.823 88.14,193.916 88.648,194C88.193,193.513 88.073,192.802 88.341,192.193C88.609,191.584 89.215,191.192 89.882,191.196L107.855,191.196C108.586,190.843 109.305,190.466 110,190.066C109.803,190.023 109.602,190.001 109.401,190L89.876,190Z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="20dp"
android:height="20dp"
android:viewportWidth="20"
android:viewportHeight="20">
<path
android:fillType="evenOdd"
android:pathData="M0 0h20v20H0z" />
<path
android:fillType="evenOdd"
android:strokeColor="#2F343D"
android:strokeWidth="1.5"
android:pathData="M4 15.106c0-3.783 4.5-3.026 4.5-4.54 0 0 0.086-1.004-0.41-1.513C7.473 8.423 7 7.665 7 6.405 7 4.525 8.343 3 10 3s3 1.524 3 3.405c0 1.243-0.46 2.017-1.105 2.648-0.472 0.496 -0.395 1.514-0.395 1.514 0 1.513 4.5 0.756 4.5 4.54 0 0-1.195 0.893 -6 0.893s-6-0.894-6-0.894z" />
</vector>
\ No newline at end of file
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M12,12c2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4 -4,1.79 -4,4 1.79,4 4,4zM12,14c-2.67,0 -8,1.34 -8,4v2h16v-2c0,-2.66 -5.33,-4 -8,-4z" />
</vector>
\ No newline at end of file
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="30dp"
android:height="30dp"
android:viewportWidth="30"
android:viewportHeight="30">
<path
android:fillType="nonZero"
android:pathData="M26.2984,12.2493L26.2989,12.2501C26.2988,12.2499 26.2987,12.2498 26.2987,12.2497C26.2986,12.2496 26.2985,12.2494 26.2984,12.2493ZM9.0351,3.1159C9.9595,3.6298 10.8333,4.2801 11.5795,5.0028C12.7824,4.7847 14.0227,4.6747 15.281,4.6747C19.0478,4.6747 22.6191,5.6639 25.3363,7.4593C26.7435,8.3896 27.8619,9.4933 28.6603,10.7402C29.5493,12.1296 30,13.6236 30,15.2246C30,16.7824 29.5493,18.2771 28.6603,19.6662C27.8619,20.9137 26.7435,22.0171 25.3363,22.9474C22.6191,24.7428 19.0481,25.7314 15.281,25.7314C14.0227,25.7314 12.7827,25.6214 11.5795,25.4036C10.833,26.126 9.9595,26.7765 9.0351,27.2905C4.0959,29.7665 0,27.3487 0,27.3487C0,27.3487 3.8081,24.1124 3.1889,21.2755C1.485,19.5274 0.5618,17.419 0.5618,15.1821C0.5618,12.9873 1.4853,10.879 3.1889,9.1306C3.808,6.2944 0.0013,3.0587 0,3.0577C0.0012,3.057 4.0966,0.6399 9.0351,3.1159Z"
android:strokeWidth="1"
android:strokeColor="#00000000">
<aapt:attr name="android:fillColor">
<gradient
android:endX="15"
android:endY="25.305122"
android:startX="15"
android:startY="-1.2754043"
android:type="linear">
<item
android:color="#FFDB2323"
android:offset="0" />
<item
android:color="#FFDB2323"
android:offset="1" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M5.9908,19.9981C4.2976,18.6585 3.2813,16.9442 3.2813,15.0757C3.2813,10.7882 8.6328,7.3125 15.2344,7.3125C21.8359,7.3125 27.1875,10.7882 27.1875,15.0757C27.1875,19.3632 21.8359,22.8389 15.2344,22.8389C13.6074,22.8389 12.0563,22.6278 10.6425,22.2454L9.6089,23.2462C9.0473,23.79 8.3891,24.2821 7.7029,24.6698C6.7935,25.1182 5.8954,25.3629 5.0068,25.4375C5.057,25.346 5.1031,25.2533 5.1527,25.1617C6.1883,23.2464 6.4677,21.5252 5.9908,19.9981Z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
<path
android:fillType="nonZero"
android:pathData="M9.5313,15.125m-1.7188,0a1.7188,1.7188 0,1 1,3.4375 0a1.7188,1.7188 0,1 1,-3.4375 0"
android:strokeWidth="1"
android:strokeColor="#00000000">
<aapt:attr name="android:fillColor">
<gradient
android:endX="9.53125"
android:endY="16.345976"
android:startX="9.53125"
android:startY="13.133343"
android:type="linear">
<item
android:color="#FFDB2323"
android:offset="0" />
<item
android:color="#FFDB2323"
android:offset="1" />
</gradient>
</aapt:attr>
</path>
<path
android:fillType="nonZero"
android:pathData="M15.1563,15.125m-1.7188,0a1.7188,1.7188 0,1 1,3.4375 0a1.7188,1.7188 0,1 1,-3.4375 0"
android:strokeWidth="1"
android:strokeColor="#00000000">
<aapt:attr name="android:fillColor">
<gradient
android:endX="15.15625"
android:endY="16.345972"
android:startX="15.15625"
android:startY="13.133308"
android:type="linear">
<item
android:color="#FFDB2323"
android:offset="0" />
<item
android:color="#FFDB2323"
android:offset="1" />
</gradient>
</aapt:attr>
</path>
<path
android:fillType="nonZero"
android:pathData="M20.9375,15.125m-1.7188,0a1.7188,1.7188 0,1 1,3.4375 0a1.7188,1.7188 0,1 1,-3.4375 0"
android:strokeWidth="1"
android:strokeColor="#00000000">
<aapt:attr name="android:fillColor">
<gradient
android:endX="20.93745"
android:endY="16.345972"
android:startX="20.93745"
android:startY="13.133308"
android:type="linear">
<item
android:color="#FFDB2323"
android:offset="0" />
<item
android:color="#FFDB2323"
android:offset="1" />
</gradient>
</aapt:attr>
</path>
</vector>
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="210dp"
android:height="171dp"
android:viewportWidth="210"
android:viewportHeight="171">
<path
android:fillColor="#FFF"
android:fillType="evenOdd"
android:pathData="M-75-65h360v640H-75z" />
<path
android:fillColor="#4FBC91"
android:pathData="M162.75 170.625a4.375 4.375 0 1 1 0-8.75 4.375 4.375 0 0 1 0 8.75zm0-2.574a1.801 1.801 0 1 0 0-3.602 1.801 1.801 0 0 0 0 3.602z" />
<path
android:fillColor="#EE475D"
android:pathData="M157.5 8.75a4.375 4.375 0 1 1 0-8.75 4.375 4.375 0 0 1 0 8.75zm0-2.574a1.801 1.801 0 1 0 0-3.602 1.801 1.801 0 0 0 0 3.602z" />
<path
android:fillColor="#4FBC91"
android:pathData="M40.25 15.266c0-0.699 0.588 -1.266 1.313-1.266 0.724 0 1.312 0.567 1.312 1.266v7.093c0 0.699-0.588 1.266-1.313 1.266-0.724 0-1.312-0.567-1.312-1.266v-7.093z" />
<path
android:fillColor="#4FBC91"
android:pathData="M38.016 20.125c-0.699 0-1.266-0.588-1.266-1.313 0-0.724 0.567 -1.312 1.266-1.312h7.093c0.699 0 1.266 0.588 1.266 1.313 0 0.724-0.567 1.312-1.266 1.312h-7.093z" />
<path
android:fillColor="#FFD120"
android:pathData="M204.244 143.012a1.321 1.321 0 0 1 1.869 1.869l-5.232 5.232a1.321 1.321 0 0 1-1.869-1.869l5.232-5.232z" />
<path
android:fillColor="#FFD120"
android:pathData="M199.012 144.88a1.321 1.321 0 0 1 1.869-1.868l5.232 5.232a1.321 1.321 0 0 1-1.869 1.869l-5.232-5.232z" />
<path
android:fillColor="#3260AB"
android:pathData="M16.48 135.347c-0.62-0.276-0.9-1-0.623-1.62a1.23 1.23 0 0 1 1.623-0.62l6.29 2.796c0.62 0.276 0.9 1 0.623 1.62a1.23 1.23 0 0 1-1.623 0.62 l-6.29-2.796z" />
<path
android:fillColor="#3260AB"
android:pathData="M20.725 139.27a1.227 1.227 0 1 1-2.244-0.998l2.794-6.292a1.227 1.227 0 1 1 2.244 0.998 l-2.794 6.292z" />
<path
android:fillColor="#F5455C"
android:pathData="M167.787 53s-10.093 2.32-11.768 13.076a1.45 1.45 0 0 0 1.094 1.642c2.803 0.684 9.579 0.838 15.731-8.26" />
<path
android:fillColor="#175CC4"
android:pathData="M207.603 24.5l0.164 0.01 a2.559 2.559 0 0 1 2.214 2.848l-0.03 0.16 c-2.889 11.207-8.962 19.465-17.257 25.174-4.172 2.872-8.637 4.91-13.103 6.258-1.565 0.473 -3.02 0.826 -4.33 1.077-0.796 0.152 -1.373 0.237 -1.696 0.272 l-0.696 0.076 -5.744-7.239 0.291 -0.688c4.896-11.589 12.514-19.288 21.92-23.734 6.415-3.033 12.845-4.214 18.267-4.214zm-17.183 6.498c-8.668 4.098-15.72 11.127-20.375 21.746l3.924 4.945c0.247-0.04 0.518 -0.087 0.814 -0.144a40.62 40.62 0 0 0 4.074-1.013c4.228-1.277 8.457-3.207 12.398-5.92 7.774-5.35 13.46-13.06 16.205-23.583-5.045 0.02 -11.056 1.14-17.04 3.969zm17.076-4.109l-0.036 0.138 h0.143v-0.11l-0.107-0.028z" />
<path
android:fillColor="#175CC4"
android:pathData="M180.862 45.768a1.242 1.242 0 0 1 1.542 1.947l-16.766 13.267a1.242 1.242 0 0 1-1.542-1.947l16.766-13.267zM158.244 47.427l-0.032 0.03 0.03-0.032 0.002 0.002zm10.482 4.44a1.24 1.24 0 0 1 0.62 1.658 1.28 1.28 0 0 1-1.688 0.609 L157.13 49.34a2.256 2.256 0 0 1-1.345-1.674 2.213 2.213 0 0 1 0.663-2.007c4.824-4.67 9.583-7.53 14.16-8.884 3.322-0.982 6.228-1.092 8.603-0.658a1.25 1.25 0 0 1 1.017 1.457 1.27 1.27 0 0 1-1.481 1c-1.99-0.364-4.487-0.27-7.406 0.593 -4.089 1.21-8.4 3.776-12.862 8.034l10.247 4.666zM175.892 70.115l0.002-0.002-0.002 0.002 zm11.166-11.137c1.447-2.657 2.035-5.044 2.075-6.985a1.257 1.257 0 0 1 1.272-1.243 1.26 1.26 0 0 1 1.22 1.296c-0.048 2.34-0.736 5.132-2.387 8.163-2.32 4.26-6.227 8.346-12 12.043a2.21 2.21 0 0 1-1.689 0.324 2.262 2.262 0 0 1-1.74-1.802l-2.283-11.334a1.27 1.27 0 0 1 0.97-1.499 1.247 1.247 0 0 1 1.471 0.989 l2.216 10.997c5.284-3.435 8.803-7.144 10.875-10.95zM191.625 44.625a4.375 4.375 0 1 1 0-8.75 4.375 4.375 0 0 1 0 8.75zm0-2.607a1.768 1.768 0 1 0 0-3.536 1.768 1.768 0 0 0 0 3.536zM193.647 29.093a1.184 1.184 0 0 1 0.207-1.706 1.295 1.295 0 0 1 1.772 0.199 l4.477 5.446c0.432 0.526 0.34 1.29-0.207 1.706a1.295 1.295 0 0 1-1.772-0.199l-4.477-5.446zM160.675 76.05a1.26 1.26 0 0 1 2.5-0.321c3.596 28.017-14.741 54.16-42.308 60.32-27.567 6.162-55.29-9.688-63.964-36.569-8.675-26.881 4.553-55.945 30.521-67.06 25.969-11.114 56.129-0.62 69.59 24.212a1.26 1.26 0 1 1-2.217 1.202c-12.84-23.688-41.61-33.698-66.38-23.096-24.771 10.602-37.39 38.326-29.115 63.968 8.275 25.641 34.72 40.76 61.015 34.883 26.296-5.876 43.787-30.814 40.358-57.54z" />
<path
android:fillColor="#3260AB"
android:pathData="M138.031 72.612c-0.73 0.101 -1.406-0.4-1.51-1.119-0.313-2.19 2.805-3.488 6.619-3.204a1.322 1.322 0 0 1 1.231 1.41 1.33 1.33 0 0 1-1.433 1.212c-1.428-0.106-2.583 0.046 -3.345 0.363 a2.04 2.04 0 0 0-0.427 0.23 1.326 1.326 0 0 1-1.135 1.108zM129.397 78.873a1.242 1.242 0 0 1-1.547-0.796 2.147 2.147 0 0 1 0.838-2.417 2.263 2.263 0 0 1 2.598 0.015 c1.32 0.926 2.699 1.862 3.966 2.682 1.616 1.051 3.79 0.692 4.97-0.831a6.39 6.39 0 0 0 0.895-1.521 1.247 1.247 0 0 1 1.61-0.666c0.633 0.25 0.939 0.954 0.682 1.573a8.767 8.767 0 0 1-1.223 2.082c-1.961 2.531-5.6 3.133-8.302 1.375-1.16-0.75-2.41-1.594-3.633-2.447a1.217 1.217 0 0 1-0.854 0.951 z" />
<path
android:fillColor="#175CC4"
android:pathData="M123.6 70.786c0.55-0.021 1.247-0.991 1.247-1.863a3.198 3.198 0 0 0-1.507-2.685c-1.57-1.094-10.968-1.9-18.037-1.015-2.246 0.33 -4.519 0.441 -6.81 0.33 a3.014 3.014 0 0 1-1.92-5.14c1.16-1.163 2.785-2.344 4.881-3.63 1.143-0.702 1.676-1.006 3.975-2.293 1.243-0.696 1.838-1.055 1.957-1.174 0.165 -0.157 0.334 -1.495 0.301 -3.084a4.416 4.416 0 0 1 2.75-4.254c2.194-0.875 5.352-1.258 8.427-1.258 1.014-0.013 1.828-0.842 1.82-1.837-0.013-0.575-0.07-1.15-0.17-1.723a8.078 8.078 0 0 1 0.203-3.591l0.724-2.527a1.267 1.267 0 0 1 2.435 0.694 l-0.725 2.53a5.552 5.552 0 0 0-0.141 2.466c0.12 0.692 0.19 1.392 0.206 2.112 0.02 2.404-1.91 4.372-4.336 4.403-2.795 0-5.678 0.35 -7.492 1.073-0.733 0.3 -1.2 1.026-1.17 1.837 0.052 2.467-0.155 4.098-1.059 4.964-0.451 0.45 -5.525 3.29-6.378 3.814-1.94 1.191-3.424 2.27-4.416 3.263a0.49 0.49 0 0 0 0.276 0.831 c2.102 0.103 4.21 0 6.32-0.31 7.64-0.956 17.502-0.118 19.745 1.39a5.729 5.729 0 0 1 2.673 4.802c0 2.146-1.638 4.403-3.798 4.403-1.23-0.05-2.4-0.18-3.522-0.383a8 8 0 0 0-2.377 0.001 10.404 10.404 0 0 1-6.024-0.944l-3.216-1.57a7.658 7.658 0 0 0-2.172-0.694c-3.543-0.568-7.253-0.346-10.405 1.187-4.47 2.172-7.21 6.74-7.575 14.489a5.304 5.304 0 0 0 2.202 4.58c3.541 2.553 7.645 4.137 12.072 4.033 0.186 -0.05 0.435 -0.106 0.734 -0.163 0.915 -0.172 1.85-0.249 2.752-0.172 2.195 0.186 3.823 1.274 4.242 3.426 0.339 1.576 0.55 3.177 0.63 4.806 0.015 0.426 0.108 0.846 0.273 1.234l1.286 2.992a10.676 10.676 0 0 1 0.77 5.675l-0.35 2.527a4.644 4.644 0 0 0 0.296 2.378c1.395 3.369 3.264 5.748 5.236 5.879 2.17 0.145 6.535-4.279 10.746-9.994a4.673 4.673 0 0 0 0.897-2.494l0.208-3.684a11.04 11.04 0 0 1 1.848-5.526c2.669-3.977 4.924-7.487 6.167-9.72 0.005 -0.349-0.054-0.423-0.126-0.463-0.852-0.104-1.37-0.714-1.312-1.41a1.265 1.265 0 0 1 1.366-1.155 2.872 2.872 0 0 1 2.284 4.259c-1.287 2.312-3.566 5.86-6.274 9.893a8.516 8.516 0 0 0-1.425 4.263l-0.207 3.686a7.2 7.2 0 0 1-1.384 3.845c-1.78 2.416-3.7 4.762-5.611 6.699-2.835 2.873-5.242 4.463-7.345 4.322-3.31-0.218-5.66-3.212-7.411-7.443a7.175 7.175 0 0 1-0.461-3.68l0.35-2.527c0.201-1.47-0.002-2.969-0.589-4.333l-1.288-2.997a5.961 5.961 0 0 1-0.474-2.12 27.394 27.394 0 0 0-0.581-4.406c-0.176-0.898-0.797-1.314-1.976-1.414-0.641-0.055-1.36 0.004 -2.066 0.137 a7.422 7.422 0 0 0-0.658 0.15 l-0.318 0.051 c-5.116 0.165 -9.787-1.623-13.783-4.504a7.826 7.826 0 0 1-3.25-6.755c0.407-8.627 3.654-14.04 8.996-16.637 3.704-1.8 7.905-2.052 11.916-1.409 1.005 0.162 1.98 0.473 2.887 0.921 l3.21 1.567a7.859 7.859 0 0 0 4.545 0.715 10.567 10.567 0 0 1 3.16 0.006 c1.032 0.186 2.076 0.303 3.125 0.348 zM65.678 89.125c0.196 0.144 0.321 0.232 0.965 0.676 1.256 0.866 2.032 1.431 2.907 2.15 3.513 2.892 5.369 5.853 4.684 9.166-1.607 7.78-1.89 9.9-1.346 11.896a1.275 1.275 0 0 1-0.869 1.565 1.245 1.245 0 0 1-1.536-0.885c-0.705-2.586-0.416-4.747 1.31-13.1 0.432 -2.095-0.96-4.315-3.812-6.663-0.904-0.744-4.298-3.085-4.579-3.517-0.447-0.687-0.735-2.867-1.088-6.907-0.24-2.743-0.448-5.948-0.448-6.75-0.09-0.256-0.366-0.61-0.774-0.989a8.34 8.34 0 0 0-1.011-0.796 1.286 1.286 0 0 1-0.387-1.754 1.234 1.234 0 0 1 1.722-0.395c0.351 0.227 0.849 0.595 1.355 1.064 0.686 0.635 1.19 1.281 1.436 1.99 0.1 0.287 0.155 0.58 0.155 0.88 0 0.702 0.205 3.856 0.438 6.525 0.14 1.594 0.284 2.986 0.423 4.042a22.1 22.1 0 0 0 0.216 1.372c0.024 0.119 0.046 0.22 0.067 0.298 0.046 0.037 0.103 0.081 0.172 0.132 z" />
<path
android:fillColor="#3260AB"
android:pathData="M94.383 130.775a1.243 1.243 0 0 1-1.569-0.776 1.225 1.225 0 0 1 0.784-1.553l0.208-0.068 0.549 -0.173a57.194 57.194 0 0 1 1.796-0.523c0.398-0.109 0.784 -0.209 1.155-0.3 1.035-0.25 1.913-0.414 2.607-0.47 2.243-0.176 9.852 0.3 11.708 0.76 0.258 0.064 0.521 0.148 0.791 0.25 0.565 0.212 1.148 0.5 1.741 0.845 a17.58 17.58 0 0 1 1.749 1.165 1.22 1.22 0 0 1 0.207 1.724 1.248 1.248 0 0 1-1.74 0.206 15.125 15.125 0 0 0-1.47-0.977 9.334 9.334 0 0 0-1.368-0.668 4.6 4.6 0 0 0-0.511-0.162c-1.579-0.39-8.92-0.851-10.91-0.694-0.534 0.042 -1.297 0.184 -2.214 0.407 -0.347 0.084 -0.71 0.178 -1.085 0.281 a54.733 54.733 0 0 0-2.428 0.726 z" />
<path
android:fillColor="#175CC4"
android:pathData="M138.587 105.114a2.007 2.007 0 0 1 1.289 1.659c0.505 4.271-0.56 7.232-2.658 9.007a2.56 2.56 0 0 1-1.476 0.59 2.53 2.53 0 0 1-1.876-0.68 2.728 2.728 0 0 1-0.856-1.867c-0.144-3.203 1.333-5.988 3.57-8.25l0.353-0.356h0.132a1.883 1.883 0 0 1 1.522-0.103zm-0.665 2.616l0.126-0.127c0.06-0.065 0.122 -0.129 0.185 -0.192a0.46 0.46 0 0 1-0.446 0.074 0.489 0.489 0 0 1-0.314-0.404c0.026 0.222 0.047 0.438 0.064 0.649 h0.385zm-2.494 5.97a0.173 0.173 0 0 0 0.055 0.119 c0.032 0.03 0.075 0.046 0.114 0.044 a0.18 0.18 0 0 0 0.099-0.037c1.273-1.077 2.012-2.867 1.868-5.68-1.38 1.643-2.226 3.541-2.136 5.554zM82.52 42.06a1.257 1.257 0 0 1 0.604-1.686 1.291 1.291 0 0 1 1.71 0.595 c0.126 0.26 0.298 0.69 0.466 1.256 0.57 1.928 0.656 3.97-0.077 5.926-1.165 3.113-4.199 5.345-9.325 6.374a9.953 9.953 0 0 0-4.28 2.024c-2.675 2.177-4.639 4.202-5.28 5.682a1.29 1.29 0 0 1-1.683 0.667 1.258 1.258 0 0 1-0.677-1.66c0.858-1.98 3.017-4.207 6.008-6.64a12.54 12.54 0 0 1 5.394-2.55c4.317-0.867 6.604-2.55 7.436-4.773 0.514 -1.373 0.45 -2.888 0.02 -4.341a7.707 7.707 0 0 0-0.316-0.873zM102.323 35.075c0.051-0.31 0.085 -0.65 0.103 -0.994 0.012 -0.206 0.015 -0.363 0.015 -0.444a1.26 1.26 0 0 1 1.257-1.262 1.26 1.26 0 0 1 1.257 1.262c0 0.124-0.005 0.327 -0.019 0.583 -0.023 0.427 -0.065 0.855 -0.132 1.265-0.25 1.523-0.707 2.51-1.937 2.683-0.314 0.044 -1.096 0.21 -1.922 0.428 a27.917 27.917 0 0 0-3.473 1.15c-2.694 1.106-4.426 2.44-4.816 3.809 0.008 0.02 0.018 0.043 0.031 0.065 0.033 0.052 0.083 0.094 0.195 0.135 0.24 0.089 0.626 0.112 1.183 0.024 1.644-0.262 4.165-1.4 7.64-3.614a10.14 10.14 0 0 1 3.477-1.404l6.268-1.277a11.201 11.201 0 0 0 3.849-1.576l2.639-1.707a1.254 1.254 0 0 1 1.737 0.378 1.265 1.265 0 0 1-0.377 1.745l-2.637 1.705a13.708 13.708 0 0 1-4.71 1.928l-6.273 1.278a7.642 7.642 0 0 0-2.624 1.06c-7.043 4.487-10.974 5.113-12.499 2.662a2.863 2.863 0 0 1-0.426-1.62l0.02-0.105c0.504-2.513 2.847-4.375 6.37-5.823 1.965-0.807 4.301-1.446 5.65-1.685 0.057 -0.159 0.11 -0.382 0.154 -0.649z" />
<path
android:fillColor="#E1E5E8"
android:pathData="M108.984 152.183c0.699-0.001 1.265 0.601 1.266 1.344 0 0.744-0.565 1.347-1.263 1.348a68.434 68.434 0 0 1-24.143-4.384c-0.658-0.247-1.004-1.016-0.772-1.717 0.232 -0.701 0.955 -1.07 1.613-0.822a66.043 66.043 0 0 0 23.3 4.23zM178.655 82.648c0-0.702 0.551 -1.272 1.233-1.273 0.682 -0.001 1.236 0.567 1.237 1.269 0.025 16.934-5.7 33.345-16.188 46.395a1.211 1.211 0 0 1-1.74 0.17 1.296 1.296 0 0 1-0.164-1.79c10.12-12.593 15.647-28.43 15.622-44.771zM108.886 12.067c-0.695 0-1.26-0.545-1.261-1.22-0.001-0.674 0.562 -1.22 1.257-1.222a73.79 73.79 0 0 1 32.17 7.311c0.622 0.3 0.876 1.034 0.566 1.637-0.31 0.604 -1.066 0.85 -1.688 0.549 a71.207 71.207 0 0 0-31.044-7.055zM39.307 82.738A1.27 1.27 0 0 1 38.03 84a1.27 1.27 0 0 1-1.28-1.259 71.2 71.2 0 0 1 7.194-31.276 1.287 1.287 0 0 1 1.705-0.59c0.637 0.3 0.905 1.054 0.6 1.681a68.707 68.707 0 0 0-6.942 30.182z" />
<path
android:fillColor="#F5455C"
android:pathData="M104.331 101.299a1.27 1.27 0 0 1-1.755-0.386 1.274 1.274 0 0 1 0.385-1.758c14.545-9.326 31.149-21.428 47.231-34.132a1.27 1.27 0 0 1 1.785 0.212 c0.435 0.551 0.34 1.352-0.21 1.787-16.143 12.75-32.812 24.9-47.436 34.277zM95.583 106.568a1.298 1.298 0 0 1-1.775-0.446 1.284 1.284 0 0 1 0.448-1.766c1.45-0.862 2.914-1.749 4.392-2.662a1.298 1.298 0 0 1 1.782 0.415 c0.377 0.604 0.19 1.398-0.417 1.773-1.49 0.921 -2.967 1.816-4.43 2.686zM54.47 104.193a1.272 1.272 0 0 1 1.55-0.903c0.68 0.174 1.088 0.86 0.912 1.533-1.88 7.187-1.197 10.04 1.981 12.295 4.098 2.907 11.562 0.449 28.456-8.679a1.278 1.278 0 0 1 1.725 0.501 1.25 1.25 0 0 1-0.506 1.707c-8.594 4.643-14.023 7.246-18.58 8.747-5.554 1.83-9.624 1.862-12.576-0.233-4.141-2.938-5.081-6.864-2.961-14.968z" />
<path
android:fillColor="#F5455C"
android:pathData="M60.02 126.125c0.679 0 1.23 0.56 1.23 1.25s-0.551 1.25-1.23 1.25c-3.627 0-6.547-0.92-8.727-2.792-3.677-3.127-4.689-8.445-3.668-15.33 0.745 -5.028 2.519-10.576 4.869-16.122a1.224 1.224 0 0 1 1.617-0.654c0.624 0.273 0.912 1.008 0.644 1.642-2.274 5.367-3.988 10.727-4.696 15.506-0.906 6.113-0.051 10.604 2.819 13.046 1.703 1.462 4.06 2.204 7.141 2.204zM120.519 98.209a1.294 1.294 0 0 1 1.767 0.345 c0.39 0.571 0.232 1.343-0.354 1.724-13.438 8.733-25.124 15.478-35.218 20.343a1.29 1.29 0 0 1-1.707-0.564 1.23 1.23 0 0 1 0.578-1.665c9.986-4.813 21.58-11.505 34.934-20.183zM129.226 92.098a1.3 1.3 0 0 1 1.8 0.34 c0.402 0.589 0.249 1.39-0.343 1.79l-2.659 1.799a1.3 1.3 0 0 1-1.8-0.34 1.284 1.284 0 0 1 0.343-1.79l2.659-1.799zM158.077 71.125a1.306 1.306 0 0 1 1.79 0.241 1.21 1.21 0 0 1-0.25 1.732c-8.42 6.149-16.329 11.75-23.727 16.8a1.305 1.305 0 0 1-1.781-0.297 1.21 1.21 0 0 1 0.307-1.722c7.376-5.036 15.263-10.62 23.661-16.754z" />
<path
android:fillColor="#FFD120"
android:pathData="M4.375 61.25a4.375 4.375 0 1 1 0-8.75 4.375 4.375 0 0 1 0 8.75zm0-2.574a1.801 1.801 0 1 0 0-3.602 1.801 1.801 0 0 0 0 3.602z" />
</vector>
\ No newline at end of file
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:pathData="M18,16.08c-0.76,0 -1.44,0.3 -1.96,0.77L8.91,12.7c0.05,-0.23 0.09,-0.46 0.09,-0.7s-0.04,-0.47 -0.09,-0.7l7.05,-4.11c0.54,0.5 1.25,0.81 2.04,0.81 1.66,0 3,-1.34 3,-3s-1.34,-3 -3,-3 -3,1.34 -3,3c0,0.24 0.04,0.47 0.09,0.7L8.04,9.81C7.5,9.31 6.79,9 6,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3c0.79,0 1.5,-0.31 2.04,-0.81l7.12,4.16c-0.05,0.21 -0.08,0.43 -0.08,0.65 0,1.61 1.31,2.92 2.92,2.92 1.61,0 2.92,-1.31 2.92,-2.92s-1.31,-2.92 -2.92,-2.92z"
android:fillColor="@color/actionMenuColor"/>
</vector>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="290dp"
android:height="40dp"
android:viewportHeight="40.0"
android:viewportWidth="290.0">
<path
android:fillColor="#00ACEE"
android:fillType="evenOdd"
android:pathData="M2,0L288,0A2,2 0,0 1,290 2L290,38A2,2 0,0 1,288 40L2,40A2,2 0,0 1,0 38L0,2A2,2 0,0 1,2 0z"
android:strokeColor="#00000000"
android:strokeWidth="1" />
<path
android:fillColor="#FFFFFF"
android:fillType="evenOdd"
android:pathData="M136.03,15.75L132.03,15.75L132.03,26.75L129.83,26.75L129.83,15.75L125.87,15.75L125.87,13.95L136.03,13.95L136.03,15.75ZM147.24,23.81L148.75,17.24L150.83,17.24L148.24,26.75L146.48,26.75L144.44,20.22L142.44,26.75L140.68,26.75L138.08,17.24L140.16,17.24L141.7,23.74L143.65,17.24L145.26,17.24L147.24,23.81ZM156.33,26.75L154.2,26.75L154.2,17.24L156.33,17.24L156.33,26.75ZM154.07,14.77C154.07,14.44 154.17,14.17 154.38,13.95C154.59,13.74 154.88,13.63 155.27,13.63C155.66,13.63 155.96,13.74 156.17,13.95C156.38,14.17 156.48,14.44 156.48,14.77C156.48,15.09 156.38,15.36 156.17,15.57C155.96,15.79 155.66,15.9 155.27,15.9C154.88,15.9 154.59,15.79 154.38,15.57C154.17,15.36 154.07,15.09 154.07,14.77ZM163.13,14.93L163.13,17.24L164.81,17.24L164.81,18.82L163.13,18.82L163.13,24.13C163.13,24.49 163.21,24.76 163.35,24.92C163.49,25.08 163.75,25.16 164.12,25.16C164.36,25.16 164.61,25.13 164.87,25.07L164.87,26.72C164.38,26.86 163.91,26.93 163.46,26.93C161.82,26.93 161,26.02 161,24.21L161,18.82L159.43,18.82L159.43,17.24L161,17.24L161,14.93L163.13,14.93ZM170.92,14.93L170.92,17.24L172.6,17.24L172.6,18.82L170.92,18.82L170.92,24.13C170.92,24.49 170.99,24.76 171.13,24.92C171.28,25.08 171.53,25.16 171.9,25.16C172.15,25.16 172.4,25.13 172.65,25.07L172.65,26.72C172.16,26.86 171.7,26.93 171.24,26.93C169.6,26.93 168.78,26.02 168.78,24.21L168.78,18.82L167.22,18.82L167.22,17.24L168.78,17.24L168.78,14.93L170.92,14.93ZM180.22,26.93C178.86,26.93 177.77,26.5 176.93,25.65C176.08,24.79 175.66,23.66 175.66,22.24L175.66,21.98C175.66,21.03 175.85,20.18 176.21,19.43C176.58,18.69 177.09,18.1 177.76,17.69C178.42,17.27 179.16,17.06 179.97,17.06C181.27,17.06 182.27,17.48 182.97,18.3C183.68,19.13 184.03,20.3 184.03,21.81L184.03,22.67L177.82,22.67C177.88,23.46 178.14,24.08 178.6,24.54C179.06,24.99 179.64,25.22 180.34,25.22C181.32,25.22 182.12,24.83 182.73,24.03L183.88,25.13C183.5,25.7 182.99,26.14 182.36,26.46C181.72,26.77 181.01,26.93 180.22,26.93ZM179.96,18.78C179.38,18.78 178.9,18.98 178.54,19.39C178.18,19.8 177.95,20.37 177.85,21.11L181.92,21.11L181.92,20.95C181.87,20.23 181.68,19.69 181.35,19.33C181.02,18.96 180.55,18.78 179.96,18.78ZM192.47,19.19C192.19,19.14 191.9,19.12 191.6,19.12C190.62,19.12 189.96,19.5 189.62,20.25L189.62,26.75L187.48,26.75L187.48,17.24L189.52,17.24L189.58,18.3C190.09,17.48 190.81,17.06 191.72,17.06C192.02,17.06 192.28,17.11 192.48,17.19L192.47,19.19Z"
android:strokeColor="#00000000"
android:strokeWidth="1" />
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M114.55,14.09C113.85,14.41 113.1,14.62 112.31,14.71C113.11,14.23 113.73,13.46 114.02,12.55C113.27,13 112.43,13.32 111.54,13.5C110.83,12.74 109.81,12.26 108.69,12.26C106.53,12.26 104.78,14.01 104.78,16.17C104.78,16.48 104.82,16.78 104.88,17.06C101.63,16.9 98.75,15.34 96.83,12.98C96.49,13.55 96.3,14.23 96.3,14.94C96.3,16.3 96.99,17.49 98.04,18.19C97.4,18.17 96.79,18 96.27,17.7L96.27,17.75C96.27,19.65 97.61,21.23 99.4,21.58C99.07,21.67 98.73,21.72 98.37,21.72C98.12,21.72 97.87,21.7 97.64,21.65C98.13,23.2 99.58,24.33 101.29,24.36C99.95,25.41 98.26,26.03 96.43,26.03C96.12,26.03 95.81,26.01 95.5,25.98C97.23,27.09 99.28,27.74 101.49,27.74C108.68,27.74 112.61,21.78 112.61,16.62L112.6,16.11C113.36,15.57 114.03,14.88 114.55,14.09Z"
android:strokeColor="#00000000"
android:strokeWidth="1" />
</vector>
\ No newline at end of file
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="10dp"
android:height="10dp"
android:viewportWidth="10.0"
android:viewportHeight="10.0">
<path
android:pathData="M5,5m-5,0a5,5 0,1 1,10 0a5,5 0,1 1,-10 0"
android:strokeColor="#00000000"
android:fillType="evenOdd"
android:fillColor="#000000"
android:strokeWidth="1"/>
</vector>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M12.65,10C11.83,7.67 9.61,6 7,6c-3.31,0 -6,2.69 -6,6s2.69,6 6,6c2.61,0 4.83,-1.67 5.65,-4H17v4h4v-4h2v-4H12.65zM7,14c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2z" />
</vector>
\ No newline at end of file
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="290dp"
android:height="40dp"
android:viewportWidth="290"
android:viewportHeight="40">
<path
android:fillColor="#428BBA"
android:fillType="evenOdd"
android:pathData="M2,0L288,0A2,2 0,0 1,290 2L290,38A2,2 0,0 1,288 40L2,40A2,2 0,0 1,0 38L0,2A2,2 0,0 1,2 0z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
<path
android:fillColor="#FFFFFFFF"
android:fillType="evenOdd"
android:pathData="M134.878,23.188L136.628,14.625L138.589,14.625L135.964,26L134.073,26L131.909,17.695L129.698,26L127.8,26L125.175,14.625L127.136,14.625L128.901,23.172L131.073,14.625L132.729,14.625L134.878,23.188ZM139.824,21.695C139.824,20.867 139.988,20.121 140.316,19.457C140.645,18.793 141.105,18.283 141.699,17.926C142.293,17.569 142.975,17.391 143.746,17.391C144.887,17.391 145.813,17.758 146.523,18.492C147.234,19.227 147.618,20.201 147.676,21.414L147.684,21.859C147.684,22.693 147.523,23.437 147.203,24.094C146.883,24.75 146.424,25.258 145.828,25.617C145.232,25.977 144.543,26.156 143.762,26.156C142.569,26.156 141.615,25.759 140.898,24.965C140.182,24.171 139.824,23.112 139.824,21.789L139.824,21.695ZM141.723,21.859C141.723,22.729 141.902,23.41 142.262,23.902C142.621,24.395 143.121,24.641 143.762,24.641C144.402,24.641 144.901,24.391 145.258,23.891C145.615,23.391 145.793,22.659 145.793,21.695C145.793,20.841 145.609,20.164 145.242,19.664C144.875,19.164 144.376,18.914 143.746,18.914C143.126,18.914 142.634,19.16 142.27,19.652C141.905,20.145 141.723,20.88 141.723,21.859ZM154.286,19.281C154.036,19.24 153.778,19.219 153.513,19.219C152.643,19.219 152.057,19.552 151.755,20.219L151.755,26L149.857,26L149.857,17.547L151.669,17.547L151.716,18.492C152.174,17.758 152.81,17.391 153.622,17.391C153.893,17.391 154.117,17.427 154.294,17.5L154.286,19.281ZM155.553,21.711C155.553,20.409 155.855,19.363 156.459,18.574C157.063,17.785 157.873,17.391 158.889,17.391C159.785,17.391 160.509,17.703 161.061,18.328L161.061,14L162.959,14L162.959,26L161.241,26L161.147,25.125C160.579,25.813 159.821,26.156 158.873,26.156C157.884,26.156 157.083,25.758 156.471,24.961C155.859,24.164 155.553,23.081 155.553,21.711ZM157.451,21.875C157.451,22.734 157.617,23.405 157.948,23.887C158.278,24.368 158.748,24.609 159.358,24.609C160.134,24.609 160.701,24.263 161.061,23.57L161.061,19.961C160.712,19.284 160.149,18.945 159.373,18.945C158.759,18.945 158.285,19.189 157.951,19.676C157.618,20.163 157.451,20.896 157.451,21.875ZM167.671,21.773L167.671,26L165.695,26L165.695,14.625L170.046,14.625C171.317,14.625 172.326,14.956 173.074,15.617C173.821,16.279 174.195,17.154 174.195,18.242C174.195,19.357 173.829,20.224 173.097,20.844C172.365,21.464 171.341,21.773 170.023,21.773L167.671,21.773ZM167.671,20.188L170.046,20.188C170.749,20.188 171.286,20.022 171.656,19.691C172.025,19.361 172.21,18.883 172.21,18.258C172.21,17.643 172.023,17.152 171.648,16.785C171.273,16.418 170.757,16.229 170.101,16.219L167.671,16.219L167.671,20.188ZM180.735,19.281C180.485,19.24 180.227,19.219 179.962,19.219C179.092,19.219 178.506,19.552 178.204,20.219L178.204,26L176.305,26L176.305,17.547L178.118,17.547L178.165,18.492C178.623,17.758 179.258,17.391 180.071,17.391C180.342,17.391 180.566,17.427 180.743,17.5L180.735,19.281ZM186.08,26.156C184.877,26.156 183.901,25.777 183.154,25.02C182.407,24.262 182.033,23.253 182.033,21.992L182.033,21.758C182.033,20.914 182.196,20.16 182.521,19.496C182.847,18.832 183.304,18.315 183.892,17.945C184.481,17.576 185.137,17.391 185.861,17.391C187.012,17.391 187.901,17.758 188.529,18.492C189.157,19.227 189.47,20.266 189.47,21.609L189.47,22.375L183.947,22.375C184.004,23.073 184.237,23.625 184.646,24.031C185.055,24.438 185.569,24.641 186.189,24.641C187.059,24.641 187.767,24.289 188.314,23.586L189.338,24.563C188.999,25.068 188.547,25.46 187.982,25.738C187.417,26.017 186.783,26.156 186.08,26.156ZM185.853,18.914C185.332,18.914 184.912,19.096 184.592,19.461C184.271,19.826 184.067,20.333 183.978,20.984L187.595,20.984L187.595,20.844C187.554,20.208 187.384,19.728 187.088,19.402C186.791,19.077 186.379,18.914 185.853,18.914ZM196.253,23.703C196.253,23.365 196.113,23.107 195.835,22.93C195.556,22.753 195.094,22.596 194.448,22.461C193.802,22.326 193.263,22.154 192.831,21.945C191.883,21.487 191.409,20.823 191.409,19.953C191.409,19.224 191.716,18.615 192.331,18.125C192.945,17.635 193.727,17.391 194.675,17.391C195.685,17.391 196.501,17.641 197.124,18.141C197.746,18.641 198.057,19.289 198.057,20.086L196.159,20.086C196.159,19.721 196.024,19.418 195.753,19.176C195.482,18.934 195.123,18.813 194.675,18.813C194.258,18.813 193.918,18.909 193.655,19.102C193.392,19.294 193.261,19.552 193.261,19.875C193.261,20.167 193.383,20.393 193.628,20.555C193.873,20.716 194.367,20.879 195.112,21.043C195.857,21.207 196.442,21.402 196.866,21.629C197.291,21.855 197.606,22.128 197.811,22.445C198.017,22.763 198.12,23.148 198.12,23.602C198.12,24.362 197.805,24.978 197.175,25.449C196.544,25.921 195.719,26.156 194.698,26.156C194.005,26.156 193.388,26.031 192.847,25.781C192.305,25.531 191.883,25.188 191.581,24.75C191.279,24.312 191.128,23.841 191.128,23.336L192.972,23.336C192.998,23.784 193.167,24.129 193.479,24.371C193.792,24.613 194.206,24.734 194.722,24.734C195.222,24.734 195.602,24.639 195.862,24.449C196.123,24.259 196.253,24.01 196.253,23.703ZM205.082,23.703C205.082,23.365 204.943,23.107 204.664,22.93C204.385,22.753 203.923,22.596 203.277,22.461C202.632,22.326 202.092,22.154 201.66,21.945C200.712,21.487 200.238,20.823 200.238,19.953C200.238,19.224 200.546,18.615 201.16,18.125C201.775,17.635 202.556,17.391 203.504,17.391C204.514,17.391 205.331,17.641 205.953,18.141C206.576,18.641 206.887,19.289 206.887,20.086L204.988,20.086C204.988,19.721 204.853,19.418 204.582,19.176C204.311,18.934 203.952,18.813 203.504,18.813C203.087,18.813 202.747,18.909 202.484,19.102C202.221,19.294 202.09,19.552 202.09,19.875C202.09,20.167 202.212,20.393 202.457,20.555C202.702,20.716 203.197,20.879 203.941,21.043C204.686,21.207 205.271,21.402 205.695,21.629C206.12,21.855 206.435,22.128 206.641,22.445C206.846,22.763 206.949,23.148 206.949,23.602C206.949,24.362 206.634,24.978 206.004,25.449C205.374,25.921 204.548,26.156 203.527,26.156C202.835,26.156 202.217,26.031 201.676,25.781C201.134,25.531 200.712,25.188 200.41,24.75C200.108,24.312 199.957,23.841 199.957,23.336L201.801,23.336C201.827,23.784 201.996,24.129 202.309,24.371C202.621,24.613 203.035,24.734 203.551,24.734C204.051,24.734 204.431,24.639 204.691,24.449C204.952,24.259 205.082,24.01 205.082,23.703Z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
<path
android:fillColor="#FFFFFFFF"
android:fillType="nonZero"
android:pathData="M104.26,11C99.146,11 95,15.146 95,20.26C95,25.375 99.146,29.521 104.26,29.521C109.375,29.521 113.521,25.375 113.521,20.26C113.521,15.146 109.375,11 104.26,11M104.26,11.556C105.995,11.552 107.69,12.07 109.127,13.042C110.054,13.669 110.852,14.467 111.479,15.394C112.451,16.83 112.969,18.526 112.965,20.26C112.969,21.995 112.451,23.69 111.479,25.127C110.852,26.054 110.054,26.852 109.127,27.479C107.69,28.451 105.995,28.969 104.26,28.965C102.526,28.969 100.83,28.451 99.394,27.479C98.467,26.852 97.669,26.054 97.042,25.127C96.07,23.69 95.552,21.995 95.556,20.26C95.552,18.526 96.07,16.83 97.042,15.394C97.669,14.467 98.467,13.669 99.393,13.042C100.83,12.07 102.526,11.552 104.26,11.556"
android:strokeWidth="1"
android:strokeColor="#00000000" />
<path
android:fillColor="#FFFFFFFF"
android:fillType="nonZero"
android:pathData="M111.032,16.558C111.066,16.804 111.084,17.068 111.084,17.351C111.084,18.134 110.937,19.014 110.497,20.115L108.14,26.93C110.516,25.549 111.978,23.008 111.977,20.26C111.979,18.966 111.654,17.693 111.032,16.559L111.032,16.558ZM104.396,20.935L102.08,27.663C103.635,28.121 105.294,28.078 106.823,27.54C106.802,27.506 106.783,27.471 106.768,27.434L104.396,20.935ZM109.47,19.871C109.47,18.917 109.128,18.257 108.834,17.743C108.443,17.106 108.076,16.569 108.076,15.933C108.076,15.223 108.613,14.563 109.372,14.563C109.406,14.563 109.438,14.567 109.472,14.569C108.05,13.264 106.19,12.541 104.26,12.544C101.662,12.543 99.238,13.851 97.813,16.022C97.994,16.028 98.165,16.032 98.309,16.032C99.116,16.032 100.366,15.934 100.366,15.934C100.782,15.909 100.831,16.52 100.415,16.57C100.415,16.57 99.997,16.618 99.532,16.643L102.343,25.002L104.031,19.937L102.829,16.643C102.414,16.618 102.02,16.57 102.02,16.57C101.604,16.545 101.652,15.909 102.068,15.934C102.068,15.934 103.342,16.032 104.101,16.032C104.908,16.032 106.158,15.934 106.158,15.934C106.574,15.909 106.623,16.52 106.207,16.57C106.207,16.57 105.788,16.618 105.324,16.643L108.113,24.938L108.908,22.415C109.263,21.313 109.47,20.531 109.47,19.872L109.47,19.871ZM96.544,20.26C96.544,23.217 98.233,25.915 100.893,27.205L97.211,17.12C96.77,18.108 96.543,19.178 96.544,20.26Z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#32373C"
android:fillType="evenOdd"
android:pathData="M11.994,0C5.37,0 0,5.37 0,11.994s5.37,11.994 11.994,11.994 11.994,-5.37 11.994,-11.994S18.618,0 11.994,0m0,0.72a11.203,11.203 0,0 1,6.303 1.925,11.359 11.359,0 0,1 3.046,3.046 11.203,11.203 0,0 1,1.925 6.303,11.203 11.203,0 0,1 -1.925,6.303 11.359,11.359 0,0 1,-3.046 3.046,11.203 11.203,0 0,1 -6.303,1.925 11.203,11.203 0,0 1,-6.303 -1.925,11.36 11.36,0 0,1 -3.046,-3.046A11.203,11.203 0,0 1,0.72 11.994a11.203,11.203 0,0 1,1.925 -6.303A11.36,11.36 0,0 1,5.69 2.645,11.203 11.203,0 0,1 11.994,0.72" />
<path
android:fillColor="#32373C"
android:fillType="evenOdd"
android:pathData="M20.765,7.199c0.043,0.318 0.067,0.66 0.067,1.027 0,1.014 -0.19,2.154 -0.76,3.58l-3.053,8.827a9.99,9.99 0,0 0,4.97 -8.64A9.948,9.948 0,0 0,20.765 7.2zM12.17,12.868l-3,8.714a9.988,9.988 0,0 0,6.143 -0.16,0.911 0.911,0 0,1 -0.071,-0.137l-3.072,-8.417zM18.741,11.49c0,-1.236 -0.443,-2.091 -0.823,-2.757 -0.507,-0.824 -0.982,-1.52 -0.982,-2.344 0,-0.919 0.696,-1.774 1.678,-1.774 0.044,0 0.086,0.005 0.13,0.008A9.958,9.958 0,0 0,11.994 2a9.984,9.984 0,0 0,-8.351 4.505c0.235,0.007 0.456,0.012 0.643,0.012 1.045,0 2.664,-0.127 2.664,-0.127 0.539,-0.032 0.602,0.76 0.064,0.824 0,0 -0.542,0.063 -1.144,0.095l3.64,10.826 2.187,-6.56 -1.557,-4.266c-0.538,-0.032 -1.048,-0.095 -1.048,-0.095 -0.539,-0.032 -0.476,-0.856 0.063,-0.824 0,0 1.65,0.127 2.633,0.127 1.045,0 2.664,-0.127 2.664,-0.127 0.539,-0.032 0.602,0.76 0.063,0.824 0,0 -0.542,0.063 -1.143,0.095l3.612,10.743 1.03,-3.267c0.459,-1.428 0.727,-2.44 0.727,-3.294zM2,11.994a9.996,9.996 0,0 0,5.633 8.995L2.864,7.926A9.956,9.956 0,0 0,2 11.994z" />
</vector>
......@@ -2,10 +2,9 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid
android:color="@color/colorPrimaryDark" />
<corners
android:radius="2dp" />
<stroke
android:width="1dp"
android:color="@color/colorAuthenticationButtonBorderAndDivider" />
<corners android:radius="2dp" />
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/colorAccent" />
<corners android:radius="4dp"/>
<corners android:radius="2dp" />
</shape>
\ No newline at end of file
......@@ -3,5 +3,4 @@
android:shape="oval">
<solid android:color="@color/colorAccent" />
</shape>
\ No newline at end of file
</shape>
......@@ -3,9 +3,13 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:theme="@style/AuthenticationTheme"
android:orientation="vertical"
tools:context=".authentication.ui.AuthenticationActivity">
<include
android:id="@+id/layout_app_bar"
layout="@layout/app_bar" />
<FrameLayout
android:id="@+id/fragment_container"
android:layout_width="match_parent"
......
......@@ -5,7 +5,6 @@
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:theme="@style/AppTheme"
tools:context=".main.ui.MainActivity">
<LinearLayout
......
......@@ -3,8 +3,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/colorPrimary"
android:theme="@style/Theme.AppCompat.Light.NoActionBar">
android:background="@color/colorPrimary">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
......
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/scroll_view"
android:id="@+id/constraint_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
android:padding="@dimen/screen_edge_left_and_right_margins"
tools:context=".authentication.login.ui.LoginFragment">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/text_headline"
style="@style/Authentication.Headline.TextView"
android:text="@string/title_log_in"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="@+id/text_username_or_email"
style="@style/Authentication.EditText"
android:layout_marginTop="32dp"
android:drawableStart="@drawable/ic_assignment_ind_black_24dp"
android:hint="@string/msg_username_or_email"
android:imeOptions="actionNext"
android:inputType="textEmailAddress|text"
app:layout_constraintLeft_toLeftOf="parent"
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"
tools:visibility="visible"
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"
android:layout_marginTop="16dp"
android:drawableStart="@drawable/ic_lock_black_24dp"
android:hint="@string/msg_password"
android:imeOptions="actionDone"
android:inputType="textPassword"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_username_or_email" />
<Button
android:id="@+id/button_cas"
style="@style/Authentication.Button"
android:layout_marginStart="@dimen/screen_edge_left_and_right_margins"
android:layout_marginTop="16dp"
android:layout_marginEnd="@dimen/screen_edge_left_and_right_margins"
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
android:id="@+id/text_new_to_rocket_chat"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/screen_edge_left_and_right_margins"
android:layout_marginTop="16dp"
android:layout_marginEnd="@dimen/screen_edge_left_and_right_margins"
android:gravity="center"
android:textColorLink="@color/colorAccent"
android:visibility="gone"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/button_cas"
tools:visibility="visible" />
<TextView
android:id="@+id/text_forgot_your_password"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/screen_edge_left_and_right_margins"
android:layout_marginTop="8dp"
android:layout_marginEnd="@dimen/screen_edge_left_and_right_margins"
android:gravity="center"
android:textColorLink="@color/colorAccent"
android:visibility="gone"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_new_to_rocket_chat"
tools:visibility="visible" />
<com.wang.avi.AVLoadingIndicatorView
android:id="@+id/view_loading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:visibility="gone"
app:indicatorName="BallPulseIndicator"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/button_cas"
tools:visibility="visible" />
<LinearLayout
android:id="@+id/social_accounts_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/screen_edge_left_and_right_margins"
android:layout_marginTop="20dp"
android:layout_marginEnd="@dimen/screen_edge_left_and_right_margins"
android:background="@color/colorPrimaryDark"
android:gravity="center"
android:orientation="vertical"
android:paddingTop="@dimen/screen_edge_left_and_right_margins"
android:paddingBottom="32dp"
android:visibility="gone"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_forgot_your_password"
tools:visibility="visible">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="@string/msg_or_continue_using_social_accounts" />
<ImageButton
android:id="@+id/button_facebook"
android:layout_width="290dp"
android:layout_height="40dp"
android:layout_marginTop="16dp"
android:clickable="false"
android:contentDescription="@string/msg_content_description_log_in_using_facebook"
android:foreground="?android:attr/selectableItemBackgroundBorderless"
android:src="@drawable/ic_facebook"
android:visibility="gone"
tools:visibility="visible" />
<ImageButton
android:id="@+id/button_github"
android:layout_width="290dp"
android:layout_height="40dp"
android:layout_marginTop="16dp"
android:clickable="false"
android:contentDescription="@string/msg_content_description_log_in_using_github"
android:foreground="?android:attr/selectableItemBackgroundBorderless"
android:src="@drawable/ic_github"
android:visibility="gone"
tools:visibility="visible" />
<ImageButton
android:id="@+id/button_google"
android:layout_width="290dp"
android:layout_height="40dp"
android:layout_marginTop="16dp"
android:clickable="false"
android:contentDescription="@string/msg_content_description_log_in_using_google"
android:foreground="?android:attr/selectableItemBackground"
android:src="@drawable/ic_google"
android:visibility="gone"
tools:visibility="visible" />
<ImageButton
android:id="@+id/button_linkedin"
android:layout_width="290dp"
android:layout_height="40dp"
android:layout_marginTop="16dp"
android:clickable="false"
android:contentDescription="@string/msg_content_description_log_in_using_linkedin"
android:foreground="?android:attr/selectableItemBackgroundBorderless"
android:src="@drawable/ic_linkedin"
android:visibility="gone"
tools:visibility="gone" />
<ImageButton
android:id="@+id/button_meteor"
android:layout_width="290dp"
android:layout_height="40dp"
android:layout_marginTop="16dp"
android:clickable="false"
android:contentDescription="@string/msg_content_description_log_in_using_meteor"
android:foreground="?android:attr/selectableItemBackgroundBorderless"
android:src="@drawable/ic_meteor"
android:visibility="gone"
tools:visibility="gone" />
<ImageButton
android:id="@+id/button_twitter"
android:layout_width="290dp"
android:layout_height="40dp"
android:layout_marginTop="16dp"
android:clickable="false"
android:contentDescription="@string/msg_content_description_log_in_using_twitter"
android:foreground="?android:attr/selectableItemBackgroundBorderless"
android:src="@drawable/ic_twitter"
android:visibility="gone"
tools:visibility="gone" />
<ImageButton
android:id="@+id/button_gitlab"
android:layout_width="290dp"
android:layout_height="40dp"
android:layout_marginTop="16dp"
android:clickable="false"
android:contentDescription="@string/msg_content_description_log_in_using_gitlab"
android:foreground="?android:attr/selectableItemBackgroundBorderless"
android:src="@drawable/ic_gitlab"
android:visibility="gone"
tools:visibility="gone" />
<ImageButton
android:id="@+id/button_wordpress"
android:layout_width="290dp"
android:layout_height="40dp"
android:layout_marginTop="16dp"
android:clickable="false"
android:contentDescription="@string/msg_content_description_log_in_using_gitlab"
android:foreground="?android:attr/selectableItemBackgroundBorderless"
android:src="@drawable/ic_wordpress"
android:visibility="gone"
tools:visibility="gone" />
</LinearLayout>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/button_fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_expand_more_black_24dp"
android:theme="@style/Theme.AppCompat"
android:tint="@color/colorWhite"
android:visibility="gone"
app:backgroundTint="@color/colorAccent"
app:elevation="@dimen/fab_elevation"
app:fabSize="mini"
app:layout_constraintBottom_toBottomOf="@+id/social_accounts_container"
app:layout_constraintLeft_toLeftOf="@+id/social_accounts_container"
app:layout_constraintRight_toRightOf="@+id/social_accounts_container"
app:layout_constraintTop_toBottomOf="@+id/social_accounts_container"
tools:visibility="visible" />
<Button
android:id="@+id/button_log_in"
style="@style/Authentication.Button"
android:text="@string/title_log_in"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>
\ No newline at end of file
<TextView
android:id="@+id/text_login"
style="@style/Authentication.TextView.Headline"
android:text="@string/title_log_in"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="@+id/text_username_or_email"
style="@style/Authentication.EditText.Border"
android:layout_marginTop="16dp"
android:drawableStart="@drawable/ic_at_black_20dp"
android:hint="@string/msg_username_or_email"
android:imeOptions="actionNext"
android:inputType="textEmailAddress|text"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_login" />
<EditText
android:id="@+id/text_password"
style="@style/Authentication.EditText.Border"
android:layout_marginTop="10dp"
android:drawableStart="@drawable/ic_key_black_20dp"
android:hint="@string/msg_password"
android:imeOptions="actionDone"
android:inputType="textPassword"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_username_or_email" />
<Button
android:id="@+id/button_log_in"
style="@style/Authentication.Button"
android:layout_marginTop="20dp"
android:backgroundTint="@color/colorAuthenticationButtonDisabled"
android:enabled="false"
android:text="@string/title_log_in"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/text_password" />
<Button
android:id="@+id/button_forgot_your_password"
style="@style/Authentication.Button.Borderless"
android:layout_marginTop="10dp"
android:text="@string/msg_forgot__your_password"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/button_log_in"
tools:visibility="visible" />
<com.wang.avi.AVLoadingIndicatorView
android:id="@+id/view_loading"
style="@style/Authentication.AVLoadingIndicatorView"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorWhite"
android:padding="@dimen/screen_edge_left_and_right_margins"
tools:context="authentication.loginoptions.ui.LoginOptionsFragment">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/accounts_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
tools:visibility="visible">
<Button
android:id="@+id/button_facebook"
style="?borderlessButtonStyle"
android:layout_width="0dp"
android:layout_height="48dp"
android:background="@drawable/rounded_border"
android:clickable="false"
android:drawableStart="@drawable/ic_facebook_24dp"
android:foreground="?selectableItemBackground"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:text="@string/msg_continue_with_facebook"
android:textAllCaps="false"
android:textColor="@color/colorPrimary"
android:textSize="16sp"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:visibility="visible" />
<Button
android:id="@+id/button_github"
style="?borderlessButtonStyle"
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_marginTop="10dp"
android:background="@drawable/rounded_border"
android:clickable="false"
android:drawableStart="@drawable/ic_github_24dp"
android:foreground="?selectableItemBackground"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:text="@string/msg_continue_with_github"
android:textAllCaps="false"
android:textColor="@color/colorPrimary"
android:textSize="16sp"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/button_facebook"
tools:visibility="visible" />
<Button
android:id="@+id/button_google"
style="?borderlessButtonStyle"
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_marginTop="10dp"
android:background="@drawable/rounded_border"
android:clickable="false"
android:drawableStart="@drawable/ic_google_24dp"
android:foreground="?selectableItemBackground"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:text="@string/msg_continue_with_google"
android:textAllCaps="false"
android:textColor="@color/colorPrimary"
android:textSize="16sp"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/button_github"
tools:visibility="visible" />
<Button
android:id="@+id/button_linkedin"
style="?borderlessButtonStyle"
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_marginTop="10dp"
android:background="@drawable/rounded_border"
android:clickable="false"
android:drawableStart="@drawable/ic_linkedin_24dp"
android:foreground="?selectableItemBackground"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:text="@string/msg_continue_with_linkedin"
android:textAllCaps="false"
android:textColor="@color/colorPrimary"
android:textSize="16sp"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/button_google"
tools:visibility="visible" />
<Button
android:id="@+id/button_gitlab"
style="?borderlessButtonStyle"
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_marginTop="10dp"
android:background="@drawable/rounded_border"
android:clickable="false"
android:drawableStart="@drawable/ic_gitlab_24dp"
android:foreground="?selectableItemBackground"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:text="@string/msg_continue_with_gitlab"
android:textAllCaps="false"
android:textColor="@color/colorPrimary"
android:textSize="16sp"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/button_linkedin"
tools:visibility="visible" />
<Button
android:id="@+id/button_wordpress"
style="?borderlessButtonStyle"
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_marginTop="10dp"
android:background="@drawable/rounded_border"
android:clickable="false"
android:drawableStart="@drawable/ic_wordpress_24dp"
android:foreground="?selectableItemBackground"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:text="@string/msg_continue_with_wordpress"
android:textAllCaps="false"
android:textColor="@color/colorPrimary"
android:textSize="16sp"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/button_gitlab"
tools:visibility="visible" />
<Button
android:id="@+id/button_cas"
style="@style/Authentication.Button"
android:layout_marginTop="10dp"
android:clickable="false"
android:text="@string/action_login_or_sign_up"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/button_wordpress"
tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/expand_more_accounts_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/accounts_container"
tools:visibility="visible">
<ImageButton
android:id="@+id/button_expand_collapse_accounts"
android:layout_width="30dp"
android:layout_height="30dp"
android:background="@drawable/circle_background_grey"
android:contentDescription="@string/msg_content_description_show_more_login_options"
android:src="@drawable/ic_expand_more_black_24dp"
android:tint="@color/colorWhite"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<View
android:layout_width="0dp"
android:layout_height="1dp"
android:layout_marginEnd="8dp"
android:background="@color/colorAuthenticationButtonBorderAndDivider"
app:layout_constraintBottom_toBottomOf="@+id/button_expand_collapse_accounts"
app:layout_constraintEnd_toStartOf="@+id/button_expand_collapse_accounts"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/button_expand_collapse_accounts" />
<View
android:layout_width="0dp"
android:layout_height="1dp"
android:layout_marginStart="8dp"
android:background="@color/colorAuthenticationButtonBorderAndDivider"
app:layout_constraintBottom_toBottomOf="@+id/button_expand_collapse_accounts"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/button_expand_collapse_accounts"
app:layout_constraintTop_toTopOf="@+id/button_expand_collapse_accounts" />
</androidx.constraintlayout.widget.ConstraintLayout>
<Button
android:id="@+id/button_login_with_email"
style="@style/Authentication.Button"
android:layout_marginTop="32dp"
android:text="@string/msg_login_with_email"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/expand_more_accounts_container"
tools:visibility="visible" />
<Button
android:id="@+id/button_create_an_account"
style="@style/Authentication.Button.Borderless"
android:layout_marginTop="10dp"
android:text="@string/msg_create_account"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/button_login_with_email"
tools:visibility="visible" />
<com.wang.avi.AVLoadingIndicatorView
android:id="@+id/view_loading"
style="@style/Authentication.AVLoadingIndicatorView"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>
\ No newline at end of file
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorWhite"
android:padding="@dimen/screen_edge_left_and_right_margins"
tools:context="authentication.onboarding.ui.OnBoardingFragment">
<ImageView
android:id="@+id/image_on_boarding"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_onboarding"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="ContentDescription" />
<TextView
android:id="@+id/text_on_boarding_title"
style="@style/Authentication.TextView.Headline"
android:layout_marginTop="32dp"
android:text="@string/msg_welcome_to_rocket_chat"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/image_on_boarding" />
<TextView
android:id="@+id/text_on_boarding_description"
style="@style/Authentication.TextView.Description"
android:text="@string/msg_team_communication"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_on_boarding_title" />
<RelativeLayout
android:id="@+id/connect_with_a_server_container"
android:layout_width="0dp"
android:layout_height="60dp"
android:layout_marginTop="32dp"
android:background="@drawable/rounded_border"
android:foreground="?selectableItemBackground"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_on_boarding_description">
<ImageView
android:id="@+id/image_connect_with_a_server"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_centerVertical="true"
android:layout_marginStart="16dp"
android:layout_marginEnd="20dp"
android:src="@drawable/ic_connect_with_a_server_black_30dp"
android:tint="@color/colorAccent"
tools:ignore="ContentDescription" />
<TextView
style="@style/Authentication.Button.Title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toStartOf="@+id/image_connect_with_a_server_chevron"
android:layout_toEndOf="@+id/image_connect_with_a_server"
android:text="@string/action_connect_server" />
<ImageView
android:id="@+id/image_connect_with_a_server_chevron"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:layout_marginEnd="8dp"
android:src="@drawable/ic_chevron_right_black_24dp"
android:tint="@color/colorAuthenticationChevronAndExpandIcon"
tools:ignore="ContentDescription" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/join_community_container"
android:layout_width="0dp"
android:layout_height="60dp"
android:layout_marginTop="10dp"
android:background="@drawable/rounded_border"
android:foreground="?selectableItemBackground"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/connect_with_a_server_container">
<ImageView
android:id="@+id/image_join_community"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_centerVertical="true"
android:layout_marginStart="16dp"
android:layout_marginEnd="20dp"
android:src="@drawable/ic_rocket_chat_30dp"
tools:ignore="ContentDescription" />
<TextView
android:id="@+id/text_join_community"
style="@style/Authentication.Button.Title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_toStartOf="@+id/image_join_community_chevron"
android:layout_toEndOf="@+id/image_join_community"
android:text="@string/action_join_community" />
<TextView
style="@style/Authentication.Button.Description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/text_join_community"
android:layout_toStartOf="@+id/image_join_community_chevron"
android:layout_toEndOf="@+id/image_join_community"
android:text="@string/community_server_url" />
<ImageView
android:id="@+id/image_join_community_chevron"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:layout_marginEnd="8dp"
android:src="@drawable/ic_chevron_right_black_24dp"
android:tint="@color/colorAuthenticationChevronAndExpandIcon"
tools:ignore="ContentDescription" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/create_server_container"
android:layout_width="0dp"
android:layout_height="60dp"
android:layout_marginTop="10dp"
android:background="@drawable/rounded_color_accent"
android:foreground="?selectableItemBackground"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/join_community_container">
<ImageView
android:id="@+id/image_add"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_centerVertical="true"
android:layout_marginStart="16dp"
android:layout_marginEnd="20dp"
android:src="@drawable/ic_add_24dp"
android:tint="@color/colorWhite"
tools:ignore="ContentDescription" />
<TextView
style="@style/Authentication.Button.Title.White"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toEndOf="@+id/image_add"
android:text="@string/action_create_server" />
</RelativeLayout>
<com.wang.avi.AVLoadingIndicatorView
android:id="@+id/view_loading"
style="@style/Authentication.AVLoadingIndicatorView"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="@dimen/screen_edge_left_and_right_margins"
tools:context=".authentication.registerusername.ui.RegisterUsernameFragment">
<TextView
android:id="@+id/text_headline"
style="@style/Authentication.Headline.TextView"
android:layout_centerHorizontal="true"
android:text="@string/title_register_username" />
android:id="@+id/text_sign_in_to_your_server"
style="@style/Authentication.TextView.Headline"
android:text="@string/title_register_username"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="@+id/text_username"
style="@style/Authentication.EditText"
android:layout_below="@id/text_headline"
android:layout_marginTop="32dp"
android:drawableStart="@drawable/ic_at_black_24dp"
style="@style/Authentication.EditText.Border"
android:layout_marginTop="16dp"
android:drawableStart="@drawable/ic_at_black_20dp"
android:hint="@string/msg_username"
android:imeOptions="actionDone"
android:inputType="text" />
<com.wang.avi.AVLoadingIndicatorView
android:id="@+id/view_loading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:visibility="gone"
app:indicatorName="BallPulseIndicator"
tools:visibility="visible" />
android:inputType="text"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/text_sign_in_to_your_server" />
<Button
android:id="@+id/button_use_this_username"
style="@style/Authentication.Button"
android:layout_alignParentBottom="true"
android:text="@string/action_use_this_username" />
android:layout_marginTop="20dp"
android:backgroundTint="@color/colorAuthenticationButtonDisabled"
android:enabled="false"
android:text="@string/action_use_this_username"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/text_username" />
<com.wang.avi.AVLoadingIndicatorView
android:id="@+id/view_loading"
style="@style/Authentication.AVLoadingIndicatorView"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</RelativeLayout>
\ No newline at end of file
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="@dimen/screen_edge_left_and_right_margins"
tools:context=".authentication.resetpassword.ui.ResetPasswordFragment">
<TextView
android:id="@+id/text_headline"
style="@style/Authentication.Headline.TextView"
android:layout_centerHorizontal="true"
android:text="@string/title_reset_password" />
android:id="@+id/text_reset_password"
style="@style/Authentication.TextView.Headline"
android:text="@string/title_reset_password"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="@+id/text_email"
style="@style/Authentication.EditText"
android:layout_below="@id/text_headline"
android:layout_marginTop="32dp"
android:drawableStart="@drawable/ic_email_black_24dp"
style="@style/Authentication.EditText.Border"
android:layout_marginTop="16dp"
android:drawableStart="@drawable/ic_email_black_20dp"
android:hint="@string/msg_email"
android:imeOptions="actionDone"
android:inputType="textEmailAddress" />
<com.wang.avi.AVLoadingIndicatorView
android:id="@+id/view_loading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:visibility="gone"
app:indicatorName="BallPulseIndicator"
tools:visibility="visible" />
android:inputType="textEmailAddress"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/text_reset_password" />
<Button
android:id="@+id/button_reset_password"
style="@style/Authentication.Button"
android:layout_alignParentBottom="true"
android:text="@string/title_reset_password" />
android:layout_marginTop="20dp"
android:backgroundTint="@color/colorAuthenticationButtonDisabled"
android:enabled="false"
android:text="@string/title_reset_password"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/text_email" />
<com.wang.avi.AVLoadingIndicatorView
android:id="@+id/view_loading"
style="@style/Authentication.AVLoadingIndicatorView"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</RelativeLayout>
\ No newline at end of file
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/relative_layout"
android:id="@+id/scroll_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:focusableInTouchMode="true"
android:background="@color/colorWhite"
android:padding="@dimen/screen_edge_left_and_right_margins"
tools:context=".authentication.server.ui.ServerFragment">
<TextView
android:id="@+id/text_headline"
style="@style/Authentication.Headline.TextView"
android:layout_centerHorizontal="true"
android:text="@string/title_sign_in_your_server" />
<EditText
android:id="@+id/text_server_url"
style="@style/Authentication.EditText"
android:layout_below="@id/text_headline"
android:layout_marginStart="-6dp"
android:layout_marginTop="32dp"
android:layout_toEndOf="@id/protocol_container"
android:cursorVisible="false"
android:hint="@string/default_server"
android:imeOptions="actionDone"
android:inputType="text|textUri"
android:paddingEnd="0dp" />
<FrameLayout
android:id="@+id/protocol_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/style_edit_text_authentication"
android:layout_marginStart="@dimen/screen_edge_left_and_right_margins"
android:layout_below="@id/text_headline"
android:layout_marginTop="32dp">
<Spinner
android:id="@+id/text_server_protocol"
android:spinnerMode="dropdown"
android:layout_width="120dp"
android:layout_height="50dp"
android:backgroundTint="@color/actionMenuColor" />
</FrameLayout>
<com.wang.avi.AVLoadingIndicatorView
android:id="@+id/view_loading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:visibility="gone"
app:indicatorName="BallPulseIndicator"
tools:visibility="visible" />
<Button
android:id="@+id/button_connect"
style="@style/Authentication.Button"
android:layout_alignParentBottom="true"
android:text="@string/action_connect" />
</RelativeLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/image_server"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_server"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="ContentDescription" />
<TextView
android:id="@+id/text_sign_in_to_your_server"
style="@style/Authentication.TextView.Headline"
android:layout_marginTop="8dp"
android:text="@string/title_sign_in_your_server"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/image_server" />
<RelativeLayout
android:id="@+id/server_url_container"
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_marginTop="24dp"
android:background="@drawable/rounded_border"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_sign_in_to_your_server">
<Spinner
android:id="@+id/spinner_server_protocol"
android:layout_width="120dp"
android:layout_height="match_parent"
android:popupBackground="@color/colorWhite" />
<EditText
android:id="@+id/text_server_url"
style="@style/Authentication.EditText.Hint"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginStart="6dp"
android:layout_marginEnd="16dp"
android:layout_toEndOf="@+id/spinner_server_protocol"
android:background="@color/colorWhite"
android:cursorVisible="false"
android:hint="@string/server_hint_url"
android:imeOptions="actionDone"
android:inputType="text|textUri" />
</RelativeLayout>
<Button
android:id="@+id/button_connect"
style="@style/Authentication.Button"
android:layout_marginTop="20dp"
android:backgroundTint="@color/colorAuthenticationButtonDisabled"
android:enabled="false"
android:text="@string/action_connect"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/server_url_container" />
<com.wang.avi.AVLoadingIndicatorView
android:id="@+id/view_loading"
style="@style/Authentication.AVLoadingIndicatorView"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/relative_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:focusableInTouchMode="true"
android:background="@color/colorWhite"
android:padding="@dimen/screen_edge_left_and_right_margins"
tools:context=".authentication.signup.ui.SignupFragment">
<ScrollView
android:id="@+id/scroll_view"
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/text_headline"
style="@style/Authentication.Headline.TextView"
android:layout_gravity="center"
android:text="@string/title_sign_up" />
<EditText
android:id="@+id/text_name"
style="@style/Authentication.EditText"
android:layout_marginTop="32dp"
android:drawableStart="@drawable/ic_person_black_24dp"
android:hint="@string/msg_name"
android:imeOptions="actionNext"
android:inputType="textCapWords" />
<EditText
android:id="@+id/text_username"
style="@style/Authentication.EditText"
android:layout_marginTop="16dp"
android:drawableStart="@drawable/ic_at_black_24dp"
android:hint="@string/msg_username"
android:imeOptions="actionNext"
android:inputType="text" />
<TextView
android:id="@+id/text_sign_up"
style="@style/Authentication.TextView.Headline"
android:text="@string/title_sign_up"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="@+id/text_password"
style="@style/Authentication.EditText"
android:layout_marginTop="16dp"
android:drawableStart="@drawable/ic_lock_black_24dp"
android:hint="@string/msg_password"
android:imeOptions="actionNext"
android:inputType="textPassword" />
<EditText
android:id="@+id/text_name"
style="@style/Authentication.EditText.Border"
android:layout_marginTop="16dp"
android:drawableStart="@drawable/ic_person_black_20dp"
android:hint="@string/msg_name"
android:imeOptions="actionNext"
android:inputType="textCapWords"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_sign_up" />
<EditText
android:id="@+id/text_email"
style="@style/Authentication.EditText"
android:layout_marginBottom="16dp"
android:layout_marginTop="16dp"
android:drawableStart="@drawable/ic_email_black_24dp"
android:hint="@string/msg_email"
android:imeOptions="actionDone"
android:inputType="textEmailAddress" />
</LinearLayout>
</ScrollView>
<EditText
android:id="@+id/text_username"
style="@style/Authentication.EditText.Border"
android:layout_marginTop="10dp"
android:drawableStart="@drawable/ic_at_black_20dp"
android:hint="@string/msg_username"
android:imeOptions="actionNext"
android:inputType="text"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_name" />
<com.wang.avi.AVLoadingIndicatorView
android:id="@+id/view_loading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/scroll_view"
android:layout_centerHorizontal="true"
android:layout_marginTop="16dp"
android:visibility="gone"
app:indicatorName="BallPulseIndicator"
tools:visibility="visible" />
<EditText
android:id="@+id/text_password"
style="@style/Authentication.EditText.Border"
android:layout_marginTop="10dp"
android:drawableStart="@drawable/ic_key_black_20dp"
android:hint="@string/msg_password"
android:imeOptions="actionNext"
android:inputType="textPassword"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_username" />
<LinearLayout
android:id="@+id/bottom_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:orientation="vertical">
<TextView
android:id="@+id/text_new_user_agreement"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="@dimen/screen_edge_left_and_right_margins"
android:gravity="center"
android:textColorLink="@color/colorAccent" />
<EditText
android:id="@+id/text_email"
style="@style/Authentication.EditText.Border"
android:layout_marginTop="10dp"
android:drawableStart="@drawable/ic_email_black_20dp"
android:hint="@string/msg_email"
android:imeOptions="actionDone"
android:inputType="textEmailAddress"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_password" />
<Button
android:id="@+id/button_sign_up"
android:id="@+id/button_register"
style="@style/Authentication.Button"
android:text="@string/title_sign_up" />
</LinearLayout>
android:layout_marginTop="20dp"
android:backgroundTint="@color/colorAuthenticationButtonDisabled"
android:enabled="false"
android:text="@string/action_register"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_email" />
<com.wang.avi.AVLoadingIndicatorView
android:id="@+id/view_loading"
style="@style/Authentication.AVLoadingIndicatorView"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</RelativeLayout>
\ No newline at end of file
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="@dimen/screen_edge_left_and_right_margins"
tools:context=".authentication.twofactor.ui.TwoFAFragment">
<TextView
android:id="@+id/text_headline"
style="@style/Authentication.Headline.TextView"
android:layout_centerHorizontal="true"
android:text="@string/title_log_in" />
android:id="@+id/text_two_factor_authentication"
style="@style/Authentication.TextView.Headline"
android:text="Two-factor Authentication"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="@+id/text_two_factor_auth"
style="@style/Authentication.EditText"
android:layout_below="@id/text_headline"
android:layout_marginTop="32dp"
android:drawableStart="@drawable/ic_vpn_key_black_24dp"
android:hint="@string/msg_2fa_code"
android:id="@+id/text_two_factor_authentication_code"
style="@style/Authentication.EditText.Border"
android:layout_marginTop="16dp"
android:imeOptions="actionDone"
android:inputType="text" />
android:inputType="text"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_two_factor_authentication" />
<Button
android:id="@+id/button_confirm"
style="@style/Authentication.Button"
android:layout_marginTop="20dp"
android:backgroundTint="@color/colorAuthenticationButtonDisabled"
android:enabled="false"
android:text="@string/action_confirm"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_two_factor_authentication_code" />
<com.wang.avi.AVLoadingIndicatorView
android:id="@+id/view_loading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
style="@style/Authentication.AVLoadingIndicatorView"
android:visibility="gone"
app:indicatorName="BallPulseIndicator"
tools:visibility="visible" />
<Button
android:id="@+id/button_log_in"
style="@style/Authentication.Button"
android:layout_alignParentBottom="true"
android:text="@string/title_log_in" />
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</RelativeLayout>
\ No newline at end of file
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
......@@ -108,7 +108,7 @@
android:layout_width="14dp"
android:layout_height="14dp"
android:layout_marginTop="16dp"
android:src="@drawable/ic_at_black_24dp"
android:src="@drawable/ic_at_black_20dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_channel_name" />
......
......@@ -32,7 +32,7 @@
android:id="@+id/image_mention"
android:layout_width="100dp"
android:layout_height="100dp"
android:src="@drawable/ic_at_black_24dp"
android:src="@drawable/ic_at_black_20dp"
android:tint="@color/icon_grey"
app:layout_constraintBottom_toTopOf="@+id/text_no_mention"
app:layout_constraintEnd_toEndOf="parent"
......
......@@ -16,6 +16,7 @@
android:id="@+id/profile_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/screen_edge_left_and_right_margins"
android:gravity="center"
android:orientation="vertical"
android:visibility="gone"
......@@ -31,25 +32,31 @@
<EditText
android:id="@+id/text_name"
style="@style/Profile.EditText"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_marginTop="32dp"
android:drawableStart="@drawable/ic_person_black_24dp"
android:drawableStart="@drawable/ic_person_black_20dp"
android:hint="@string/msg_name"
android:inputType="textCapWords" />
<EditText
android:id="@+id/text_username"
style="@style/Profile.EditText"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_marginTop="16dp"
android:drawableStart="@drawable/ic_at_black_24dp"
android:drawableStart="@drawable/ic_at_black_20dp"
android:hint="@string/msg_username"
android:inputType="text" />
<EditText
android:id="@+id/text_email"
style="@style/Profile.EditText"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
android:drawableStart="@drawable/ic_email_black_24dp"
android:drawableStart="@drawable/ic_email_black_20dp"
android:hint="@string/msg_email"
android:inputType="textEmailAddress" />
</LinearLayout>
......
......@@ -84,7 +84,7 @@
android:id="@+id/image_account_expand"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_expand_more_24dp"
android:src="@drawable/ic_expand_more_black_24dp"
android:tint="@color/colorWhite"
app:layout_constraintBottom_toBottomOf="@+id/text_server_url"
app:layout_constraintEnd_toEndOf="parent" />
......
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_terms_of_Service"
android:title="@string/action_terms_of_service"
app:showAsAction="never" />
<item
android:id="@+id/action_privacy_policy"
android:title="@string/action_privacy_policy"
app:showAsAction="never" />
</menu>
\ No newline at end of file
......@@ -45,6 +45,11 @@
<string name="action_select_photo_from_gallery">Select photo from gallery</string> <!-- TODO Add translation -->
<string name="action_take_photo">Select photo from gallery</string> <!-- TODO Add translation -->
<string name="action_reset_avatar">Reset avatar</string> <!-- TODO Add translation -->
<string name="action_connect_server">Connect with a server</string> <!-- TODO Add translation -->
<string name="action_join_community">Join in the community</string> <!-- TODO Add translation -->
<string name="action_create_server">Create a new server</string> <!-- TODO Add translation -->
<string name="action_register">Register</string> <!-- TODO Add translation -->
<string name="action_confirm">Confirm</string> <!-- TODO Add translation -->
<!-- Settings List -->
<string-array name="settings_actions">
......@@ -60,17 +65,16 @@
<string name="msg_username">Benutzername</string>
<string name="msg_username_or_email">Benutzername oder E-Mail</string>
<string name="msg_password">Passwort</string>
<string name="msg_name">Name</string>
<string name="msg_email">E-Mail</string>
<string name="msg_name">Name</string> <!-- TODO Add translation -->
<string name="msg_email">EMail</string> <!-- TODO Add translation -->
<string name="msg_avatar_url">Avatar URL</string>
<string name="msg_or_continue_using_social_accounts">Oder weiter mit einem Social Account</string>
<string name="msg_new_user">Neuer Benutzer? %1$s</string>
<string name="msg_forgot_password">Passwort vergessen? %1$s</string>
<string name="msg_forgot__your_password">Passwort vergessen?</string>
<string name="msg_reset">Zurücksetzen</string>
<string name="msg_check_your_email_to_reset_your_password">E-Mail gesendet! Prüfe dein E-Mail Posteingang um dein Passwort zurückzusetzen.</string>
<string name="msg_invalid_email">Bitte eine korrekte E-Mail Adresse eingeben</string>
<string name="msg_new_user_agreement">Beim weitergehen akzeptieren Sie usere\n%1$s und %2$s</string>
<string name="msg_2fa_code">2FA Code</string>
<string name="msg_yesterday">Gestern</string>
<string name="msg_today">Heute</string>
<string name="msg_message">Nachricht</string>
......@@ -87,6 +91,7 @@
<string name="msg_content_description_log_in_using_gitlab">Login mit Gitlab</string>
<string name="msg_content_description_log_in_using_wordpress">Login mit WordPress</string>
<string name="msg_content_description_send_message">Sende Nachricht</string>
<string name="msg_content_description_show_more_login_options">Show more login options</string> <!-- TODO Translate-->
<string name="msg_content_description_show_attachment_options">Zeige Anhang Optionen</string>
<string name="msg_you">Du</string>
<string name="msg_unknown">Unbekannt</string>
......@@ -108,9 +113,7 @@
<string name="msg_ver_not_minimum">Die Server Version scheint älter als die minimale Version %1$s zu sein.\nBitte updaten Sie Ihren Server um sich einloggen zu können!</string>
<string name="msg_no_chat_title">Keine Chat Nachrichten</string>
<string name="msg_no_chat_description">Starte die Konversation um Ihre \nNachrichten hier zu sehen.</string>
<string name="msg_proceed">WEITER</string>
<string name="msg_cancel">ABBRUCH</string>
<string name="msg_warning">WARNUNG</string>
<string name="msg_http_insecure">Bei HTTP verbinden Sie sich usicher zu einem Server. Wir empfehlen HTTPS zu benutzen.</string>
<string name="msg_error_checking_server_version">Ein Fehler ist aufgetreten beim prüfen der Server Version, bitte versuchen Sie es noch einmal</string>
<string name="msg_invalid_server_protocol">Das ausgewählte Protokoll wird vom Server nicht akzeptiert, versuchen Sie HTTPS</string>
......@@ -127,6 +130,18 @@
<string name="msg_file_description">Datei Beschreibung</string>
<string name="msg_send">Sende</string>
<string name="msg_sent_attachment">Sende Anhang</string>
<string name="msg_welcome_to_rocket_chat">Welcome to Rocket.Chat</string> <!-- TODO Add translation -->
<string name="msg_team_communication">Open Source Communication</string> <!-- TODO Add translation -->
<string name="msg_login_with_email">Login with <b>e-mail</b></string> <!-- TODO Add translation -->
<string name="msg_create_account">Create an account</string> <!-- TODO Add translation -->
<string name="msg_continue_with_facebook">Continue with <b>Facebook</b></string> <!-- TODO Add translation -->
<string name="msg_continue_with_github">Continue with <b>Github</b></string> <!-- TODO Add translation -->
<string name="msg_continue_with_google">Continue with <b>Google</b></string> <!-- TODO Add translation -->
<string name="msg_continue_with_linkedin">Continue with <b>Linkedin</b></string> <!-- TODO Add translation -->
<string name="msg_continue_with_gitlab">Continue with <b>GitLab</b></string> <!-- TODO Add translation -->
<string name="msg_continue_with_wordpress">Continue with <b>WordPress</b></string> <!-- TODO Add translation -->
<string name="msg_two_factor_authentication">Two-factor Authentication</string> <!-- TODO Add translation -->
<string name="msg__your_2fa_code">What’s your 2FA code?</string> <!-- TODO Add translation -->
<!-- Create channel messages -->
<string name="msg_private_channel">Privat</string>
......@@ -286,6 +301,5 @@
<string name="notif_success_sending">Nachricht gesendet nach %1$s!</string>
<string name="read_by">Gelesen von</string>
<string name="message_information_title">Nachricht Information</string>
<!--TODO - Add proper translation-->
<string name="message_room_changed_privacy">Room type changed to: %1$s by %2$s</string>
<string name="message_room_changed_privacy">Room type changed to: %1$s by %2$s</string> <!--TODO - Add proper translation-->
</resources>
\ No newline at end of file
......@@ -44,6 +44,11 @@
<string name="action_select_photo_from_gallery">Select photo from gallery</string> <!-- TODO Add translation -->
<string name="action_take_photo">Select photo from gallery</string> <!-- TODO Add translation -->
<string name="action_reset_avatar">Reset avatar</string> <!-- TODO Add translation -->
<string name="action_connect_server">Connect with a server</string> <!-- TODO Add translation -->
<string name="action_join_community">Join in the community</string> <!-- TODO Add translation -->
<string name="action_create_server">Create a new server</string> <!-- TODO Add translation -->
<string name="action_register">Register</string> <!-- TODO Add translation -->
<string name="action_confirm">Confirm</string> <!-- TODO Add translation -->
<!-- Settings List -->
<string-array name="settings_actions">
......@@ -57,19 +62,18 @@
<string name="msg_no_data_to_display">No hay información para mostrar</string>
<string name="msg_profile_update_successfully">Actualización de perfil con éxito</string>
<string name="msg_username">usuario</string>
<string name="msg_username_or_email">nombre de usuario o correo electrónico</string>
<string name="msg_password">contraseña</string>
<string name="msg_name">nombre</string>
<string name="msg_email">correo electrónico</string>
<string name="msg_username_or_email">Nombre de usuario o correo electrónico</string>
<string name="msg_password">Contraseña</string>
<string name="msg_name">Nombre</string>
<string name="msg_email">Correo electrónico</string>
<string name="msg_avatar_url">URL del avatar</string>
<string name="msg_or_continue_using_social_accounts">O continuar usando cuentas sociales</string>
<string name="msg_new_user">Nuevo usuario? %1$s</string>
<string name="msg_forgot_password">Se te olvidó tu contraseña? %1$s</string>
<string name="msg_forgot__your_password">Se te olvidó tu contraseña?</string>
<string name="msg_reset">reiniciar</string>
<string name="msg_check_your_email_to_reset_your_password">¡Email enviado! Verifique su bandeja de entrada para restablecer su contraseña.</string>
<string name="msg_invalid_email">Por favor escriba un correo electrónico válido</string>
<string name="msg_new_user_agreement">Al continuar estás aceptando nuestra\n%1$s y %2$s</string>
<string name="msg_2fa_code">Código 2FA</string>
<string name="msg_yesterday">Ayer</string>
<string name="msg_today">Hoy</string>
<string name="msg_message">Mensaje</string>
......@@ -86,6 +90,7 @@
<string name="msg_content_description_log_in_using_gitlab">Inicia sesión usando Gitlab</string>
<string name="msg_content_description_log_in_using_wordpress">Login using WordPress</string> <!-- TODO Translate-->
<string name="msg_content_description_send_message">Enviar mensaje</string>
<string name="msg_content_description_show_more_login_options">Show more login options</string> <!-- TODO Translate-->
<string name="msg_content_description_show_attachment_options">Mostrar opciones de archivo adjunto</string>
<string name="msg_you"></string>
<string name="msg_unknown">Desconocido</string>
......@@ -106,9 +111,7 @@
<string name="msg_ver_not_minimum">
Parece que la versión del servidor está por debajo de la versión mínima requerida %1$s.\nActualice su servidor para iniciar sesión!
</string>
<string name="msg_proceed">PROCEDER</string>
<string name="msg_cancel">CANCELAR</string>
<string name="msg_warning">ADVERTENCIA</string>
<string name="msg_http_insecure">Al usar HTTP, te estás conectando a un servidor inseguro. No te recomendamos que hagas eso.</string>
<string name="msg_error_checking_server_version">Se ha producido un error al verificar la versión de su servidor, intente de nuevo</string>
<string name="msg_invalid_server_protocol">El protocolo seleccionado no es aceptado por este servidor, intente usar HTTPS</string>
......@@ -127,6 +130,18 @@
<string name="msg_delete_message">Borrar mensaje</string>
<string name="msg_delete_description">Seguro que quieres borrar este mensaje</string>
<string name="msg_no_search_found">No se han encontrado resultados</string>
<string name="msg_welcome_to_rocket_chat">Welcome to Rocket.Chat</string> <!-- TODO Add translation -->
<string name="msg_team_communication">Open Source Communication</string> <!-- TODO Add translation -->
<string name="msg_login_with_email">Login with <b>e-mail</b></string> <!-- TODO Add translation -->
<string name="msg_create_account">Create an account</string> <!-- TODO Add translation -->
<string name="msg_continue_with_facebook">Continue with <b>Facebook</b></string> <!-- TODO Add translation -->
<string name="msg_continue_with_github">Continue with <b>Github</b></string> <!-- TODO Add translation -->
<string name="msg_continue_with_google">Continue with <b>Google</b></string> <!-- TODO Add translation -->
<string name="msg_continue_with_linkedin">Continue with <b>Linkedin</b></string> <!-- TODO Add translation -->
<string name="msg_continue_with_gitlab">Continue with <b>GitLab</b></string> <!-- TODO Add translation -->
<string name="msg_continue_with_wordpress">Continue with <b>WordPress</b></string> <!-- TODO Add translation -->
<string name="msg_two_factor_authentication">Two-factor Authentication</string> <!-- TODO Add translation -->
<string name="msg__your_2fa_code">What’s your 2FA code?</string> <!-- TODO Add translation -->
<!-- Create channel messages -->
<string name="msg_private_channel">Privado</string>
......
......@@ -20,6 +20,7 @@
<string name="title_about">À propos</string>
<string name="title_create_channel">Créer salon</string>
<!-- Actions -->
<string name="action_connect">Se connecter</string>
<string name="action_use_this_username">Utilisez ce nom d\'utilisateur</string>
......@@ -45,6 +46,11 @@
<string name="action_select_photo_from_gallery">Sélectionner depuis la gallerie</string>
<string name="action_take_photo">Prendre une photo</string>
<string name="action_reset_avatar">Réinitialiser l\'avatar</string>
<string name="action_connect_server">Connect with a server</string> <!-- TODO Add translation -->
<string name="action_join_community">Join in the community</string> <!-- TODO Add translation -->
<string name="action_create_server">Create a new server</string> <!-- TODO Add translation -->
<string name="action_register">Register</string> <!-- TODO Add translation -->
<string name="action_confirm">Confirm</string> <!-- TODO Add translation -->
<!-- Settings List -->
<string-array name="settings_actions">
......@@ -58,19 +64,18 @@
<string name="msg_no_data_to_display">Aucune donnée à afficher</string>
<string name="msg_profile_update_successfully">Mise à jour du profil avec succès</string>
<string name="msg_username">nom d\'utilisateur</string>
<string name="msg_username_or_email">nom d\'utilisateur ou email</string>
<string name="msg_password">mot de passe</string>
<string name="msg_name">prénom</string>
<string name="msg_email">email</string>
<string name="msg_username_or_email">Nom d\'utilisateur ou email</string>
<string name="msg_password">Mot de passe</string>
<string name="msg_name">Prénom</string>
<string name="msg_email">Email</string>
<string name="msg_avatar_url">URL de l\'avatar</string>
<string name="msg_or_continue_using_social_accounts">Ou continuer en utilisant les comptes sociaux</string>
<string name="msg_new_user">Nouvel utilisateur? %1$s</string>
<string name="msg_forgot_password">Mot de passe oublié ? %1$s</string>
<string name="msg_forgot__your_password">Mot de passe oublié ? %1$s</string>
<string name="msg_reset">Réinitialiser</string>
<string name="msg_check_your_email_to_reset_your_password">Email envoyé ! Consultez votre boîte mail pour réinitialiser votre mot de passe.</string>
<string name="msg_invalid_email">Veuillez entrer une adresse valide</string>
<string name="msg_new_user_agreement">En procédant, vous acceptez notre\n%1$s et %2$s</string>
<string name="msg_2fa_code">Code 2FA</string>
<string name="msg_yesterday">Hier</string>
<string name="msg_today">Aujourd\'hui</string>
<string name="msg_message">Message</string>
......@@ -87,6 +92,7 @@
<string name="msg_content_description_log_in_using_gitlab">Connectez-vous en utilisant Gitlab</string>
<string name="msg_content_description_log_in_using_wordpress">Connectez-vous en utilisant WordPress</string>
<string name="msg_content_description_send_message">Envoyer message</string>
<string name="msg_content_description_show_more_login_options">Show more login options</string> <!-- TODO Translate-->
<string name="msg_content_description_show_attachment_options">Afficher les options de fichiers</string>
<string name="msg_you">Vous</string>
<string name="msg_unknown">Inconnu</string>
......@@ -107,9 +113,7 @@
<string name="msg_ver_not_minimum">
On dirait que la version de votre serveur est inférieure à la version minimale requise %1$s.\nVeuillez mettre à jour votre serveur pour vous connecter!
</string>
<string name="msg_proceed">PROCÉDER</string>
<string name="msg_cancel">ANNULER</string>
<string name="msg_warning">ATTENTION</string>
<string name="msg_http_insecure">Lorsque vous utilisez HTTP, vous vous connectez à un serveur non sécurisé. Nous ne vous recommandons pas de le faire.</string>
<string name="msg_error_checking_server_version">Une erreur est survenue lors de la vérification de la version de votre serveur, veuillez réessayer</string>
<string name="msg_invalid_server_protocol">Le protocole sélectionné n\'est pas accepté par ce serveur, essayez d\'utiliser HTTPS</string>
......@@ -131,6 +135,18 @@
<string name="msg_send">envoyer</string>
<string name="msg_delete_message">Supprimer Message</string>
<string name="msg_delete_description">Êtes-vous sûr de vouloir supprimer ce message</string>
<string name="msg_welcome_to_rocket_chat">Welcome to Rocket.Chat</string> <!-- TODO Add translation -->
<string name="msg_team_communication">Open Source Communication</string> <!-- TODO Add translation -->
<string name="msg_login_with_email">Login with <b>e-mail</b></string> <!-- TODO Add translation -->
<string name="msg_create_account">Create an account</string> <!-- TODO Add translation -->
<string name="msg_continue_with_facebook">Continue with <b>Facebook</b></string> <!-- TODO Add translation -->
<string name="msg_continue_with_github">Continue with <b>Github</b></string> <!-- TODO Add translation -->
<string name="msg_continue_with_google">Continue with <b>Google</b></string> <!-- TODO Add translation -->
<string name="msg_continue_with_linkedin">Continue with <b>Linkedin</b></string> <!-- TODO Add translation -->
<string name="msg_continue_with_gitlab">Continue with <b>GitLab</b></string> <!-- TODO Add translation -->
<string name="msg_continue_with_wordpress">Continue with <b>WordPress</b></string> <!-- TODO Add translation -->
<string name="msg_two_factor_authentication">Two-factor Authentication</string> <!-- TODO Add translation -->
<string name="msg__your_2fa_code">What’s your 2FA code?</string> <!-- TODO Add translation -->
<!-- TODO - Add proper translation -->
<string name="msg_view_more">view more</string>
<!-- TODO - Add proper translation -->
......
......@@ -45,6 +45,11 @@
<string name="action_select_photo_from_gallery">गैलरी से फोटो का चयन करें</string>
<string name="action_take_photo">फोटो खेचिये</string>
<string name="action_reset_avatar">अवतार रीसेट करें</string>
<string name="action_connect_server">सर्वर से कनेक्ट करें</string>
<string name="action_join_community">समुदाय में शामिल हों</string>
<string name="action_create_server">नया सर्वर बनाएं</string>
<string name="action_register">रजिस्टर</string>
<string name="action_confirm">पुष्टि करें</string>
<!-- Settings List -->
<string-array name="settings_actions">
......@@ -65,12 +70,11 @@
<string name="msg_avatar_url">अवतार यूआरएल</string>
<string name="msg_or_continue_using_social_accounts">या सामाजिक खाते का उपयोग करना जारी रखें</string>
<string name="msg_new_user">नया उपयोगकर्ता? %1$s</string>
<string name="msg_forgot_password">पासवर्ड भूल गए? %1$s</string>
<string name="msg_forgot__your_password">पासवर्ड भूल गए?</string>
<string name="msg_reset">रीसेट करें</string>
<string name="msg_check_your_email_to_reset_your_password">ईमेल गया गया है! अपना पासवर्ड रीसेट करने के लिए अपने इनबॉक्स की जांच करें।</string>
<string name="msg_invalid_email">कृपया एक वैध ई-मेल टाइप करें</string>
<string name="msg_new_user_agreement">आगे बढ़कर आप हमारे %1$s और %2$s से सहमत हो रहे हैं</string>
<string name="msg_2fa_code">कोड 2FA</string>
<string name="msg_yesterday">कल</string>
<string name="msg_today">आज</string>
<string name="msg_message">संदेश</string>
......@@ -87,6 +91,7 @@
<string name="msg_content_description_log_in_using_gitlab">Gitlab द्वारा लॉगिन करें</string>
<string name="msg_content_description_log_in_using_wordpress">WordPress द्वारा लॉगिन करें</string>
<string name="msg_content_description_send_message">मेसेज भेजें</string>
<string name="msg_content_description_show_more_login_options">Show more login options</string> <!-- TODO Translate-->
<string name="msg_content_description_show_attachment_options">अटैचमेंट विकल्प दिखाएं</string>
<string name="msg_you">आप</string>
<string name="msg_unknown">अनजान</string>
......@@ -108,9 +113,7 @@
<string name="msg_ver_not_minimum">
ऐसा लगता है कि आपका सर्वर संस्करण न्यूनतम आवश्यक संस्करण %1$s से कम है।\nकृपया लॉगिन करने के लिए अपने सर्वर को अपग्रेड करें!
</string>
<string name="msg_proceed">आगे बढ़ें</string>
<string name="msg_cancel">रद्द करना</string>
<string name="msg_warning">चेतावनी</string>
<string name="msg_http_insecure">HTTP का उपयोग करते समय, आप एक असुरक्षित सर्वर से कनेक्ट हो रहे हैं। हम आपको ऐसा करने की सलाह नहीं देते हैं।</string>
<string name="msg_error_checking_server_version">आपके सर्वर संस्करण की जांच करते समय एक त्रुटि आई है, कृपया पुनः प्रयास करें</string>
<string name="msg_invalid_server_protocol">चयनित प्रोटोकॉल इस सर्वर द्वारा स्वीकार नहीं किया गया है, HTTPS का उपयोग करने का प्रयास करें</string>
......@@ -133,6 +136,18 @@
<string name="msg_sent_attachment">एक अनुलग्नक भेजा</string>
<string name="msg_delete_message">संदेश को हटाएं</string>
<string name="msg_delete_description">क्या आप निश्चित रूप से यह संदेश हटाना चाहते हैं</string>
<string name="msg_welcome_to_rocket_chat">Rocket.Chat में आपका स्वागत है</string>
<string name="msg_team_communication">ओपन सोर्स कम्युनिकेशन</string>
<string name="msg_login_with_email">ई-मेल के साथ लॉगिन करें</string>
<string name="msg_create_account">खाता बनाएं</string>
<string name="msg_continue_with_facebook"><b>Facebook</b> के साथ जारी रखें</string>
<string name="msg_continue_with_github"><b>Github</b> के साथ जारी रखें</string>
<string name="msg_continue_with_google"><b>Google</b> के साथ जारी रखें</string>
<string name="msg_continue_with_linkedin"><b>Linkedin</b> के साथ जारी रखें</string>
<string name="msg_continue_with_gitlab"><b>GitLab</b> के साथ जारी रखें</string>
<string name="msg_continue_with_wordpress"><b>WordPress</b> के साथ जारी रखें</string>
<string name="msg_two_factor_authentication">दो तरीकों से प्रमाणीकरण</string>
<string name="msg__your_2fa_code">आपका 2FA कोड क्या है?</string>
<!-- Create channel messages -->
<string name="msg_private_channel">प्राइवेट</string>
......
......@@ -47,6 +47,11 @@
<string name="action_select_photo_from_gallery">Select photo from gallery</string> <!-- TODO Add translation -->
<string name="action_take_photo">Take photo</string> <!-- TODO Add translation -->
<string name="action_reset_avatar">Reset avatar</string> <!-- TODO Add translation -->
<string name="action_connect_server">Connect with a server</string> <!-- TODO Add translation -->
<string name="action_join_community">Join in the community</string> <!-- TODO Add translation -->
<string name="action_create_server">Create a new server</string> <!-- TODO Add translation -->
<string name="action_register">Register</string> <!-- TODO Add translation -->
<string name="action_confirm">Confirm</string> <!-- TODO Add translation -->
<!-- Settings List -->
<string-array name="settings_actions">
......@@ -67,12 +72,11 @@
<string name="msg_avatar_url">アバター URL</string>
<string name="msg_or_continue_using_social_accounts">またはソーシャルアカウントを使用する</string>
<string name="msg_new_user">新規ユーザー? %1$s</string>
<string name="msg_forgot_password">パスワードをお忘れですか? %1$s</string>
<string name="msg_forgot__your_password">パスワードをお忘れですか? %1$s</string>
<string name="msg_reset">リセット</string>
<string name="msg_check_your_email_to_reset_your_password">メールが送信されました! パスワードを変更するためには、受信ボックスを確認してください。</string>
<string name="msg_invalid_email">有効なメールアドレスを入力してください</string>
<string name="msg_new_user_agreement">サインアップすることで、\n%1$s と %2$s に同意したとみなします。</string>
<string name="msg_2fa_code">2FA コード</string>
<string name="msg_today">Today</string> <!-- TODO Add translation -->
<string name="msg_yesterday">昨日</string>
<string name="msg_message">メッセージ</string>
......@@ -80,15 +84,16 @@
<string name="msg_invalid_2fa_code">無効な 2FA コード</string>
<string name="msg_invalid_file">無効なファイル</string>
<string name="msg_invalid_server_url">無効なサーバー URL</string>
<string name="msg_content_description_log_in_using_facebook">Login using Facebook</string>
<string name="msg_content_description_log_in_using_github">Login using Github</string>
<string name="msg_content_description_log_in_using_google">Login using Google</string>
<string name="msg_content_description_log_in_using_linkedin">Login using Linkedin</string>
<string name="msg_content_description_log_in_using_meteor">Login using Meteor</string>
<string name="msg_content_description_log_in_using_twitter">Login using Twitter</string>
<string name="msg_content_description_log_in_using_gitlab">Login using Gitlab</string>
<string name="msg_content_description_log_in_using_wordpress">Login using WordPress</string>
<string name="msg_content_description_log_in_using_facebook">Login using Facebook</string> <!-- TODO Add translation -->
<string name="msg_content_description_log_in_using_github">Login using Github</string> <!-- TODO Add translation -->
<string name="msg_content_description_log_in_using_google">Login using Google</string> <!-- TODO Add translation -->
<string name="msg_content_description_log_in_using_linkedin">Login using Linkedin</string> <!-- TODO Add translation -->
<string name="msg_content_description_log_in_using_meteor">Login using Meteor</string> <!-- TODO Add translation -->
<string name="msg_content_description_log_in_using_twitter">Login using Twitter</string> <!-- TODO Add translation -->
<string name="msg_content_description_log_in_using_gitlab">Login using Gitlab</string> <!-- TODO Add translation -->
<string name="msg_content_description_log_in_using_wordpress">Login using WordPress</string> <!-- TODO Add translation -->
<string name="msg_content_description_send_message">メッセージを送信</string> <!-- TODO Add translation -->
<string name="msg_content_description_show_more_login_options">Show more login options</string> <!-- TODO Translate-->
<string name="msg_content_description_show_attachment_options">添付ファイルオプションを表示する</string>
<string name="msg_you">あなた</string>
<string name="msg_unknown">不明</string>
......@@ -113,9 +118,7 @@
</string>
<string name="msg_no_chat_title">メッセージがまだありません</string>
<string name="msg_no_chat_description">ここから会話を始めましょう</string>
<string name="msg_proceed">PROCEED</string>
<string name="msg_cancel">キャンセル</string>
<string name="msg_warning">警告</string>
<string name="msg_http_insecure">HTTPを使用している場合、安全ではないサーバーに接続します。安全なサーバーに接続することをお勧めします。</string>
<string name="msg_error_checking_server_version">サーバーのバージョンを確認中にエラーが発生しました。もう一度お試しください。</string>
<string name="msg_invalid_server_protocol">選択したプロトコルはこのサーバーでは使用できません。HTTPSを選択してください。</string>
......@@ -132,6 +135,18 @@
<string name="msg_file_description">ファイルの説明</string>
<string name="msg_send">送信</string>
<string name="msg_sent_attachment">添付ファイルを送信しました</string>
<string name="msg_welcome_to_rocket_chat">Welcome to Rocket.Chat</string> <!-- TODO Add translation -->
<string name="msg_team_communication">Team Communication</string> <!-- TODO Add translation -->
<string name="msg_login_with_email">Login with <b>e-mail</b></string> <!-- TODO Add translation -->
<string name="msg_create_account">Create an account</string> <!-- TODO Add translation -->
<string name="msg_continue_with_facebook">Continue with <b>Facebook</b></string> <!-- TODO Add translation -->
<string name="msg_continue_with_github">Continue with <b>Github</b></string> <!-- TODO Add translation -->
<string name="msg_continue_with_google">Continue with <b>Google</b></string> <!-- TODO Add translation -->
<string name="msg_continue_with_linkedin">Continue with <b>Linkedin</b></string> <!-- TODO Add translation -->
<string name="msg_continue_with_gitlab">Continue with <b>GitLab</b></string> <!-- TODO Add translation -->
<string name="msg_continue_with_wordpress">Continue with <b>WordPress</b></string> <!-- TODO Add translation -->
<string name="msg_two_factor_authentication">Two-factor Authentication</string> <!-- TODO Add translation -->
<string name="msg__your_2fa_code">What’s your 2FA code?</string> <!-- TODO Add translation -->
<!-- TODO - Add proper translation -->
<string name="msg_view_more">view more</string>
<!-- TODO - Add proper translation -->
......
......@@ -45,6 +45,11 @@
<string name="action_select_photo_from_gallery">Escolher foto da galeria</string>
<string name="action_take_photo">Tirar foto</string>
<string name="action_reset_avatar">Resetar avatar</string>
<string name="action_connect_server">Connect with a server</string> <!-- TODO Add translation -->
<string name="action_join_community">Join in the community</string> <!-- TODO Add translation -->
<string name="action_create_server">Create a new server</string> <!-- TODO Add translation -->
<string name="action_register">Register</string> <!-- TODO Add translation -->
<string name="action_confirm">Confirm</string> <!-- TODO Add translation -->
<!-- Settings List -->
<string-array name="settings_actions">
......@@ -58,19 +63,18 @@
<string name="msg_no_data_to_display">Nenhum dado para exibir</string>
<string name="msg_profile_update_successfully">Perfil atualizado com sucesso</string>
<string name="msg_username">nome de usuário</string>
<string name="msg_username_or_email">nome de usuário ou email</string>
<string name="msg_password">senha</string>
<string name="msg_username_or_email">Nome de usuário ou email</string>
<string name="msg_password">Senha</string>
<string name="msg_name">nome</string>
<string name="msg_email">email</string>
<string name="msg_avatar_url">URL do avatar</string>
<string name="msg_or_continue_using_social_accounts">Ou continue através de contas sociais</string>
<string name="msg_new_user">Novo usuário? %1$s</string>
<string name="msg_forgot_password">Esqueceu a senha? %1$s</string>
<string name="msg_forgot__your_password">Esqueceu sua senha?</string>
<string name="msg_reset">Redefinir</string>
<string name="msg_check_your_email_to_reset_your_password">Email enviado! Verifique sua caixa de entrada para redefinir sua senha.</string>
<string name="msg_invalid_email">Por favor informe um e-mail válido</string>
<string name="msg_new_user_agreement">Ao proceder você concorda com nossos %1$s e %2$s</string>
<string name="msg_2fa_code">Código 2FA</string>
<string name="msg_yesterday">Ontem</string>
<string name="msg_today">Hoje</string>
<string name="msg_message">Mensagem</string>
......@@ -87,6 +91,7 @@
<string name="msg_content_description_log_in_using_gitlab">Fazer login através do Gitlab</string>
<string name="msg_content_description_log_in_using_wordpress">Fazer login através do WordPress</string>
<string name="msg_content_description_send_message">Enviar mensagem</string>
<string name="msg_content_description_show_more_login_options">Mostrar mais opções de login</string>
<string name="msg_content_description_show_attachment_options">Mostrar opções de anexo</string>
<string name="msg_you">Você</string>
<string name="msg_unknown">Desconhecido</string>
......@@ -110,9 +115,7 @@
</string>
<string name="msg_no_chat_title">Nenhuma mensagem de chat</string>
<string name="msg_no_chat_description">Comece a conversar para ver suas\nmensagens aqui.</string>
<string name="msg_proceed">CONTINUAR</string>
<string name="msg_cancel">CANCELAR</string>
<string name="msg_warning">AVISO</string>
<string name="msg_http_insecure">Usando HTTP, você estará conectando a um servidor não seguro, não recomendamos sua utilização.</string>
<string name="msg_error_checking_server_version">Ocorreu um erro verificando a versão do servidor, por favor tente novamente</string>
<string name="msg_invalid_server_protocol">O protocolo selecionado não é suportado pelo servidor, por favor utilize HTTPS e tente novamente</string>
......@@ -132,6 +135,18 @@
<string name="msg_send">Enviar</string>
<string name="msg_delete_message">Remove mensagem</string>
<string name="msg_delete_description">Tem certeza que quer apagar esta mensagem?</string>
<string name="msg_welcome_to_rocket_chat">Welcome to Rocket.Chat</string> <!-- TODO Add translation -->
<string name="msg_team_communication">Open Source Communication</string> <!-- TODO Add translation -->
<string name="msg_login_with_email">Login with <b>e-mail</b></string> <!-- TODO Add translation -->
<string name="msg_create_account">Create an account</string> <!-- TODO Add translation -->
<string name="msg_continue_with_facebook">Continue with <b>Facebook</b></string> <!-- TODO Add translation -->
<string name="msg_continue_with_github">Continue with <b>Github</b></string> <!-- TODO Add translation -->
<string name="msg_continue_with_google">Continue with <b>Google</b></string> <!-- TODO Add translation -->
<string name="msg_continue_with_linkedin">Continue with <b>Linkedin</b></string> <!-- TODO Add translation -->
<string name="msg_continue_with_gitlab">Continue with <b>GitLab</b></string> <!-- TODO Add translation -->
<string name="msg_continue_with_wordpress">Continue with <b>WordPress</b></string> <!-- TODO Add translation -->
<string name="msg_two_factor_authentication">Two-factor Authentication</string> <!-- TODO Add translation -->
<string name="msg__your_2fa_code">What’s your 2FA code?</string> <!-- TODO Add translation -->
<string name="msg_view_more">visualizar mais</string>
<string name="msg_view_less">visualizar menos</string>
......
......@@ -45,6 +45,11 @@
<string name="action_select_photo_from_gallery">Выбрать из галереи</string>
<string name="action_take_photo">Сделать снимок</string>
<string name="action_reset_avatar">Восстановить аватар</string>
<string name="action_connect_server">Connect with a server</string> <!-- TODO Add translation -->
<string name="action_join_community">Join in the community</string> <!-- TODO Add translation -->
<string name="action_create_server">Create a new server</string> <!-- TODO Add translation -->
<string name="action_register">Register</string> <!-- TODO Add translation -->
<string name="action_confirm">Confirm</string> <!-- TODO Add translation -->
<!-- Settings List -->
<string-array name="settings_actions">
......@@ -65,12 +70,11 @@
<string name="msg_avatar_url">URL аватара</string>
<string name="msg_or_continue_using_social_accounts">Или продолжить, используя социальные учетные записи</string>
<string name="msg_new_user">Новый пользователь? %1$s</string>
<string name="msg_forgot_password">Забыли пароль? %1$s</string>
<string name="msg_forgot__your_password">Забыли пароль?</string>
<string name="msg_reset">Сброс</string>
<string name="msg_check_your_email_to_reset_your_password">Письмо отправлено! Проверьте свой почтовый ящик, чтобы сбросить пароль.</string>
<string name="msg_invalid_email">Введите действующий e-mail</string>
<string name="msg_new_user_agreement">Продолжая, вы принимаете\n%1$s и %2$s</string>
<string name="msg_2fa_code">Код 2FA</string>
<string name="msg_yesterday">Вчера</string>
<string name="msg_today">Сегодня</string>
<string name="msg_message">Сообщение</string>
......@@ -87,6 +91,7 @@
<string name="msg_content_description_log_in_using_gitlab">Войти с помощью Gitlab</string>
<string name="msg_content_description_log_in_using_wordpress">Войти с помощью WordPress</string>
<string name="msg_content_description_send_message">Отправить сообщение</string>
<string name="msg_content_description_show_more_login_options">Show more login options</string> <!-- TODO Translate-->
<string name="msg_content_description_show_attachment_options">Показать параметры вложения</string>
<string name="msg_you">Вы</string>
<string name="msg_unknown">Неизвестный</string>
......@@ -107,9 +112,7 @@
<string name="msg_ver_not_minimum">Похоже, версия сервера меньше минимально необходимой %1$s.\nДля работы потребуется обновить сервер!</string>
<string name="msg_no_chat_title">Нет сообщений</string>
<string name="msg_no_chat_description">Начните общаться, чтобы увидеть\nтут сообщения.</string>
<string name="msg_proceed">ПРОДОЛЖИТЬ</string>
<string name="msg_cancel">ОТМЕНА</string>
<string name="msg_warning">ПРЕДУПРЕЖДЕНИЕ</string>
<string name="msg_http_insecure">При использовании HTTP вы подключаетесь к небезопасному серверу. Мы не рекомендуем вам это делать.</string>
<string name="msg_error_checking_server_version">При проверке версии вашего сервера произошла ошибка, повторите попытку.</string>
<string name="msg_invalid_server_protocol">Выбранный протокол не разрешен на этом сервере, попробуйте использовать HTTPS</string>
......@@ -130,6 +133,18 @@
<string name="msg_delete_description">Вы уверены, что хотите удалить это сообщение?</string>
<string name="msg_channel_name">Название канала</string>
<string name="msg_search">Поиск</string>
<string name="msg_welcome_to_rocket_chat">Welcome to Rocket.Chat</string> <!-- TODO Add translation -->
<string name="msg_team_communication">Open Source Communication</string> <!-- TODO Add translation -->
<string name="msg_login_with_email">Login with <b>e-mail</b></string> <!-- TODO Add translation -->
<string name="msg_create_account">Create an account</string> <!-- TODO Add translation -->
<string name="msg_continue_with_facebook">Continue with <b>Facebook</b></string> <!-- TODO Add translation -->
<string name="msg_continue_with_github">Continue with <b>Github</b></string> <!-- TODO Add translation -->
<string name="msg_continue_with_google">Continue with <b>Google</b></string> <!-- TODO Add translation -->
<string name="msg_continue_with_linkedin">Continue with <b>Linkedin</b></string> <!-- TODO Add translation -->
<string name="msg_continue_with_gitlab">Continue with <b>GitLab</b></string> <!-- TODO Add translation -->
<string name="msg_continue_with_wordpress">Continue with <b>WordPress</b></string> <!-- TODO Add translation -->
<string name="msg_two_factor_authentication">Two-factor Authentication</string> <!-- TODO Add translation -->
<string name="msg__your_2fa_code">What’s your 2FA code?</string> <!-- TODO Add translation -->
<string name="msg_view_more">больше</string>
<string name="msg_view_less">меньше</string>
......
......@@ -45,6 +45,11 @@
<string name="action_select_photo_from_gallery">Galeriden resim seç</string>
<string name="action_take_photo">Fotoğraf çek</string>
<string name="action_reset_avatar">Avatarı kaldır</string>
<string name="action_connect_server">Connect with a server</string> <!-- TODO Add translation -->
<string name="action_join_community">Join in the community</string> <!-- TODO Add translation -->
<string name="action_create_server">Create a new server</string> <!-- TODO Add translation -->
<string name="action_register">Register</string> <!-- TODO Add translation -->
<string name="action_confirm">Confirm</string> <!-- TODO Add translation -->
<!-- Settings List -->
<string-array name="settings_actions">
......@@ -65,12 +70,11 @@
<string name="msg_avatar_url">avatar URL</string>
<string name="msg_or_continue_using_social_accounts">Veya sosyal medya hesaplarınızdan birini kullanarak devam edin</string>
<string name="msg_new_user">Yeni kullanıcı? %1$s</string>
<string name="msg_forgot_password">Şifremi unuttum? %1$s</string>
<string name="msg_forgot__your_password">Forgot your password?</string> <!-- TODO Add translation -->
<string name="msg_reset">Sıfırla</string>
<string name="msg_check_your_email_to_reset_your_password">Eposta gönderilmiştir! Şifrenizi sıfırlamak için eposta kutunuzu kontrol ediniz.</string>
<string name="msg_invalid_email">Lütfen, geçerli bir eposta adresi giriniz</string>
<string name="msg_new_user_agreement">Devam ederek \n%1$s ve %2$s kabul ediyorsunuz</string>
<string name="msg_2fa_code">2FA Kodu</string>
<string name="msg_more_than_ninety_nine_unread_messages" translatable="false">99+</string>
<string name="msg_yesterday">Dün</string>
<string name="msg_today">Bugün</string>
......@@ -88,6 +92,7 @@
<string name="msg_content_description_log_in_using_gitlab">Gitlab Hesabı ile Giriş Yap</string>
<string name="msg_content_description_log_in_using_wordpress">Wordpress Hesabı ile Giriş Yap</string>
<string name="msg_content_description_send_message">Mesaj gönder</string>
<string name="msg_content_description_show_more_login_options">Show more login options</string> <!-- TODO Add translation -->
<string name="msg_content_description_show_attachment_options">Dosya eki seçeneklerini göster</string>
<string name="msg_you">Siz</string>
<string name="msg_unknown">Bilinmeyen</string>
......@@ -112,9 +117,7 @@
</string>
<string name="msg_no_chat_title">Mesaj bulunmamaktadır</string>
<string name="msg_no_chat_description">Mesajlarınızı burada görmek için \nyazışmaya başlayın.</string>
<string name="msg_proceed">İLERLE</string>
<string name="msg_cancel">İPTAL</string>
<string name="msg_warning">UYARI</string>
<string name="msg_http_insecure">HTTP kullanırken güvensiz bir sunucuya bağlanıyorsunuz, bunu önermiyoruz.</string>
<string name="msg_error_checking_server_version">Sunucu sürümünüzü kontrol ederken bir hata oluştu, lütfen tekrar deneyiniz</string>
<string name="msg_invalid_server_protocol">Seçilen protokol bu sunucu tarafından kabul edilmiyor, lütfen HTTPS kullanın</string>
......@@ -131,6 +134,18 @@
<string name="msg_file_description">Dosya açıklaması</string>
<string name="msg_send">Gönder</string>
<string name="msg_sent_attachment">Bir dosya eki gönderildi</string>
<string name="msg_welcome_to_rocket_chat">Welcome to Rocket.Chat</string> <!-- TODO Add translation -->
<string name="msg_team_communication">Team Communication</string> <!-- TODO Add translation -->
<string name="msg_login_with_email">Login with <b>e-mail</b></string> <!-- TODO Add translation -->
<string name="msg_create_account">Create an account</string> <!-- TODO Add translation -->
<string name="msg_continue_with_facebook">Continue with <b>Facebook</b></string> <!-- TODO Add translation -->
<string name="msg_continue_with_github">Continue with <b>Github</b></string> <!-- TODO Add translation -->
<string name="msg_continue_with_google">Continue with <b>Google</b></string> <!-- TODO Add translation -->
<string name="msg_continue_with_linkedin">Continue with <b>Linkedin</b></string> <!-- TODO Add translation -->
<string name="msg_continue_with_gitlab">Continue with <b>GitLab</b></string> <!-- TODO Add translation -->
<string name="msg_continue_with_wordpress">Continue with <b>WordPress</b></string> <!-- TODO Add translation -->
<string name="msg_two_factor_authentication">Two-factor Authentication</string> <!-- TODO Add translation -->
<string name="msg__your_2fa_code">What’s your 2FA code?</string> <!-- TODO Add translation -->
<!-- Create channel messages -->
<string name="msg_private_channel">Özel</string>
......
......@@ -45,6 +45,11 @@
<string name="action_select_photo_from_gallery">Вибрати з галереї</string>
<string name="action_take_photo">Зробити знімок</string>
<string name="action_reset_avatar">Відновити аватар</string>
<string name="action_connect_server">Connect with a server</string> <!-- TODO Add translation -->
<string name="action_join_community">Join in the community</string> <!-- TODO Add translation -->
<string name="action_create_server">Create a new server</string> <!-- TODO Add translation -->
<string name="action_register">Register</string> <!-- TODO Add translation -->
<string name="action_confirm">Confirm</string> <!-- TODO Add translation -->
<!-- Settings List -->
<string-array name="settings_actions">
......@@ -65,12 +70,11 @@
<string name="msg_avatar_url">URL аватара</string>
<string name="msg_or_continue_using_social_accounts">Або продовжити, за допомогою акаунта у соціальних мережах</string>
<string name="msg_new_user">Новий користувач? %1$s</string>
<string name="msg_forgot_password">Забули пароль? %1$s</string>
<string name="msg_forgot__your_password">Забули пароль? %1$s</string>
<string name="msg_reset">Скинути</string>
<string name="msg_check_your_email_to_reset_your_password">Лист було відправлено! Перевірте свою поштову скриньку, щоб скинути пароль.</string>
<string name="msg_invalid_email">Введіть діючий e-mail</string>
<string name="msg_new_user_agreement">Продовжуючи, ви погоджуєтеся з %1$s і %2$s</string>
<string name="msg_2fa_code">Код 2FA</string>
<string name="msg_yesterday">Вчора</string>
<string name="msg_today">Сьогодні</string>
<string name="msg_message">"Повідомлення "</string>
......@@ -87,6 +91,7 @@
<string name="msg_content_description_log_in_using_gitlab">Увійти за допомогою Gitlab</string>
<string name="msg_content_description_log_in_using_wordpress">Увійти за допомогою Wordpress</string>
<string name="msg_content_description_send_message">Надіслати повідомлення</string>
<string name="msg_content_description_show_more_login_options">Show more login options</string> <!-- TODO Translate-->
<string name="msg_content_description_show_attachment_options">Показати параметри долучення</string>
<string name="msg_you">Ви</string>
<string name="msg_unknown">Невідомий</string>
......@@ -105,9 +110,7 @@
<string name="msg_update_app_version_in_order_to_continue">Версія сервера застаріла. Будь ласка, зв\'яжіться з адміністратором, щоб оновити сервер.</string>
<string name="msg_ver_not_recommended">Здається, версія сервера менше ніж рекомендована %1$s. \nМожна увійти, але можуть виникнути ті чи інші проблеми під час роботи.</string>
<string name="msg_ver_not_minimum">Здається, версія сервера менше ніж мінімально необхідна %1$s.\nДля роботи потрібно оновити сервер!</string>
<string name="msg_proceed">ПРОДОВЖИТИ</string>
<string name="msg_cancel">ВІДМІНА</string>
<string name="msg_warning">ПОПЕРЕДЖЕННЯ</string>
<string name="msg_http_insecure">При використанні HTTP ви підключаєтеся до потенційно небезпечного сервера. Ми не радимо вам це робити.</string>
<string name="msg_error_checking_server_version">Під час перевірки версії вашого сервера сталася помилка, спробуйте ще раз.</string>
<string name="msg_invalid_server_protocol">Використання обраного протоколу не дозволено на цьому сервері, спробуйте використати HTTPS</string>
......@@ -129,6 +132,18 @@
<string name="msg_send">Надіслати</string>
<string name="msg_delete_message">Видалити повідомлення</string>
<string name="msg_delete_description">Ви впевнені, що хочете видалити це повідомлення?</string>
<string name="msg_welcome_to_rocket_chat">Welcome to Rocket.Chat</string> <!-- TODO Add translation -->
<string name="msg_team_communication">Team Communication</string> <!-- TODO Add translation -->
<string name="msg_login_with_email">Login with <b>e-mail</b></string> <!-- TODO Add translation -->
<string name="msg_create_account">Create an account</string> <!-- TODO Add translation -->
<string name="msg_continue_with_facebook">Continue with <b>Facebook</b></string> <!-- TODO Add translation -->
<string name="msg_continue_with_github">Continue with <b>Github</b></string> <!-- TODO Add translation -->
<string name="msg_continue_with_google">Continue with <b>Google</b></string> <!-- TODO Add translation -->
<string name="msg_continue_with_linkedin">Continue with <b>Linkedin</b></string> <!-- TODO Add translation -->
<string name="msg_continue_with_gitlab">Continue with <b>GitLab</b></string> <!-- TODO Add translation -->
<string name="msg_continue_with_wordpress">Continue with <b>WordPress</b></string> <!-- TODO Add translation -->
<string name="msg_two_factor_authentication">Two-factor Authentication</string> <!-- TODO Add translation -->
<string name="msg__your_2fa_code">What’s your 2FA code?</string> <!-- TODO Add translation -->
<!-- TODO - Add proper translation -->
<string name="msg_view_more">view more</string>
<!-- TODO - Add proper translation -->
......
......@@ -2,9 +2,9 @@
<resources>
<!-- Main colors -->
<color name="colorPrimary">#FF303030</color> <!-- Material Grey 850 -->
<color name="colorPrimaryDark">#FF212121</color> <!-- Material Grey 900 -->
<color name="colorAccent">#FF1976D2</color> <!-- Material Blue 700 -->
<color name="colorPrimary">#FF2F343D</color>
<color name="colorPrimaryDark">#FF2F343D</color>
<color name="colorAccent">#FF1D74F5</color>
<!-- Text colors -->
<color name="colorPrimaryText">#DE000000</color>
......@@ -22,6 +22,12 @@
<color name="colorBlack">#FF000000</color>
<color name="colorRed">#FFFF0000</color>
<!-- Authentication colors -->
<color name="colorAuthenticationButtonBorderAndDivider">#FFE1E5E8</color>
<color name="colorAuthenticationButtonDisabled">#FFE1E5E8</color>
<color name="colorAuthenticationChevronAndExpandIcon">#FFCBCED1</color>
<color name="colorAuthenticationSecondaryText">#FF9EA2A8</color>
<color name="darkGray">#FFa0a0a0</color>
<color name="actionMenuColor">#FF727272</color>
<color name="whitesmoke">#FFf1f1f1</color>
......
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="default_protocol" translatable="false">https://</string>
<string name="default_server" translatable="false">open.rocket.chat</string>
<string name="server_hint_url" translatable="false">your-company.rocket.chat</string>
<string name="community_server_url" translatable="false">open.rocket.chat</string>
<string name="create_server_url" translatable="false">cloud.rocket.chat/trial</string>
</resources>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Default screen margins, per the Android Design guidelines. -->
<!-- Default screen margins, per the Android Design guidelines -->
<dimen name="screen_edge_left_and_right_margins">16dp</dimen>
<dimen name="screen_edge_left_and_right_padding">16dp</dimen>
<!-- Authentication -->
<dimen name="button_account_margin_top">10dp</dimen>
<dimen name="message_item_top_and_bottom_padding">6dp</dimen>
<dimen name="member_item_top_and_bottom_padding">6dp</dimen>
<dimen name="edit_text_margin">10dp</dimen>
<dimen name="edit_text_drawable_padding">16dp</dimen>
<dimen name="edit_text_margin">16dp</dimen>
<dimen name="edit_text_drawable_padding">10dp</dimen>
<dimen name="text_view_drawable_padding">8dp</dimen>
......@@ -25,7 +28,6 @@
<!-- ChatRoom -->
<dimen name="chat_item_top_and_bottom_padding">12dp</dimen>
<!-- Emoji -->
<dimen name="picker_padding_bottom">16dp</dimen>
<dimen name="supposed_keyboard_height">252dp</dimen>
......
......@@ -14,7 +14,7 @@ https://github.com/RocketChat/java-code-styles/blob/master/CODING_STYLE.md#strin
<!-- Titles -->
<string name="title_sign_in_your_server">Sign in to your server</string>
<string name="title_log_in">Log in</string>
<string name="title_log_in">Login</string>
<string name="title_register_username">Register username</string>
<string name="title_reset_password">Reset password</string>
<string name="title_sign_up">Sign up</string>
......@@ -57,6 +57,11 @@ https://github.com/RocketChat/java-code-styles/blob/master/CODING_STYLE.md#strin
<string name="action_select_photo_from_gallery">Select photo from gallery</string>
<string name="action_take_photo">Take photo</string>
<string name="action_reset_avatar">Reset avatar</string>
<string name="action_connect_server">Connect with a server</string>
<string name="action_join_community">Join in the community</string>
<string name="action_create_server">Create a new server</string>
<string name="action_register">Register</string>
<string name="action_confirm">Confirm</string>
<!-- Settings List -->
<string-array name="settings_actions">
......@@ -70,19 +75,18 @@ https://github.com/RocketChat/java-code-styles/blob/master/CODING_STYLE.md#strin
<string name="msg_no_data_to_display">No data to display</string>
<string name="msg_profile_update_successfully">Profile update successfully</string>
<string name="msg_username">username</string>
<string name="msg_username_or_email">username or email</string>
<string name="msg_password">password</string>
<string name="msg_name">name</string>
<string name="msg_email">email</string>
<string name="msg_username_or_email">Username or email</string>
<string name="msg_password">Password</string>
<string name="msg_name">Name</string>
<string name="msg_email">Email</string>
<string name="msg_avatar_url">avatar URL</string>
<string name="msg_or_continue_using_social_accounts">Or continue using social accounts</string>
<string name="msg_new_user">New user? %1$s</string>
<string name="msg_forgot_password">Forgot password? %1$s</string>
<string name="msg_forgot__your_password">Forgot your password?</string>
<string name="msg_reset">Reset</string>
<string name="msg_check_your_email_to_reset_your_password">Email sent! Check your inbox to reset your password.</string>
<string name="msg_invalid_email">Please type a valid e-mail</string>
<string name="msg_new_user_agreement">By proceeding you are agreeing to our\n%1$s and %2$s</string>
<string name="msg_2fa_code">2FA Code</string>
<string name="msg_more_than_ninety_nine_unread_messages" translatable="false">99+</string>
<string name="msg_yesterday">Yesterday</string>
<string name="msg_today">Today</string>
......@@ -100,6 +104,7 @@ https://github.com/RocketChat/java-code-styles/blob/master/CODING_STYLE.md#strin
<string name="msg_content_description_log_in_using_gitlab">Login using Gitlab</string>
<string name="msg_content_description_log_in_using_wordpress">Login using WordPress</string>
<string name="msg_content_description_send_message">Send message</string>
<string name="msg_content_description_show_more_login_options">Show more login options</string>
<string name="msg_content_description_show_attachment_options">Show attachment options</string>
<string name="msg_you">You</string>
<string name="msg_unknown">Unknown</string>
......@@ -125,10 +130,8 @@ https://github.com/RocketChat/java-code-styles/blob/master/CODING_STYLE.md#strin
</string>
<string name="msg_no_chat_title">No chat messages</string>
<string name="msg_no_chat_description">Start conversing to see your\nmessages here.</string>
<string name="msg_proceed">PROCEED</string>
<string name="msg_cancel">CANCEL</string>
<string name="msg_warning">WARNING</string>
<string name="msg_http_insecure">When using HTTP, you\'re connecting to an insecure server. We don\'t recommend you doing that.</string>
<string name="msg_http_insecure">When using HTTP you\'re connecting to an insecure server. We don\'t recommend you doing that.</string>
<string name="msg_error_checking_server_version">An error has occurred while checking your server version, please try again</string>
<string name="msg_invalid_server_protocol">The selected protocol is not accepted by this server, try using HTTPS</string>
<string name="msg_image_saved_successfully">Image has been saved to gallery</string>
......@@ -144,6 +147,18 @@ https://github.com/RocketChat/java-code-styles/blob/master/CODING_STYLE.md#strin
<string name="msg_file_description">File description</string>
<string name="msg_send">Send</string>
<string name="msg_sent_attachment">Sent an attachment</string>
<string name="msg_welcome_to_rocket_chat">Welcome to Rocket.Chat</string>
<string name="msg_team_communication">Team Communication</string>
<string name="msg_login_with_email">Login with <b>e-mail</b></string>
<string name="msg_create_account">Create an account</string>
<string name="msg_continue_with_facebook">Continue with <b>Facebook</b></string>
<string name="msg_continue_with_github">Continue with <b>Github</b></string>
<string name="msg_continue_with_google">Continue with <b>Google</b></string>
<string name="msg_continue_with_linkedin">Continue with <b>Linkedin</b></string>
<string name="msg_continue_with_gitlab">Continue with <b>GitLab</b></string>
<string name="msg_continue_with_wordpress">Continue with <b>WordPress</b></string>
<string name="msg_two_factor_authentication">Two-factor Authentication</string>
<string name="msg__your_2fa_code">What’s your 2FA code?</string>
<!-- Create channel messages -->
<string name="msg_private_channel">Private</string>
......@@ -301,7 +316,7 @@ https://github.com/RocketChat/java-code-styles/blob/master/CODING_STYLE.md#strin
<string name="notif_success_sending">Message sent to %1$s!</string>
<string name="read_by">Read by</string>
<string name="message_information_title">Message information</string>
<string name="message_room_changed_privacy">Room type changed to: %1$s by %2$s</string>
<string name="foss" tools:ignore="MissingTranslation">(FOSS)</string>
<string name="message_room_changed_privacy">Room type changed to: %1$s by %2$s</string>
</resources>
\ No newline at end of file
<resources xmlns:tools="http://schemas.android.com/tools">
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
......@@ -6,56 +6,104 @@
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="android:statusBarColor">@color/colorPrimaryDark</item>
<item name="windowActionModeOverlay">true</item>
<item name="searchViewStyle">@style/ChatRoom.SearchView</item>
</style>
<style name="AuthenticationTheme" parent="Theme.AppCompat.NoActionBar">
<item name="android:statusBarColor">@color/colorPrimaryDark</item>
<item name="android:windowBackground">@color/colorPrimary</item>
<item name="actionModeBackground">@color/colorPrimary</item>
<item name="android:actionModeBackground">@color/colorPrimary</item>
</style>
<!-- Widget styles. -->
<style name="Authentication.TextView" parent="TextAppearance.AppCompat.Medium">
<!-- Authentication -->
<style name="Authentication.TextView.Headline" parent="TextAppearance.AppCompat">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">50dp</item>
<item name="android:layout_marginStart">@dimen/screen_edge_left_and_right_margins</item>
<item name="android:paddingStart">@dimen/edit_text_margin</item>
<item name="android:maxLines">1</item>
<item name="android:drawablePadding">@dimen/edit_text_drawable_padding</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:textSize">24sp</item>
<item name="android:lineSpacingExtra">4sp</item>
<item name="android:fontFamily">sans-serif</item>
<item name="android:background">@drawable/style_edit_text_authentication</item>
<item name="android:textColor">@color/colorPrimary</item>
<item name="android:textStyle">bold</item>
</style>
<style name="Authentication.Headline.TextView" parent="TextAppearance.AppCompat.Headline">
<style name="Authentication.TextView.Description" parent="TextAppearance.AppCompat">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:layout_marginTop">20dp</item>
<item name="android:layout_marginStart">@dimen/screen_edge_left_and_right_margins</item>
<item name="android:layout_marginEnd">@dimen/screen_edge_left_and_right_margins</item>
<item name="android:textSize">16sp</item>
<item name="android:fontFamily">sans-serif</item>
<item name="android:lineSpacingExtra">4sp</item>
<item name="android:textColor">#FF54585E</item>
<item name="android:textStyle">normal</item>
</style>
<style name="Authentication.EditText" parent="Widget.AppCompat.EditText">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">50dp</item>
<item name="android:layout_marginStart">@dimen/screen_edge_left_and_right_margins</item>
<item name="android:layout_marginEnd">@dimen/screen_edge_left_and_right_margins</item>
<style name="Authentication.Button.Title" parent="TextAppearance.AppCompat">
<item name="android:textSize">17sp</item>
<item name="android:fontFamily">sans-serif</item>
<item name="android:textStyle">bold</item>
<item name="android:textColor">@color/colorAccent</item>
<item name="android:letterSpacing">0.04</item>
</style>
<style name="Authentication.Button.Title.White" parent="Authentication.Button.Title">
<item name="android:textColor">@color/colorWhite</item>
</style>
<style name="Authentication.Button.Description" parent="TextAppearance.AppCompat">
<item name="android:textSize">15sp</item>
<item name="android:fontFamily">sans-serif</item>
<item name="android:textStyle">normal</item>
<item name="android:textColor">@color/colorAuthenticationSecondaryText</item>
<item name="android:letterSpacing">0.04</item>
</style>
<style name="Authentication.EditText.Hint" parent="TextAppearance.AppCompat">
<item name="android:textSize">17sp</item>
<item name="android:fontFamily">sans-serif</item>
<item name="android:textStyle">normal</item>
<item name="android:textColorHint">@color/colorAuthenticationSecondaryText</item>
<item name="android:lineSpacingExtra">3sp</item>
</style>
<style name="Authentication.EditText.Border" parent="Widget.AppCompat.EditText">
<item name="android:layout_width">0dp</item>
<item name="android:layout_height">48dp</item>
<item name="android:paddingStart">@dimen/edit_text_margin</item>
<item name="android:paddingEnd">@dimen/edit_text_margin</item>
<item name="android:maxLines">1</item>
<item name="android:drawablePadding">@dimen/edit_text_drawable_padding</item>
<item name="android:drawableTint" tools:ignore="NewApi">@color/colorDrawableTintGrey</item>
<item name="android:textColor">@color/colorPrimaryText</item>
<item name="android:textColorHint">@color/colorAuthenticationSecondaryText</item>
<item name="android:fontFamily">sans-serif</item>
<item name="android:background">@drawable/rounded_border</item>
</style>
<style name="Authentication.Button" parent="Widget.AppCompat.Button.Borderless">
<item name="android:layout_width">0dp</item>
<item name="android:layout_height">48dp</item>
<item name="android:foreground">?selectableItemBackground</item>
<item name="android:textSize">18sp</item>
<item name="android:fontFamily">sans-serif-medium</item>
<item name="android:textStyle">normal</item>
<item name="android:textColor">@color/colorWhite</item>
<item name="android:lineSpacingExtra">2sp</item>
<item name="android:textAllCaps">false</item>
<item name="android:background">@drawable/rounded_color_accent</item>
</style>
<style name="Authentication.Button.Borderless" parent="Widget.AppCompat.Button.Borderless">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">48dp</item>
<item name="android:textAllCaps">false</item>
<item name="android:textColor">@color/colorAccent</item>
<item name="android:textSize">18sp</item>
<item name="android:textStyle">bold</item>
<item name="android:fontFamily">sans-serif</item>
<item name="android:background">@drawable/style_edit_text_authentication</item>
<item name="android:lineSpacingExtra">2sp</item>
</style>
<style name="Authentication.Button" parent="Widget.AppCompat.Button">
<item name="android:layout_width">match_parent</item>
<style name="Authentication.AVLoadingIndicatorView" parent="AVLoadingIndicatorView">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:background">@drawable/effect_ripple</item>
<item name="indicatorColor">@color/colorPrimary</item>
<item name="indicatorName">BallPulseIndicator</item>
</style>
<style name="EditText.Password" parent="TextAppearance.AppCompat">
......@@ -115,7 +163,8 @@
<item name="android:textSize">10sp</item>
</style>
<style name="Profile.EditText" parent="Authentication.EditText">
// REMARK: To be removed.
<style name="Profile.EditText" parent="Authentication.EditText.Border">
<item name="android:background">@drawable/style_edit_text_profile</item>
</style>
......
......@@ -111,12 +111,6 @@ class DrawingActivity : DaggerAppCompatActivity(), DrawView {
}
private fun colorSelector() {
image_color_black.setOnClickListener {
custom_draw_view.setColor(
ResourcesCompat.getColor(resources, R.color.color_black, null)
)
scaleColorView(image_color_black)
}
image_color_red.setOnClickListener {
custom_draw_view.setColor(
ResourcesCompat.getColor(resources, R.color.color_red, null)
......
......@@ -4,4 +4,4 @@ distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.9-all.zip
distributionSha256Sum=39e2d5803bbd5eaf6c8efe07067b0e5a00235e8c71318642b2ed262920b27721
distributionSha256Sum=39e2d5803bbd5eaf6c8efe07067b0e5a00235e8c71318642b2ed262920b27721
\ No newline at end of file
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