Unverified Commit 9ddd590f authored by Filipe de Lima Brito's avatar Filipe de Lima Brito Committed by GitHub

Merge branch 'develop' into feature/create-channel

parents f9941f0d 5db6d4d8
...@@ -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 2024 versionCode 2026
versionName "2.3.0" versionName "2.3.2"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
multiDexEnabled true multiDexEnabled true
} }
......
...@@ -16,13 +16,22 @@ import chat.rocket.android.app.migration.model.RealmBasedServerInfo ...@@ -16,13 +16,22 @@ import chat.rocket.android.app.migration.model.RealmBasedServerInfo
import chat.rocket.android.app.migration.model.RealmPublicSetting import chat.rocket.android.app.migration.model.RealmPublicSetting
import chat.rocket.android.app.migration.model.RealmSession import chat.rocket.android.app.migration.model.RealmSession
import chat.rocket.android.app.migration.model.RealmUser import chat.rocket.android.app.migration.model.RealmUser
import chat.rocket.android.authentication.domain.model.toToken
import chat.rocket.android.dagger.DaggerAppComponent import chat.rocket.android.dagger.DaggerAppComponent
import chat.rocket.android.dagger.qualifier.ForMessages import chat.rocket.android.dagger.qualifier.ForMessages
import chat.rocket.android.helper.CrashlyticsTree import chat.rocket.android.helper.CrashlyticsTree
import chat.rocket.android.infrastructure.LocalRepository import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.server.domain.* import chat.rocket.android.infrastructure.installCrashlyticsWrapper
import chat.rocket.android.server.domain.AccountsRepository
import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.domain.GetSettingsInteractor
import chat.rocket.android.server.domain.PublicSettings
import chat.rocket.android.server.domain.SITE_URL
import chat.rocket.android.server.domain.SaveCurrentServerInteractor
import chat.rocket.android.server.domain.SettingsRepository
import chat.rocket.android.server.domain.TokenRepository
import chat.rocket.android.server.domain.favicon
import chat.rocket.android.server.domain.model.Account import chat.rocket.android.server.domain.model.Account
import chat.rocket.android.server.domain.wideTile
import chat.rocket.android.util.extensions.avatarUrl import chat.rocket.android.util.extensions.avatarUrl
import chat.rocket.android.util.extensions.serverLogoUrl import chat.rocket.android.util.extensions.serverLogoUrl
import chat.rocket.android.widget.emoji.EmojiRepository import chat.rocket.android.widget.emoji.EmojiRepository
...@@ -34,13 +43,16 @@ import com.facebook.drawee.backends.pipeline.DraweeConfig ...@@ -34,13 +43,16 @@ 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.* import dagger.android.AndroidInjector
import dagger.android.DispatchingAndroidInjector
import dagger.android.HasActivityInjector
import dagger.android.HasBroadcastReceiverInjector
import dagger.android.HasServiceInjector
import io.fabric.sdk.android.Fabric import io.fabric.sdk.android.Fabric
import io.realm.Realm import io.realm.Realm
import io.realm.RealmConfiguration import io.realm.RealmConfiguration
import kotlinx.coroutines.experimental.CommonPool import kotlinx.coroutines.experimental.CommonPool
import kotlinx.coroutines.experimental.launch import kotlinx.coroutines.experimental.launch
import kotlinx.coroutines.experimental.runBlocking
import timber.log.Timber import timber.log.Timber
import java.lang.ref.WeakReference import java.lang.ref.WeakReference
import javax.inject.Inject import javax.inject.Inject
...@@ -69,7 +81,7 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje ...@@ -69,7 +81,7 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje
@Inject @Inject
lateinit var getCurrentServerInteractor: GetCurrentServerInteractor lateinit var getCurrentServerInteractor: GetCurrentServerInteractor
@Inject @Inject
lateinit var multiServerRepository: MultiServerTokenRepository lateinit var settingsInteractor: GetSettingsInteractor
@Inject @Inject
lateinit var settingsRepository: SettingsRepository lateinit var settingsRepository: SettingsRepository
@Inject @Inject
...@@ -81,8 +93,6 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje ...@@ -81,8 +93,6 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje
@Inject @Inject
lateinit var prefs: SharedPreferences lateinit var prefs: SharedPreferences
@Inject @Inject
lateinit var getAccountsInteractor: GetAccountsInteractor
@Inject
lateinit var localRepository: LocalRepository lateinit var localRepository: LocalRepository
@Inject @Inject
...@@ -101,8 +111,6 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje ...@@ -101,8 +111,6 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje
.lifecycle .lifecycle
.addObserver(appLifecycleObserver) .addObserver(appLifecycleObserver)
// TODO - remove this on the future, temporary migration stuff for pre-release versions.
migrateInternalTokens()
context = WeakReference(applicationContext) context = WeakReference(applicationContext)
AndroidThreeTen.init(this) AndroidThreeTen.init(this)
...@@ -127,6 +135,29 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje ...@@ -127,6 +135,29 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje
} catch (ex: Exception) { } catch (ex: Exception) {
Timber.d(ex, "Error migrating old accounts") Timber.d(ex, "Error migrating old accounts")
} }
// TODO - remove this
checkCurrentServer()
}
private fun checkCurrentServer() {
val currentServer = getCurrentServerInteractor.get() ?: "<unknown>"
if (currentServer == "<unknown>") {
val message = "null currentServer"
Timber.d(IllegalStateException(message), message)
}
val settings = settingsInteractor.get(currentServer)
if (settings.isEmpty()) {
val message = "Empty settings for: $currentServer"
Timber.d(IllegalStateException(message), message)
}
val baseUrl = settings[SITE_URL]
if (baseUrl == null) {
val message = "Server $currentServer SITE_URL"
Timber.d(IllegalStateException(message), message)
}
} }
private fun migrateFromLegacy() { private fun migrateFromLegacy() {
...@@ -233,32 +264,13 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje ...@@ -233,32 +264,13 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje
} }
} }
private fun migrateInternalTokens() {
if (!prefs.getBoolean(INTERNAL_TOKEN_MIGRATION_NEEDED, true)) {
Timber.d("Tokens already migrated")
return
}
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() {
val core = CrashlyticsCore.Builder().disabled(BuildConfig.DEBUG).build() val core = CrashlyticsCore.Builder().disabled(BuildConfig.DEBUG).build()
Fabric.with(this, Crashlytics.Builder().core(core).build()) Fabric.with(this, Crashlytics.Builder().core(core).build())
installCrashlyticsWrapper(this@RocketChatApplication,
getCurrentServerInteractor, settingsInteractor,
accountRepository, localRepository)
} }
private fun setupFresco() { private fun setupFresco() {
...@@ -301,6 +313,4 @@ private fun LocalRepository.hasMigrated() = getBoolean(LocalRepository.MIGRATION ...@@ -301,6 +313,4 @@ private fun LocalRepository.hasMigrated() = getBoolean(LocalRepository.MIGRATION
private fun LocalRepository.needOldMessagesCleanUp() = getBoolean(CLEANUP_OLD_MESSAGES_NEEDED, true) private fun LocalRepository.needOldMessagesCleanUp() = getBoolean(CLEANUP_OLD_MESSAGES_NEEDED, true)
private fun LocalRepository.setOldMessagesCleanedUp() = save(CLEANUP_OLD_MESSAGES_NEEDED, false) private fun LocalRepository.setOldMessagesCleanedUp() = save(CLEANUP_OLD_MESSAGES_NEEDED, false)
private const val INTERNAL_TOKEN_MIGRATION_NEEDED = "INTERNAL_TOKEN_MIGRATION_NEEDED"
private const val CLEANUP_OLD_MESSAGES_NEEDED = "CLEANUP_OLD_MESSAGES_NEEDED" private const val CLEANUP_OLD_MESSAGES_NEEDED = "CLEANUP_OLD_MESSAGES_NEEDED"
\ No newline at end of file
...@@ -2,6 +2,7 @@ package chat.rocket.android.authentication.di ...@@ -2,6 +2,7 @@ package chat.rocket.android.authentication.di
import chat.rocket.android.authentication.presentation.AuthenticationNavigator import chat.rocket.android.authentication.presentation.AuthenticationNavigator
import chat.rocket.android.authentication.ui.AuthenticationActivity import chat.rocket.android.authentication.ui.AuthenticationActivity
import chat.rocket.android.dagger.qualifier.ForAuthentication
import chat.rocket.android.dagger.scope.PerActivity import chat.rocket.android.dagger.scope.PerActivity
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
......
...@@ -42,12 +42,13 @@ class LoginPresenter @Inject constructor( ...@@ -42,12 +42,13 @@ class LoginPresenter @Inject constructor(
private val localRepository: LocalRepository, private val localRepository: LocalRepository,
private val getAccountsInteractor: GetAccountsInteractor, private val getAccountsInteractor: GetAccountsInteractor,
private val settingsInteractor: GetSettingsInteractor, private val settingsInteractor: GetSettingsInteractor,
serverInteractor: GetCurrentServerInteractor, serverInteractor: GetConnectingServerInteractor,
private val saveCurrentServer: SaveCurrentServerInteractor,
private val saveAccountInteractor: SaveAccountInteractor, private val saveAccountInteractor: SaveAccountInteractor,
private val 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 var currentServer = serverInteractor.get()!!
private lateinit var client: RocketChatClient private lateinit var client: RocketChatClient
private lateinit var settings: PublicSettings private lateinit var settings: PublicSettings
private lateinit var usernameOrEmail: String private lateinit var usernameOrEmail: String
...@@ -108,6 +109,7 @@ class LoginPresenter @Inject constructor( ...@@ -108,6 +109,7 @@ class LoginPresenter @Inject constructor(
} }
private fun setupConnectionInfo(serverUrl: String) { private fun setupConnectionInfo(serverUrl: String) {
currentServer = serverUrl
client = factory.create(serverUrl) client = factory.create(serverUrl)
settings = settingsInteractor.get(serverUrl) settings = settingsInteractor.get(serverUrl)
} }
...@@ -352,6 +354,7 @@ class LoginPresenter @Inject constructor( ...@@ -352,6 +354,7 @@ class LoginPresenter @Inject constructor(
val username = retryIO("me()") { 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)
saveCurrentServer.save(currentServer)
saveAccount(username) saveAccount(username)
saveToken(token) saveToken(token)
registerPushToken() registerPushToken()
......
...@@ -27,7 +27,8 @@ class RegisterUsernamePresenter @Inject constructor( ...@@ -27,7 +27,8 @@ class RegisterUsernamePresenter @Inject constructor(
private val factory: RocketChatClientFactory, private val factory: RocketChatClientFactory,
private val saveAccountInteractor: SaveAccountInteractor, private val saveAccountInteractor: SaveAccountInteractor,
private val getAccountsInteractor: GetAccountsInteractor, private val getAccountsInteractor: GetAccountsInteractor,
serverInteractor: GetCurrentServerInteractor, serverInteractor: GetConnectingServerInteractor,
private val saveCurrentServer: SaveCurrentServerInteractor,
settingsInteractor: GetSettingsInteractor settingsInteractor: GetSettingsInteractor
) { ) {
private val currentServer = serverInteractor.get()!! private val currentServer = serverInteractor.get()!!
...@@ -47,6 +48,7 @@ class RegisterUsernamePresenter @Inject constructor( ...@@ -47,6 +48,7 @@ class RegisterUsernamePresenter @Inject constructor(
val registeredUsername = me.username val registeredUsername = me.username
if (registeredUsername != null) { if (registeredUsername != null) {
saveAccount(registeredUsername) saveAccount(registeredUsername)
saveCurrentServer.save(currentServer)
tokenRepository.save(currentServer, Token(userId, authToken)) tokenRepository.save(currentServer, Token(userId, authToken))
registerPushToken() registerPushToken()
navigator.toChatList() navigator.toChatList()
......
...@@ -2,6 +2,7 @@ package chat.rocket.android.authentication.resetpassword.presentation ...@@ -2,6 +2,7 @@ package chat.rocket.android.authentication.resetpassword.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.server.domain.GetConnectingServerInteractor
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.isEmail import chat.rocket.android.util.extensions.isEmail
...@@ -19,7 +20,7 @@ class ResetPasswordPresenter @Inject constructor( ...@@ -19,7 +20,7 @@ class ResetPasswordPresenter @Inject constructor(
private val strategy: CancelStrategy, private val strategy: CancelStrategy,
private val navigator: AuthenticationNavigator, private val navigator: AuthenticationNavigator,
factory: RocketChatClientFactory, factory: RocketChatClientFactory,
serverInteractor: GetCurrentServerInteractor serverInteractor: GetConnectingServerInteractor
) { ) {
private val currentServer = serverInteractor.get()!! private val currentServer = serverInteractor.get()!!
private val client: RocketChatClient = factory.create(currentServer) private val client: RocketChatClient = factory.create(currentServer)
......
...@@ -6,7 +6,7 @@ import chat.rocket.android.core.behaviours.showMessage ...@@ -6,7 +6,7 @@ import chat.rocket.android.core.behaviours.showMessage
import chat.rocket.android.core.lifecycle.CancelStrategy import chat.rocket.android.core.lifecycle.CancelStrategy
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.SaveConnectingServerInteractor
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.extensions.isValidUrl import chat.rocket.android.util.extensions.isValidUrl
...@@ -16,7 +16,7 @@ import javax.inject.Inject ...@@ -16,7 +16,7 @@ import javax.inject.Inject
class ServerPresenter @Inject constructor(private val view: ServerView, class ServerPresenter @Inject constructor(private val view: ServerView,
private val strategy: CancelStrategy, private val strategy: CancelStrategy,
private val navigator: AuthenticationNavigator, private val navigator: AuthenticationNavigator,
private val serverInteractor: SaveCurrentServerInteractor, private val serverInteractor: SaveConnectingServerInteractor,
private val refreshSettingsInteractor: RefreshSettingsInteractor, private val refreshSettingsInteractor: RefreshSettingsInteractor,
private val getAccountsInteractor: GetAccountsInteractor, private val getAccountsInteractor: GetAccountsInteractor,
factory: RocketChatClientFactory factory: RocketChatClientFactory
......
...@@ -22,7 +22,8 @@ class SignupPresenter @Inject constructor( ...@@ -22,7 +22,8 @@ class SignupPresenter @Inject constructor(
private val strategy: CancelStrategy, private val strategy: CancelStrategy,
private val navigator: AuthenticationNavigator, private val navigator: AuthenticationNavigator,
private val localRepository: LocalRepository, private val localRepository: LocalRepository,
private val serverInteractor: GetCurrentServerInteractor, private val serverInteractor: GetConnectingServerInteractor,
private val saveCurrentServerInteractor: SaveCurrentServerInteractor,
private val factory: RocketChatClientFactory, private val factory: RocketChatClientFactory,
private val saveAccountInteractor: SaveAccountInteractor, private val saveAccountInteractor: SaveAccountInteractor,
private val getAccountsInteractor: GetAccountsInteractor, private val getAccountsInteractor: GetAccountsInteractor,
...@@ -60,6 +61,7 @@ class SignupPresenter @Inject constructor( ...@@ -60,6 +61,7 @@ class SignupPresenter @Inject constructor(
// TODO This function returns a user token so should we save it? // TODO This function returns a user token so should we save it?
retryIO("login") { client.login(username, password) } retryIO("login") { client.login(username, password) }
val me = retryIO("me") { client.me() } val me = retryIO("me") { client.me() }
saveCurrentServerInteractor.save(currentServer)
localRepository.save(LocalRepository.CURRENT_USERNAME_KEY, me.username) localRepository.save(LocalRepository.CURRENT_USERNAME_KEY, me.username)
saveAccount(me) saveAccount(me)
registerPushToken() registerPushToken()
......
...@@ -25,7 +25,8 @@ class TwoFAPresenter @Inject constructor(private val view: TwoFAView, ...@@ -25,7 +25,8 @@ class TwoFAPresenter @Inject constructor(private val view: TwoFAView,
private val navigator: AuthenticationNavigator, private val navigator: AuthenticationNavigator,
private val tokenRepository: TokenRepository, private val tokenRepository: TokenRepository,
private val localRepository: LocalRepository, private val localRepository: LocalRepository,
private val serverInteractor: GetCurrentServerInteractor, private val serverInteractor: GetConnectingServerInteractor,
private val saveCurrentServerInteractor: SaveCurrentServerInteractor,
private val factory: RocketChatClientFactory, private val factory: RocketChatClientFactory,
private val saveAccountInteractor: SaveAccountInteractor, private val saveAccountInteractor: SaveAccountInteractor,
private val getAccountsInteractor: GetAccountsInteractor, private val getAccountsInteractor: GetAccountsInteractor,
...@@ -55,6 +56,7 @@ class TwoFAPresenter @Inject constructor(private val view: TwoFAView, ...@@ -55,6 +56,7 @@ class TwoFAPresenter @Inject constructor(private val view: TwoFAView,
} }
val me = retryIO("me") { client.me() } val me = retryIO("me") { client.me() }
saveAccount(me) saveAccount(me)
saveCurrentServerInteractor.save(currentServer)
tokenRepository.save(server, token) tokenRepository.save(server, token)
registerPushToken() registerPushToken()
navigator.toChatList() navigator.toChatList()
......
...@@ -32,17 +32,25 @@ class AuthenticationActivity : AppCompatActivity(), HasSupportFragmentInjector { ...@@ -32,17 +32,25 @@ class AuthenticationActivity : AppCompatActivity(), HasSupportFragmentInjector {
setContentView(R.layout.activity_authentication) setContentView(R.layout.activity_authentication)
setTheme(R.style.AuthenticationTheme) setTheme(R.style.AuthenticationTheme)
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
}
override fun onStart() {
super.onStart()
val deepLinkInfo = intent.getLoginDeepLinkInfo() val deepLinkInfo = intent.getLoginDeepLinkInfo()
launch(UI + job) { launch(UI + job) {
val newServer = intent.getBooleanExtra(INTENT_ADD_NEW_SERVER, false) val newServer = intent.getBooleanExtra(INTENT_ADD_NEW_SERVER, false)
// if we got authenticateWithDeepLink information, pass true to newServer also // if we got authenticateWithDeepLink information, pass true to newServer also
presenter.loadCredentials(newServer || deepLinkInfo != null) { authenticated -> presenter.loadCredentials(newServer || deepLinkInfo != null) { authenticated ->
if (!authenticated) { if (!authenticated) {
showServerInput(savedInstanceState, deepLinkInfo) showServerInput(deepLinkInfo)
}
} }
} }
} }
override fun onStop() {
job.cancel()
super.onStop()
} }
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
...@@ -53,17 +61,12 @@ class AuthenticationActivity : AppCompatActivity(), HasSupportFragmentInjector { ...@@ -53,17 +61,12 @@ class AuthenticationActivity : AppCompatActivity(), HasSupportFragmentInjector {
} }
} }
override fun onDestroy() {
job.cancel()
super.onDestroy()
}
override fun supportFragmentInjector(): AndroidInjector<Fragment> { override fun supportFragmentInjector(): AndroidInjector<Fragment> {
return fragmentDispatchingAndroidInjector return fragmentDispatchingAndroidInjector
} }
fun showServerInput(savedInstanceState: Bundle?, deepLinkInfo: LoginDeepLinkInfo?) { fun showServerInput(deepLinkInfo: LoginDeepLinkInfo?) {
addFragment("ServerFragment", R.id.fragment_container) { addFragment("ServerFragment", R.id.fragment_container, allowStateLoss = true) {
ServerFragment.newInstance(deepLinkInfo) ServerFragment.newInstance(deepLinkInfo)
} }
} }
......
package chat.rocket.android.chatroom.di package chat.rocket.android.chatroom.di
import android.arch.lifecycle.LifecycleOwner import android.arch.lifecycle.LifecycleOwner
import chat.rocket.android.chatroom.presentation.ChatRoomNavigator
import chat.rocket.android.chatroom.presentation.ChatRoomView import chat.rocket.android.chatroom.presentation.ChatRoomView
import chat.rocket.android.chatroom.ui.ChatRoomActivity
import chat.rocket.android.chatroom.ui.ChatRoomFragment import chat.rocket.android.chatroom.ui.ChatRoomFragment
import chat.rocket.android.core.lifecycle.CancelStrategy import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.dagger.scope.PerFragment import chat.rocket.android.dagger.scope.PerFragment
...@@ -12,20 +10,22 @@ import dagger.Provides ...@@ -12,20 +10,22 @@ import dagger.Provides
import kotlinx.coroutines.experimental.Job import kotlinx.coroutines.experimental.Job
@Module @Module
@PerFragment
class ChatRoomFragmentModule { class ChatRoomFragmentModule {
@Provides @Provides
@PerFragment
fun chatRoomView(frag: ChatRoomFragment): ChatRoomView { fun chatRoomView(frag: ChatRoomFragment): ChatRoomView {
return frag return frag
} }
@Provides @Provides
@PerFragment
fun provideLifecycleOwner(frag: ChatRoomFragment): LifecycleOwner { fun provideLifecycleOwner(frag: ChatRoomFragment): LifecycleOwner {
return frag return frag
} }
@Provides @Provides
@PerFragment
fun provideCancelStrategy(owner: LifecycleOwner, jobs: Job): CancelStrategy { fun provideCancelStrategy(owner: LifecycleOwner, jobs: Job): CancelStrategy {
return CancelStrategy(owner, jobs) return CancelStrategy(owner, jobs)
} }
......
package chat.rocket.android.chatroom.di package chat.rocket.android.chatroom.di
import chat.rocket.android.chatroom.ui.ChatRoomFragment import chat.rocket.android.chatroom.ui.ChatRoomFragment
import chat.rocket.android.dagger.scope.PerFragment
import dagger.Module import dagger.Module
import dagger.android.ContributesAndroidInjector import dagger.android.ContributesAndroidInjector
...@@ -8,5 +9,6 @@ import dagger.android.ContributesAndroidInjector ...@@ -8,5 +9,6 @@ import dagger.android.ContributesAndroidInjector
abstract class ChatRoomFragmentProvider { abstract class ChatRoomFragmentProvider {
@ContributesAndroidInjector(modules = [ChatRoomFragmentModule::class]) @ContributesAndroidInjector(modules = [ChatRoomFragmentModule::class])
@PerFragment
abstract fun provideChatRoomFragment(): ChatRoomFragment abstract fun provideChatRoomFragment(): ChatRoomFragment
} }
\ No newline at end of file
...@@ -818,7 +818,7 @@ class ChatRoomPresenter @Inject constructor( ...@@ -818,7 +818,7 @@ class ChatRoomPresenter @Inject constructor(
} }
} }
if (typingStatusList.isNotEmpty()) { if (typingStatusList.isNotEmpty()) {
view.showTypingStatus(typingStatusList) view.showTypingStatus(typingStatusList.toList()) // copy typingStatusList
} else { } else {
view.hideTypingStatusView() view.hideTypingStatusView()
} }
......
...@@ -31,7 +31,7 @@ interface ChatRoomView : LoadingView, MessageView { ...@@ -31,7 +31,7 @@ interface ChatRoomView : LoadingView, MessageView {
* *
* @param usernameList The list of username to show. * @param usernameList The list of username to show.
*/ */
fun showTypingStatus(usernameList: ArrayList<String>) fun showTypingStatus(usernameList: List<String>)
/** /**
* Hides the typing status view. * Hides the typing status view.
......
...@@ -355,7 +355,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -355,7 +355,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
} }
} }
override fun showTypingStatus(usernameList: ArrayList<String>) { override fun showTypingStatus(usernameList: List<String>) {
ui { ui {
when (usernameList.size) { when (usernameList.size) {
1 -> { 1 -> {
......
...@@ -14,6 +14,7 @@ import androidx.core.text.color ...@@ -14,6 +14,7 @@ import androidx.core.text.color
import androidx.core.text.scale import androidx.core.text.scale
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.chatroom.domain.MessageReply import chat.rocket.android.chatroom.domain.MessageReply
import chat.rocket.android.dagger.scope.PerFragment
import chat.rocket.android.helper.MessageHelper import chat.rocket.android.helper.MessageHelper
import chat.rocket.android.helper.MessageParser import chat.rocket.android.helper.MessageParser
import chat.rocket.android.infrastructure.LocalRepository import chat.rocket.android.infrastructure.LocalRepository
...@@ -47,6 +48,7 @@ import okhttp3.HttpUrl ...@@ -47,6 +48,7 @@ import okhttp3.HttpUrl
import java.security.InvalidParameterException import java.security.InvalidParameterException
import javax.inject.Inject import javax.inject.Inject
@PerFragment
class ViewModelMapper @Inject constructor( class ViewModelMapper @Inject constructor(
private val context: Context, private val context: Context,
private val parser: MessageParser, private val parser: MessageParser,
...@@ -59,8 +61,8 @@ class ViewModelMapper @Inject constructor( ...@@ -59,8 +61,8 @@ class ViewModelMapper @Inject constructor(
) { ) {
private val currentServer = serverInteractor.get()!! private val currentServer = serverInteractor.get()!!
private val settings: Map<String, Value<Any>> = getSettingsInteractor.get(currentServer) private val settings = getSettingsInteractor.get(currentServer)
private val baseUrl = settings.baseUrl() private val baseUrl = currentServer
private val token = tokenRepository.get(currentServer) private val token = tokenRepository.get(currentServer)
private val currentUsername: String? = localRepository.get(LocalRepository.CURRENT_USERNAME_KEY) private val currentUsername: String? = localRepository.get(LocalRepository.CURRENT_USERNAME_KEY)
private val secondaryTextColor = ContextCompat.getColor(context, R.color.colorSecondaryText) private val secondaryTextColor = ContextCompat.getColor(context, R.color.colorSecondaryText)
......
package chat.rocket.android.chatrooms.di package chat.rocket.android.chatrooms.di
import chat.rocket.android.chatrooms.ui.ChatRoomsFragment import chat.rocket.android.chatrooms.ui.ChatRoomsFragment
import chat.rocket.android.dagger.scope.PerFragment
import dagger.Module import dagger.Module
import dagger.android.ContributesAndroidInjector import dagger.android.ContributesAndroidInjector
...@@ -8,5 +9,6 @@ import dagger.android.ContributesAndroidInjector ...@@ -8,5 +9,6 @@ import dagger.android.ContributesAndroidInjector
abstract class ChatRoomsFragmentProvider { abstract class ChatRoomsFragmentProvider {
@ContributesAndroidInjector(modules = [ChatRoomsFragmentModule::class]) @ContributesAndroidInjector(modules = [ChatRoomsFragmentModule::class])
@PerFragment
abstract fun provideChatRoomsFragment(): ChatRoomsFragment abstract fun provideChatRoomsFragment(): ChatRoomsFragment
} }
\ No newline at end of file
...@@ -3,6 +3,7 @@ package chat.rocket.android.chatrooms.ui ...@@ -3,6 +3,7 @@ package chat.rocket.android.chatrooms.ui
import android.app.AlertDialog import android.app.AlertDialog
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.renderscript.RSInvalidStateException
import android.support.v4.app.Fragment import android.support.v4.app.Fragment
import android.support.v7.app.AppCompatActivity import android.support.v7.app.AppCompatActivity
import android.support.v7.util.DiffUtil import android.support.v7.util.DiffUtil
...@@ -37,11 +38,13 @@ import chat.rocket.android.widget.DividerItemDecoration ...@@ -37,11 +38,13 @@ import chat.rocket.android.widget.DividerItemDecoration
import chat.rocket.common.model.RoomType import chat.rocket.common.model.RoomType
import chat.rocket.core.internal.realtime.socket.model.State import chat.rocket.core.internal.realtime.socket.model.State
import chat.rocket.core.model.ChatRoom import chat.rocket.core.model.ChatRoom
import com.crashlytics.android.Crashlytics
import dagger.android.support.AndroidSupportInjection import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_chat_rooms.* import kotlinx.android.synthetic.main.fragment_chat_rooms.*
import kotlinx.coroutines.experimental.Job import kotlinx.coroutines.experimental.Job
import kotlinx.coroutines.experimental.NonCancellable.isActive import kotlinx.coroutines.experimental.NonCancellable.isActive
import timber.log.Timber import timber.log.Timber
import java.security.InvalidParameterException
import javax.inject.Inject import javax.inject.Inject
private const val BUNDLE_CHAT_ROOM_ID = "BUNDLE_CHAT_ROOM_ID" private const val BUNDLE_CHAT_ROOM_ID = "BUNDLE_CHAT_ROOM_ID"
......
...@@ -14,6 +14,7 @@ import chat.rocket.android.app.RocketChatDatabase ...@@ -14,6 +14,7 @@ import chat.rocket.android.app.RocketChatDatabase
import chat.rocket.android.authentication.infraestructure.SharedPreferencesMultiServerTokenRepository import chat.rocket.android.authentication.infraestructure.SharedPreferencesMultiServerTokenRepository
import chat.rocket.android.authentication.infraestructure.SharedPreferencesTokenRepository import chat.rocket.android.authentication.infraestructure.SharedPreferencesTokenRepository
import chat.rocket.android.chatroom.service.MessageService import chat.rocket.android.chatroom.service.MessageService
import chat.rocket.android.dagger.qualifier.ForAuthentication
import chat.rocket.android.dagger.qualifier.ForMessages import chat.rocket.android.dagger.qualifier.ForMessages
import chat.rocket.android.helper.MessageParser import chat.rocket.android.helper.MessageParser
import chat.rocket.android.infrastructure.LocalRepository import chat.rocket.android.infrastructure.LocalRepository
...@@ -23,6 +24,7 @@ import chat.rocket.android.push.PushManager ...@@ -23,6 +24,7 @@ import chat.rocket.android.push.PushManager
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.HttpLoggingInterceptor
import chat.rocket.android.util.TimberLogger import chat.rocket.android.util.TimberLogger
import chat.rocket.common.internal.FallbackSealedClassJsonAdapter import chat.rocket.common.internal.FallbackSealedClassJsonAdapter
import chat.rocket.common.internal.ISO8601Date import chat.rocket.common.internal.ISO8601Date
...@@ -42,7 +44,6 @@ import dagger.Module ...@@ -42,7 +44,6 @@ import dagger.Module
import dagger.Provides import dagger.Provides
import kotlinx.coroutines.experimental.Job import kotlinx.coroutines.experimental.Job
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import ru.noties.markwon.SpannableConfiguration import ru.noties.markwon.SpannableConfiguration
import ru.noties.markwon.spans.SpannableTheme import ru.noties.markwon.spans.SpannableTheme
import timber.log.Timber import timber.log.Timber
...@@ -54,7 +55,9 @@ class AppModule { ...@@ -54,7 +55,9 @@ class AppModule {
@Provides @Provides
@Singleton @Singleton
fun provideRocketChatClient(okHttpClient: OkHttpClient, repository: TokenRepository, logger: PlatformLogger): RocketChatClient { fun provideRocketChatClient(okHttpClient: OkHttpClient,
repository: TokenRepository,
logger: PlatformLogger): RocketChatClient {
return RocketChatClient.create { return RocketChatClient.create {
httpClient = okHttpClient httpClient = okHttpClient
tokenRepository = repository tokenRepository = repository
...@@ -68,7 +71,8 @@ class AppModule { ...@@ -68,7 +71,8 @@ class AppModule {
@Provides @Provides
@Singleton @Singleton
fun provideRocketChatDatabase(context: Application): RocketChatDatabase { fun provideRocketChatDatabase(context: Application): RocketChatDatabase {
return Room.databaseBuilder(context.applicationContext, RocketChatDatabase::class.java, "rocketchat-db").build() return Room.databaseBuilder(context.applicationContext, RocketChatDatabase::class.java,
"rocketchat-db").build()
} }
@Provides @Provides
...@@ -91,8 +95,10 @@ class AppModule { ...@@ -91,8 +95,10 @@ class AppModule {
@Provides @Provides
@Singleton @Singleton
fun provideHttpLoggingInterceptor(): HttpLoggingInterceptor { fun provideHttpLoggingInterceptor(): HttpLoggingInterceptor {
val interceptor = HttpLoggingInterceptor(HttpLoggingInterceptor.Logger { message -> val interceptor = HttpLoggingInterceptor(object : HttpLoggingInterceptor.Logger {
override fun log(message: String) {
Timber.d(message) Timber.d(message)
}
}) })
if (BuildConfig.DEBUG) { if (BuildConfig.DEBUG) {
interceptor.level = HttpLoggingInterceptor.Level.BODY interceptor.level = HttpLoggingInterceptor.Level.BODY
...@@ -168,6 +174,12 @@ class AppModule { ...@@ -168,6 +174,12 @@ class AppModule {
return SharedPrefsCurrentServerRepository(prefs) return SharedPrefsCurrentServerRepository(prefs)
} }
@Provides
@ForAuthentication
fun provideConnectingServerRepository(prefs: SharedPreferences): CurrentServerRepository {
return SharedPrefsConnectingServerRepository(prefs)
}
@Provides @Provides
@Singleton @Singleton
fun provideSettingsRepository(localRepository: LocalRepository): SettingsRepository { fun provideSettingsRepository(localRepository: LocalRepository): SettingsRepository {
......
package chat.rocket.android.dagger.qualifier
import javax.inject.Qualifier
@Qualifier
@Retention(AnnotationRetention.RUNTIME)
annotation class ForAuthentication
\ No newline at end of file
...@@ -2,10 +2,6 @@ package chat.rocket.android.dagger.qualifier ...@@ -2,10 +2,6 @@ package chat.rocket.android.dagger.qualifier
import javax.inject.Qualifier import javax.inject.Qualifier
/**
* Created by luciofm on 4/14/18.
*/
@Qualifier @Qualifier
@Retention(AnnotationRetention.RUNTIME) @Retention(AnnotationRetention.RUNTIME)
annotation class ForMessages annotation class ForMessages
\ No newline at end of file
package chat.rocket.android.chatroom.di package chat.rocket.android.chatroom.di
import chat.rocket.android.dagger.scope.PerFragment
import chat.rocket.android.favoritemessages.ui.FavoriteMessagesFragment import chat.rocket.android.favoritemessages.ui.FavoriteMessagesFragment
import dagger.Module import dagger.Module
import dagger.android.ContributesAndroidInjector import dagger.android.ContributesAndroidInjector
...@@ -8,5 +9,6 @@ import dagger.android.ContributesAndroidInjector ...@@ -8,5 +9,6 @@ import dagger.android.ContributesAndroidInjector
abstract class FavoriteMessagesFragmentProvider { abstract class FavoriteMessagesFragmentProvider {
@ContributesAndroidInjector(modules = [FavoriteMessagesFragmentModule::class]) @ContributesAndroidInjector(modules = [FavoriteMessagesFragmentModule::class])
@PerFragment
abstract fun provideFavoriteMessageFragment(): FavoriteMessagesFragment abstract fun provideFavoriteMessageFragment(): FavoriteMessagesFragment
} }
\ No newline at end of file
package chat.rocket.android.infrastructure
import android.app.Application
import chat.rocket.android.BuildConfig
import chat.rocket.android.server.domain.AccountsRepository
import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.domain.GetSettingsInteractor
import chat.rocket.android.server.domain.SITE_URL
import com.crashlytics.android.Crashlytics
import kotlinx.coroutines.experimental.runBlocking
fun installCrashlyticsWrapper(context: Application,
currentServerInteractor: GetCurrentServerInteractor,
settingsInteractor: GetSettingsInteractor,
accountRepository: AccountsRepository,
localRepository: LocalRepository) {
if (isCrashlyticsEnabled()) {
Thread.setDefaultUncaughtExceptionHandler(RocketChatUncaughtExceptionHandler(currentServerInteractor,
settingsInteractor, accountRepository, localRepository))
}
}
private fun isCrashlyticsEnabled(): Boolean {
return !BuildConfig.DEBUG
}
private class RocketChatUncaughtExceptionHandler(
val currentServerInteractor: GetCurrentServerInteractor,
val settingsInteractor: GetSettingsInteractor,
val accountRepository: AccountsRepository,
val localRepository: LocalRepository)
: Thread.UncaughtExceptionHandler {
val crashlyticsHandler: Thread.UncaughtExceptionHandler? = Thread.getDefaultUncaughtExceptionHandler()
override fun uncaughtException(t: Thread, e: Throwable) {
val currentServer = currentServerInteractor.get() ?: "<unknown>"
Crashlytics.setString(KEY_CURRENT_SERVER, currentServer)
runBlocking {
val accounts = accountRepository.load()
Crashlytics.setString(KEY_ACCOUNTS, accounts.toString())
}
val settings = settingsInteractor.get(currentServer)
Crashlytics.setInt(KEY_SETTINGS_SIZE, settings.size)
val baseUrl = settings[SITE_URL]?.toString()
Crashlytics.setString(KEY_SETTINGS_BASE_URL, baseUrl)
val user = localRepository.getCurrentUser(currentServer)
Crashlytics.setString(KEY_CURRENT_USER, user?.toString())
Crashlytics.setString(KEY_CURRENT_USERNAME, localRepository.username())
if (crashlyticsHandler != null) {
crashlyticsHandler.uncaughtException(t, e)
} else {
throw RuntimeException("Missing default exception handler")
}
}
}
private const val KEY_CURRENT_SERVER = "CURRENT_SERVER"
private const val KEY_CURRENT_USER = "CURRENT_USER"
private const val KEY_CURRENT_USERNAME = "CURRENT_USERNAME"
private const val KEY_ACCOUNTS = "ACCOUNTS"
private const val KEY_SETTINGS_SIZE = "SETTINGS_SIZE"
private const val KEY_SETTINGS_BASE_URL = "SETTINGS_BASE_URL"
\ No newline at end of file
package chat.rocket.android.pinnedmessages.di package chat.rocket.android.pinnedmessages.di
import chat.rocket.android.chatroom.di.PinnedMessagesFragmentModule import chat.rocket.android.chatroom.di.PinnedMessagesFragmentModule
import chat.rocket.android.dagger.scope.PerFragment
import chat.rocket.android.pinnedmessages.ui.PinnedMessagesFragment import chat.rocket.android.pinnedmessages.ui.PinnedMessagesFragment
import dagger.Module import dagger.Module
import dagger.android.ContributesAndroidInjector import dagger.android.ContributesAndroidInjector
...@@ -9,5 +10,6 @@ import dagger.android.ContributesAndroidInjector ...@@ -9,5 +10,6 @@ import dagger.android.ContributesAndroidInjector
abstract class PinnedMessagesFragmentProvider { abstract class PinnedMessagesFragmentProvider {
@ContributesAndroidInjector(modules = [PinnedMessagesFragmentModule::class]) @ContributesAndroidInjector(modules = [PinnedMessagesFragmentModule::class])
@PerFragment
abstract fun providePinnedMessageFragment(): PinnedMessagesFragment abstract fun providePinnedMessageFragment(): PinnedMessagesFragment
} }
\ No newline at end of file
package chat.rocket.android.server.domain
import chat.rocket.android.dagger.qualifier.ForAuthentication
import javax.inject.Inject
class GetConnectingServerInteractor @Inject constructor(
@ForAuthentication private val repository: CurrentServerRepository
) {
fun get(): String? = repository.get()
fun clear() {
repository.clear()
}
}
\ No newline at end of file
package chat.rocket.android.server.domain
import chat.rocket.android.dagger.qualifier.ForAuthentication
import javax.inject.Inject
class SaveConnectingServerInteractor @Inject constructor(
@ForAuthentication private val repository: CurrentServerRepository
) {
fun save(url: String) = repository.save(url)
}
\ No newline at end of file
...@@ -5,11 +5,6 @@ import chat.rocket.core.model.Value ...@@ -5,11 +5,6 @@ import chat.rocket.core.model.Value
typealias PublicSettings = Map<String, Value<Any>> typealias PublicSettings = Map<String, Value<Any>>
interface SettingsRepository {
fun save(url: String, settings: PublicSettings)
fun get(url: String): PublicSettings
}
// Authentication methods. // Authentication methods.
const val LDAP_ENABLE = "LDAP_Enable" const val LDAP_ENABLE = "LDAP_Enable"
const val CAS_ENABLE = "CAS_enabled" const val CAS_ENABLE = "CAS_enabled"
...@@ -105,3 +100,8 @@ fun PublicSettings.uploadMaxFileSize(): Int { ...@@ -105,3 +100,8 @@ 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? fun PublicSettings.siteName(): String? = this[SITE_NAME]?.value as String?
interface SettingsRepository {
fun save(url: String, settings: PublicSettings)
fun get(url: String): PublicSettings
}
\ No newline at end of file
...@@ -5,6 +5,7 @@ import chat.rocket.android.infrastructure.LocalRepository.Companion.SETTINGS_KEY ...@@ -5,6 +5,7 @@ import chat.rocket.android.infrastructure.LocalRepository.Companion.SETTINGS_KEY
import chat.rocket.android.server.domain.PublicSettings import chat.rocket.android.server.domain.PublicSettings
import chat.rocket.android.server.domain.SettingsRepository import chat.rocket.android.server.domain.SettingsRepository
import chat.rocket.core.internal.SettingsAdapter import chat.rocket.core.internal.SettingsAdapter
import timber.log.Timber
class SharedPreferencesSettingsRepository( class SharedPreferencesSettingsRepository(
private val localRepository: LocalRepository private val localRepository: LocalRepository
...@@ -13,11 +14,27 @@ class SharedPreferencesSettingsRepository( ...@@ -13,11 +14,27 @@ class SharedPreferencesSettingsRepository(
private val adapter = SettingsAdapter().lenient() private val adapter = SettingsAdapter().lenient()
override fun save(url: String, settings: PublicSettings) { override fun save(url: String, settings: PublicSettings) {
if (settings.isEmpty()) {
val message = "Saving empty settings for $SETTINGS_KEY$url"
Timber.d(IllegalStateException(message), message)
}
localRepository.save("$SETTINGS_KEY$url", adapter.toJson(settings)) localRepository.save("$SETTINGS_KEY$url", adapter.toJson(settings))
} }
override fun get(url: String): PublicSettings { override fun get(url: String): PublicSettings {
val settings = localRepository.get("$SETTINGS_KEY$url") val settingsStr = localRepository.get("$SETTINGS_KEY$url")
return if (settings == null) hashMapOf() else adapter.fromJson(settings) ?: hashMapOf() return if (settingsStr == null) {
val message = "NULL Settings for: $SETTINGS_KEY$url"
Timber.d(IllegalStateException(message), message)
hashMapOf()
} else {
val settings = adapter.fromJson(settingsStr)
if (settings == null) {
val message = "NULL Settings for: $SETTINGS_KEY$url with saved settings: $settingsStr"
Timber.d(IllegalStateException(message), message)
}
settings ?: hashMapOf()
}
} }
} }
\ No newline at end of file
package chat.rocket.android.server.infraestructure
import android.content.SharedPreferences
import chat.rocket.android.server.domain.CurrentServerRepository
class SharedPrefsConnectingServerRepository(private val preferences: SharedPreferences) : CurrentServerRepository {
override fun save(url: String) {
preferences.edit().putString(CONNECTING_SERVER_KEY, url).apply()
}
override fun get(): String? {
return preferences.getString(CONNECTING_SERVER_KEY, null)
}
companion object {
private const val CONNECTING_SERVER_KEY = "connecting_server"
}
override fun clear() {
preferences.edit().remove(CONNECTING_SERVER_KEY).apply()
}
}
\ No newline at end of file
...@@ -31,11 +31,16 @@ fun View.isVisible(): Boolean { ...@@ -31,11 +31,16 @@ fun View.isVisible(): Boolean {
fun ViewGroup.inflate(@LayoutRes resource: Int, attachToRoot: Boolean = false): View = fun ViewGroup.inflate(@LayoutRes resource: Int, attachToRoot: Boolean = false): View =
LayoutInflater.from(context).inflate(resource, this, attachToRoot) LayoutInflater.from(context).inflate(resource, this, attachToRoot)
fun AppCompatActivity.addFragment(tag: String, layoutId: Int, newInstance: () -> Fragment) { fun AppCompatActivity.addFragment(tag: String, layoutId: Int, allowStateLoss: Boolean = false,
newInstance: () -> Fragment) {
val fragment = supportFragmentManager.findFragmentByTag(tag) ?: newInstance() val fragment = supportFragmentManager.findFragmentByTag(tag) ?: newInstance()
supportFragmentManager.beginTransaction() val transaction = supportFragmentManager.beginTransaction()
.replace(layoutId, fragment, tag) .replace(layoutId, fragment, tag)
.commit() if (allowStateLoss) {
transaction.commitAllowingStateLoss()
} else {
transaction.commit()
}
} }
fun AppCompatActivity.addFragmentBackStack( fun AppCompatActivity.addFragmentBackStack(
......
...@@ -5,12 +5,17 @@ import android.support.customtabs.CustomTabsIntent ...@@ -5,12 +5,17 @@ import android.support.customtabs.CustomTabsIntent
import android.support.v4.content.res.ResourcesCompat import android.support.v4.content.res.ResourcesCompat
import android.view.View import android.view.View
import chat.rocket.android.R import chat.rocket.android.R
import timber.log.Timber
fun View.openTabbedUrl(url: Uri) { fun View.openTabbedUrl(url: Uri) {
with(this) { with(this) {
val tabsbuilder = CustomTabsIntent.Builder() val tabsbuilder = CustomTabsIntent.Builder()
tabsbuilder.setToolbarColor(ResourcesCompat.getColor(context.resources, R.color.colorPrimary, context.theme)) tabsbuilder.setToolbarColor(ResourcesCompat.getColor(context.resources, R.color.colorPrimary, context.theme))
val customTabsIntent = tabsbuilder.build() val customTabsIntent = tabsbuilder.build()
try {
customTabsIntent.launchUrl(context, url) customTabsIntent.launchUrl(context, url)
} catch (ex: Exception) {
Timber.d(ex, "Unable to launch URL")
}
} }
} }
\ No newline at end of file
...@@ -4,8 +4,7 @@ ...@@ -4,8 +4,7 @@
<string name="title_sign_in_your_server">अपने सर्वर में साइन इन करें</string> <string name="title_sign_in_your_server">अपने सर्वर में साइन इन करें</string>
<string name="title_log_in">लॉग इन करें</string> <string name="title_log_in">लॉग इन करें</string>
<string name="title_register_username">रजिस्टर उपयोगकर्ता नाम</string> <string name="title_register_username">रजिस्टर उपयोगकर्ता नाम</string>
// TODO: Add proper translation. <string name="title_reset_password">पासवर्ड रीसेट करें</string>
<string name="title_reset_password">Reset password</string>
<string name="title_sign_up">साइन अप करें</string> <string name="title_sign_up">साइन अप करें</string>
<string name="title_authentication">प्रमाणीकरण</string> <string name="title_authentication">प्रमाणीकरण</string>
<string name="title_legal_terms">कानूनी शर्तें</string> <string name="title_legal_terms">कानूनी शर्तें</string>
...@@ -40,8 +39,7 @@ ...@@ -40,8 +39,7 @@
<string name="action_away">दूर</string> <string name="action_away">दूर</string>
<string name="action_busy">व्यस्त</string> <string name="action_busy">व्यस्त</string>
<string name="action_invisible">अदृश्य</string> <string name="action_invisible">अदृश्य</string>
// TODO: Add proper translation. <string name="action_save_to_gallery">गैलरी में सहेजें</string>
<string name="action_save_to_gallery">Save to gallery</string>
<!-- Settings List --> <!-- Settings List -->
<string-array name="settings_actions"> <string-array name="settings_actions">
...@@ -61,14 +59,10 @@ ...@@ -61,14 +59,10 @@
<string name="msg_avatar_url">अवतार यूआरएल</string> <string name="msg_avatar_url">अवतार यूआरएल</string>
<string name="msg_or_continue_using_social_accounts">या सामाजिक खाते का उपयोग करना जारी रखें</string> <string name="msg_or_continue_using_social_accounts">या सामाजिक खाते का उपयोग करना जारी रखें</string>
<string name="msg_new_user">नया उपयोगकर्ता? %1$s</string> <string name="msg_new_user">नया उपयोगकर्ता? %1$s</string>
// TODO: Add proper translation. <string name="msg_forgot_password">पासवर्ड भूल गए? %1$s</string>
<string name="msg_forgot_password">Forgot password? %1$s</string> <string name="msg_reset">रीसेट करें</string>
// TODO: Add proper translation. <string name="msg_check_your_email_to_reset_your_password">ईमेल गया गया है! अपना पासवर्ड रीसेट करने के लिए अपने इनबॉक्स की जांच करें।</string>
<string name="msg_reset">Reset</string> <string name="msg_invalid_email">कृपया एक वैध ई-मेल टाइप करें</string>
// TODO: Add proper translation.
<string name="msg_check_your_email_to_reset_your_password">Email sent! Check your inbox to reset your password.</string>
// TODO: Add proper translation.
<string name="msg_invalid_email">Please type a valid e-mail</string>
<string name="msg_new_user_agreement">आगे बढ़कर आप हमारे %1$s और %2$s से सहमत हो रहे हैं</string> <string name="msg_new_user_agreement">आगे बढ़कर आप हमारे %1$s और %2$s से सहमत हो रहे हैं</string>
<string name="msg_2fa_code">कोड 2FA</string> <string name="msg_2fa_code">कोड 2FA</string>
<string name="msg_yesterday">कल</string> <string name="msg_yesterday">कल</string>
...@@ -100,9 +94,8 @@ ...@@ -100,9 +94,8 @@
<string name="msg_no_messages_yet">अभी तक कोई पोस्ट नहीं</string> <string name="msg_no_messages_yet">अभी तक कोई पोस्ट नहीं</string>
<string name="msg_version">वर्शन %1$s</string> <string name="msg_version">वर्शन %1$s</string>
<string name="msg_build">बिल्ड %1$d</string> <string name="msg_build">बिल्ड %1$d</string>
// TODO: Add proper translation. <string name="msg_update_app_version_in_order_to_continue">पुराना सर्वर वर्शन। जारी रखने के लिए सर्वर वर्शन को अद्यतन करने के लिए कृपया सर्वर व्यवस्थापक से संपर्क करें।</string>
<string name="msg_update_app_version_in_order_to_continue">Out to date server version. Please contact the server admin to update the server version in order to continue.</string> <string name="msg_ok">ठीक है</string>
<string name="msg_ok">OK</string>
<string name="msg_ver_not_recommended"> <string name="msg_ver_not_recommended">
ऐसा लगता है कि आपका सर्वर संस्करण अनुशंसित संस्करण %1$s के नीचे है।\nआप अभी भी लॉगिन कर सकते हैं लेकिन आप अप्रत्याशित व्यवहार का अनुभव कर सकते हैं ऐसा लगता है कि आपका सर्वर संस्करण अनुशंसित संस्करण %1$s के नीचे है।\nआप अभी भी लॉगिन कर सकते हैं लेकिन आप अप्रत्याशित व्यवहार का अनुभव कर सकते हैं
</string> </string>
...@@ -120,14 +113,10 @@ ...@@ -120,14 +113,10 @@
<string name="msg_no_chat_title">कोई चैट संदेश नहीं</string> <string name="msg_no_chat_title">कोई चैट संदेश नहीं</string>
<string name="msg_no_chat_description">यहां अपने संदेश देखने के लिए\nबातचीत शुरू करें।</string> <string name="msg_no_chat_description">यहां अपने संदेश देखने के लिए\nबातचीत शुरू करें।</string>
<string name="msg_edited">(संपादित)</string> <string name="msg_edited">(संपादित)</string>
// TODO: Add proper translation. <string name="msg_and">\u0020और\u0020</string>
<string name="msg_and">\u0020and\u0020</string> <string name="msg_is_typing">\u0020टाइप कर रहा/रही है…</string>
// TODO: Add proper translation. <string name="msg_are_typing">\u0020टाइप कर रहे हैं…</string>
<string name="msg_is_typing">\u0020is typing…</string> <string name="msg_several_users_are_typing">कई उपयोगकर्ता टाइप कर रहे हैं…</string>
// TODO: Add proper translation.
<string name="msg_are_typing">\u0020are typing…</string>
// TODO: Add proper translation.
<string name="msg_several_users_are_typing">Several users are typing…</string>
<string name="msg_no_search_found">कोई परिणाम नहीं मिला</string> <string name="msg_no_search_found">कोई परिणाम नहीं मिला</string>
// TODO: Add proper translation. // TODO: Add proper translation.
<string name="msg_channel_name">Channel name</string> <string name="msg_channel_name">Channel name</string>
...@@ -161,8 +150,7 @@ ...@@ -161,8 +150,7 @@
<string name="message_unmuted">उपयोगकर्ता %1$s %2$s द्वारा अनम्यूट किया गया</string> <string name="message_unmuted">उपयोगकर्ता %1$s %2$s द्वारा अनम्यूट किया गया</string>
<string name="message_role_add">%1$s %3$s द्वारा %2$s सेट किया गया था</string> <string name="message_role_add">%1$s %3$s द्वारा %2$s सेट किया गया था</string>
<string name="message_role_removed">%1$s अब %3$s द्वारा %2$s नहीं है</string> <string name="message_role_removed">%1$s अब %3$s द्वारा %2$s नहीं है</string>
// TODO:Add proper translation. <string name="message_credentials_saved_successfully">प्रमाण पत्र सफलतापूर्वक सहेजे गए</string>
<string name="message_credentials_saved_successfully">Credentials saved successfully</string>
<!-- Message actions --> <!-- Message actions -->
...@@ -174,8 +162,7 @@ ...@@ -174,8 +162,7 @@
<string name="action_msg_pin">संदेश को पिन करें</string> <string name="action_msg_pin">संदेश को पिन करें</string>
<string name="action_msg_unpin">संदेश को पिन से हटाएँ</string> <string name="action_msg_unpin">संदेश को पिन से हटाएँ</string>
<string name="action_msg_star">संदेश को स्टार करें</string> <string name="action_msg_star">संदेश को स्टार करें</string>
// TODO: Add proper translation. <string name="action_msg_unstar">सन्देश अतारांकित करें</string>
<string name="action_msg_unstar">Unstar Message</string>
<string name="action_msg_share">शेयर करें</string> <string name="action_msg_share">शेयर करें</string>
<string name="action_title_editing">संपादन संदेश</string> <string name="action_title_editing">संपादन संदेश</string>
<string name="action_msg_add_reaction">प्रतिक्रिया जोड़ें</string> <string name="action_msg_add_reaction">प्रतिक्रिया जोड़ें</string>
...@@ -184,8 +171,7 @@ ...@@ -184,8 +171,7 @@
<string name="permission_editing_not_allowed">संपादन की अनुमति नहीं है</string> <string name="permission_editing_not_allowed">संपादन की अनुमति नहीं है</string>
<string name="permission_deleting_not_allowed">हटाने की अनुमति नहीं है</string> <string name="permission_deleting_not_allowed">हटाने की अनुमति नहीं है</string>
<string name="permission_pinning_not_allowed">पिनि करने की अनुमति नहीं है</string> <string name="permission_pinning_not_allowed">पिनि करने की अनुमति नहीं है</string>
// TODO: Add proper translation. <string name="permission_starring_not_allowed">तारांकित की अनुमति नहीं है</string>
<string name="permission_starring_not_allowed">Starring is not allowed</string>
<!-- Members List --> <!-- Members List -->
<string name="title_members_list">सदस्य</string> <string name="title_members_list">सदस्य</string>
...@@ -196,17 +182,15 @@ ...@@ -196,17 +182,15 @@
<string name="no_pinned_description">सभी पिन किए गए संदेश यहां\nदिखाई देते हैं।</string> <string name="no_pinned_description">सभी पिन किए गए संदेश यहां\nदिखाई देते हैं।</string>
<!-- Favorite Messages --> <!-- Favorite Messages -->
<!-- TODO Add proper translation--> <string name="title_favorite_messages">पसंदीदा संदेश</string>
<string name="title_favorite_messages">Favorite Messages</string> <string name="no_favorite_messages">कोई पसंदीदा संदेश है</string>
<string name="no_favorite_messages">No favorite messages</string> <string name="no_favorite_description">सभी पसंदीदा संदेश यहां\nदिखाई देते हैं</string>
<string name="no_favorite_description">All the favorite messages\nappear here</string>
<!-- Files --> <!-- Files -->
<!-- TODO Add proper translation--> <string name="msg_files">फ़ाइलें</string>
<string name="msg_files">Files</string> <string name="title_files_total">फ़ाइलें (%d)</string>
<string name="title_files_total">Files (%d)</string> <string name="msg_no_files">कोई फाइल नहीं है</string>
<string name="msg_no_files">No files</string> <string name="msg_all_files_appear_here">सभी फाइलें यहां दिखाई देती हैं</string>
<string name="msg_all_files_appear_here">All the files appear here</string>
<!-- Upload Messages --> <!-- Upload Messages -->
<string name="max_file_size_exceeded">फ़ाइल का आकार %1$d बाइट्स ने %2$d बाइट्स के अधिकतम अपलोड आकार को पार कर लिया है</string> <string name="max_file_size_exceeded">फ़ाइल का आकार %1$d बाइट्स ने %2$d बाइट्स के अधिकतम अपलोड आकार को पार कर लिया है</string>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment