Commit 4505780c authored by Lucio Maciel's avatar Lucio Maciel

Better multi server support, support push notifications

parent d2cc3161
...@@ -3,30 +3,31 @@ package chat.rocket.android.app ...@@ -3,30 +3,31 @@ package chat.rocket.android.app
import android.app.Activity import android.app.Activity
import android.app.Application import android.app.Application
import android.app.Service import android.app.Service
import android.content.SharedPreferences
import androidx.content.edit
import chat.rocket.android.BuildConfig import chat.rocket.android.BuildConfig
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.server.domain.GetCurrentServerInteractor import chat.rocket.android.server.domain.*
import chat.rocket.android.server.domain.MultiServerTokenRepository
import chat.rocket.android.server.domain.SettingsRepository
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.TokenRepository
import com.crashlytics.android.Crashlytics import com.crashlytics.android.Crashlytics
import com.crashlytics.android.core.CrashlyticsCore import com.crashlytics.android.core.CrashlyticsCore
import com.facebook.drawee.backends.pipeline.DraweeConfig import com.facebook.drawee.backends.pipeline.DraweeConfig
import com.facebook.drawee.backends.pipeline.Fresco import com.facebook.drawee.backends.pipeline.Fresco
import com.facebook.imagepipeline.core.ImagePipelineConfig import com.facebook.imagepipeline.core.ImagePipelineConfig
import com.jakewharton.threetenabp.AndroidThreeTen import com.jakewharton.threetenabp.AndroidThreeTen
import dagger.android.AndroidInjector
import dagger.android.DispatchingAndroidInjector
import dagger.android.HasActivityInjector
import dagger.android.HasServiceInjector
import io.fabric.sdk.android.Fabric import io.fabric.sdk.android.Fabric
import kotlinx.coroutines.experimental.runBlocking
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
import android.content.BroadcastReceiver
import dagger.android.*
class RocketChatApplication : Application(), HasActivityInjector, HasServiceInjector {
class RocketChatApplication : Application(), HasActivityInjector, HasServiceInjector,
HasBroadcastReceiverInjector {
@Inject @Inject
lateinit var activityDispatchingAndroidInjector: DispatchingAndroidInjector<Activity> lateinit var activityDispatchingAndroidInjector: DispatchingAndroidInjector<Activity>
...@@ -34,6 +35,9 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje ...@@ -34,6 +35,9 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje
@Inject @Inject
lateinit var serviceDispatchingAndroidInjector: DispatchingAndroidInjector<Service> lateinit var serviceDispatchingAndroidInjector: DispatchingAndroidInjector<Service>
@Inject
lateinit var broadcastReceiverInjector: DispatchingAndroidInjector<BroadcastReceiver>
@Inject @Inject
lateinit var imagePipelineConfig: ImagePipelineConfig lateinit var imagePipelineConfig: ImagePipelineConfig
@Inject @Inject
...@@ -48,14 +52,19 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje ...@@ -48,14 +52,19 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje
lateinit var settingsRepository: SettingsRepository lateinit var settingsRepository: SettingsRepository
@Inject @Inject
lateinit var tokenRepository: TokenRepository lateinit var tokenRepository: TokenRepository
@Inject
lateinit var prefs: SharedPreferences
@Inject
lateinit var getAccountsInteractor: GetAccountsInteractor
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
DaggerAppComponent.builder().application(this).build().inject(this) DaggerAppComponent.builder().application(this).build().inject(this)
// TODO - remove this when we have a proper service handling connection... // TODO - remove this on the future, temporary migration stuff for pre-release versions.
initCurrentServer() prefs.edit { putBoolean(INTERNAL_TOKEN_MIGRATION_NEEDED, true) }
migrateInternalTokens()
AndroidThreeTen.init(this) AndroidThreeTen.init(this)
EmojiRepository.load(this) EmojiRepository.load(this)
...@@ -65,14 +74,27 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje ...@@ -65,14 +74,27 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje
setupTimber() setupTimber()
} }
// TODO - remove this when we have a proper service handling connection... private fun migrateInternalTokens() {
private fun initCurrentServer() { if (!prefs.getBoolean(INTERNAL_TOKEN_MIGRATION_NEEDED, true)) {
val currentServer = getCurrentServerInteractor.get() Timber.d("Tokens already migrated")
val serverToken = currentServer?.let { multiServerRepository.get(currentServer) } return
val settings = currentServer?.let { settingsRepository.get(currentServer) } }
if (currentServer != null && serverToken != null && settings != null) {
tokenRepository.save(Token(serverToken.userId, serverToken.authToken)) getCurrentServerInteractor.get()?.let { serverUrl ->
multiServerRepository.get(serverUrl)?.let { token ->
tokenRepository.save(serverUrl, Token(token.userId, token.authToken))
}
}
runBlocking {
getAccountsInteractor.get().forEach { account ->
multiServerRepository.get(account.serverUrl)?.let { token ->
tokenRepository.save(account.serverUrl, token.toToken())
}
}
} }
prefs.edit { putBoolean(INTERNAL_TOKEN_MIGRATION_NEEDED, false) }
} }
private fun setupCrashlytics() { private fun setupCrashlytics() {
...@@ -99,4 +121,10 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje ...@@ -99,4 +121,10 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje
override fun serviceInjector(): AndroidInjector<Service> { override fun serviceInjector(): AndroidInjector<Service> {
return serviceDispatchingAndroidInjector return serviceDispatchingAndroidInjector
} }
override fun broadcastReceiverInjector(): AndroidInjector<BroadcastReceiver> {
return broadcastReceiverInjector
}
} }
private const val INTERNAL_TOKEN_MIGRATION_NEEDED = "INTERNAL_TOKEN_MIGRATION_NEEDED"
\ No newline at end of file
package chat.rocket.android.authentication.domain.model package chat.rocket.android.authentication.domain.model
import chat.rocket.common.model.Token
import se.ansman.kotshi.JsonSerializable import se.ansman.kotshi.JsonSerializable
@JsonSerializable @JsonSerializable
data class TokenModel(val userId: String, val authToken: String) data class TokenModel(val userId: String, val authToken: String)
fun TokenModel.toToken() = Token(userId, authToken)
\ No newline at end of file
package chat.rocket.android.authentication.infraestructure
import chat.rocket.common.model.Token
import chat.rocket.core.TokenRepository
class MemoryTokenRepository : TokenRepository {
var savedToken: Token? = null
override fun get(): Token? {
return savedToken
}
override fun save(token: Token) {
savedToken = token
}
}
\ No newline at end of file
package chat.rocket.android.authentication.infraestructure
import android.content.SharedPreferences
import androidx.content.edit
import chat.rocket.android.authentication.domain.model.TokenModel
import chat.rocket.android.server.domain.TokenRepository
import chat.rocket.common.model.Token
import com.squareup.moshi.Moshi
import timber.log.Timber
class SharedPreferencesTokenRepository(private val prefs: SharedPreferences, moshi: Moshi) : TokenRepository {
private var servers = prefs.getStringSet(KEY_SERVERS, emptySet()).toMutableSet()
private var currentUrl: String? = null
private var currentToken: Token? = null
private val adapter = moshi.adapter<TokenModel>(TokenModel::class.java)
override fun get(url: String): Token? {
if (currentToken != null && url == currentUrl) {
return currentToken
}
try {
prefs.getString(tokenKey(url), null)?.let { tokenStr ->
val model = adapter.fromJson(tokenStr)
model?.let {
val token = Token(model.userId, model.authToken)
currentToken = token
currentUrl = url
}
}
} catch (ex: Exception) {
Timber.d(ex, "Error parsing token for ${tokenKey(url)}")
ex.printStackTrace()
}
return currentToken
}
override fun save(url: String, token: Token) {
try {
val model = TokenModel(token.userId, token.authToken)
val str = adapter.toJson(model)
servers.add(url)
prefs.edit {
putString(tokenKey(url), str)
putStringSet(KEY_SERVERS, servers)
}
currentToken = token
currentUrl = url
} catch (ex: Exception) {
Timber.d(ex, "Error saving token for ${tokenKey(url)}")
ex.printStackTrace()
}
}
override fun remove(url: String) {
servers.remove(url)
prefs.edit {
remove(url)
putStringSet(KEY_SERVERS, servers)
}
}
override fun clear() {
servers.forEach { server ->
prefs.edit { remove(server) }
}
servers.clear()
prefs.edit {
remove(KEY_SERVERS)
}
}
private fun tokenKey(url: String) = "$KEY_TOKEN$url"
}
private const val KEY_TOKEN = "KEY_TOKEN_"
private const val KEY_SERVERS = "KEY_SERVERS"
\ No newline at end of file
package chat.rocket.android.authentication.login.presentation package chat.rocket.android.authentication.login.presentation
import chat.rocket.android.authentication.domain.model.TokenModel import chat.rocket.android.authentication.domain.model.TokenModel
import chat.rocket.android.authentication.domain.model.toToken
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.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.generateRandomString import chat.rocket.android.util.extensions.generateRandomString
import chat.rocket.android.util.extensions.isEmailValid import chat.rocket.android.util.extensions.isEmailValid
import chat.rocket.android.util.extensions.launchUI import chat.rocket.android.util.extensions.launchUI
import chat.rocket.android.util.extensions.registerPushToken
import chat.rocket.common.RocketChatException import chat.rocket.common.RocketChatException
import chat.rocket.common.RocketChatTwoFactorException import chat.rocket.common.RocketChatTwoFactorException
import chat.rocket.common.util.ifNull import chat.rocket.common.util.ifNull
...@@ -26,12 +27,13 @@ import javax.inject.Inject ...@@ -26,12 +27,13 @@ import javax.inject.Inject
class LoginPresenter @Inject constructor(private val view: LoginView, class LoginPresenter @Inject constructor(private val view: LoginView,
private val strategy: CancelStrategy, private val strategy: CancelStrategy,
private val navigator: AuthenticationNavigator, private val navigator: AuthenticationNavigator,
private val multiServerRepository: MultiServerTokenRepository, private val tokenRepository: TokenRepository,
private val localRepository: LocalRepository, private val localRepository: LocalRepository,
private val getAccountsInteractor: GetAccountsInteractor,
private val settingsInteractor: GetSettingsInteractor, private val settingsInteractor: GetSettingsInteractor,
private val serverInteractor: GetCurrentServerInteractor, private val serverInteractor: GetCurrentServerInteractor,
private val saveAccountInteractor: SaveAccountInteractor, private val saveAccountInteractor: SaveAccountInteractor,
factory: RocketChatClientFactory) { private val factory: RocketChatClientFactory) {
// TODO - we should validate the current server when opening the app, and have a nonnull get() // TODO - we should validate the current server when opening the app, and have a nonnull get()
private val currentServer = serverInteractor.get()!! private val currentServer = serverInteractor.get()!!
private val client: RocketChatClient = factory.create(currentServer) private val client: RocketChatClient = factory.create(currentServer)
...@@ -198,16 +200,17 @@ class LoginPresenter @Inject constructor(private val view: LoginView, ...@@ -198,16 +200,17 @@ class LoginPresenter @Inject constructor(private val view: LoginView,
fun signup() = navigator.toSignUp() fun signup() = navigator.toSignUp()
private suspend fun saveToken(server: String, tokenModel: TokenModel, username: String?) { private suspend fun saveToken(server: String, tokenModel: TokenModel, username: String?) {
multiServerRepository.save(server, tokenModel) localRepository.save(LocalRepository.CURRENT_USERNAME_KEY, username)
localRepository.save(LocalRepository.USERNAME_KEY, username) tokenRepository.save(server, tokenModel.toToken())
registerPushToken() registerPushToken()
} }
private suspend fun registerPushToken() { private suspend fun registerPushToken() {
localRepository.get(LocalRepository.KEY_PUSH_TOKEN)?.let { localRepository.get(LocalRepository.KEY_PUSH_TOKEN)?.let {
client.registerPushToken(it) client.registerPushToken(it, getAccountsInteractor.get(), factory)
} }
// TODO: Schedule push token registering when it comes up null // TODO: When the push token is null, at some point we should receive it with
// onTokenRefresh() on FirebaseTokenService, we need to confirm it.
} }
private suspend fun saveAccount(me: Myself) { private suspend fun saveAccount(me: Myself) {
......
package chat.rocket.android.authentication.presentation package chat.rocket.android.authentication.presentation
import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.server.domain.GetAccountInteractor
import chat.rocket.android.server.domain.GetCurrentServerInteractor import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.domain.MultiServerTokenRepository
import chat.rocket.android.server.domain.SettingsRepository import chat.rocket.android.server.domain.SettingsRepository
import chat.rocket.android.server.infraestructure.ConnectionManager import chat.rocket.android.server.domain.TokenRepository
import chat.rocket.android.server.infraestructure.ConnectionManagerFactory
import chat.rocket.common.model.Token
import chat.rocket.core.TokenRepository
import javax.inject.Inject import javax.inject.Inject
class AuthenticationPresenter @Inject constructor(private val navigator: AuthenticationNavigator, class AuthenticationPresenter @Inject constructor(
private val navigator: AuthenticationNavigator,
private val getCurrentServerInteractor: GetCurrentServerInteractor, private val getCurrentServerInteractor: GetCurrentServerInteractor,
private val multiServerRepository: MultiServerTokenRepository, private val getAccountInteractor: GetAccountInteractor,
private val settingsRepository: SettingsRepository, private val settingsRepository: SettingsRepository,
private val tokenRepository: TokenRepository) { private val localRepository: LocalRepository,
private val tokenRepository: TokenRepository
fun loadCredentials(newServer: Boolean, callback: (authenticated: Boolean) -> Unit) { ) {
suspend fun loadCredentials(newServer: Boolean, callback: (authenticated: Boolean) -> Unit) {
val currentServer = getCurrentServerInteractor.get() val currentServer = getCurrentServerInteractor.get()
val serverToken = currentServer?.let { multiServerRepository.get(currentServer) } val serverToken = currentServer?.let { tokenRepository.get(currentServer) }
val settings = currentServer?.let { settingsRepository.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) { if (newServer || currentServer == null || serverToken == null || settings == null) {
callback(false) callback(false)
} else { } else {
tokenRepository.save(Token(serverToken.userId, serverToken.authToken))
callback(true) callback(true)
navigator.toChatList() navigator.toChatList()
} }
......
...@@ -62,7 +62,7 @@ class SignupPresenter @Inject constructor(private val view: SignupView, ...@@ -62,7 +62,7 @@ class SignupPresenter @Inject constructor(private val view: SignupView,
// 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) client.login(username, password)
val me = client.me() val me = client.me()
localRepository.save(LocalRepository.USERNAME_KEY, me.username) localRepository.save(LocalRepository.CURRENT_USERNAME_KEY, me.username)
saveAccount(me) saveAccount(me)
registerPushToken() registerPushToken()
navigator.toChatList() navigator.toChatList()
......
package chat.rocket.android.authentication.twofactor.presentation package chat.rocket.android.authentication.twofactor.presentation
import chat.rocket.android.authentication.domain.model.TokenModel
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
...@@ -10,24 +9,25 @@ import chat.rocket.android.server.domain.* ...@@ -10,24 +9,25 @@ 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.launchUI import chat.rocket.android.util.extensions.launchUI
import chat.rocket.android.util.extensions.registerPushToken
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
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.model.Myself import chat.rocket.core.model.Myself
import javax.inject.Inject import javax.inject.Inject
class TwoFAPresenter @Inject constructor(private val view: TwoFAView, class TwoFAPresenter @Inject constructor(private val view: TwoFAView,
private val strategy: CancelStrategy, private val strategy: CancelStrategy,
private val navigator: AuthenticationNavigator, private val navigator: AuthenticationNavigator,
private val multiServerRepository: MultiServerTokenRepository, private val tokenRepository: TokenRepository,
private val localRepository: LocalRepository, private val localRepository: LocalRepository,
private val serverInteractor: GetCurrentServerInteractor, private val serverInteractor: GetCurrentServerInteractor,
private val factory: RocketChatClientFactory, private val factory: RocketChatClientFactory,
private val saveAccountInteractor: SaveAccountInteractor, private val saveAccountInteractor: SaveAccountInteractor,
private val getAccountsInteractor: GetAccountsInteractor,
settingsInteractor: GetSettingsInteractor) { settingsInteractor: GetSettingsInteractor) {
private val currentServer = serverInteractor.get()!! private val currentServer = serverInteractor.get()!!
private val client: RocketChatClient = factory.create(currentServer) private val client: RocketChatClient = factory.create(currentServer)
...@@ -54,10 +54,7 @@ class TwoFAPresenter @Inject constructor(private val view: TwoFAView, ...@@ -54,10 +54,7 @@ class TwoFAPresenter @Inject constructor(private val view: TwoFAView,
client.login(usernameOrEmail, password, twoFactorAuthenticationCode) client.login(usernameOrEmail, password, twoFactorAuthenticationCode)
val me = client.me() val me = client.me()
saveAccount(me) saveAccount(me)
multiServerRepository.save( tokenRepository.save(server, token)
server,
TokenModel(token.userId, token.authToken)
)
registerPushToken() registerPushToken()
navigator.toChatList() navigator.toChatList()
} catch (exception: RocketChatException) { } catch (exception: RocketChatException) {
...@@ -85,9 +82,10 @@ class TwoFAPresenter @Inject constructor(private val view: TwoFAView, ...@@ -85,9 +82,10 @@ class TwoFAPresenter @Inject constructor(private val view: TwoFAView,
private suspend fun registerPushToken() { private suspend fun registerPushToken() {
localRepository.get(LocalRepository.KEY_PUSH_TOKEN)?.let { localRepository.get(LocalRepository.KEY_PUSH_TOKEN)?.let {
client.registerPushToken(it) client.registerPushToken(it, getAccountsInteractor.get(), factory)
} }
// TODO: Schedule push token registering when it comes up null // TODO: When the push token is null, at some point we should receive it with
// onTokenRefresh() on FirebaseTokenService, we need to confirm it.
} }
private suspend fun saveAccount(me: Myself) { private suspend fun saveAccount(me: Myself) {
......
...@@ -9,29 +9,39 @@ import chat.rocket.android.R ...@@ -9,29 +9,39 @@ import chat.rocket.android.R
import chat.rocket.android.authentication.presentation.AuthenticationPresenter import chat.rocket.android.authentication.presentation.AuthenticationPresenter
import chat.rocket.android.authentication.server.ui.ServerFragment import chat.rocket.android.authentication.server.ui.ServerFragment
import chat.rocket.android.util.extensions.addFragment import chat.rocket.android.util.extensions.addFragment
import chat.rocket.android.util.extensions.launchUI
import dagger.android.AndroidInjection import dagger.android.AndroidInjection
import dagger.android.AndroidInjector import dagger.android.AndroidInjector
import dagger.android.DispatchingAndroidInjector import dagger.android.DispatchingAndroidInjector
import dagger.android.support.HasSupportFragmentInjector import dagger.android.support.HasSupportFragmentInjector
import kotlinx.coroutines.experimental.Job
import kotlinx.coroutines.experimental.android.UI
import kotlinx.coroutines.experimental.launch
import javax.inject.Inject import javax.inject.Inject
class AuthenticationActivity : AppCompatActivity(), HasSupportFragmentInjector { class AuthenticationActivity : AppCompatActivity(), HasSupportFragmentInjector {
@Inject lateinit var fragmentDispatchingAndroidInjector: DispatchingAndroidInjector<Fragment> @Inject lateinit var fragmentDispatchingAndroidInjector: DispatchingAndroidInjector<Fragment>
@Inject lateinit var presenter: AuthenticationPresenter @Inject lateinit var presenter: AuthenticationPresenter
val job = Job()
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
AndroidInjection.inject(this) AndroidInjection.inject(this)
val newServer = intent.getBooleanExtra(INTENT_ADD_NEW_SERVER, false) super.onCreate(savedInstanceState)
launch(UI + job) {
val newServer = intent.getBooleanExtra(INTENT_ADD_NEW_SERVER, false)
presenter.loadCredentials(newServer) { authenticated -> presenter.loadCredentials(newServer) { authenticated ->
if (authenticated) { if (!authenticated) {
// just call onCreate, and the presenter will call the navigator...
super.onCreate(savedInstanceState)
} else {
showServerInput(savedInstanceState) showServerInput(savedInstanceState)
} }
} }
} }
}
override fun onDestroy() {
job.cancel()
super.onDestroy()
}
override fun supportFragmentInjector(): AndroidInjector<Fragment> { override fun supportFragmentInjector(): AndroidInjector<Fragment> {
return fragmentDispatchingAndroidInjector return fragmentDispatchingAndroidInjector
......
...@@ -97,7 +97,7 @@ class MessageReactionsAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() ...@@ -97,7 +97,7 @@ class MessageReactionsAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>()
val countTextView = findViewById<TextView>(R.id.text_count) val countTextView = findViewById<TextView>(R.id.text_count)
emojiTextView.text = reaction.unicode emojiTextView.text = reaction.unicode
countTextView.text = reaction.count.toString() countTextView.text = reaction.count.toString()
val myself = localRepository.get(LocalRepository.USERNAME_KEY) val myself = localRepository.get(LocalRepository.CURRENT_USERNAME_KEY)
if (reaction.usernames.contains(myself)) { if (reaction.usernames.contains(myself)) {
val context = itemView.context val context = itemView.context
val resources = context.resources val resources = context.resources
......
...@@ -361,7 +361,7 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView, ...@@ -361,7 +361,7 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
try { try {
val members = client.getMembers(chatRoomId, roomTypeOf(chatRoomType), offset, 50).result val members = client.getMembers(chatRoomId, roomTypeOf(chatRoomType), offset, 50).result
usersRepository.saveAll(members) usersRepository.saveAll(members)
val self = localRepository.get(LocalRepository.USERNAME_KEY) val self = localRepository.get(LocalRepository.CURRENT_USERNAME_KEY)
// Take at most the 100 most recent messages distinguished by user. Can return less. // Take at most the 100 most recent messages distinguished by user. Can return less.
val recentMessages = messagesRepository.getRecentMessages(chatRoomId, 100) val recentMessages = messagesRepository.getRecentMessages(chatRoomId, 100)
.filterNot { filterSelfOut && it.sender?.username == self } .filterNot { filterSelfOut && it.sender?.username == self }
...@@ -408,7 +408,7 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView, ...@@ -408,7 +408,7 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
if (users.isNotEmpty()) { if (users.isNotEmpty()) {
usersRepository.saveAll(users) usersRepository.saveAll(users)
} }
val self = localRepository.get(LocalRepository.USERNAME_KEY) val self = localRepository.get(LocalRepository.CURRENT_USERNAME_KEY)
view.populatePeopleSuggestions(users.map { view.populatePeopleSuggestions(users.map {
val username = it.username ?: "" val username = it.username ?: ""
val name = it.name ?: "" val name = it.name ?: ""
......
...@@ -15,7 +15,6 @@ import chat.rocket.android.helper.UrlHelper ...@@ -15,7 +15,6 @@ 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.widget.emoji.EmojiParser import chat.rocket.android.widget.emoji.EmojiParser
import chat.rocket.core.TokenRepository
import chat.rocket.core.model.Message import chat.rocket.core.model.Message
import chat.rocket.core.model.MessageType import chat.rocket.core.model.MessageType
import chat.rocket.core.model.Value import chat.rocket.core.model.Value
...@@ -23,6 +22,7 @@ import chat.rocket.core.model.attachment.* ...@@ -23,6 +22,7 @@ import chat.rocket.core.model.attachment.*
import chat.rocket.core.model.isSystemMessage import chat.rocket.core.model.isSystemMessage
import chat.rocket.core.model.url.Url import chat.rocket.core.model.url.Url
import kotlinx.coroutines.experimental.CommonPool import kotlinx.coroutines.experimental.CommonPool
import kotlinx.coroutines.experimental.launch
import kotlinx.coroutines.experimental.withContext import kotlinx.coroutines.experimental.withContext
import okhttp3.HttpUrl import okhttp3.HttpUrl
import timber.log.Timber import timber.log.Timber
...@@ -32,15 +32,17 @@ import javax.inject.Inject ...@@ -32,15 +32,17 @@ import javax.inject.Inject
class ViewModelMapper @Inject constructor(private val context: Context, class ViewModelMapper @Inject constructor(private val context: Context,
private val parser: MessageParser, private val parser: MessageParser,
private val messagesRepository: MessagesRepository, private val messagesRepository: MessagesRepository,
private val getAccountInteractor: GetAccountInteractor,
tokenRepository: TokenRepository, tokenRepository: TokenRepository,
localRepository: LocalRepository,
serverInteractor: GetCurrentServerInteractor, serverInteractor: GetCurrentServerInteractor,
getSettingsInteractor: GetSettingsInteractor) { getSettingsInteractor: GetSettingsInteractor,
localRepository: LocalRepository) {
private var settings: Map<String, Value<Any>> = getSettingsInteractor.get(serverInteractor.get()!!)!! private val currentServer = serverInteractor.get()!!
private val settings: Map<String, Value<Any>> = getSettingsInteractor.get(currentServer)
private val baseUrl = settings.baseUrl() private val baseUrl = settings.baseUrl()
private val currentUsername: String? = localRepository.get(LocalRepository.USERNAME_KEY) private val token = tokenRepository.get(currentServer)
private val token = tokenRepository.get() private val currentUsername: String? = localRepository.get(LocalRepository.CURRENT_USERNAME_KEY)
suspend fun map(message: Message): List<BaseViewModel<*>> { suspend fun map(message: Message): List<BaseViewModel<*>> {
return translate(message) return translate(message)
...@@ -249,6 +251,7 @@ class ViewModelMapper @Inject constructor(private val context: Context, ...@@ -249,6 +251,7 @@ class ViewModelMapper @Inject constructor(private val context: Context,
val quoteMessage: Message = quote val quoteMessage: Message = quote
quoteViewModel = mapMessage(quoteMessage) quoteViewModel = mapMessage(quoteMessage)
} }
return parser.renderMarkdown(message.message, quoteViewModel, currentUsername) return parser.renderMarkdown(message.message, quoteViewModel, currentUsername)
} }
......
...@@ -4,6 +4,7 @@ import android.app.Application ...@@ -4,6 +4,7 @@ import android.app.Application
import chat.rocket.android.app.RocketChatApplication import chat.rocket.android.app.RocketChatApplication
import chat.rocket.android.dagger.module.ActivityBuilder import chat.rocket.android.dagger.module.ActivityBuilder
import chat.rocket.android.dagger.module.AppModule import chat.rocket.android.dagger.module.AppModule
import chat.rocket.android.dagger.module.ReceiverBuilder
import chat.rocket.android.dagger.module.ServiceBuilder import chat.rocket.android.dagger.module.ServiceBuilder
import chat.rocket.android.push.FirebaseTokenService import chat.rocket.android.push.FirebaseTokenService
import dagger.BindsInstance import dagger.BindsInstance
...@@ -12,7 +13,8 @@ import dagger.android.support.AndroidSupportInjectionModule ...@@ -12,7 +13,8 @@ import dagger.android.support.AndroidSupportInjectionModule
import javax.inject.Singleton import javax.inject.Singleton
@Singleton @Singleton
@Component(modules = [AndroidSupportInjectionModule::class, AppModule::class, ActivityBuilder::class, ServiceBuilder::class]) @Component(modules = [AndroidSupportInjectionModule::class,
AppModule::class, ActivityBuilder::class, ServiceBuilder::class, ReceiverBuilder::class])
interface AppComponent { interface AppComponent {
@Component.Builder @Component.Builder
......
package chat.rocket.android.dagger.module package chat.rocket.android.dagger.module
import android.app.Application import android.app.Application
import android.app.NotificationManager
import android.arch.persistence.room.Room import android.arch.persistence.room.Room
import android.content.Context import android.content.Context
import android.content.SharedPreferences import android.content.SharedPreferences
import androidx.content.systemService
import chat.rocket.android.BuildConfig import chat.rocket.android.BuildConfig
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.app.RocketChatDatabase import chat.rocket.android.app.RocketChatDatabase
import chat.rocket.android.authentication.infraestructure.MemoryTokenRepository import chat.rocket.android.authentication.infraestructure.SharedPreferencesTokenRepository
import chat.rocket.android.authentication.infraestructure.SharedPreferencesMultiServerTokenRepository import chat.rocket.android.authentication.infraestructure.SharedPreferencesMultiServerTokenRepository
import chat.rocket.android.dagger.qualifier.ForFresco import chat.rocket.android.dagger.qualifier.ForFresco
import chat.rocket.android.helper.FrescoAuthInterceptor import chat.rocket.android.helper.FrescoAuthInterceptor
import chat.rocket.android.helper.MessageParser import chat.rocket.android.helper.MessageParser
import chat.rocket.android.infrastructure.LocalRepository import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.infrastructure.SharedPrefsLocalRepository import chat.rocket.android.infrastructure.SharedPrefsLocalRepository
import chat.rocket.android.push.GroupedPush
import chat.rocket.android.server.domain.* import chat.rocket.android.server.domain.*
import chat.rocket.android.server.infraestructure.* import chat.rocket.android.server.infraestructure.*
import chat.rocket.android.util.AppJsonAdapterFactory import chat.rocket.android.util.AppJsonAdapterFactory
import chat.rocket.android.util.TimberLogger import chat.rocket.android.util.TimberLogger
import chat.rocket.common.internal.FallbackSealedClassJsonAdapter
import chat.rocket.common.util.PlatformLogger import chat.rocket.common.util.PlatformLogger
import chat.rocket.core.RocketChatClient import chat.rocket.core.RocketChatClient
import chat.rocket.core.TokenRepository
import com.facebook.drawee.backends.pipeline.DraweeConfig import com.facebook.drawee.backends.pipeline.DraweeConfig
import com.facebook.imagepipeline.backends.okhttp3.OkHttpImagePipelineConfigFactory import com.facebook.imagepipeline.backends.okhttp3.OkHttpImagePipelineConfigFactory
import com.facebook.imagepipeline.core.ImagePipelineConfig import com.facebook.imagepipeline.core.ImagePipelineConfig
...@@ -110,8 +113,8 @@ class AppModule { ...@@ -110,8 +113,8 @@ class AppModule {
@Provides @Provides
@ForFresco @ForFresco
@Singleton @Singleton
fun provideFrescoAuthIntercepter(tokenRepository: TokenRepository): Interceptor { fun provideFrescoAuthIntercepter(tokenRepository: TokenRepository, currentServerInteractor: GetCurrentServerInteractor): Interceptor {
return FrescoAuthInterceptor(tokenRepository) return FrescoAuthInterceptor(tokenRepository, currentServerInteractor)
} }
@Provides @Provides
...@@ -144,8 +147,8 @@ class AppModule { ...@@ -144,8 +147,8 @@ class AppModule {
@Provides @Provides
@Singleton @Singleton
fun provideTokenRepository(): TokenRepository { fun provideTokenRepository(prefs: SharedPreferences, moshi: Moshi): TokenRepository {
return MemoryTokenRepository() return SharedPreferencesTokenRepository(prefs, moshi)
} }
@Provides @Provides
...@@ -192,7 +195,10 @@ class AppModule { ...@@ -192,7 +195,10 @@ class AppModule {
@Provides @Provides
@Singleton @Singleton
fun provideMoshi(): Moshi { fun provideMoshi(): Moshi {
return Moshi.Builder().add(AppJsonAdapterFactory.INSTANCE).build() return Moshi.Builder()
.add(FallbackSealedClassJsonAdapter.ADAPTER_FACTORY)
.add(AppJsonAdapterFactory.INSTANCE)
.build()
} }
@Provides @Provides
...@@ -245,4 +251,11 @@ class AppModule { ...@@ -245,4 +251,11 @@ class AppModule {
@Singleton @Singleton
fun provideAccountsRepository(preferences: SharedPreferences, moshi: Moshi): AccountsRepository = fun provideAccountsRepository(preferences: SharedPreferences, moshi: Moshi): AccountsRepository =
SharedPreferencesAccountsRepository(preferences, moshi) SharedPreferencesAccountsRepository(preferences, moshi)
@Provides
fun provideNotificationManager(context: Context): NotificationManager = context.systemService()
@Provides
@Singleton
fun provideGroupedPush() = GroupedPush()
} }
\ No newline at end of file
package chat.rocket.android.dagger.module
import chat.rocket.android.push.DeleteReceiver
import chat.rocket.android.push.di.DeleteReceiverProvider
import dagger.Module
import dagger.android.ContributesAndroidInjector
@Module
abstract class ReceiverBuilder {
@ContributesAndroidInjector(modules = [DeleteReceiverProvider::class])
abstract fun bindDeleteReceiver(): DeleteReceiver
}
\ No newline at end of file
package chat.rocket.android.dagger.module package chat.rocket.android.dagger.module
import chat.rocket.android.push.FirebaseTokenService import chat.rocket.android.push.FirebaseTokenService
import chat.rocket.android.push.GcmListenerService
import chat.rocket.android.push.di.FirebaseTokenServiceProvider import chat.rocket.android.push.di.FirebaseTokenServiceProvider
import chat.rocket.android.push.di.GcmListenerServiceProvider
import dagger.Module import dagger.Module
import dagger.android.ContributesAndroidInjector import dagger.android.ContributesAndroidInjector
...@@ -9,4 +11,7 @@ import dagger.android.ContributesAndroidInjector ...@@ -9,4 +11,7 @@ import dagger.android.ContributesAndroidInjector
@ContributesAndroidInjector(modules = [FirebaseTokenServiceProvider::class]) @ContributesAndroidInjector(modules = [FirebaseTokenServiceProvider::class])
abstract fun bindFirebaseTokenService(): FirebaseTokenService abstract fun bindFirebaseTokenService(): FirebaseTokenService
@ContributesAndroidInjector(modules = [GcmListenerServiceProvider::class])
abstract fun bindGcmListenerService(): GcmListenerService
} }
\ No newline at end of file
package chat.rocket.android.helper package chat.rocket.android.helper
import chat.rocket.core.TokenRepository import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.domain.TokenRepository
import okhttp3.Interceptor import okhttp3.Interceptor
import okhttp3.Response import okhttp3.Response
class FrescoAuthInterceptor(private val tokenRepository: TokenRepository) : Interceptor { class FrescoAuthInterceptor(
private val tokenRepository: TokenRepository,
private val currentServerInteractor: GetCurrentServerInteractor
) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response { override fun intercept(chain: Interceptor.Chain): Response {
val token = tokenRepository.get()
var request = chain.request() var request = chain.request()
currentServerInteractor.get()?.let { serverUrl ->
val token = tokenRepository.get(serverUrl)
token?.let {
return@let token?.let {
val url = request.url().newBuilder().apply { val url = request.url().newBuilder().apply {
addQueryParameter("rc_uid", token.userId) addQueryParameter("rc_uid", token.userId)
addQueryParameter("rc_token", token.authToken) addQueryParameter("rc_token", token.authToken)
...@@ -18,7 +24,7 @@ class FrescoAuthInterceptor(private val tokenRepository: TokenRepository) : Inte ...@@ -18,7 +24,7 @@ class FrescoAuthInterceptor(private val tokenRepository: TokenRepository) : Inte
url(url) url(url)
}.build() }.build()
} }
}
return chain.proceed(request) return chain.proceed(request)
} }
} }
\ No newline at end of file
...@@ -6,7 +6,7 @@ interface LocalRepository { ...@@ -6,7 +6,7 @@ interface LocalRepository {
const val KEY_PUSH_TOKEN = "KEY_PUSH_TOKEN" const val KEY_PUSH_TOKEN = "KEY_PUSH_TOKEN"
const val TOKEN_KEY = "token_" const val TOKEN_KEY = "token_"
const val SETTINGS_KEY = "settings_" const val SETTINGS_KEY = "settings_"
const val USERNAME_KEY = "my_username" const val CURRENT_USERNAME_KEY = "username_"
} }
fun save(key: String, value: String?) fun save(key: String, value: String?)
......
...@@ -20,6 +20,6 @@ class SharedPrefsLocalRepository(private val preferences: SharedPreferences) : L ...@@ -20,6 +20,6 @@ class SharedPrefsLocalRepository(private val preferences: SharedPreferences) : L
clear(LocalRepository.KEY_PUSH_TOKEN) clear(LocalRepository.KEY_PUSH_TOKEN)
clear(LocalRepository.TOKEN_KEY + server) clear(LocalRepository.TOKEN_KEY + server)
clear(LocalRepository.SETTINGS_KEY + server) clear(LocalRepository.SETTINGS_KEY + server)
clear(LocalRepository.USERNAME_KEY + server) clear(LocalRepository.CURRENT_USERNAME_KEY)
} }
} }
\ No newline at end of file
...@@ -10,28 +10,32 @@ import chat.rocket.android.server.domain.model.Account ...@@ -10,28 +10,32 @@ import chat.rocket.android.server.domain.model.Account
import chat.rocket.android.server.infraestructure.ConnectionManagerFactory import chat.rocket.android.server.infraestructure.ConnectionManagerFactory
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.extensions.registerPushToken
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.logout import chat.rocket.core.internal.rest.logout
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.unregisterPushToken import chat.rocket.core.internal.rest.unregisterPushToken
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
class MainPresenter @Inject constructor(private val view: MainView, class MainPresenter @Inject constructor(
private val view: MainView,
private val strategy: CancelStrategy, private val strategy: CancelStrategy,
private val navigator: MainNavigator, private val navigator: MainNavigator,
private val multiServerRepository: MultiServerTokenRepository, private val tokenRepository: TokenRepository,
private val serverInteractor: GetCurrentServerInteractor, private val serverInteractor: GetCurrentServerInteractor,
private val localRepository: LocalRepository, private val localRepository: LocalRepository,
private val navHeaderMapper: NavHeaderViewModelMapper, private val navHeaderMapper: NavHeaderViewModelMapper,
private val saveAccountInteractor: SaveAccountInteractor, private val saveAccountInteractor: SaveAccountInteractor,
private val getAccountsInteractor: GetAccountsInteractor, private val getAccountsInteractor: GetAccountsInteractor,
private val removeAccountInterector: RemoveAccountInterector, private val removeAccountInterector: RemoveAccountInterector,
private val factory: RocketChatClientFactory,
getSettingsInteractor: GetSettingsInteractor, getSettingsInteractor: GetSettingsInteractor,
managerFactory: ConnectionManagerFactory, managerFactory: ConnectionManagerFactory
factory: RocketChatClientFactory) { ) {
private val currentServer = serverInteractor.get()!! private val currentServer = serverInteractor.get()!!
private val manager = managerFactory.create(currentServer) private val manager = managerFactory.create(currentServer)
private val client: RocketChatClient = factory.create(currentServer) private val client: RocketChatClient = factory.create(currentServer)
...@@ -79,7 +83,7 @@ class MainPresenter @Inject constructor(private val view: MainView, ...@@ -79,7 +83,7 @@ class MainPresenter @Inject constructor(private val view: MainView,
client.logout() client.logout()
disconnect() disconnect()
removeAccountInterector.remove(currentServer) removeAccountInterector.remove(currentServer)
multiServerRepository.clear(currentServer) tokenRepository.remove(currentServer)
navigator.toNewServer() navigator.toNewServer()
} catch (exception: RocketChatException) { } catch (exception: RocketChatException) {
exception.message?.let { exception.message?.let {
...@@ -120,4 +124,10 @@ class MainPresenter @Inject constructor(private val view: MainView, ...@@ -120,4 +124,10 @@ class MainPresenter @Inject constructor(private val view: MainView,
fun addNewServer() { fun addNewServer() {
navigator.toServerScreen() navigator.toServerScreen()
} }
suspend fun refreshToken(token: String?) {
token?.let {
client.registerPushToken(it, getAccountsInteractor.get(), factory)
}
}
} }
\ No newline at end of file
package chat.rocket.android.main.ui package chat.rocket.android.main.ui
import android.app.Activity import android.app.Activity
import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.support.v4.app.Fragment import android.support.v4.app.Fragment
import android.support.v7.app.AppCompatActivity import android.support.v7.app.AppCompatActivity
...@@ -10,7 +9,6 @@ import android.view.Gravity ...@@ -10,7 +9,6 @@ import android.view.Gravity
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.authentication.ui.AuthenticationActivity
import chat.rocket.android.main.adapter.AccountSelector import chat.rocket.android.main.adapter.AccountSelector
import chat.rocket.android.main.adapter.AccountsAdapter import chat.rocket.android.main.adapter.AccountsAdapter
import chat.rocket.android.main.presentation.MainPresenter import chat.rocket.android.main.presentation.MainPresenter
...@@ -21,6 +19,8 @@ import chat.rocket.android.util.extensions.fadeIn ...@@ -21,6 +19,8 @@ import chat.rocket.android.util.extensions.fadeIn
import chat.rocket.android.util.extensions.fadeOut import chat.rocket.android.util.extensions.fadeOut
import chat.rocket.android.util.extensions.rotateBy import chat.rocket.android.util.extensions.rotateBy
import chat.rocket.android.util.extensions.showToast import chat.rocket.android.util.extensions.showToast
import com.google.android.gms.gcm.GoogleCloudMessaging
import com.google.android.gms.iid.InstanceID
import dagger.android.AndroidInjection import dagger.android.AndroidInjection
import dagger.android.AndroidInjector import dagger.android.AndroidInjector
import dagger.android.DispatchingAndroidInjector import dagger.android.DispatchingAndroidInjector
...@@ -29,6 +29,8 @@ import dagger.android.support.HasSupportFragmentInjector ...@@ -29,6 +29,8 @@ import dagger.android.support.HasSupportFragmentInjector
import kotlinx.android.synthetic.main.activity_main.* import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.app_bar.* import kotlinx.android.synthetic.main.app_bar.*
import kotlinx.android.synthetic.main.nav_header.view.* import kotlinx.android.synthetic.main.nav_header.view.*
import kotlinx.coroutines.experimental.CommonPool
import kotlinx.coroutines.experimental.launch
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
...@@ -43,6 +45,12 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector, HasSupp ...@@ -43,6 +45,12 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector, HasSupp
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main) setContentView(R.layout.activity_main)
launch(CommonPool) {
val token = InstanceID.getInstance(this@MainActivity).getToken(getString(R.string.gcm_sender_id), GoogleCloudMessaging.INSTANCE_ID_SCOPE, null)
Timber.d("GCM token: $token")
presenter.refreshToken(token)
}
presenter.connect() presenter.connect()
presenter.loadCurrentInfo() presenter.loadCurrentInfo()
setupToolbar() setupToolbar()
......
package chat.rocket.android.push
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import dagger.android.AndroidInjection
import javax.inject.Inject
/**
* BroadcastReceiver for dismissed notifications.
*/
class DeleteReceiver : BroadcastReceiver() {
@Inject
lateinit var groupedPushes: GroupedPush
override fun onReceive(context: Context, intent: Intent) {
AndroidInjection.inject(this, context)
val notId = intent.extras?.getInt(EXTRA_NOT_ID)
val host = intent.extras?.getString(EXTRA_HOSTNAME)
if (host != null && notId != null) {
clearNotificationsByHostAndNotificationId(host, notId)
}
}
/**
* Clear notifications by the host they belong to and its unique id.
*/
fun clearNotificationsByHostAndNotificationId(host: String, notificationId: Int) {
if (groupedPushes.hostToPushMessageList.isNotEmpty()) {
val notifications = groupedPushes.hostToPushMessageList[host]
notifications?.let {
notifications.removeAll {
it.notificationId.toInt() == notificationId
}
}
}
}
}
\ No newline at end of file
...@@ -2,6 +2,8 @@ package chat.rocket.android.push ...@@ -2,6 +2,8 @@ package chat.rocket.android.push
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.infrastructure.LocalRepository import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.server.domain.GetCurrentServerInteractor
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.RocketChatClient
import chat.rocket.core.internal.rest.registerPushToken import chat.rocket.core.internal.rest.registerPushToken
...@@ -16,14 +18,16 @@ import javax.inject.Inject ...@@ -16,14 +18,16 @@ import javax.inject.Inject
class FirebaseTokenService : FirebaseInstanceIdService() { class FirebaseTokenService : FirebaseInstanceIdService() {
@Inject @Inject
lateinit var client: RocketChatClient lateinit var factory: RocketChatClientFactory
@Inject
lateinit var getCurrentServerInteractor: GetCurrentServerInteractor
@Inject @Inject
lateinit var localRepository: LocalRepository lateinit var localRepository: LocalRepository
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
AndroidInjection.inject(this); AndroidInjection.inject(this)
} }
override fun onTokenRefresh() { override fun onTokenRefresh() {
...@@ -31,11 +35,14 @@ class FirebaseTokenService : FirebaseInstanceIdService() { ...@@ -31,11 +35,14 @@ class FirebaseTokenService : FirebaseInstanceIdService() {
// default push gateway. We should register this project's own project sender id into it. // default push gateway. We should register this project's own project sender id into it.
val gcmToken = InstanceID.getInstance(this) val gcmToken = InstanceID.getInstance(this)
.getToken(getString(R.string.gcm_sender_id), GoogleCloudMessaging.INSTANCE_ID_SCOPE, null) .getToken(getString(R.string.gcm_sender_id), GoogleCloudMessaging.INSTANCE_ID_SCOPE, null)
val currentServer = getCurrentServerInteractor.get()!!
val client = factory.create(currentServer)
gcmToken?.let { gcmToken?.let {
localRepository.save(LocalRepository.KEY_PUSH_TOKEN, gcmToken) localRepository.save(LocalRepository.KEY_PUSH_TOKEN, gcmToken)
launch { launch {
try { try {
Timber.d("Registering push token: $gcmToken for ${client.url}")
client.registerPushToken(gcmToken) client.registerPushToken(gcmToken)
} catch (ex: RocketChatException) { } catch (ex: RocketChatException) {
Timber.e(ex) Timber.e(ex)
......
...@@ -2,12 +2,22 @@ package chat.rocket.android.push ...@@ -2,12 +2,22 @@ package chat.rocket.android.push
import android.os.Bundle import android.os.Bundle
import com.google.android.gms.gcm.GcmListenerService import com.google.android.gms.gcm.GcmListenerService
import dagger.android.AndroidInjection
import javax.inject.Inject
class GcmListenerService : GcmListenerService() { class GcmListenerService : GcmListenerService() {
@Inject
lateinit var pushManager: PushManager
override fun onCreate() {
super.onCreate()
AndroidInjection.inject(this)
}
override fun onMessageReceived(from: String?, data: Bundle?) { override fun onMessageReceived(from: String?, data: Bundle?) {
data?.let { data?.let {
PushManager.handle(this, data) pushManager.handle(data)
} }
} }
} }
\ No newline at end of file
package chat.rocket.android.push
import java.util.concurrent.atomic.AtomicInteger
import javax.inject.Singleton
typealias TupleGroupIdMessageCount = Pair<Int, AtomicInteger>
class GroupedPush {
// Notifications received from the same server are grouped in a single bundled notification.
// This map associates a host to a group id.
val groupMap = HashMap<String, TupleGroupIdMessageCount>()
// Map a hostname to a list of push messages that pertain to it.
val hostToPushMessageList = HashMap<String, MutableList<PushMessage>>()
}
\ No newline at end of file
package chat.rocket.android.push.di
import chat.rocket.android.dagger.module.AppModule
import chat.rocket.android.push.DeleteReceiver
import dagger.Module
import dagger.android.ContributesAndroidInjector
@Module
abstract class DeleteReceiverProvider {
@ContributesAndroidInjector(modules = [AppModule::class])
abstract fun provideDeleteReceiver(): DeleteReceiver
}
\ No newline at end of file
package chat.rocket.android.push.di
import chat.rocket.android.dagger.module.AppModule
import chat.rocket.android.push.GcmListenerService
import dagger.Module
import dagger.android.ContributesAndroidInjector
@Module abstract class GcmListenerServiceProvider {
@ContributesAndroidInjector(modules = [AppModule::class])
abstract fun provideGcmListenerService(): GcmListenerService
}
\ No newline at end of file
package chat.rocket.android.server.domain
import javax.inject.Inject
class GetAccountInteractor @Inject constructor(val repository: AccountsRepository) {
suspend fun get(url: String) = repository.load().firstOrNull { account ->
url == account.serverUrl
}
}
\ No newline at end of file
package chat.rocket.android.server.domain;
import java.util.List;
import io.reactivex.Scheduler;
import io.reactivex.Single;
public class GetServersInteractor {
private final ServersRepository repository;
private final Scheduler executionScheduler;
public GetServersInteractor(ServersRepository repository, Scheduler executionScheduler) {
this.repository = repository;
this.executionScheduler = executionScheduler;
}
}
package chat.rocket.android.server.domain
import chat.rocket.android.server.domain.model.Server
import chat.rocket.android.server.infraestructure.ServerEntity
import io.reactivex.Completable
import io.reactivex.Single
interface ServersRepository {
val servers: Single<List<Server>>
fun saveServer(server: Server): Completable
fun updateServer(server: Server): Completable
}
\ No newline at end of file
...@@ -96,3 +96,4 @@ fun PublicSettings.uploadMaxFileSize(): Int { ...@@ -96,3 +96,4 @@ fun PublicSettings.uploadMaxFileSize(): Int {
} }
fun PublicSettings.baseUrl(): String? = this[SITE_URL]?.value as String fun PublicSettings.baseUrl(): String? = this[SITE_URL]?.value as String
fun PublicSettings.siteName(): String? = this[SITE_NAME]?.value as String?
\ No newline at end of file
package chat.rocket.android.server.domain
interface TokenRepository : chat.rocket.core.TokenRepository {
fun remove(url: String)
fun clear()
}
\ No newline at end of file
...@@ -2,16 +2,16 @@ package chat.rocket.android.server.infraestructure ...@@ -2,16 +2,16 @@ package chat.rocket.android.server.infraestructure
import chat.rocket.common.util.PlatformLogger import chat.rocket.common.util.PlatformLogger
import chat.rocket.core.RocketChatClient import chat.rocket.core.RocketChatClient
import chat.rocket.core.TokenRepository import chat.rocket.android.server.domain.TokenRepository
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@Singleton @Singleton
class RocketChatClientFactory @Inject constructor(val okHttpClient: OkHttpClient, class RocketChatClientFactory @Inject constructor(private val okHttpClient: OkHttpClient,
val repository: TokenRepository, private val repository: TokenRepository,
val logger: PlatformLogger) { private val logger: PlatformLogger) {
private val cache = HashMap<String, RocketChatClient>() private val cache = HashMap<String, RocketChatClient>()
fun create(url: String): RocketChatClient { fun create(url: String): RocketChatClient {
......
package chat.rocket.android.server.infraestructure
import chat.rocket.android.server.domain.ServersRepository
import chat.rocket.android.server.domain.model.Server
import io.reactivex.Completable
import io.reactivex.Single
class RoomServersRepository : ServersRepository {
override val servers: Single<List<Server>>
get() = TODO("not implemented")
override fun saveServer(server: Server): Completable {
TODO("not implemented")
}
override fun updateServer(server: Server): Completable {
TODO("not implemented")
}
}
package chat.rocket.android.server.presentation package chat.rocket.android.server.presentation
import chat.rocket.android.core.lifecycle.CancelStrategy import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.server.domain.* import chat.rocket.android.server.domain.*
import chat.rocket.android.server.infraestructure.ConnectionManagerFactory import chat.rocket.android.server.infraestructure.ConnectionManagerFactory
import chat.rocket.android.util.extensions.launchUI import chat.rocket.android.util.extensions.launchUI
import chat.rocket.common.model.Token
import chat.rocket.common.util.ifNull import chat.rocket.common.util.ifNull
import chat.rocket.core.TokenRepository
import javax.inject.Inject import javax.inject.Inject
class ChangeServerPresenter @Inject constructor(private val view: ChangeServerView, class ChangeServerPresenter @Inject constructor(
private val view: ChangeServerView,
private val navigator: ChangeServerNavigator, private val navigator: ChangeServerNavigator,
private val strategy: CancelStrategy, private val strategy: CancelStrategy,
private val saveCurrentServerInteractor: SaveCurrentServerInteractor, private val saveCurrentServerInteractor: SaveCurrentServerInteractor,
private val getCurrentServerInteractor: GetCurrentServerInteractor, private val getCurrentServerInteractor: GetCurrentServerInteractor,
private val getAccountInteractor: GetAccountInteractor,
private val getAccountsInteractor: GetAccountsInteractor, private val getAccountsInteractor: GetAccountsInteractor,
private val multiServerRepository: MultiServerTokenRepository,
private val settingsRepository: SettingsRepository, private val settingsRepository: SettingsRepository,
private val tokenRepository: TokenRepository, private val tokenRepository: TokenRepository,
private val connectionManager: ConnectionManagerFactory) { private val localRepository: LocalRepository,
private val connectionManager: ConnectionManagerFactory
) {
fun loadServer(newUrl: String?) { fun loadServer(newUrl: String?) {
launchUI(strategy) { launchUI(strategy) {
view.showProgress() view.showProgress()
...@@ -29,7 +31,7 @@ class ChangeServerPresenter @Inject constructor(private val view: ChangeServerVi ...@@ -29,7 +31,7 @@ class ChangeServerPresenter @Inject constructor(private val view: ChangeServerVi
} }
url?.let { serverUrl -> url?.let { serverUrl ->
val token = multiServerRepository.get(serverUrl) val token = tokenRepository.get(serverUrl)
if (token == null) { if (token == null) {
view.showInvalidCredentials() view.showInvalidCredentials()
view.hideProgress() view.hideProgress()
...@@ -47,7 +49,11 @@ class ChangeServerPresenter @Inject constructor(private val view: ChangeServerVi ...@@ -47,7 +49,11 @@ class ChangeServerPresenter @Inject constructor(private val view: ChangeServerVi
connectionManager.get(url)?.disconnect() connectionManager.get(url)?.disconnect()
} }
tokenRepository.save(Token(token.userId, token.authToken)) // Save the current username.
getAccountInteractor.get(serverUrl)?.let { account ->
localRepository.save(LocalRepository.CURRENT_USERNAME_KEY, account.userName)
}
saveCurrentServerInteractor.save(serverUrl) saveCurrentServerInteractor.save(serverUrl)
view.hideProgress() view.hideProgress()
navigator.toChatRooms() navigator.toChatRooms()
......
...@@ -39,7 +39,7 @@ class ChangeServerActivity : AppCompatActivity(), ChangeServerView { ...@@ -39,7 +39,7 @@ class ChangeServerActivity : AppCompatActivity(), ChangeServerView {
private const val INTENT_SERVER_URL = "INTENT_SERVER_URL" private const val INTENT_SERVER_URL = "INTENT_SERVER_URL"
private const val INTENT_CHAT_ROOM_NAME = "INTENT_CHAT_ROOM_NAME" private const val INTENT_CHAT_ROOM_NAME = "INTENT_CHAT_ROOM_NAME"
private const val INTENT_CHAT_ROOM_TYPE = "INTENT_CHAT_ROOM_NAME" private const val INTENT_CHAT_ROOM_TYPE = "INTENT_CHAT_ROOM_TYPE"
fun Context.changeServerIntent(serverUrl: String?): Intent { fun Context.changeServerIntent(serverUrl: String?): Intent {
return Intent(this, ChangeServerActivity::class.java).apply { return Intent(this, ChangeServerActivity::class.java).apply {
......
package chat.rocket.android.util.extensions
import chat.rocket.android.server.domain.model.Account
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.core.RocketChatClient
import chat.rocket.core.internal.rest.registerPushToken
import kotlinx.coroutines.experimental.CommonPool
import kotlinx.coroutines.experimental.launch
import timber.log.Timber
suspend fun RocketChatClient.registerPushToken(
token: String,
accounts: List<Account>,
factory: RocketChatClientFactory
) {
launch(CommonPool) {
accounts.forEach { account ->
try {
factory.create(account.serverUrl).registerPushToken(token)
} catch (ex: Exception) {
Timber.d(ex, "Error registering Push token for ${account.serverUrl}")
ex.printStackTrace()
}
}
}
}
\ No newline at end of file
...@@ -24,7 +24,7 @@ ext { ...@@ -24,7 +24,7 @@ ext {
threeTenABP : '1.0.5', threeTenABP : '1.0.5',
rxBinding : '2.0.0', rxBinding : '2.0.0',
fresco : '1.8.1', fresco : '1.8.1',
kotshi : '0.3.0', kotshi : '1.0.2',
frescoImageViewer : '0.5.1', frescoImageViewer : '0.5.1',
markwon : '1.0.3', markwon : '1.0.3',
sheetMenu : '1.3.3', sheetMenu : '1.3.3',
......
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