Commit 46821a8a authored by Filipe de Lima Brito's avatar Filipe de Lima Brito

Merge branch 'develop-2.x' of github.com:RocketChat/Rocket.Chat.Android into...

Merge branch 'develop-2.x' of github.com:RocketChat/Rocket.Chat.Android into improvement/get-own-current-status
parents 3e9e5475 c0a12e1e
...@@ -13,8 +13,8 @@ android { ...@@ -13,8 +13,8 @@ android {
applicationId "chat.rocket.android" applicationId "chat.rocket.android"
minSdkVersion 21 minSdkVersion 21
targetSdkVersion versions.targetSdk targetSdkVersion versions.targetSdk
versionCode 2003 versionCode 2005
versionName "2.0.0-beta3" versionName "2.0.0-beta4"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
multiDexEnabled true multiDexEnabled true
} }
......
...@@ -18,10 +18,11 @@ import chat.rocket.android.app.migration.model.RealmUser ...@@ -18,10 +18,11 @@ import chat.rocket.android.app.migration.model.RealmUser
import chat.rocket.android.authentication.domain.model.toToken import chat.rocket.android.authentication.domain.model.toToken
import chat.rocket.android.dagger.DaggerAppComponent import chat.rocket.android.dagger.DaggerAppComponent
import chat.rocket.android.helper.CrashlyticsTree import chat.rocket.android.helper.CrashlyticsTree
import chat.rocket.android.helper.UrlHelper
import chat.rocket.android.infrastructure.LocalRepository import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.server.domain.* import chat.rocket.android.server.domain.*
import chat.rocket.android.server.domain.model.Account import chat.rocket.android.server.domain.model.Account
import chat.rocket.android.util.extensions.avatarUrl
import chat.rocket.android.util.extensions.serverLogoUrl
import chat.rocket.android.widget.emoji.EmojiRepository import chat.rocket.android.widget.emoji.EmojiRepository
import chat.rocket.common.model.Token import chat.rocket.common.model.Token
import chat.rocket.core.model.Value import chat.rocket.core.model.Value
...@@ -148,12 +149,12 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje ...@@ -148,12 +149,12 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje
private fun migrateServerInfo(url: String, authToken: String, settings: PublicSettings, user: RealmUser) { private fun migrateServerInfo(url: String, authToken: String, settings: PublicSettings, user: RealmUser) {
val userId = user._id val userId = user._id
val avatar = UrlHelper.getAvatarUrl(url, user.username!!) val avatar = url.avatarUrl(user.username!!)
val icon = settings.favicon()?.let { val icon = settings.favicon()?.let {
UrlHelper.getServerLogoUrl(url, it) url.serverLogoUrl(it)
} }
val logo = settings.wideTile()?.let { val logo = settings.wideTile()?.let {
UrlHelper.getServerLogoUrl(url, it) url.serverLogoUrl(it)
} }
val account = Account(url, icon, logo, user.username!!, avatar) val account = Account(url, icon, logo, user.username!!, avatar)
launch(CommonPool) { launch(CommonPool) {
......
package chat.rocket.android.authentication.login.presentation package chat.rocket.android.authentication.login.presentation
import chat.rocket.android.BuildConfig
import chat.rocket.android.authentication.presentation.AuthenticationNavigator import chat.rocket.android.authentication.presentation.AuthenticationNavigator
import chat.rocket.android.core.lifecycle.CancelStrategy import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.helper.NetworkHelper import chat.rocket.android.helper.NetworkHelper
import chat.rocket.android.helper.OauthHelper import chat.rocket.android.helper.OauthHelper
import chat.rocket.android.helper.UrlHelper
import chat.rocket.android.infrastructure.LocalRepository import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.server.domain.* import chat.rocket.android.server.domain.*
import chat.rocket.android.server.domain.model.Account import chat.rocket.android.server.domain.model.Account
import chat.rocket.android.server.infraestructure.RocketChatClientFactory import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.server.presentation.CheckServerPresenter import chat.rocket.android.server.presentation.CheckServerPresenter
import chat.rocket.android.util.VersionInfo
import chat.rocket.android.util.extensions.* import chat.rocket.android.util.extensions.*
import chat.rocket.android.util.retryIO
import chat.rocket.common.RocketChatException import chat.rocket.common.RocketChatException
import chat.rocket.common.RocketChatTwoFactorException import chat.rocket.common.RocketChatTwoFactorException
import chat.rocket.common.model.Token import chat.rocket.common.model.Token
import chat.rocket.common.util.ifNull import chat.rocket.common.util.ifNull
import chat.rocket.core.RocketChatClient import chat.rocket.core.RocketChatClient
import chat.rocket.core.internal.rest.* import chat.rocket.core.internal.rest.*
import chat.rocket.core.model.Myself
import kotlinx.coroutines.experimental.delay import kotlinx.coroutines.experimental.delay
import timber.log.Timber import timber.log.Timber
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
...@@ -103,7 +100,7 @@ class LoginPresenter @Inject constructor(private val view: LoginView, ...@@ -103,7 +100,7 @@ class LoginPresenter @Inject constructor(private val view: LoginView,
private fun setupCasView() { private fun setupCasView() {
if (settings.isCasAuthenticationEnabled()) { if (settings.isCasAuthenticationEnabled()) {
val token = generateRandomString(17) val token = generateRandomString(17)
view.setupCasButtonListener(UrlHelper.getCasUrl(settings.casLoginUrl(), currentServer, token), token) view.setupCasButtonListener(settings.casLoginUrl().casUrl(currentServer, token), token)
view.showCasButton() view.showCasButton()
} }
} }
...@@ -118,7 +115,9 @@ class LoginPresenter @Inject constructor(private val view: LoginView, ...@@ -118,7 +115,9 @@ class LoginPresenter @Inject constructor(private val view: LoginView,
private fun setupOauthServicesView() { private fun setupOauthServicesView() {
launchUI(strategy) { launchUI(strategy) {
try { try {
val services = client.settingsOauth().services val services = retryIO("settingsOauth()") {
client.settingsOauth().services
}
if (services.isNotEmpty()) { if (services.isNotEmpty()) {
val state = "{\"loginStyle\":\"popup\",\"credentialToken\":\"${generateRandomString(40)}\",\"isCordova\":true}".encodeToBase64() val state = "{\"loginStyle\":\"popup\",\"credentialToken\":\"${generateRandomString(40)}\",\"isCordova\":true}".encodeToBase64()
var totalSocialAccountsEnabled = 0 var totalSocialAccountsEnabled = 0
...@@ -195,30 +194,32 @@ class LoginPresenter @Inject constructor(private val view: LoginView, ...@@ -195,30 +194,32 @@ class LoginPresenter @Inject constructor(private val view: LoginView,
view.disableUserInput() view.disableUserInput()
view.showLoading() view.showLoading()
try { try {
val token = when (loginType) { val token = retryIO("login") {
TYPE_LOGIN_USER_EMAIL -> { when (loginType) {
if (usernameOrEmail.isEmail()) { TYPE_LOGIN_USER_EMAIL -> {
client.loginWithEmail(usernameOrEmail, password) if (usernameOrEmail.isEmail()) {
} else { client.loginWithEmail(usernameOrEmail, password)
if (settings.isLdapAuthenticationEnabled()) {
client.loginWithLdap(usernameOrEmail, password)
} else { } else {
client.login(usernameOrEmail, password) if (settings.isLdapAuthenticationEnabled()) {
client.loginWithLdap(usernameOrEmail, password)
} else {
client.login(usernameOrEmail, password)
}
} }
} }
} TYPE_LOGIN_CAS -> {
TYPE_LOGIN_CAS -> { delay(3, TimeUnit.SECONDS)
delay(3, TimeUnit.SECONDS) client.loginWithCas(credentialToken)
client.loginWithCas(credentialToken) }
} TYPE_LOGIN_OAUTH -> {
TYPE_LOGIN_OAUTH -> { client.loginWithOauth(credentialToken, credentialSecret)
client.loginWithOauth(credentialToken, credentialSecret) }
} else -> {
else -> { throw IllegalStateException("Expected TYPE_LOGIN_USER_EMAIL, TYPE_LOGIN_CAS or TYPE_LOGIN_OAUTH")
throw IllegalStateException("Expected TYPE_LOGIN_USER_EMAIL, TYPE_LOGIN_CAS or TYPE_LOGIN_OAUTH") }
} }
} }
val username = client.me().username val username = retryIO("me()") { client.me().username }
if (username != null) { if (username != null) {
localRepository.save(LocalRepository.CURRENT_USERNAME_KEY, username) localRepository.save(LocalRepository.CURRENT_USERNAME_KEY, username)
saveAccount(username) saveAccount(username)
...@@ -258,12 +259,12 @@ class LoginPresenter @Inject constructor(private val view: LoginView, ...@@ -258,12 +259,12 @@ class LoginPresenter @Inject constructor(private val view: LoginView,
private suspend fun saveAccount(username: String) { private suspend fun saveAccount(username: String) {
val icon = settings.favicon()?.let { val icon = settings.favicon()?.let {
UrlHelper.getServerLogoUrl(currentServer, it) currentServer.serverLogoUrl(it)
} }
val logo = settings.wideTile()?.let { val logo = settings.wideTile()?.let {
UrlHelper.getServerLogoUrl(currentServer, it) currentServer.serverLogoUrl(it)
} }
val thumb = UrlHelper.getAvatarUrl(currentServer, username) val thumb = currentServer.avatarUrl(username)
val account = Account(currentServer, icon, logo, username, thumb) val account = Account(currentServer, icon, logo, username, thumb)
saveAccountInteractor.save(account) saveAccountInteractor.save(account)
} }
......
...@@ -3,13 +3,15 @@ package chat.rocket.android.authentication.registerusername.presentation ...@@ -3,13 +3,15 @@ package chat.rocket.android.authentication.registerusername.presentation
import chat.rocket.android.authentication.presentation.AuthenticationNavigator import chat.rocket.android.authentication.presentation.AuthenticationNavigator
import chat.rocket.android.core.lifecycle.CancelStrategy import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.helper.NetworkHelper import chat.rocket.android.helper.NetworkHelper
import chat.rocket.android.helper.UrlHelper
import chat.rocket.android.infrastructure.LocalRepository import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.server.domain.* import chat.rocket.android.server.domain.*
import chat.rocket.android.server.domain.model.Account import chat.rocket.android.server.domain.model.Account
import chat.rocket.android.server.infraestructure.RocketChatClientFactory import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.extensions.avatarUrl
import chat.rocket.android.util.extensions.launchUI import chat.rocket.android.util.extensions.launchUI
import chat.rocket.android.util.extensions.registerPushToken import chat.rocket.android.util.extensions.registerPushToken
import chat.rocket.android.util.extensions.serverLogoUrl
import chat.rocket.android.util.retryIO
import chat.rocket.common.RocketChatException import chat.rocket.common.RocketChatException
import chat.rocket.common.model.Token import chat.rocket.common.model.Token
import chat.rocket.common.util.ifNull import chat.rocket.common.util.ifNull
...@@ -41,7 +43,9 @@ class RegisterUsernamePresenter @Inject constructor( ...@@ -41,7 +43,9 @@ class RegisterUsernamePresenter @Inject constructor(
if (NetworkHelper.hasInternetAccess()) { if (NetworkHelper.hasInternetAccess()) {
view.showLoading() view.showLoading()
try { try {
val me = client.updateOwnBasicInformation(username = username) val me = retryIO("updateOwnBasicInformation(username = $username)") {
client.updateOwnBasicInformation(username = username)
}
val registeredUsername = me.username val registeredUsername = me.username
if (registeredUsername != null) { if (registeredUsername != null) {
saveAccount(registeredUsername) saveAccount(registeredUsername)
...@@ -75,12 +79,12 @@ class RegisterUsernamePresenter @Inject constructor( ...@@ -75,12 +79,12 @@ class RegisterUsernamePresenter @Inject constructor(
private suspend fun saveAccount(username: String) { private suspend fun saveAccount(username: String) {
val icon = settings.favicon()?.let { val icon = settings.favicon()?.let {
UrlHelper.getServerLogoUrl(currentServer, it) currentServer.serverLogoUrl(it)
} }
val logo = settings.wideTile()?.let { val logo = settings.wideTile()?.let {
UrlHelper.getServerLogoUrl(currentServer, it) currentServer.serverLogoUrl(it)
} }
val thumb = UrlHelper.getAvatarUrl(currentServer, username) val thumb = currentServer.avatarUrl(username)
val account = Account(currentServer, icon, logo, username, thumb) val account = Account(currentServer, icon, logo, username, thumb)
saveAccountInteractor.save(account) saveAccountInteractor.save(account)
} }
......
...@@ -3,10 +3,10 @@ package chat.rocket.android.authentication.server.presentation ...@@ -3,10 +3,10 @@ package chat.rocket.android.authentication.server.presentation
import chat.rocket.android.authentication.presentation.AuthenticationNavigator import chat.rocket.android.authentication.presentation.AuthenticationNavigator
import chat.rocket.android.core.lifecycle.CancelStrategy import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.helper.NetworkHelper import chat.rocket.android.helper.NetworkHelper
import chat.rocket.android.helper.UrlHelper
import chat.rocket.android.server.domain.GetAccountsInteractor import chat.rocket.android.server.domain.GetAccountsInteractor
import chat.rocket.android.server.domain.RefreshSettingsInteractor import chat.rocket.android.server.domain.RefreshSettingsInteractor
import chat.rocket.android.server.domain.SaveCurrentServerInteractor import chat.rocket.android.server.domain.SaveCurrentServerInteractor
import chat.rocket.android.util.extensions.isValidUrl
import chat.rocket.android.util.extensions.launchUI import chat.rocket.android.util.extensions.launchUI
import chat.rocket.common.util.ifNull import chat.rocket.common.util.ifNull
import javax.inject.Inject import javax.inject.Inject
...@@ -18,7 +18,7 @@ class ServerPresenter @Inject constructor(private val view: ServerView, ...@@ -18,7 +18,7 @@ class ServerPresenter @Inject constructor(private val view: ServerView,
private val refreshSettingsInteractor: RefreshSettingsInteractor, private val refreshSettingsInteractor: RefreshSettingsInteractor,
private val getAccountsInteractor: GetAccountsInteractor) { private val getAccountsInteractor: GetAccountsInteractor) {
fun connect(server: String) { fun connect(server: String) {
if (!UrlHelper.isValidUrl(server)) { if (!server.isValidUrl()) {
view.showInvalidServerUrlMessage() view.showInvalidServerUrlMessage()
} else { } else {
launchUI(strategy) { launchUI(strategy) {
......
...@@ -3,20 +3,22 @@ package chat.rocket.android.authentication.signup.presentation ...@@ -3,20 +3,22 @@ package chat.rocket.android.authentication.signup.presentation
import chat.rocket.android.authentication.presentation.AuthenticationNavigator import chat.rocket.android.authentication.presentation.AuthenticationNavigator
import chat.rocket.android.core.lifecycle.CancelStrategy import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.helper.NetworkHelper import chat.rocket.android.helper.NetworkHelper
import chat.rocket.android.helper.UrlHelper
import chat.rocket.android.infrastructure.LocalRepository import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.main.viewmodel.NavHeaderViewModel
import chat.rocket.android.server.domain.* import chat.rocket.android.server.domain.*
import chat.rocket.android.server.domain.model.Account import chat.rocket.android.server.domain.model.Account
import chat.rocket.android.server.infraestructure.RocketChatClientFactory import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.extensions.avatarUrl
import chat.rocket.android.util.extensions.launchUI import chat.rocket.android.util.extensions.launchUI
import chat.rocket.android.util.extensions.privacyPolicyUrl
import chat.rocket.android.util.extensions.registerPushToken import chat.rocket.android.util.extensions.registerPushToken
import chat.rocket.android.util.extensions.serverLogoUrl
import chat.rocket.android.util.extensions.termsOfServiceUrl
import chat.rocket.android.util.retryIO
import chat.rocket.common.RocketChatException import chat.rocket.common.RocketChatException
import chat.rocket.common.util.ifNull import chat.rocket.common.util.ifNull
import chat.rocket.core.RocketChatClient import chat.rocket.core.RocketChatClient
import chat.rocket.core.internal.rest.login import chat.rocket.core.internal.rest.login
import chat.rocket.core.internal.rest.me import chat.rocket.core.internal.rest.me
import chat.rocket.core.internal.rest.registerPushToken
import chat.rocket.core.internal.rest.signup import chat.rocket.core.internal.rest.signup
import chat.rocket.core.model.Myself import chat.rocket.core.model.Myself
import javax.inject.Inject import javax.inject.Inject
...@@ -60,10 +62,10 @@ class SignupPresenter @Inject constructor(private val view: SignupView, ...@@ -60,10 +62,10 @@ class SignupPresenter @Inject constructor(private val view: SignupView,
try { try {
// TODO This function returns a user so should we save it? // TODO This function returns a user so should we save it?
client.signup(email, name, username, password) retryIO("signup") { client.signup(email, name, username, password) }
// TODO This function returns a user token so should we save it? // TODO This function returns a user token so should we save it?
client.login(username, password) retryIO("login") { client.login(username, password) }
val me = client.me() val me = retryIO("me") { client.me() }
localRepository.save(LocalRepository.CURRENT_USERNAME_KEY, me.username) localRepository.save(LocalRepository.CURRENT_USERNAME_KEY, me.username)
saveAccount(me) saveAccount(me)
registerPushToken() registerPushToken()
...@@ -88,13 +90,13 @@ class SignupPresenter @Inject constructor(private val view: SignupView, ...@@ -88,13 +90,13 @@ class SignupPresenter @Inject constructor(private val view: SignupView,
fun termsOfService() { fun termsOfService() {
serverInteractor.get()?.let { serverInteractor.get()?.let {
navigator.toWebPage(UrlHelper.getTermsOfServiceUrl(it)) navigator.toWebPage(it.termsOfServiceUrl())
} }
} }
fun privacyPolicy() { fun privacyPolicy() {
serverInteractor.get()?.let { serverInteractor.get()?.let {
navigator.toWebPage(UrlHelper.getPrivacyPolicyUrl(it)) navigator.toWebPage(it.privacyPolicyUrl())
} }
} }
...@@ -108,12 +110,12 @@ class SignupPresenter @Inject constructor(private val view: SignupView, ...@@ -108,12 +110,12 @@ class SignupPresenter @Inject constructor(private val view: SignupView,
private suspend fun saveAccount(me: Myself) { private suspend fun saveAccount(me: Myself) {
val icon = settings.favicon()?.let { val icon = settings.favicon()?.let {
UrlHelper.getServerLogoUrl(currentServer, it) currentServer.serverLogoUrl(it)
} }
val logo = settings.wideTile()?.let { val logo = settings.wideTile()?.let {
UrlHelper.getServerLogoUrl(currentServer, it) currentServer.serverLogoUrl(it)
} }
val thumb = UrlHelper.getAvatarUrl(currentServer, me.username!!) val thumb = currentServer.avatarUrl(me.username!!)
val account = Account(currentServer, icon, logo, me.username!!, thumb) val account = Account(currentServer, icon, logo, me.username!!, thumb)
saveAccountInteractor.save(account) saveAccountInteractor.save(account)
} }
......
...@@ -3,13 +3,15 @@ package chat.rocket.android.authentication.twofactor.presentation ...@@ -3,13 +3,15 @@ package chat.rocket.android.authentication.twofactor.presentation
import chat.rocket.android.authentication.presentation.AuthenticationNavigator import chat.rocket.android.authentication.presentation.AuthenticationNavigator
import chat.rocket.android.core.lifecycle.CancelStrategy import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.helper.NetworkHelper import chat.rocket.android.helper.NetworkHelper
import chat.rocket.android.helper.UrlHelper
import chat.rocket.android.infrastructure.LocalRepository import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.server.domain.* import chat.rocket.android.server.domain.*
import chat.rocket.android.server.domain.model.Account import chat.rocket.android.server.domain.model.Account
import chat.rocket.android.server.infraestructure.RocketChatClientFactory import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.extensions.avatarUrl
import chat.rocket.android.util.extensions.launchUI import chat.rocket.android.util.extensions.launchUI
import chat.rocket.android.util.extensions.registerPushToken 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 import chat.rocket.common.RocketChatAuthException
import chat.rocket.common.RocketChatException import chat.rocket.common.RocketChatException
import chat.rocket.common.util.ifNull import chat.rocket.common.util.ifNull
...@@ -50,9 +52,10 @@ class TwoFAPresenter @Inject constructor(private val view: TwoFAView, ...@@ -50,9 +52,10 @@ class TwoFAPresenter @Inject constructor(private val view: TwoFAView,
view.showLoading() view.showLoading()
try { try {
// The token is saved via the client TokenProvider // The token is saved via the client TokenProvider
val token = val token = retryIO("login") {
client.login(usernameOrEmail, password, twoFactorAuthenticationCode) client.login(usernameOrEmail, password, twoFactorAuthenticationCode)
val me = client.me() }
val me = retryIO("me") { client.me() }
saveAccount(me) saveAccount(me)
tokenRepository.save(server, token) tokenRepository.save(server, token)
registerPushToken() registerPushToken()
...@@ -90,12 +93,12 @@ class TwoFAPresenter @Inject constructor(private val view: TwoFAView, ...@@ -90,12 +93,12 @@ class TwoFAPresenter @Inject constructor(private val view: TwoFAView,
private suspend fun saveAccount(me: Myself) { private suspend fun saveAccount(me: Myself) {
val icon = settings.favicon()?.let { val icon = settings.favicon()?.let {
UrlHelper.getServerLogoUrl(currentServer, it) currentServer.serverLogoUrl(it)
} }
val logo = settings.wideTile()?.let { val logo = settings.wideTile()?.let {
UrlHelper.getServerLogoUrl(currentServer, it) currentServer.serverLogoUrl(it)
} }
val thumb = UrlHelper.getAvatarUrl(currentServer, me.username!!) val thumb = currentServer.avatarUrl(me.username!!)
val account = Account(currentServer, icon, logo, me.username!!, thumb) val account = Account(currentServer, icon, logo, me.username!!, thumb)
saveAccountInteractor.save(account) saveAccountInteractor.save(account)
} }
......
...@@ -37,6 +37,7 @@ import kotlinx.android.synthetic.main.message_composer.* ...@@ -37,6 +37,7 @@ import kotlinx.android.synthetic.main.message_composer.*
import kotlinx.android.synthetic.main.message_list.* import kotlinx.android.synthetic.main.message_list.*
import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.atomic.AtomicInteger
import javax.inject.Inject import javax.inject.Inject
import kotlin.math.absoluteValue
fun newInstance(chatRoomId: String, fun newInstance(chatRoomId: String,
chatRoomName: String, chatRoomName: String,
...@@ -132,6 +133,10 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -132,6 +133,10 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
} }
override fun onDestroyView() { override fun onDestroyView() {
recycler_view.removeOnScrollListener(endlessRecyclerViewScrollListener)
recycler_view.removeOnScrollListener(onScrollListener)
recycler_view.removeOnLayoutChangeListener(layoutChangeListener)
presenter.unsubscribeMessages(chatRoomId) presenter.unsubscribeMessages(chatRoomId)
handler.removeCallbacksAndMessages(null) handler.removeCallbacksAndMessages(null)
unsubscribeTextMessage() unsubscribeTextMessage()
...@@ -205,53 +210,16 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -205,53 +210,16 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
recycler_view.layoutManager = linearLayoutManager recycler_view.layoutManager = linearLayoutManager
recycler_view.itemAnimator = DefaultItemAnimator() recycler_view.itemAnimator = DefaultItemAnimator()
if (dataSet.size >= 30) { if (dataSet.size >= 30) {
recycler_view.addOnScrollListener(object : EndlessRecyclerViewScrollListener(linearLayoutManager) { endlessRecyclerViewScrollListener = object :
EndlessRecyclerViewScrollListener(recycler_view.layoutManager as LinearLayoutManager) {
override fun onLoadMore(page: Int, totalItemsCount: Int, recyclerView: RecyclerView?) { override fun onLoadMore(page: Int, totalItemsCount: Int, recyclerView: RecyclerView?) {
presenter.loadMessages(chatRoomId, chatRoomType, page * 30L) presenter.loadMessages(chatRoomId, chatRoomType, page * 30L)
} }
})
}
recycler_view.addOnLayoutChangeListener { _, _, _, _, bottom, _, _, _, oldBottom ->
val y = oldBottom - bottom
if (Math.abs(y) > 0) {
// if y is positive the keyboard is up else it's down
recycler_view.post {
if (y > 0 || Math.abs(verticalScrollOffset.get()) >= Math.abs(y)) {
recycler_view.scrollBy(0, y)
} else {
recycler_view.scrollBy(0, verticalScrollOffset.get())
}
}
} }
recycler_view.addOnScrollListener(endlessRecyclerViewScrollListener)
} }
recycler_view.addOnLayoutChangeListener(layoutChangeListener)
recycler_view.addOnScrollListener(object : RecyclerView.OnScrollListener() { recycler_view.addOnScrollListener(onScrollListener)
var state = AtomicInteger(RecyclerView.SCROLL_STATE_IDLE)
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
state.compareAndSet(RecyclerView.SCROLL_STATE_IDLE, newState)
when (newState) {
RecyclerView.SCROLL_STATE_IDLE -> {
if (!state.compareAndSet(RecyclerView.SCROLL_STATE_SETTLING, newState)) {
state.compareAndSet(RecyclerView.SCROLL_STATE_DRAGGING, newState)
}
}
RecyclerView.SCROLL_STATE_DRAGGING -> {
state.compareAndSet(RecyclerView.SCROLL_STATE_IDLE, newState)
}
RecyclerView.SCROLL_STATE_SETTLING -> {
state.compareAndSet(RecyclerView.SCROLL_STATE_DRAGGING, newState)
}
}
}
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
if (state.get() != RecyclerView.SCROLL_STATE_IDLE) {
verticalScrollOffset.getAndAdd(dy)
}
}
})
} }
val oldMessagesCount = adapter.itemCount val oldMessagesCount = adapter.itemCount
...@@ -264,6 +232,49 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -264,6 +232,49 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
} }
} }
private val layoutChangeListener = View.OnLayoutChangeListener { _, _, _, _, bottom, _, _, _, oldBottom ->
val y = oldBottom - bottom
if (y.absoluteValue > 0 && isAdded) {
// if y is positive the keyboard is up else it's down
recycler_view.post {
if (y > 0 || verticalScrollOffset.get().absoluteValue >= y.absoluteValue) {
recycler_view.scrollBy(0, y)
} else {
recycler_view.scrollBy(0, verticalScrollOffset.get())
}
}
}
}
private lateinit var endlessRecyclerViewScrollListener: EndlessRecyclerViewScrollListener
private val onScrollListener = object : RecyclerView.OnScrollListener() {
var state = AtomicInteger(RecyclerView.SCROLL_STATE_IDLE)
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
state.compareAndSet(RecyclerView.SCROLL_STATE_IDLE, newState)
when (newState) {
RecyclerView.SCROLL_STATE_IDLE -> {
if (!state.compareAndSet(RecyclerView.SCROLL_STATE_SETTLING, newState)) {
state.compareAndSet(RecyclerView.SCROLL_STATE_DRAGGING, newState)
}
}
RecyclerView.SCROLL_STATE_DRAGGING -> {
state.compareAndSet(RecyclerView.SCROLL_STATE_IDLE, newState)
}
RecyclerView.SCROLL_STATE_SETTLING -> {
state.compareAndSet(RecyclerView.SCROLL_STATE_DRAGGING, newState)
}
}
}
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
if (state.get() != RecyclerView.SCROLL_STATE_IDLE) {
verticalScrollOffset.getAndAdd(dy)
}
}
}
override fun sendMessage(text: String) { override fun sendMessage(text: String) {
if (!text.isBlank()) { if (!text.isBlank()) {
if (!text.startsWith("/")) { if (!text.startsWith("/")) {
......
...@@ -9,9 +9,9 @@ import android.text.style.ForegroundColorSpan ...@@ -9,9 +9,9 @@ import android.text.style.ForegroundColorSpan
import android.text.style.StyleSpan import android.text.style.StyleSpan
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.helper.MessageParser import chat.rocket.android.helper.MessageParser
import chat.rocket.android.helper.UrlHelper
import chat.rocket.android.infrastructure.LocalRepository import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.server.domain.* import chat.rocket.android.server.domain.*
import chat.rocket.android.util.extensions.avatarUrl
import chat.rocket.android.widget.emoji.EmojiParser import chat.rocket.android.widget.emoji.EmojiParser
import chat.rocket.core.model.Message import chat.rocket.core.model.Message
import chat.rocket.core.model.MessageType import chat.rocket.core.model.MessageType
...@@ -235,7 +235,7 @@ class ViewModelMapper @Inject constructor(private val context: Context, ...@@ -235,7 +235,7 @@ class ViewModelMapper @Inject constructor(private val context: Context,
val username = message.sender?.username ?: "?" val username = message.sender?.username ?: "?"
return baseUrl?.let { return baseUrl?.let {
UrlHelper.getAvatarUrl(baseUrl, username) baseUrl.avatarUrl(username)
} }
} }
......
...@@ -12,6 +12,7 @@ import chat.rocket.android.server.infraestructure.ConnectionManagerFactory ...@@ -12,6 +12,7 @@ import chat.rocket.android.server.infraestructure.ConnectionManagerFactory
import chat.rocket.android.server.infraestructure.chatRooms import chat.rocket.android.server.infraestructure.chatRooms
import chat.rocket.android.server.infraestructure.state import chat.rocket.android.server.infraestructure.state
import chat.rocket.android.util.extensions.launchUI import chat.rocket.android.util.extensions.launchUI
import chat.rocket.android.util.retryIO
import chat.rocket.common.RocketChatException import chat.rocket.common.RocketChatException
import chat.rocket.common.model.BaseRoom import chat.rocket.common.model.BaseRoom
import chat.rocket.common.model.RoomType import chat.rocket.common.model.RoomType
...@@ -94,7 +95,9 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView, ...@@ -94,7 +95,9 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
try { try {
val roomList = getChatRoomsInteractor.getByName(currentServer, name) val roomList = getChatRoomsInteractor.getByName(currentServer, name)
if (roomList.isEmpty()) { if (roomList.isEmpty()) {
val (users, rooms) = client.spotlight(name) val (users, rooms) = retryIO("spotlight($name)") {
client.spotlight(name)
}
val chatRoomsCombined = mutableListOf<ChatRoom>() val chatRoomsCombined = mutableListOf<ChatRoom>()
chatRoomsCombined.addAll(usersToChatRooms(users)) chatRoomsCombined.addAll(usersToChatRooms(users))
chatRoomsCombined.addAll(roomsToChatRooms(rooms)) chatRoomsCombined.addAll(roomsToChatRooms(rooms))
...@@ -161,7 +164,7 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView, ...@@ -161,7 +164,7 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
} }
private suspend fun loadRooms(): List<ChatRoom> { private suspend fun loadRooms(): List<ChatRoom> {
val chatRooms = manager.chatRooms().update val chatRooms = retryIO("chatRooms") { manager.chatRooms().update }
val sortedRooms = sortRooms(chatRooms) val sortedRooms = sortRooms(chatRooms)
Timber.d("Loaded rooms: ${sortedRooms.size}") Timber.d("Loaded rooms: ${sortedRooms.size}")
saveChatRoomsInteractor.save(currentServer, sortedRooms) saveChatRoomsInteractor.save(currentServer, sortedRooms)
......
...@@ -12,11 +12,11 @@ import android.view.View ...@@ -12,11 +12,11 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.TextView import android.widget.TextView
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.helper.UrlHelper
import chat.rocket.android.infrastructure.LocalRepository import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.infrastructure.checkIfMyself import chat.rocket.android.infrastructure.checkIfMyself
import chat.rocket.android.server.domain.PublicSettings import chat.rocket.android.server.domain.PublicSettings
import chat.rocket.android.server.domain.useRealName import chat.rocket.android.server.domain.useRealName
import chat.rocket.android.util.extensions.avatarUrl
import chat.rocket.android.util.extensions.content import chat.rocket.android.util.extensions.content
import chat.rocket.android.util.extensions.inflate import chat.rocket.android.util.extensions.inflate
import chat.rocket.android.util.extensions.setVisible import chat.rocket.android.util.extensions.setVisible
...@@ -75,7 +75,7 @@ class ChatRoomsAdapter(private val context: Context, ...@@ -75,7 +75,7 @@ class ChatRoomsAdapter(private val context: Context,
private fun bindAvatar(chatRoom: ChatRoom, drawee: SimpleDraweeView) { private fun bindAvatar(chatRoom: ChatRoom, drawee: SimpleDraweeView) {
val avatarId = if (chatRoom.type is RoomType.DirectMessage) chatRoom.name else "@${chatRoom.name}" val avatarId = if (chatRoom.type is RoomType.DirectMessage) chatRoom.name else "@${chatRoom.name}"
drawee.setImageURI(UrlHelper.getAvatarUrl(chatRoom.client.url, avatarId)) drawee.setImageURI(chatRoom.client.url.avatarUrl(avatarId))
} }
private fun bindName(chatRoom: ChatRoom, textView: TextView) { private fun bindName(chatRoom: ChatRoom, textView: TextView) {
......
...@@ -158,15 +158,16 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView { ...@@ -158,15 +158,16 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
listJob?.cancel() listJob?.cancel()
listJob = launch(UI) { listJob = launch(UI) {
val adapter = recycler_view.adapter as SimpleSectionedRecyclerViewAdapter val adapter = recycler_view.adapter as SimpleSectionedRecyclerViewAdapter
// FIXME https://fabric.io/rocketchat3/android/apps/chat.rocket.android.dev/issues/5a90d4718cb3c2fa63b3f557?time=last-seven-days // FIXME https://fabric.io/rocketchat3/android/apps/chat.rocket.android/issues/5ac2916c36c7b235275ccccf
// TODO - fix this bug to reenable DiffUtil // TODO - fix this bug to re-enable DiffUtil
val diff = async(CommonPool) { /*val diff = async(CommonPool) {
DiffUtil.calculateDiff(RoomsDiffCallback(adapter.baseAdapter.dataSet, newDataSet)) DiffUtil.calculateDiff(RoomsDiffCallback(adapter.baseAdapter.dataSet, newDataSet))
}.await() }.await()*/
if (isActive) { if (isActive) {
adapter.baseAdapter.updateRooms(newDataSet) adapter.baseAdapter.updateRooms(newDataSet)
diff.dispatchUpdatesTo(adapter) // TODO - fix crash to re-enable diff.dispatchUpdatesTo(adapter)
adapter.notifyDataSetChanged()
//Set sections always after data set is updated //Set sections always after data set is updated
setSections() setSections()
......
package chat.rocket.android.helper package chat.rocket.android.helper
import chat.rocket.android.util.extensions.removeTrailingSlash
object OauthHelper { object OauthHelper {
/** /**
...@@ -27,7 +29,7 @@ object OauthHelper { ...@@ -27,7 +29,7 @@ object OauthHelper {
fun getGoogleOauthUrl(clientId: String, serverUrl: String, state: String): String { fun getGoogleOauthUrl(clientId: String, serverUrl: String, state: String): String {
return "https://accounts.google.com/o/oauth2/v2/auth" + return "https://accounts.google.com/o/oauth2/v2/auth" +
"?client_id=$clientId" + "?client_id=$clientId" +
"&redirect_uri=${UrlHelper.removeTrailingSlash(serverUrl)}/_oauth/google?close" + "&redirect_uri=${serverUrl.removeTrailingSlash()}/_oauth/google?close" +
"&state=$state" + "&state=$state" +
"&response_type=code" + "&response_type=code" +
"&scope=email%20profile" "&scope=email%20profile"
...@@ -44,7 +46,7 @@ object OauthHelper { ...@@ -44,7 +46,7 @@ object OauthHelper {
fun getLinkedinOauthUrl(clientId: String, serverUrl: String, state: String): String { fun getLinkedinOauthUrl(clientId: String, serverUrl: String, state: String): String {
return "https://linkedin.com/oauth/v2/authorization" + return "https://linkedin.com/oauth/v2/authorization" +
"?client_id=$clientId" + "?client_id=$clientId" +
"&redirect_uri=${UrlHelper.removeTrailingSlash(serverUrl)}/_oauth/linkedin?close" + "&redirect_uri=${serverUrl.removeTrailingSlash()}/_oauth/linkedin?close" +
"&state=$state" + "&state=$state" +
"&response_type=code" "&response_type=code"
} }
...@@ -60,7 +62,7 @@ object OauthHelper { ...@@ -60,7 +62,7 @@ object OauthHelper {
fun getGitlabOauthUrl(clientId: String, serverUrl: String, state: String): String { fun getGitlabOauthUrl(clientId: String, serverUrl: String, state: String): String {
return "https://gitlab.com/oauth/authorize" + return "https://gitlab.com/oauth/authorize" +
"?client_id=$clientId" + "?client_id=$clientId" +
"&redirect_uri=${UrlHelper.removeTrailingSlash(serverUrl)}/_oauth/gitlab?close" + "&redirect_uri=${serverUrl.removeTrailingSlash()}/_oauth/gitlab?close" +
"&state=$state" + "&state=$state" +
"&response_type=code" + "&response_type=code" +
"&scope=read_user" "&scope=read_user"
......
package chat.rocket.android.helper
import android.util.Patterns
object UrlHelper {
/**
* Returns the avatar URL.
*
* @param serverUrl The server URL.
* @param avatarName The avatar name.
* @return The avatar URL.
*/
fun getAvatarUrl(serverUrl: String, avatarName: String, format: String = "jpeg"): String =
removeTrailingSlash(serverUrl) + "/avatar/" + removeTrailingSlash(avatarName) + "?format=$format"
/**
* Returns the server logo URL.
*
* @param serverUrl The server URL.
* @param favicon The faviconLarge from the server settings.
* @return The server logo URL.
*/
fun getServerLogoUrl(serverUrl: String, favicon: String): String =
removeTrailingSlash(serverUrl) + "/$favicon"
/**
* Returns the CAS URL.
*
* @param casLoginUrl The CAS login URL from the server settings.
* @param serverUrl The server URL.
* @param token The token to be send to the CAS server.
* @return The avatar URL.
*/
fun getCasUrl(casLoginUrl: String, serverUrl: String, token: String): String =
removeTrailingSlash(casLoginUrl) + "?service=" + removeTrailingSlash(serverUrl) + "/_cas/" + token
/**
* Returns the server's Terms of Service URL.
*
* @param serverUrl The server URL.
* @return The server's Terms of Service URL.
*/
fun getTermsOfServiceUrl(serverUrl: String) = removeTrailingSlash(serverUrl) + "/terms-of-service"
/**
* Returns the server's Privacy Policy URL.
*
* @param serverUrl The server URL.
* @return The server's Privacy Policy URL.
*/
fun getPrivacyPolicyUrl(serverUrl: String) = removeTrailingSlash(serverUrl) + "/privacy-policy"
/**
* Returns an URL without trailing slash.
*
* @param serverUrl The URL to remove the trailing slash (if exists).
* @return An URL without trailing slash.
*/
fun removeTrailingSlash(serverUrl: String): String {
return if (serverUrl[serverUrl.length - 1] == '/') {
serverUrl.replace("/+$", "")
} else {
serverUrl
}
}
/**
* Checks if the given URL is valid or not.
* @param url The url to check its valid.
* @return True if url is valid, false otherwise.
*/
fun isValidUrl(url: String): Boolean = Patterns.WEB_URL.matcher(url).matches()
}
\ No newline at end of file
package chat.rocket.android.main.presentation package chat.rocket.android.main.presentation
import chat.rocket.android.core.lifecycle.CancelStrategy import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.helper.UrlHelper
import chat.rocket.android.infrastructure.LocalRepository import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.main.viewmodel.NavHeaderViewModel import chat.rocket.android.main.viewmodel.NavHeaderViewModel
import chat.rocket.android.main.viewmodel.NavHeaderViewModelMapper import chat.rocket.android.main.viewmodel.NavHeaderViewModelMapper
...@@ -12,6 +11,8 @@ import chat.rocket.android.server.infraestructure.RocketChatClientFactory ...@@ -12,6 +11,8 @@ import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.server.presentation.CheckServerPresenter import chat.rocket.android.server.presentation.CheckServerPresenter
import chat.rocket.android.util.extensions.launchUI import chat.rocket.android.util.extensions.launchUI
import chat.rocket.android.util.extensions.registerPushToken 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 import chat.rocket.common.RocketChatAuthException
import chat.rocket.common.RocketChatException import chat.rocket.common.RocketChatException
import chat.rocket.common.util.ifNull import chat.rocket.common.util.ifNull
...@@ -54,7 +55,8 @@ class MainPresenter @Inject constructor( ...@@ -54,7 +55,8 @@ class MainPresenter @Inject constructor(
checkServerInfo() checkServerInfo()
launchUI(strategy) { launchUI(strategy) {
try { try {
val model = navHeaderMapper.mapToViewModel(client.me()) val me = retryIO("me") { client.me() }
val model = navHeaderMapper.mapToViewModel(me)
saveAccount(model) saveAccount(model)
view.setupNavHeader(model, getAccountsInteractor.get()) view.setupNavHeader(model, getAccountsInteractor.get())
} catch (ex: Exception) { } catch (ex: Exception) {
...@@ -77,7 +79,7 @@ class MainPresenter @Inject constructor( ...@@ -77,7 +79,7 @@ class MainPresenter @Inject constructor(
private suspend fun saveAccount(viewModel: NavHeaderViewModel) { private suspend fun saveAccount(viewModel: NavHeaderViewModel) {
val icon = settings.favicon()?.let { val icon = settings.favicon()?.let {
UrlHelper.getServerLogoUrl(currentServer, it) currentServer.serverLogoUrl(it)
} }
val account = Account( val account = Account(
currentServer, currentServer,
...@@ -96,7 +98,7 @@ class MainPresenter @Inject constructor( ...@@ -96,7 +98,7 @@ class MainPresenter @Inject constructor(
launchUI(strategy) { launchUI(strategy) {
try { try {
clearTokens() clearTokens()
client.logout() retryIO("logout") { client.logout() }
} catch (exception: RocketChatException) { } catch (exception: RocketChatException) {
Timber.d(exception, "Error calling logout") Timber.d(exception, "Error calling logout")
exception.message?.let { exception.message?.let {
...@@ -123,7 +125,11 @@ class MainPresenter @Inject constructor( ...@@ -123,7 +125,11 @@ class MainPresenter @Inject constructor(
serverInteractor.clear() serverInteractor.clear()
val pushToken = localRepository.get(LocalRepository.KEY_PUSH_TOKEN) val pushToken = localRepository.get(LocalRepository.KEY_PUSH_TOKEN)
if (pushToken != null) { if (pushToken != null) {
client.unregisterPushToken(pushToken) try {
retryIO("unregisterPushToken") { client.unregisterPushToken(pushToken) }
} catch (ex: Exception) {
Timber.d(ex, "Error unregistering push token")
}
} }
localRepository.clearAllFromServer(currentServer) localRepository.clearAllFromServer(currentServer)
} }
......
package chat.rocket.android.main.viewmodel package chat.rocket.android.main.viewmodel
import chat.rocket.android.helper.UrlHelper
import chat.rocket.android.server.domain.* import chat.rocket.android.server.domain.*
import chat.rocket.android.util.extensions.avatarUrl
import chat.rocket.android.util.extensions.serverLogoUrl
import chat.rocket.core.model.Myself import chat.rocket.core.model.Myself
import javax.inject.Inject import javax.inject.Inject
...@@ -15,9 +16,9 @@ class NavHeaderViewModelMapper @Inject constructor( ...@@ -15,9 +16,9 @@ class NavHeaderViewModelMapper @Inject constructor(
fun mapToViewModel(me: Myself): NavHeaderViewModel { fun mapToViewModel(me: Myself): NavHeaderViewModel {
val displayName = mapDisplayName(me) val displayName = mapDisplayName(me)
val status = me.status val status = me.status
val avatar = me.username?.let { UrlHelper.getAvatarUrl(currentServer, it) } val avatar = me.username?.let { currentServer.avatarUrl(it) }
val image = settings.wideTile() ?: settings.faviconLarge() val image = settings.wideTile() ?: settings.faviconLarge()
val logo = image?.let { UrlHelper.getServerLogoUrl(currentServer, it) } val logo = image?.let { currentServer.serverLogoUrl(it) }
return NavHeaderViewModel(displayName, status, avatar, currentServer, logo) return NavHeaderViewModel(displayName, status, avatar, currentServer, logo)
} }
......
...@@ -6,6 +6,7 @@ import chat.rocket.android.members.viewmodel.MemberViewModelMapper ...@@ -6,6 +6,7 @@ import chat.rocket.android.members.viewmodel.MemberViewModelMapper
import chat.rocket.android.server.domain.GetCurrentServerInteractor import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.infraestructure.RocketChatClientFactory import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.extensions.launchUI import chat.rocket.android.util.extensions.launchUI
import chat.rocket.android.util.retryIO
import chat.rocket.common.RocketChatException import chat.rocket.common.RocketChatException
import chat.rocket.common.model.roomTypeOf import chat.rocket.common.model.roomTypeOf
import chat.rocket.common.util.ifNull import chat.rocket.common.util.ifNull
...@@ -26,7 +27,9 @@ class MembersPresenter @Inject constructor(private val view: MembersView, ...@@ -26,7 +27,9 @@ class MembersPresenter @Inject constructor(private val view: MembersView,
try { try {
view.showLoading() view.showLoading()
val members = client.getMembers(chatRoomId, roomTypeOf(chatRoomType), offset, 60) val members = retryIO("getMembers($chatRoomId, $chatRoomType, $offset)") {
client.getMembers(chatRoomId, roomTypeOf(chatRoomType), offset, 60)
}
val memberViewModels = mapper.mapToViewModelList(members.result) val memberViewModels = mapper.mapToViewModelList(members.result)
view.showMembers(memberViewModels, members.total) view.showMembers(memberViewModels, members.total)
} catch (ex: RocketChatException) { } catch (ex: RocketChatException) {
......
package chat.rocket.android.members.viewmodel package chat.rocket.android.members.viewmodel
import chat.rocket.android.helper.UrlHelper
import chat.rocket.android.server.domain.useRealName import chat.rocket.android.server.domain.useRealName
import chat.rocket.android.util.extensions.avatarUrl
import chat.rocket.common.model.User import chat.rocket.common.model.User
import chat.rocket.core.model.Value import chat.rocket.core.model.Value
...@@ -25,7 +25,7 @@ class MemberViewModel(private val member: User, private val settings: Map<String ...@@ -25,7 +25,7 @@ class MemberViewModel(private val member: User, private val settings: Map<String
private fun getUserAvatar(): String? { private fun getUserAvatar(): String? {
val username = member.username ?: "?" val username = member.username ?: "?"
return baseUrl?.let { return baseUrl?.let {
UrlHelper.getAvatarUrl(baseUrl, username, "png") baseUrl.avatarUrl(username,"png")
} }
} }
......
package chat.rocket.android.profile.presentation package chat.rocket.android.profile.presentation
import chat.rocket.android.core.lifecycle.CancelStrategy import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.helper.UrlHelper
import chat.rocket.android.server.domain.GetCurrentServerInteractor import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.infraestructure.RocketChatClientFactory import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.extensions.avatarUrl
import chat.rocket.android.util.extensions.launchUI import chat.rocket.android.util.extensions.launchUI
import chat.rocket.android.util.retryIO
import chat.rocket.common.RocketChatException import chat.rocket.common.RocketChatException
import chat.rocket.common.util.ifNull import chat.rocket.common.util.ifNull
import chat.rocket.core.RocketChatClient import chat.rocket.core.RocketChatClient
...@@ -25,9 +26,9 @@ class ProfilePresenter @Inject constructor(private val view: ProfileView, ...@@ -25,9 +26,9 @@ class ProfilePresenter @Inject constructor(private val view: ProfileView,
launchUI(strategy) { launchUI(strategy) {
view.showLoading() view.showLoading()
try { try {
val myself = client.me() val myself = retryIO("me") { client.me() }
myselfId = myself.id myselfId = myself.id
val avatarUrl = UrlHelper.getAvatarUrl(serverUrl, myself.username!!) val avatarUrl = serverUrl.avatarUrl(myself.username!!)
view.showProfile( view.showProfile(
avatarUrl, avatarUrl,
myself.name ?: "", myself.name ?: "",
...@@ -51,9 +52,9 @@ class ProfilePresenter @Inject constructor(private val view: ProfileView, ...@@ -51,9 +52,9 @@ class ProfilePresenter @Inject constructor(private val view: ProfileView,
view.showLoading() view.showLoading()
try { try {
if(avatarUrl!="") { if(avatarUrl!="") {
client.setAvatar(avatarUrl) retryIO { client.setAvatar(avatarUrl) }
} }
val user = client.updateProfile(myselfId, email, name, username) val user = retryIO { client.updateProfile(myselfId, email, name, username) }
view.showProfileUpdateSuccessfullyMessage() view.showProfileUpdateSuccessfullyMessage()
loadUserProfile() loadUserProfile()
} catch (exception: RocketChatException) { } catch (exception: RocketChatException) {
......
...@@ -5,7 +5,6 @@ import chat.rocket.android.infrastructure.LocalRepository ...@@ -5,7 +5,6 @@ import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.server.domain.GetCurrentServerInteractor import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.infraestructure.RocketChatClientFactory import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.common.RocketChatException import chat.rocket.common.RocketChatException
import chat.rocket.core.RocketChatClient
import chat.rocket.core.internal.rest.registerPushToken import chat.rocket.core.internal.rest.registerPushToken
import com.google.android.gms.gcm.GoogleCloudMessaging import com.google.android.gms.gcm.GoogleCloudMessaging
import com.google.android.gms.iid.InstanceID import com.google.android.gms.iid.InstanceID
......
...@@ -5,6 +5,7 @@ import chat.rocket.android.authentication.server.presentation.VersionCheckView ...@@ -5,6 +5,7 @@ import chat.rocket.android.authentication.server.presentation.VersionCheckView
import chat.rocket.android.core.lifecycle.CancelStrategy import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.util.VersionInfo import chat.rocket.android.util.VersionInfo
import chat.rocket.android.util.extensions.launchUI import chat.rocket.android.util.extensions.launchUI
import chat.rocket.android.util.retryIO
import chat.rocket.core.RocketChatClient import chat.rocket.core.RocketChatClient
import chat.rocket.core.internal.rest.serverInfo import chat.rocket.core.internal.rest.serverInfo
import timber.log.Timber import timber.log.Timber
...@@ -14,21 +15,25 @@ abstract class CheckServerPresenter constructor(private val strategy: CancelStra ...@@ -14,21 +15,25 @@ abstract class CheckServerPresenter constructor(private val strategy: CancelStra
private val view: VersionCheckView) { private val view: VersionCheckView) {
internal fun checkServerInfo() { internal fun checkServerInfo() {
launchUI(strategy) { launchUI(strategy) {
val serverInfo = client.serverInfo() try {
val thisServerVersion = serverInfo.version val serverInfo = retryIO(description = "serverInfo", times = 5) { client.serverInfo() }
val isRequiredVersion = isRequiredServerVersion(thisServerVersion) val thisServerVersion = serverInfo.version
val isRecommendedVersion = isRecommendedServerVersion(thisServerVersion) val isRequiredVersion = isRequiredServerVersion(thisServerVersion)
if (isRequiredVersion) { val isRecommendedVersion = isRecommendedServerVersion(thisServerVersion)
if (isRecommendedVersion) { if (isRequiredVersion) {
Timber.i("Your version is nice! (Requires: 0.62.0, Yours: $thisServerVersion)") if (isRecommendedVersion) {
Timber.i("Your version is nice! (Requires: 0.62.0, Yours: $thisServerVersion)")
} else {
view.alertNotRecommendedVersion()
}
} else { } else {
view.alertNotRecommendedVersion() if (!isRecommendedVersion) {
} view.blockAndAlertNotRequiredVersion()
} else { Timber.i("Oops. Looks like your server is out-of-date! Minimum server version required ${BuildConfig.REQUIRED_SERVER_VERSION}!")
if (!isRecommendedVersion) { }
view.blockAndAlertNotRequiredVersion()
Timber.i("Oops. Looks like your server is out-of-date! Minimum server version required ${BuildConfig.REQUIRED_SERVER_VERSION}!")
} }
} catch (ex: Exception) {
Timber.d(ex, "Error getting server info")
} }
} }
} }
......
...@@ -4,6 +4,7 @@ import chat.rocket.android.core.lifecycle.CancelStrategy ...@@ -4,6 +4,7 @@ import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.server.domain.GetCurrentServerInteractor import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.infraestructure.RocketChatClientFactory import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.extensions.launchUI import chat.rocket.android.util.extensions.launchUI
import chat.rocket.android.util.retryIO
import chat.rocket.common.RocketChatException import chat.rocket.common.RocketChatException
import chat.rocket.core.RocketChatClient import chat.rocket.core.RocketChatClient
import chat.rocket.core.internal.rest.me import chat.rocket.core.internal.rest.me
...@@ -22,7 +23,10 @@ class PasswordPresenter @Inject constructor (private val view: PasswordView, ...@@ -22,7 +23,10 @@ class PasswordPresenter @Inject constructor (private val view: PasswordView,
try { try {
view.showLoading() view.showLoading()
client.updateProfile(client.me().id, null, null, password, null) val me = retryIO("me") { client.me() }
retryIO("updateProfile(${me.id})") {
client.updateProfile(me.id, null, null, password, null)
}
view.showPasswordSuccessfullyUpdatedMessage() view.showPasswordSuccessfullyUpdatedMessage()
view.hideLoading() view.hideLoading()
......
package chat.rocket.android.util
import chat.rocket.common.RocketChatNetworkErrorException
import kotlinx.coroutines.experimental.delay
import timber.log.Timber
const val DEFAULT_RETRY = 3
suspend fun <T> retryIO(
description: String = "<missing description>",
times: Int = DEFAULT_RETRY,
initialDelay: Long = 100, // 0.1 second
maxDelay: Long = 1000, // 1 second
factor: Double = 2.0,
block: suspend () -> T): T
{
var currentDelay = initialDelay
repeat(times - 1) { currentTry ->
try {
return block()
} catch (e: RocketChatNetworkErrorException) {
Timber.d(e, "failed call($currentTry): $description")
e.printStackTrace()
}
delay(currentDelay)
currentDelay = (currentDelay * factor).toLong().coerceAtMost(maxDelay)
}
return block() // last attempt
}
\ No newline at end of file
...@@ -2,6 +2,7 @@ package chat.rocket.android.util.extensions ...@@ -2,6 +2,7 @@ package chat.rocket.android.util.extensions
import chat.rocket.android.server.domain.model.Account import chat.rocket.android.server.domain.model.Account
import chat.rocket.android.server.infraestructure.RocketChatClientFactory import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.retryIO
import chat.rocket.core.RocketChatClient import chat.rocket.core.RocketChatClient
import chat.rocket.core.internal.rest.registerPushToken import chat.rocket.core.internal.rest.registerPushToken
import kotlinx.coroutines.experimental.CommonPool import kotlinx.coroutines.experimental.CommonPool
...@@ -16,7 +17,9 @@ suspend fun RocketChatClient.registerPushToken( ...@@ -16,7 +17,9 @@ suspend fun RocketChatClient.registerPushToken(
launch(CommonPool) { launch(CommonPool) {
accounts.forEach { account -> accounts.forEach { account ->
try { try {
factory.create(account.serverUrl).registerPushToken(token) retryIO(description = "register push token: ${account.serverUrl}") {
factory.create(account.serverUrl).registerPushToken(token)
}
} catch (ex: Exception) { } catch (ex: Exception) {
Timber.d(ex, "Error registering Push token for ${account.serverUrl}") Timber.d(ex, "Error registering Push token for ${account.serverUrl}")
ex.printStackTrace() ex.printStackTrace()
......
package chat.rocket.android.util.extensions
import android.util.Patterns
fun String.removeTrailingSlash(): String {
return if (isNotEmpty() && this[length - 1] == '/') {
this.replace("/+$", "")
} else {
this
}
}
fun String.avatarUrl(avatar: String, format: String = "jpeg") =
"${removeTrailingSlash()}/avatar/${avatar.removeTrailingSlash()}?format=$format"
fun String.serverLogoUrl(favicon: String) = "${removeTrailingSlash()}/$favicon"
fun String.casUrl(serverUrl: String, token: String) =
"${removeTrailingSlash()}?service=${serverUrl.removeTrailingSlash()}/_cas/$token"
fun String.termsOfServiceUrl() = "${removeTrailingSlash()}/terms-of-service"
fun String.privacyPolicyUrl() = "${removeTrailingSlash()}/privacy-policy"
fun String.isValidUrl(): Boolean = Patterns.WEB_URL.matcher(this).matches()
\ No newline at end of file
...@@ -29,14 +29,14 @@ fun Uri.getFileName(context: Context): String? { ...@@ -29,14 +29,14 @@ fun Uri.getFileName(context: Context): String? {
fun Uri.getFileSize(context: Context): Int { fun Uri.getFileSize(context: Context): Int {
val cursor = context.contentResolver.query(this, null, null, null, null, null) val cursor = context.contentResolver.query(this, null, null, null, null, null)
var fileSize: String? = null val fileSize = cursor?.use {
cursor.use { cursor ->
val sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE) val sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE)
if (cursor != null && cursor.moveToFirst()) { if (cursor.moveToFirst()) {
if (!cursor.isNull(sizeIndex)) { if (!cursor.isNull(sizeIndex)) {
fileSize = cursor.getString(sizeIndex) return@use cursor.getString(sizeIndex)
} }
} }
return@use null
} }
return fileSize?.toIntOrNull() ?: -1 return fileSize?.toIntOrNull() ?: -1
} }
......
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