Commit 30a02575 authored by Filipe de Lima Brito's avatar Filipe de Lima Brito

Merge branch 'v3.0.0' into redesignOnboarding

parents 8c74508e 93d43d3b
......@@ -6,6 +6,7 @@ if (isPlay) { apply plugin: 'io.fabric' }
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
apply plugin: "com.github.ben-manes.versions"
android {
compileSdkVersion versions.compileSdk
......@@ -15,8 +16,8 @@ android {
applicationId "chat.rocket.android"
minSdkVersion versions.minSdk
targetSdkVersion versions.targetSdk
versionCode 2036
versionName "2.5.1"
versionCode 2042
versionName "2.6.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
multiDexEnabled true
......@@ -104,6 +105,7 @@ dependencies {
implementation libraries.browser
implementation libraries.androidKtx
implementation libraries.fragmentsKtx
implementation libraries.dagger
implementation libraries.daggerSupport
......@@ -117,6 +119,8 @@ dependencies {
kapt libraries.roomProcessor
implementation libraries.lifecycleExtensions
kapt libraries.lifecycleCompiler
implementation libraries.viewmodelKtx
implementation libraries.workmanager
implementation libraries.rxKotlin
implementation libraries.rxAndroid
......@@ -145,14 +149,14 @@ dependencies {
implementation libraries.aVLoadingIndicatorView
implementation "com.github.luciofm:livedata-ktx:b1e8bbc25a"
implementation libraries.livedataKtx
// Proprietary libraries
playImplementation libraries.fcm
playImplementation libraries.firebaseAnalytics
playImplementation libraries.playServicesAuth
playImplementation('com.crashlytics.sdk.android:crashlytics:2.9.4@aar') { transitive = true }
playImplementation('com.crashlytics.sdk.android:answers:1.4.2@aar') { transitive = true }
playImplementation('com.crashlytics.sdk.android:crashlytics:2.9.5@aar') { transitive = true }
playImplementation('com.crashlytics.sdk.android:answers:1.4.3@aar') { transitive = true }
testImplementation libraries.junit
testImplementation libraries.truth
......
package chat.rocket.android.util.helper.analytics
package chat.rocket.android.analytics
import chat.rocket.android.util.helper.analytics.event.AuthenticationEvent
import chat.rocket.android.util.helper.analytics.event.ScreenViewEvent
import chat.rocket.android.util.helper.analytics.event.SubscriptionTypeEvent
import chat.rocket.android.analytics.event.AuthenticationEvent
import chat.rocket.android.analytics.event.ScreenViewEvent
import chat.rocket.android.analytics.event.SubscriptionTypeEvent
object AnalyticsManager : Analytics {
class AnswersAnalytics : Analytics {
override fun logLogin(event: AuthenticationEvent, loginSucceeded: Boolean) {
// Do absolutely nothing
......
package chat.rocket.android.analytics
import android.content.Context
import chat.rocket.android.analytics.event.AuthenticationEvent
import chat.rocket.android.analytics.event.ScreenViewEvent
import chat.rocket.android.analytics.event.SubscriptionTypeEvent
import javax.inject.Inject
class GoogleAnalyticsForFirebase @Inject constructor(val context: Context) :
Analytics {
override fun logLogin(event: AuthenticationEvent, loginSucceeded: Boolean) {
// Do absolutely nothing
}
override fun logSignUp(event: AuthenticationEvent, signUpSucceeded: Boolean) {
// Do absolutely nothing
}
override fun logScreenView(event: ScreenViewEvent) {
// Do absolutely nothing
}
override fun logMessageSent(event: SubscriptionTypeEvent, serverUrl: String) {
// Do absolutely nothing
}
override fun logMediaUploaded(event: SubscriptionTypeEvent, mimeType: String) {
// Do absolutely nothing
}
override fun logReaction(event: SubscriptionTypeEvent) {
// Do absolutely nothing
}
override fun logServerSwitch(serverUrl: String, serverCount: Int) {
// Do absolutely nothing
}
}
package chat.rocket.android.push
class FirebaseTokenService {
}
\ No newline at end of file
fun refreshPushToken() {
}
package chat.rocket.android.util
import chat.rocket.android.main.presentation.MainPresenter
fun refreshFCMToken(presenter: MainPresenter) {
//Do absolutely nothing
}
fun invalidateFirebaseToken(token: String) {
//Do absolutely nothing
}
\ No newline at end of file
app/src/main/ic_launcher-web.png

39.1 KB | W: | H:

app/src/main/ic_launcher-web.png

63.8 KB | W: | H:

app/src/main/ic_launcher-web.png
app/src/main/ic_launcher-web.png
app/src/main/ic_launcher-web.png
app/src/main/ic_launcher-web.png
  • 2-up
  • Swipe
  • Onion skin
package chat.rocket.android.about.di
import chat.rocket.android.about.ui.AboutFragment
import dagger.Module
import dagger.android.ContributesAndroidInjector
@Module
abstract class AboutFragmentProvider {
@ContributesAndroidInjector()
abstract fun provideAboutFragment(): AboutFragment
}
......@@ -7,10 +7,10 @@ import android.view.ViewGroup
import androidx.fragment.app.Fragment
import chat.rocket.android.BuildConfig
import chat.rocket.android.R
import chat.rocket.android.analytics.AnalyticsManager
import chat.rocket.android.analytics.event.ScreenViewEvent
import chat.rocket.android.main.ui.MainActivity
import chat.rocket.android.server.domain.AnalyticsTrackingInteractor
import chat.rocket.android.util.helper.analytics.AnalyticsManager
import chat.rocket.android.util.helper.analytics.event.ScreenViewEvent
import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.app_bar.*
import kotlinx.android.synthetic.main.fragment_about.*
import javax.inject.Inject
......@@ -19,7 +19,12 @@ internal const val TAG_ABOUT_FRAGMENT = "AboutFragment"
class AboutFragment : Fragment() {
@Inject
lateinit var analyticsTrackingInteractor: AnalyticsTrackingInteractor
lateinit var analyticsManager: AnalyticsManager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
AndroidSupportInjection.inject(this)
}
override fun onCreateView(
inflater: LayoutInflater,
......@@ -32,15 +37,15 @@ class AboutFragment : Fragment() {
setupToolbar()
setupViews()
if (analyticsTrackingInteractor.get()) {
AnalyticsManager.logScreenView(ScreenViewEvent.About)
}
analyticsManager.logScreenView(ScreenViewEvent.About)
}
private fun setupViews() {
text_version_name.text = BuildConfig.VERSION_NAME
text_build_number.text = getString(R.string.msg_build, BuildConfig.VERSION_CODE,
BuildConfig.GIT_SHA, BuildConfig.FLAVOR)
text_build_number.text = getString(
R.string.msg_build, BuildConfig.VERSION_CODE,
BuildConfig.GIT_SHA, BuildConfig.FLAVOR
)
}
private fun setupToolbar() {
......
package chat.rocket.android.util.helper.analytics
package chat.rocket.android.analytics
import chat.rocket.android.util.helper.analytics.event.AuthenticationEvent
import chat.rocket.android.util.helper.analytics.event.ScreenViewEvent
import chat.rocket.android.util.helper.analytics.event.SubscriptionTypeEvent
import chat.rocket.android.analytics.event.AuthenticationEvent
import chat.rocket.android.analytics.event.ScreenViewEvent
import chat.rocket.android.analytics.event.SubscriptionTypeEvent
interface Analytics {
......
package chat.rocket.android.analytics
import chat.rocket.android.analytics.event.AuthenticationEvent
import chat.rocket.android.analytics.event.ScreenViewEvent
import chat.rocket.android.analytics.event.SubscriptionTypeEvent
import chat.rocket.android.server.domain.AnalyticsTrackingInteractor
import chat.rocket.android.server.domain.GetAccountsInteractor
import chat.rocket.android.server.domain.GetCurrentServerInteractor
import javax.inject.Inject
class AnalyticsManager @Inject constructor(
private val analyticsTrackingInteractor: AnalyticsTrackingInteractor,
getCurrentServerInteractor: GetCurrentServerInteractor,
getAccountsInteractor: GetAccountsInteractor,
private val analytics: List<Analytics>
) {
val serverUrl = getCurrentServerInteractor.get()
val accounts = getAccountsInteractor.get()
fun logLogin(
event: AuthenticationEvent,
loginSucceeded: Boolean
) {
if (analyticsTrackingInteractor.get()) {
analytics.forEach { it.logLogin(event, loginSucceeded) }
}
}
fun logSignUp(
event: AuthenticationEvent,
signUpSucceeded: Boolean
) {
if (analyticsTrackingInteractor.get()) {
analytics.forEach { it.logSignUp(event, signUpSucceeded) }
}
}
fun logScreenView(event: ScreenViewEvent) {
if (analyticsTrackingInteractor.get()) {
analytics.forEach { it.logScreenView(event) }
}
}
fun logMessageSent(event: SubscriptionTypeEvent) {
if (analyticsTrackingInteractor.get() && serverUrl != null) {
analytics.forEach { it.logMessageSent(event, serverUrl) }
}
}
fun logMediaUploaded(event: SubscriptionTypeEvent, mimeType: String) {
if (analyticsTrackingInteractor.get()) {
analytics.forEach { it.logMediaUploaded(event, mimeType) }
}
}
fun logReaction(event: SubscriptionTypeEvent) {
if (analyticsTrackingInteractor.get()) {
analytics.forEach { it.logReaction(event) }
}
}
fun logServerSwitch() {
if (analyticsTrackingInteractor.get() && serverUrl != null) {
analytics.forEach { it.logServerSwitch(serverUrl, accounts.size) }
}
}
}
package chat.rocket.android.util.helper.analytics.event
package chat.rocket.android.analytics.event
sealed class AuthenticationEvent(val methodName: String) {
object AuthenticationWithUserAndPassword : AuthenticationEvent("User and password")
object AuthenticationWithCas : AuthenticationEvent("CAS")
object AuthenticationWithSaml : AuthenticationEvent("SAML")
object AuthenticationWithOauth : AuthenticationEvent("Oauth")
object AuthenticationWithDeeplink : AuthenticationEvent("Deep link")
}
package chat.rocket.android.util.helper.analytics.event
package chat.rocket.android.analytics.event
sealed class ScreenViewEvent(val screenName: String) {
......@@ -23,4 +23,4 @@ sealed class ScreenViewEvent(val screenName: String) {
object Settings : ScreenViewEvent("SettingsFragment")
object SignUp : ScreenViewEvent("SignupFragment")
object TwoFa : ScreenViewEvent("TwoFAFragment")
}
\ No newline at end of file
}
package chat.rocket.android.util.helper.analytics.event
package chat.rocket.android.analytics.event
sealed class SubscriptionTypeEvent(val subscriptionTypeName: String) {
object DirectMessage : SubscriptionTypeEvent("Direct Message")
object Channel : SubscriptionTypeEvent("Channel")
object Group : SubscriptionTypeEvent("Group")
}
\ No newline at end of file
}
......@@ -8,9 +8,15 @@ import android.content.Context
import android.content.SharedPreferences
import androidx.core.content.edit
import androidx.lifecycle.ProcessLifecycleOwner
import androidx.work.Worker
import chat.rocket.android.BuildConfig
import chat.rocket.android.dagger.DaggerAppComponent
import chat.rocket.android.dagger.injector.HasWorkerInjector
import chat.rocket.android.dagger.qualifier.ForMessages
import chat.rocket.android.emoji.Emoji
import chat.rocket.android.emoji.EmojiRepository
import chat.rocket.android.emoji.Fitzpatrick
import chat.rocket.android.emoji.internal.EmojiCategory
import chat.rocket.android.helper.CrashlyticsTree
import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.server.domain.AccountsRepository
......@@ -18,22 +24,26 @@ import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.domain.GetSettingsInteractor
import chat.rocket.android.server.domain.SITE_URL
import chat.rocket.android.server.domain.TokenRepository
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.retryIO
import chat.rocket.android.util.setupFabric
import chat.rocket.common.RocketChatException
import chat.rocket.core.internal.rest.getCustomEmojis
import com.facebook.drawee.backends.pipeline.DraweeConfig
import com.facebook.drawee.backends.pipeline.Fresco
import com.facebook.imagepipeline.core.ImagePipelineConfig
import com.jakewharton.threetenabp.AndroidThreeTen
import dagger.android.AndroidInjector
import dagger.android.DispatchingAndroidInjector
import dagger.android.HasActivityInjector
import dagger.android.HasBroadcastReceiverInjector
import dagger.android.HasServiceInjector
import kotlinx.coroutines.experimental.launch
import timber.log.Timber
import java.lang.ref.WeakReference
import javax.inject.Inject
class RocketChatApplication : Application(), HasActivityInjector, HasServiceInjector,
HasBroadcastReceiverInjector {
HasBroadcastReceiverInjector, HasWorkerInjector {
@Inject
lateinit var appLifecycleObserver: AppLifecycleObserver
......@@ -47,6 +57,9 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje
@Inject
lateinit var broadcastReceiverInjector: DispatchingAndroidInjector<BroadcastReceiver>
@Inject
lateinit var workerInjector: DispatchingAndroidInjector<Worker>
@Inject
lateinit var imagePipelineConfig: ImagePipelineConfig
@Inject
......@@ -63,6 +76,8 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje
lateinit var localRepository: LocalRepository
@Inject
lateinit var accountRepository: AccountsRepository
@Inject
lateinit var factory: RocketChatClientFactory
@Inject
@field:ForMessages
......@@ -98,6 +113,9 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje
// TODO - remove REALM files.
// TODO - remove this
checkCurrentServer()
// TODO - FIXME - we need to proper inject the EmojiRepository and initialize it properly
loadEmojis()
}
private fun checkCurrentServer() {
......@@ -132,17 +150,13 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje
}
}
override fun activityInjector(): AndroidInjector<Activity> {
return activityDispatchingAndroidInjector
}
override fun activityInjector() = activityDispatchingAndroidInjector
override fun serviceInjector(): AndroidInjector<Service> {
return serviceDispatchingAndroidInjector
}
override fun serviceInjector() = serviceDispatchingAndroidInjector
override fun broadcastReceiverInjector(): AndroidInjector<BroadcastReceiver> {
return broadcastReceiverInjector
}
override fun broadcastReceiverInjector() = broadcastReceiverInjector
override fun workerInjector() = workerInjector
companion object {
var context: WeakReference<Context>? = null
......@@ -150,6 +164,44 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje
return context?.get()
}
}
// TODO - FIXME - This is a big Workaround
/**
* Load all emojis for the current server. Simple emojis are always the same for every server,
* but custom emojis vary according to the its url.
*/
fun loadEmojis() {
EmojiRepository.init(this)
val currentServer = getCurrentServerInteractor.get()
currentServer?.let { server ->
launch {
val client = factory.create(server)
EmojiRepository.setCurrentServerUrl(server)
val customEmojiList = mutableListOf<Emoji>()
try {
for (customEmoji in retryIO("getCustomEmojis()") { client.getCustomEmojis() }) {
customEmojiList.add(Emoji(
shortname = ":${customEmoji.name}:",
category = EmojiCategory.CUSTOM.name,
url = "$currentServer/emoji-custom/${customEmoji.name}.${customEmoji.extension}",
count = 0,
fitzpatrick = Fitzpatrick.Default.type,
keywords = customEmoji.aliases,
shortnameAlternates = customEmoji.aliases,
siblings = mutableListOf(),
unicode = "",
isDefault = true
))
}
EmojiRepository.load(this@RocketChatApplication, customEmojis = customEmojiList)
} catch (ex: RocketChatException) {
Timber.e(ex)
EmojiRepository.load(this@RocketChatApplication as Context)
}
}
}
}
}
private fun LocalRepository.needOldMessagesCleanUp() = getBoolean(CLEANUP_OLD_MESSAGES_NEEDED, true)
......
package chat.rocket.android.authentication.login.presentation
import chat.rocket.android.analytics.AnalyticsManager
import chat.rocket.android.analytics.event.AuthenticationEvent
import chat.rocket.android.authentication.domain.model.LoginDeepLinkInfo
import chat.rocket.android.authentication.presentation.AuthenticationNavigator
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.helper.OauthHelper
import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.server.domain.*
import chat.rocket.android.server.domain.GetConnectingServerInteractor
import chat.rocket.android.server.domain.GetSettingsInteractor
import chat.rocket.android.server.domain.PublicSettings
import chat.rocket.android.server.domain.SaveAccountInteractor
import chat.rocket.android.server.domain.SaveCurrentServerInteractor
import chat.rocket.android.server.domain.TokenRepository
import chat.rocket.android.server.domain.casLoginUrl
import chat.rocket.android.server.domain.favicon
import chat.rocket.android.server.domain.gitlabUrl
import chat.rocket.android.server.domain.isCasAuthenticationEnabled
import chat.rocket.android.server.domain.isFacebookAuthenticationEnabled
import chat.rocket.android.server.domain.isGithubAuthenticationEnabled
import chat.rocket.android.server.domain.isGitlabAuthenticationEnabled
import chat.rocket.android.server.domain.isGoogleAuthenticationEnabled
import chat.rocket.android.server.domain.isLdapAuthenticationEnabled
import chat.rocket.android.server.domain.isLinkedinAuthenticationEnabled
import chat.rocket.android.server.domain.isLoginFormEnabled
import chat.rocket.android.server.domain.isPasswordResetEnabled
import chat.rocket.android.server.domain.isRegistrationEnabledForNewUsers
import chat.rocket.android.server.domain.isWordpressAuthenticationEnabled
import chat.rocket.android.server.domain.model.Account
import chat.rocket.android.server.domain.wideTile
import chat.rocket.android.server.domain.wordpressUrl
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.extensions.avatarUrl
......@@ -15,11 +38,8 @@ import chat.rocket.android.util.extensions.encodeToBase64
import chat.rocket.android.util.extensions.generateRandomString
import chat.rocket.android.util.extensions.isEmail
import chat.rocket.android.util.extensions.parseColor
import chat.rocket.android.util.extensions.registerPushToken
import chat.rocket.android.util.extensions.samlUrl
import chat.rocket.android.util.extensions.serverLogoUrl
import chat.rocket.android.util.helper.analytics.AnalyticsManager
import chat.rocket.android.util.helper.analytics.event.AuthenticationEvent
import chat.rocket.android.util.retryIO
import chat.rocket.common.RocketChatAuthException
import chat.rocket.common.RocketChatException
......@@ -60,9 +80,8 @@ class LoginPresenter @Inject constructor(
private val navigator: AuthenticationNavigator,
private val tokenRepository: TokenRepository,
private val localRepository: LocalRepository,
private val getAccountsInteractor: GetAccountsInteractor,
private val settingsInteractor: GetSettingsInteractor,
private val analyticsTrackingInteractor: AnalyticsTrackingInteractor,
private val analyticsManager: AnalyticsManager,
serverInteractor: GetConnectingServerInteractor,
private val saveCurrentServer: SaveCurrentServerInteractor,
private val saveAccountInteractor: SaveAccountInteractor,
......@@ -312,12 +331,10 @@ class LoginPresenter @Inject constructor(
)
localRepository.saveCurrentUser(currentServer, user)
saveCurrentServer.save(currentServer)
localRepository.save(LocalRepository.CURRENT_USERNAME_KEY, myself.username)
saveAccount(myself.username!!)
saveToken(token)
registerPushToken()
if (analyticsTrackingInteractor.get()) {
AnalyticsManager.logLogin(loginMethod, true)
}
analyticsManager.logLogin(loginMethod, true)
if (loginType == TYPE_LOGIN_USER_EMAIL) {
view.saveSmartLockCredentials(usernameOrEmail, password)
}
......@@ -331,9 +348,7 @@ class LoginPresenter @Inject constructor(
navigator.toTwoFA(usernameOrEmail, password)
}
else -> {
if (analyticsTrackingInteractor.get()) {
AnalyticsManager.logLogin(loginMethod, false)
}
analyticsManager.logLogin(loginMethod, false)
exception.message?.let {
view.showMessage(it)
}.ifNull {
......@@ -472,12 +487,4 @@ class LoginPresenter @Inject constructor(
private fun saveToken(token: Token) {
tokenRepository.save(currentServer, token)
}
private suspend fun registerPushToken() {
localRepository.get(LocalRepository.KEY_PUSH_TOKEN)?.let {
client.registerPushToken(it, getAccountsInteractor.get(), factory)
}
// TODO: When the push token is null, at some point we should receive it with
// onTokenRefresh() on FirebaseTokenService, we need to confirm it.
}
}
\ No newline at end of file
......@@ -6,7 +6,6 @@ import android.content.Intent
import android.graphics.PorterDuff
import android.os.Build
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.text.style.ClickableSpan
import android.view.LayoutInflater
import android.view.View
......@@ -18,20 +17,30 @@ import android.widget.LinearLayout
import android.widget.ScrollView
import androidx.core.view.isVisible
import androidx.core.view.postDelayed
import androidx.fragment.app.Fragment
import chat.rocket.android.R
import chat.rocket.android.analytics.AnalyticsManager
import chat.rocket.android.analytics.event.ScreenViewEvent
import chat.rocket.android.authentication.domain.model.LoginDeepLinkInfo
import chat.rocket.android.authentication.login.presentation.LoginPresenter
import chat.rocket.android.authentication.login.presentation.LoginView
import chat.rocket.android.helper.*
import chat.rocket.android.server.domain.AnalyticsTrackingInteractor
import chat.rocket.android.util.extensions.*
import chat.rocket.android.util.helper.analytics.AnalyticsManager
import chat.rocket.android.util.helper.analytics.event.ScreenViewEvent
import chat.rocket.android.webview.sso.ui.INTENT_SSO_TOKEN
import chat.rocket.android.webview.sso.ui.ssoWebViewIntent
import chat.rocket.android.helper.KeyboardHelper
import chat.rocket.android.helper.TextHelper
import chat.rocket.android.helper.getCredentials
import chat.rocket.android.helper.hasCredentialsSupport
import chat.rocket.android.helper.requestStoredCredentials
import chat.rocket.android.helper.saveCredentials
import chat.rocket.android.util.extensions.inflate
import chat.rocket.android.util.extensions.shake
import chat.rocket.android.util.extensions.showToast
import chat.rocket.android.util.extensions.textContent
import chat.rocket.android.util.extensions.ui
import chat.rocket.android.util.extensions.vibrateSmartPhone
import chat.rocket.android.webview.oauth.ui.INTENT_OAUTH_CREDENTIAL_SECRET
import chat.rocket.android.webview.oauth.ui.INTENT_OAUTH_CREDENTIAL_TOKEN
import chat.rocket.android.webview.oauth.ui.oauthWebViewIntent
import chat.rocket.android.webview.sso.ui.INTENT_SSO_TOKEN
import chat.rocket.android.webview.sso.ui.ssoWebViewIntent
import chat.rocket.common.util.ifNull
import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_authentication_log_in.*
......@@ -49,7 +58,7 @@ class LoginFragment : Fragment(), LoginView {
@Inject
lateinit var presenter: LoginPresenter
@Inject
lateinit var analyticsTrackingInteractor: AnalyticsTrackingInteractor
lateinit var analyticsManager: AnalyticsManager
private var isOauthViewEnable = false
private var deepLinkInfo: LoginDeepLinkInfo? = null
......@@ -93,9 +102,7 @@ class LoginFragment : Fragment(), LoginView {
image_key.isVisible = false
}
if (analyticsTrackingInteractor.get()) {
AnalyticsManager.logScreenView(ScreenViewEvent.Login)
}
analyticsManager.logScreenView(ScreenViewEvent.Login)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
......
package chat.rocket.android.authentication.loginoptions.presentation
import chat.rocket.android.analytics.AnalyticsManager
import chat.rocket.android.analytics.event.AuthenticationEvent
import chat.rocket.android.authentication.login.presentation.*
import chat.rocket.android.authentication.presentation.AuthenticationNavigator
import chat.rocket.android.core.lifecycle.CancelStrategy
......@@ -33,17 +35,17 @@ private const val SERVICE_NAME_LINKEDIN = "linkedin"
private const val SERVICE_NAME_GILAB = "gitlab"
class LoginOptionsPresenter @Inject constructor(
private val view: LoginOptionsView,
private val strategy: CancelStrategy,
private val factory: RocketChatClientFactory,
private val navigator: AuthenticationNavigator,
private val settingsInteractor: GetSettingsInteractor,
private val localRepository: LocalRepository,
private val saveCurrentServer: SaveCurrentServerInteractor,
private val saveAccountInteractor: SaveAccountInteractor,
private val getAccountsInteractor: GetAccountsInteractor,
private val tokenRepository: TokenRepository,
serverInteractor: GetConnectingServerInteractor
private val view: LoginOptionsView,
private val strategy: CancelStrategy,
private val factory: RocketChatClientFactory,
private val navigator: AuthenticationNavigator,
private val settingsInteractor: GetSettingsInteractor,
private val localRepository: LocalRepository,
private val saveCurrentServer: SaveCurrentServerInteractor,
private val saveAccountInteractor: SaveAccountInteractor,
private val analyticsManager: AnalyticsManager,
private val tokenRepository: TokenRepository,
serverInteractor: GetConnectingServerInteractor
) {
// TODO - we should validate the current server when opening the app, and have a nonnull get()
private var currentServer = serverInteractor.get()!!
......@@ -245,7 +247,11 @@ class LoginOptionsPresenter @Inject constructor(
saveCurrentServer.save(currentServer)
saveAccount(myself.username!!)
saveToken(token)
registerPushToken()
// TODO
// analyticsManager.logSignUp(
// AuthenticationEvent.AuthenticationWithOauth,
// true
// )
navigator.toChatList()
} else if (loginType == TYPE_LOGIN_OAUTH) {
navigator.toRegisterUsername(token.userId, token.authToken)
......@@ -284,14 +290,6 @@ class LoginOptionsPresenter @Inject constructor(
tokenRepository.save(currentServer, token)
}
private suspend fun registerPushToken() {
localRepository.get(LocalRepository.KEY_PUSH_TOKEN)?.let {
client.registerPushToken(it, getAccountsInteractor.get(), factory)
}
// TODO: When the push token is null, at some point we should receive it with
// onTokenRefresh() on FirebaseTokenService, we need to confirm it.
}
fun toCreateAccount() {
navigator.toCreateAccount()
}
......
package chat.rocket.android.authentication.registerusername.presentation
import chat.rocket.android.analytics.AnalyticsManager
import chat.rocket.android.analytics.event.AuthenticationEvent
import chat.rocket.android.authentication.presentation.AuthenticationNavigator
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.server.domain.AnalyticsTrackingInteractor
import chat.rocket.android.server.domain.GetAccountsInteractor
import chat.rocket.android.server.domain.GetConnectingServerInteractor
import chat.rocket.android.server.domain.GetSettingsInteractor
import chat.rocket.android.server.domain.PublicSettings
......@@ -17,10 +16,7 @@ import chat.rocket.android.server.domain.wideTile
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.extensions.avatarUrl
import chat.rocket.android.util.extensions.registerPushToken
import chat.rocket.android.util.extensions.serverLogoUrl
import chat.rocket.android.util.helper.analytics.AnalyticsManager
import chat.rocket.android.util.helper.analytics.event.AuthenticationEvent
import chat.rocket.android.util.retryIO
import chat.rocket.common.RocketChatException
import chat.rocket.common.model.Token
......@@ -34,11 +30,9 @@ class RegisterUsernamePresenter @Inject constructor(
private val strategy: CancelStrategy,
private val navigator: AuthenticationNavigator,
private val tokenRepository: TokenRepository,
private val localRepository: LocalRepository,
private val factory: RocketChatClientFactory,
factory: RocketChatClientFactory,
private val saveAccountInteractor: SaveAccountInteractor,
private val getAccountsInteractor: GetAccountsInteractor,
private val analyticsTrackingInteractor: AnalyticsTrackingInteractor,
private val analyticsManager: AnalyticsManager,
serverInteractor: GetConnectingServerInteractor,
private val saveCurrentServer: SaveCurrentServerInteractor,
settingsInteractor: GetSettingsInteractor
......@@ -62,16 +56,14 @@ class RegisterUsernamePresenter @Inject constructor(
saveAccount(registeredUsername)
saveCurrentServer.save(currentServer)
tokenRepository.save(currentServer, Token(userId, authToken))
registerPushToken()
if (analyticsTrackingInteractor.get()) {
AnalyticsManager.logSignUp(AuthenticationEvent.AuthenticationWithOauth, true)
}
analyticsManager.logSignUp(
AuthenticationEvent.AuthenticationWithOauth,
true
)
navigator.toChatList()
}
} catch (exception: RocketChatException) {
if (analyticsTrackingInteractor.get()) {
AnalyticsManager.logSignUp(AuthenticationEvent.AuthenticationWithOauth, false)
}
analyticsManager.logSignUp(AuthenticationEvent.AuthenticationWithOauth, false)
exception.message?.let {
view.showMessage(it)
}.ifNull {
......@@ -84,14 +76,6 @@ class RegisterUsernamePresenter @Inject constructor(
}
}
private suspend fun registerPushToken() {
localRepository.get(LocalRepository.KEY_PUSH_TOKEN)?.let {
client.registerPushToken(it, getAccountsInteractor.get(), factory)
}
// 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(username: String) {
val icon = settings.favicon()?.let {
currentServer.serverLogoUrl(it)
......
......@@ -3,17 +3,23 @@ package chat.rocket.android.authentication.registerusername.ui
import DrawableHelper
import android.os.Build
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import chat.rocket.android.R
import chat.rocket.android.analytics.AnalyticsManager
import chat.rocket.android.analytics.event.ScreenViewEvent
import chat.rocket.android.authentication.registerusername.presentation.RegisterUsernamePresenter
import chat.rocket.android.authentication.registerusername.presentation.RegisterUsernameView
import chat.rocket.android.server.domain.AnalyticsTrackingInteractor
import chat.rocket.android.util.extensions.*
import chat.rocket.android.util.helper.analytics.AnalyticsManager
import chat.rocket.android.util.helper.analytics.event.ScreenViewEvent
import chat.rocket.android.util.extensions.inflate
import chat.rocket.android.util.extensions.setVisible
import chat.rocket.android.util.extensions.shake
import chat.rocket.android.util.extensions.showKeyboard
import chat.rocket.android.util.extensions.showToast
import chat.rocket.android.util.extensions.textContent
import chat.rocket.android.util.extensions.ui
import chat.rocket.android.util.extensions.vibrateSmartPhone
import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_authentication_register_username.*
import javax.inject.Inject
......@@ -24,7 +30,7 @@ class RegisterUsernameFragment : Fragment(), RegisterUsernameView {
@Inject
lateinit var presenter: RegisterUsernamePresenter
@Inject
lateinit var analyticsTrackingInteractor: AnalyticsTrackingInteractor
lateinit var analyticsManager: AnalyticsManager
private lateinit var userId: String
private lateinit var authToken: String
......@@ -69,9 +75,7 @@ class RegisterUsernameFragment : Fragment(), RegisterUsernameView {
setupOnClickListener()
if (analyticsTrackingInteractor.get()) {
AnalyticsManager.logScreenView(ScreenViewEvent.RegisterUsername)
}
analyticsManager.logScreenView(ScreenViewEvent.RegisterUsername)
}
override fun alertBlankUsername() {
......
......@@ -3,18 +3,24 @@ package chat.rocket.android.authentication.resetpassword.ui
import DrawableHelper
import android.os.Build
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.fragment.app.Fragment
import chat.rocket.android.R
import chat.rocket.android.analytics.AnalyticsManager
import chat.rocket.android.analytics.event.ScreenViewEvent
import chat.rocket.android.authentication.resetpassword.presentation.ResetPasswordPresenter
import chat.rocket.android.authentication.resetpassword.presentation.ResetPasswordView
import chat.rocket.android.server.domain.AnalyticsTrackingInteractor
import chat.rocket.android.util.extensions.*
import chat.rocket.android.util.helper.analytics.AnalyticsManager
import chat.rocket.android.util.helper.analytics.event.ScreenViewEvent
import chat.rocket.android.util.extensions.inflate
import chat.rocket.android.util.extensions.setVisible
import chat.rocket.android.util.extensions.shake
import chat.rocket.android.util.extensions.showKeyboard
import chat.rocket.android.util.extensions.showToast
import chat.rocket.android.util.extensions.textContent
import chat.rocket.android.util.extensions.ui
import chat.rocket.android.util.extensions.vibrateSmartPhone
import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_authentication_reset_password.*
import javax.inject.Inject
......@@ -25,7 +31,7 @@ class ResetPasswordFragment : Fragment(), ResetPasswordView {
@Inject
lateinit var presenter: ResetPasswordPresenter
@Inject
lateinit var analyticsTrackingInteractor: AnalyticsTrackingInteractor
lateinit var analyticsManager: AnalyticsManager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
......@@ -52,9 +58,7 @@ class ResetPasswordFragment : Fragment(), ResetPasswordView {
setupOnClickListener()
if (analyticsTrackingInteractor.get()) {
AnalyticsManager.logScreenView(ScreenViewEvent.ResetPassword)
}
analyticsManager.logScreenView(ScreenViewEvent.ResetPassword)
}
override fun alertBlankEmail() {
......
......@@ -3,7 +3,6 @@ package chat.rocket.android.authentication.server.ui
import android.app.AlertDialog
import android.net.Uri
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
......@@ -11,17 +10,25 @@ import android.view.ViewTreeObserver
import android.widget.AdapterView
import android.widget.ArrayAdapter
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import chat.rocket.android.BuildConfig
import chat.rocket.android.R
import chat.rocket.android.analytics.AnalyticsManager
import chat.rocket.android.analytics.event.ScreenViewEvent
import chat.rocket.android.authentication.domain.model.LoginDeepLinkInfo
import chat.rocket.android.authentication.server.presentation.ServerPresenter
import chat.rocket.android.authentication.server.presentation.ServerView
import chat.rocket.android.authentication.ui.AuthenticationActivity
import chat.rocket.android.helper.KeyboardHelper
import chat.rocket.android.server.domain.AnalyticsTrackingInteractor
import chat.rocket.android.util.extensions.*
import chat.rocket.android.util.helper.analytics.AnalyticsManager
import chat.rocket.android.util.helper.analytics.event.ScreenViewEvent
import chat.rocket.android.util.extensions.hintContent
import chat.rocket.android.util.extensions.ifEmpty
import chat.rocket.android.util.extensions.inflate
import chat.rocket.android.util.extensions.sanitize
import chat.rocket.android.util.extensions.setLightStatusBar
import chat.rocket.android.util.extensions.setVisible
import chat.rocket.android.util.extensions.showToast
import chat.rocket.android.util.extensions.textContent
import chat.rocket.android.util.extensions.ui
import chat.rocket.common.util.ifNull
import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.app_bar_chat_room.*
......@@ -35,10 +42,11 @@ class ServerFragment : Fragment(), ServerView {
@Inject
lateinit var presenter: ServerPresenter
@Inject
lateinit var analyticsTrackingInteractor: AnalyticsTrackingInteractor
lateinit var analyticsManager: AnalyticsManager
private var deepLinkInfo: LoginDeepLinkInfo? = null
private val layoutListener = ViewTreeObserver.OnGlobalLayoutListener {
text_server_url.isCursorVisible = KeyboardHelper.isSoftKeyboardShown(constraint_layout.rootView)
text_server_url.isCursorVisible =
KeyboardHelper.isSoftKeyboardShown(constraint_layout.rootView)
}
private var protocol = "https://"
private var ignoreChange = false
......@@ -50,7 +58,11 @@ class ServerFragment : Fragment(), ServerView {
deepLinkInfo = arguments?.getParcelable(DEEP_LINK_INFO)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? =
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? =
container?.inflate(R.layout.fragment_authentication_server)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
......@@ -65,45 +77,51 @@ class ServerFragment : Fragment(), ServerView {
presenter.deepLink(it)
}
spinner_server_protocol.adapter = ArrayAdapter<String>(activity,
android.R.layout.simple_dropdown_item_1line, arrayOf("https://", "http://"))
spinner_server_protocol.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
when(position) {
0 -> {
protocol = "https://"
}
1 -> {
if (ignoreChange) {
protocol = "http://"
} else {
ui {
AlertDialog.Builder(it)
.setTitle(R.string.msg_warning)
.setMessage(R.string.msg_http_insecure)
.setPositiveButton(R.string.msg_proceed) { _, _ ->
protocol = "http://"
}
.setNegativeButton(R.string.msg_cancel) { _, _ ->
spinner_server_protocol.setSelection(0)
}
.setCancelable(false)
.create()
.show()
spinner_server_protocol.adapter = ArrayAdapter<String>(
activity,
android.R.layout.simple_dropdown_item_1line, arrayOf("https://", "http://")
)
spinner_server_protocol.onItemSelectedListener =
object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(
parent: AdapterView<*>?,
view: View?,
position: Int,
id: Long
) {
when (position) {
0 -> {
protocol = "https://"
}
1 -> {
if (ignoreChange) {
protocol = "http://"
} else {
ui {
AlertDialog.Builder(it)
.setTitle(R.string.msg_warning)
.setMessage(R.string.msg_http_insecure)
.setPositiveButton(R.string.msg_proceed) { _, _ ->
protocol = "http://"
}
.setNegativeButton(R.string.msg_cancel) { _, _ ->
spinner_server_protocol.setSelection(0)
}
.setCancelable(false)
.create()
.show()
}
}
}
}
ignoreChange = false
}
}
ignoreChange = false
}
override fun onNothingSelected(parent: AdapterView<*>?) {
}
}
override fun onNothingSelected(parent: AdapterView<*>?) {
}
}
if (analyticsTrackingInteractor.get()) {
AnalyticsManager.logScreenView(ScreenViewEvent.Server)
}
analyticsManager.logScreenView(ScreenViewEvent.Server)
}
private fun setupToobar() {
......@@ -121,7 +139,8 @@ class ServerFragment : Fragment(), ServerView {
constraint_layout.viewTreeObserver.removeOnGlobalLayoutListener(layoutListener)
}
override fun showInvalidServerUrlMessage() = showMessage(getString(R.string.msg_invalid_server_url))
override fun showInvalidServerUrlMessage() =
showMessage(getString(R.string.msg_invalid_server_url))
override fun showLoading() {
ui {
......@@ -157,7 +176,12 @@ class ServerFragment : Fragment(), ServerView {
ui {
hideLoading()
AlertDialog.Builder(it)
.setMessage(getString(R.string.msg_ver_not_recommended, BuildConfig.RECOMMENDED_SERVER_VERSION))
.setMessage(
getString(
R.string.msg_ver_not_recommended,
BuildConfig.RECOMMENDED_SERVER_VERSION
)
)
.setPositiveButton(R.string.msg_ok) { _, _ ->
performConnect()
}
......@@ -170,7 +194,12 @@ class ServerFragment : Fragment(), ServerView {
ui {
hideLoading()
AlertDialog.Builder(it)
.setMessage(getString(R.string.msg_ver_not_minimum, BuildConfig.REQUIRED_SERVER_VERSION))
.setMessage(
getString(
R.string.msg_ver_not_minimum,
BuildConfig.REQUIRED_SERVER_VERSION
)
)
.setPositiveButton(R.string.msg_ok, null)
.setOnDismissListener {
// reset the deeplink info, so the user can log to another server...
......@@ -197,7 +226,9 @@ class ServerFragment : Fragment(), ServerView {
override fun updateServerUrl(url: HttpUrl) {
if (activity != null && view != null) {
if (url.scheme() == "https") spinner_server_protocol.setSelection(0) else spinner_server_protocol.setSelection(1)
if (url.scheme() == "https") spinner_server_protocol.setSelection(0) else spinner_server_protocol.setSelection(
1
)
protocol = "${url.scheme()}://"
val serverUrl = url.toString().removePrefix("${url.scheme()}://")
......
package chat.rocket.android.authentication.signup.presentation
import chat.rocket.android.analytics.AnalyticsManager
import chat.rocket.android.analytics.event.AuthenticationEvent
import chat.rocket.android.authentication.presentation.AuthenticationNavigator
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.server.domain.AnalyticsTrackingInteractor
import chat.rocket.android.server.domain.GetAccountsInteractor
import chat.rocket.android.server.domain.GetConnectingServerInteractor
import chat.rocket.android.server.domain.GetSettingsInteractor
import chat.rocket.android.server.domain.PublicSettings
......@@ -17,15 +17,11 @@ import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.extensions.avatarUrl
import chat.rocket.android.util.extensions.privacyPolicyUrl
import chat.rocket.android.util.extensions.registerPushToken
import chat.rocket.android.util.extensions.serverLogoUrl
import chat.rocket.android.util.extensions.termsOfServiceUrl
import chat.rocket.android.util.helper.analytics.AnalyticsManager
import chat.rocket.android.util.helper.analytics.event.AuthenticationEvent
import chat.rocket.android.util.retryIO
import chat.rocket.common.RocketChatException
import chat.rocket.common.util.ifNull
import chat.rocket.core.RocketChatClient
import chat.rocket.core.internal.rest.login
import chat.rocket.core.internal.rest.me
import chat.rocket.core.internal.rest.signup
......@@ -39,14 +35,12 @@ class SignupPresenter @Inject constructor(
private val localRepository: LocalRepository,
private val serverInteractor: GetConnectingServerInteractor,
private val saveCurrentServerInteractor: SaveCurrentServerInteractor,
private val analyticsTrackingInteractor: AnalyticsTrackingInteractor,
private val analyticsManager: AnalyticsManager,
private val factory: RocketChatClientFactory,
private val saveAccountInteractor: SaveAccountInteractor,
private val getAccountsInteractor: GetAccountsInteractor,
settingsInteractor: GetSettingsInteractor
) {
private val currentServer = serverInteractor.get()!!
private val client: RocketChatClient = factory.create(currentServer)
private var settings: PublicSettings = settingsInteractor.get(serverInteractor.get()!!)
fun signup(name: String, username: String, password: String, email: String) {
......@@ -80,22 +74,17 @@ class SignupPresenter @Inject constructor(
saveCurrentServerInteractor.save(currentServer)
localRepository.save(LocalRepository.CURRENT_USERNAME_KEY, me.username)
saveAccount(me)
registerPushToken()
if (analyticsTrackingInteractor.get()) {
AnalyticsManager.logSignUp(
AuthenticationEvent.AuthenticationWithUserAndPassword,
true
)
}
analyticsManager.logSignUp(
AuthenticationEvent.AuthenticationWithUserAndPassword,
true
)
view.saveSmartLockCredentials(username, password)
navigator.toChatList()
} catch (exception: RocketChatException) {
if (analyticsTrackingInteractor.get()) {
AnalyticsManager.logSignUp(
AuthenticationEvent.AuthenticationWithUserAndPassword,
false
)
}
analyticsManager.logSignUp(
AuthenticationEvent.AuthenticationWithUserAndPassword,
false
)
exception.message?.let {
view.showMessage(it)
}.ifNull {
......@@ -122,14 +111,6 @@ class SignupPresenter @Inject constructor(
}
}
private suspend fun registerPushToken() {
localRepository.get(LocalRepository.KEY_PUSH_TOKEN)?.let {
client.registerPushToken(it, getAccountsInteractor.get(), factory)
}
// 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) {
val icon = settings.favicon()?.let {
currentServer.serverLogoUrl(it)
......
......@@ -13,20 +13,19 @@ import android.view.ViewTreeObserver
import androidx.fragment.app.Fragment
import chat.rocket.android.R
import chat.rocket.android.R.string.message_credentials_saved_successfully
import chat.rocket.android.analytics.AnalyticsManager
import chat.rocket.android.analytics.event.ScreenViewEvent
import chat.rocket.android.authentication.signup.presentation.SignupPresenter
import chat.rocket.android.authentication.signup.presentation.SignupView
import chat.rocket.android.helper.KeyboardHelper
import chat.rocket.android.helper.TextHelper
import chat.rocket.android.helper.saveCredentials
import chat.rocket.android.server.domain.AnalyticsTrackingInteractor
import chat.rocket.android.util.extensions.setVisible
import chat.rocket.android.util.extensions.shake
import chat.rocket.android.util.extensions.showToast
import chat.rocket.android.util.extensions.textContent
import chat.rocket.android.util.extensions.ui
import chat.rocket.android.util.extensions.vibrateSmartPhone
import chat.rocket.android.util.helper.analytics.AnalyticsManager
import chat.rocket.android.util.helper.analytics.event.ScreenViewEvent
import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_authentication_sign_up.*
import javax.inject.Inject
......@@ -38,7 +37,7 @@ class SignupFragment : Fragment(), SignupView {
@Inject
lateinit var presenter: SignupPresenter
@Inject
lateinit var analyticsTrackingInteractor: AnalyticsTrackingInteractor
lateinit var analyticsManager: AnalyticsManager
private val layoutListener = ViewTreeObserver.OnGlobalLayoutListener {
if (KeyboardHelper.isSoftKeyboardShown(constraint_layout.rootView)) {
text_new_user_agreement.setVisible(false)
......@@ -82,9 +81,7 @@ class SignupFragment : Fragment(), SignupView {
)
}
if (analyticsTrackingInteractor.get()) {
AnalyticsManager.logScreenView(ScreenViewEvent.SignUp)
}
analyticsManager.logScreenView(ScreenViewEvent.SignUp)
}
override fun onDestroyView() {
......
package chat.rocket.android.authentication.twofactor.presentation
import chat.rocket.android.analytics.AnalyticsManager
import chat.rocket.android.analytics.event.AuthenticationEvent
import chat.rocket.android.authentication.presentation.AuthenticationNavigator
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.server.domain.*
import chat.rocket.android.server.domain.GetConnectingServerInteractor
import chat.rocket.android.server.domain.GetSettingsInteractor
import chat.rocket.android.server.domain.PublicSettings
import chat.rocket.android.server.domain.SaveAccountInteractor
import chat.rocket.android.server.domain.SaveCurrentServerInteractor
import chat.rocket.android.server.domain.TokenRepository
import chat.rocket.android.server.domain.favicon
import chat.rocket.android.server.domain.model.Account
import chat.rocket.android.server.domain.wideTile
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.extensions.avatarUrl
import chat.rocket.android.util.extensions.registerPushToken
import chat.rocket.android.util.extensions.serverLogoUrl
import chat.rocket.android.util.helper.analytics.AnalyticsManager
import chat.rocket.android.util.helper.analytics.event.AuthenticationEvent
import chat.rocket.android.util.retryIO
import chat.rocket.common.RocketChatAuthException
import chat.rocket.common.RocketChatException
import chat.rocket.common.util.ifNull
import chat.rocket.core.RocketChatClient
import chat.rocket.core.internal.rest.login
import chat.rocket.core.internal.rest.me
import chat.rocket.core.model.Myself
......@@ -30,14 +35,12 @@ class TwoFAPresenter @Inject constructor(
private val localRepository: LocalRepository,
private val serverInteractor: GetConnectingServerInteractor,
private val saveCurrentServerInteractor: SaveCurrentServerInteractor,
private val analyticsTrackingInteractor: AnalyticsTrackingInteractor,
private val analyticsManager: AnalyticsManager,
private val factory: RocketChatClientFactory,
private val saveAccountInteractor: SaveAccountInteractor,
private val getAccountsInteractor: GetAccountsInteractor,
settingsInteractor: GetSettingsInteractor
) {
private val currentServer = serverInteractor.get()!!
private val client: RocketChatClient = factory.create(currentServer)
private var settings: PublicSettings = settingsInteractor.get(serverInteractor.get()!!)
// TODO: If the usernameOrEmail and password was informed by the user on the previous screen, then we should pass only the pin, like this: fun authenticate(pin: EditText)
......@@ -67,24 +70,20 @@ class TwoFAPresenter @Inject constructor(
saveAccount(me)
saveCurrentServerInteractor.save(currentServer)
tokenRepository.save(server, token)
registerPushToken()
if (analyticsTrackingInteractor.get()) {
AnalyticsManager.logLogin(
AuthenticationEvent.AuthenticationWithUserAndPassword,
true
)
}
localRepository.save(LocalRepository.CURRENT_USERNAME_KEY, me.username)
analyticsManager.logLogin(
AuthenticationEvent.AuthenticationWithUserAndPassword,
true
)
navigator.toChatList()
} catch (exception: RocketChatException) {
if (exception is RocketChatAuthException) {
view.alertInvalidTwoFactorAuthenticationCode()
} else {
if (analyticsTrackingInteractor.get()) {
AnalyticsManager.logLogin(
AuthenticationEvent.AuthenticationWithUserAndPassword,
false
)
}
analyticsManager.logLogin(
AuthenticationEvent.AuthenticationWithUserAndPassword,
false
)
exception.message?.let {
view.showMessage(it)
}.ifNull {
......@@ -101,14 +100,6 @@ class TwoFAPresenter @Inject constructor(
fun signup() = navigator.toSignUp()
private suspend fun registerPushToken() {
localRepository.get(LocalRepository.KEY_PUSH_TOKEN)?.let {
client.registerPushToken(it, getAccountsInteractor.get(), factory)
}
// 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) {
val icon = settings.favicon()?.let {
currentServer.serverLogoUrl(it)
......
......@@ -10,9 +10,10 @@ import android.view.ViewGroup
import android.view.inputmethod.InputMethodManager
import androidx.fragment.app.Fragment
import chat.rocket.android.R
import chat.rocket.android.analytics.AnalyticsManager
import chat.rocket.android.analytics.event.ScreenViewEvent
import chat.rocket.android.authentication.twofactor.presentation.TwoFAPresenter
import chat.rocket.android.authentication.twofactor.presentation.TwoFAView
import chat.rocket.android.server.domain.AnalyticsTrackingInteractor
import chat.rocket.android.util.extensions.inflate
import chat.rocket.android.util.extensions.setVisible
import chat.rocket.android.util.extensions.shake
......@@ -20,8 +21,6 @@ import chat.rocket.android.util.extensions.showToast
import chat.rocket.android.util.extensions.textContent
import chat.rocket.android.util.extensions.ui
import chat.rocket.android.util.extensions.vibrateSmartPhone
import chat.rocket.android.util.helper.analytics.AnalyticsManager
import chat.rocket.android.util.helper.analytics.event.ScreenViewEvent
import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_authentication_two_fa.*
import javax.inject.Inject
......@@ -32,7 +31,7 @@ class TwoFAFragment : Fragment(), TwoFAView {
@Inject
lateinit var presenter: TwoFAPresenter
@Inject
lateinit var analyticsTrackingInteractor: AnalyticsTrackingInteractor
lateinit var analyticsManager: AnalyticsManager
lateinit var username: String
lateinit var password: String
......@@ -66,9 +65,7 @@ class TwoFAFragment : Fragment(), TwoFAView {
setupOnClickListener()
if (analyticsTrackingInteractor.get()) {
AnalyticsManager.logScreenView(ScreenViewEvent.TwoFa)
}
analyticsManager.logScreenView(ScreenViewEvent.TwoFa)
}
override fun alertBlankTwoFactorAuthenticationCode() {
......
......@@ -9,15 +9,14 @@ import androidx.recyclerview.widget.DefaultItemAnimator
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import chat.rocket.android.R
import chat.rocket.android.analytics.AnalyticsManager
import chat.rocket.android.analytics.event.ScreenViewEvent
import chat.rocket.android.chatinformation.adapter.ReadReceiptAdapter
import chat.rocket.android.chatinformation.presentation.MessageInfoPresenter
import chat.rocket.android.chatinformation.presentation.MessageInfoView
import chat.rocket.android.chatinformation.viewmodel.ReadReceiptViewModel
import chat.rocket.android.server.domain.AnalyticsTrackingInteractor
import chat.rocket.android.util.extensions.setVisible
import chat.rocket.android.util.extensions.showToast
import chat.rocket.android.util.helper.analytics.AnalyticsManager
import chat.rocket.android.util.helper.analytics.event.ScreenViewEvent
import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_message_info.*
import javax.inject.Inject
......@@ -37,7 +36,7 @@ class MessageInfoFragment : Fragment(), MessageInfoView {
@Inject
lateinit var presenter: MessageInfoPresenter
@Inject
lateinit var analyticsTrackingInteractor: AnalyticsTrackingInteractor
lateinit var analyticsManager: AnalyticsManager
private lateinit var adapter: ReadReceiptAdapter
private lateinit var messageId: String
......@@ -67,9 +66,7 @@ class MessageInfoFragment : Fragment(), MessageInfoView {
setupRecyclerView()
presenter.loadReadReceipts(messageId = messageId)
if (analyticsTrackingInteractor.get()) {
AnalyticsManager.logScreenView(ScreenViewEvent.MessageInfo)
}
analyticsManager.logScreenView(ScreenViewEvent.MessageInfo)
}
private fun setupRecyclerView() {
......
......@@ -158,23 +158,36 @@ class ChatRoomAdapter(
}
fun prependData(dataSet: List<BaseUiModel<*>>) {
val item = dataSet.indexOfFirst { newItem ->
this.dataSet.indexOfFirst { it.messageId == newItem.messageId && it.viewType == newItem.viewType } > -1
}
if (item == -1) {
this.dataSet.addAll(0, dataSet)
notifyItemRangeInserted(0, dataSet.size)
} else {
dataSet.forEach { item ->
val index = this.dataSet.indexOfFirst {
item.messageId == it.messageId && item.viewType == it.viewType
}
if (index > -1) {
this.dataSet[index] = item
notifyItemChanged(index)
}
//---At first we will update all already saved elements with received updated ones
val filteredDataSet = dataSet.filter { newItem ->
val matchedIndex = this.dataSet.indexOfFirst { it.messageId == newItem.messageId && it.viewType == newItem.viewType }
if (matchedIndex > -1) {
this.dataSet[matchedIndex] = newItem
notifyItemChanged(matchedIndex)
}
return@filter (matchedIndex < 0)
}
val minAdditionDate = filteredDataSet.minBy { it.message.timestamp } ?: return
//---In the most cases we will just add new elements to the top of messages heap
if (this.dataSet.isEmpty() || minAdditionDate.message.timestamp > this.dataSet[0].message.timestamp) {
this.dataSet.addAll(0, filteredDataSet)
notifyItemRangeInserted(0, filteredDataSet.size)
return
}
//---Else branch: merging messages---
//---We are inserting new received elements into set. Sort them by time+type and show
if (filteredDataSet.isEmpty()) return
this.dataSet.addAll(0, filteredDataSet)
val tmp = this.dataSet.sortedWith(Comparator { t, t2 ->
val timeComparison = t.message.timestamp.compareTo(t2.message.timestamp)
if (timeComparison == 0) {
return@Comparator t.viewType.compareTo(t2.viewType)
}
timeComparison
}).reversed()
this.dataSet.clear()
this.dataSet.addAll(tmp)
notifyDataSetChanged()
}
fun updateItem(message: BaseUiModel<*>) {
......
package chat.rocket.android.chatroom.presentation
import android.graphics.Bitmap
import android.net.Uri
import chat.rocket.android.R
import chat.rocket.android.analytics.AnalyticsManager
import chat.rocket.android.analytics.event.SubscriptionTypeEvent
import chat.rocket.android.chatroom.adapter.AutoCompleteType
import chat.rocket.android.chatroom.adapter.PEOPLE
import chat.rocket.android.chatroom.adapter.ROOMS
......@@ -17,7 +20,6 @@ import chat.rocket.android.db.DatabaseManager
import chat.rocket.android.helper.MessageHelper
import chat.rocket.android.helper.UserHelper
import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.server.domain.AnalyticsTrackingInteractor
import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.domain.GetSettingsInteractor
import chat.rocket.android.server.domain.JobSchedulerInteractor
......@@ -31,11 +33,10 @@ import chat.rocket.android.server.domain.uploadMimeTypeFilter
import chat.rocket.android.server.domain.useRealName
import chat.rocket.android.server.infraestructure.ConnectionManagerFactory
import chat.rocket.android.server.infraestructure.state
import chat.rocket.android.util.extension.compressImageAndGetByteArray
import chat.rocket.android.util.extension.compressImageAndGetInputStream
import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.extensions.avatarUrl
import chat.rocket.android.util.helper.analytics.AnalyticsManager
import chat.rocket.android.util.helper.analytics.event.SubscriptionTypeEvent
import chat.rocket.android.util.retryIO
import chat.rocket.common.RocketChatException
import chat.rocket.common.model.RoomType
......@@ -81,6 +82,7 @@ import org.threeten.bp.Instant
import timber.log.Timber
import java.io.InputStream
import java.util.*
import java.util.zip.DeflaterInputStream
import javax.inject.Inject
class ChatRoomPresenter @Inject constructor(
......@@ -93,7 +95,7 @@ class ChatRoomPresenter @Inject constructor(
private val usersRepository: UsersRepository,
private val roomsRepository: RoomRepository,
private val localRepository: LocalRepository,
private val analyticsTrackingInteractor: AnalyticsTrackingInteractor,
private val analyticsManager: AnalyticsManager,
private val userHelper: UserHelper,
private val mapper: UiModelMapper,
private val jobSchedulerInteractor: JobSchedulerInteractor,
......@@ -183,7 +185,8 @@ class ChatRoomPresenter @Inject constructor(
isBroadcast = chatIsBroadcast, isRoom = true
)
)
if (oldMessages.isNotEmpty()) {
val lastSyncDate = messagesRepository.getLastSyncDate(chatRoomId)
if (oldMessages.isNotEmpty() && lastSyncDate != null) {
view.showMessages(oldMessages, clearDataSet)
loadMissingMessages()
} else {
......@@ -225,6 +228,19 @@ class ChatRoomPresenter @Inject constructor(
client.messages(chatRoomId, roomTypeOf(chatRoomType), offset, 30).result
}
messagesRepository.saveAll(messages)
//we are saving last sync date of latest synced chat room message
if (offset == 0L) {
//if success - saving last synced time
if (messages.isEmpty()) {
//chat history is empty - just saving current date
messagesRepository.saveLastSyncDate(chatRoomId, System.currentTimeMillis())
} else {
//assume that BE returns ordered messages, the first message is the latest one
messagesRepository.saveLastSyncDate(chatRoomId, messages.first().timestamp)
}
}
view.showMessages(
mapper.map(
messages,
......@@ -274,7 +290,7 @@ class ChatRoomPresenter @Inject constructor(
timestamp = Instant.now().toEpochMilli(),
sender = SimpleUser(null, username, username),
attachments = null,
avatar = currentServer.avatarUrl(username!!),
avatar = currentServer.avatarUrl(username ?: ""),
channels = null,
editedAt = null,
editedBy = null,
......@@ -300,9 +316,7 @@ class ChatRoomPresenter @Inject constructor(
), false
)
client.sendMessage(id, chatRoomId, text)
if (analyticsTrackingInteractor.get()) {
logMessageSent(currentServer)
}
logMessageSent()
} catch (ex: Exception) {
// Ok, not very beautiful, but the backend sends us a not valid response
// When someone sends a message on a read-only channel, so we just ignore it
......@@ -334,14 +348,15 @@ class ChatRoomPresenter @Inject constructor(
view.showFileSelection(settings.uploadMimeTypeFilter())
}
fun uploadFile(roomId: String, uri: Uri, msg: String) {
fun uploadFile(roomId: String, uri: Uri, msg: String, bitmap: Bitmap? = null) {
launchUI(strategy) {
view.showLoading()
try {
withContext(DefaultDispatcher) {
val fileName = uriInteractor.getFileName(uri) ?: uri.toString()
val fileSize = uriInteractor.getFileSize(uri)
val mimeType = uriInteractor.getMimeType(uri)
val byteArray = bitmap?.compressImageAndGetByteArray(mimeType)
val fileSize = byteArray?.size ?: uriInteractor.getFileSize(uri)
val maxFileSizeAllowed = settings.uploadMaxFileSize()
when {
......@@ -349,16 +364,6 @@ class ChatRoomPresenter @Inject constructor(
fileSize > maxFileSizeAllowed && maxFileSizeAllowed !in -1..0 ->
view.showInvalidFileSize(fileSize, maxFileSizeAllowed)
else -> {
var inputStream: InputStream? = uriInteractor.getInputStream(uri)
if (mimeType.contains("image")) {
uriInteractor.getBitmap(uri)?.let {
it.compressImageAndGetInputStream(mimeType)?.let {
inputStream = it
}
}
}
retryIO("uploadFile($roomId, $fileName, $mimeType") {
client.uploadFile(
roomId,
......@@ -367,12 +372,10 @@ class ChatRoomPresenter @Inject constructor(
msg,
description = fileName
) {
inputStream
byteArray?.inputStream() ?: uriInteractor.getInputStream(uri)
}
}
if (analyticsTrackingInteractor.get()) {
logMediaUploaded(mimeType)
}
logMediaUploaded(mimeType)
}
}
}
......@@ -490,45 +493,49 @@ class ChatRoomPresenter @Inject constructor(
private fun loadMissingMessages() {
launch(parent = strategy.jobs) {
if (chatRoomId != null && chatRoomType != null) {
val roomType = roomTypeOf(chatRoomType!!)
messagesRepository.getByRoomId(chatRoomId!!)
.sortedByDescending { it.timestamp }.firstOrNull()?.let { lastMessage ->
val instant = Instant.ofEpochMilli(lastMessage.timestamp).toString()
try {
val messages =
retryIO(description = "history($chatRoomId, $roomType, $instant)") {
client.history(
chatRoomId!!, roomType, count = 50,
oldest = instant
)
}
Timber.d("History: $messages")
if (messages.result.isNotEmpty()) {
val models = mapper.map(messages.result, RoomUiModel(
roles = chatRoles,
isBroadcast = chatIsBroadcast,
// FIXME: Why are we fixing isRoom attribute to true here?
isRoom = true
))
messagesRepository.saveAll(messages.result)
launchUI(strategy) {
view.showNewMessage(models, true)
}
chatRoomId?.let { chatRoomId ->
val roomType = roomTypeOf(chatRoomType)
val lastSyncDate = messagesRepository.getLastSyncDate(chatRoomId)
// lastSyncDate or 0. LastSyncDate could be in case when we sent some messages offline(and saved them locally),
// but never has obtained chatMessages(or history) from remote. In this case we should sync all chat history from beginning
val instant = Instant.ofEpochMilli(lastSyncDate ?: 0).toString()
//
try {
val messages =
retryIO(description = "history($chatRoomId, $roomType, $instant)") {
client.history(
chatRoomId!!, roomType, count = 50,
oldest = instant
)
}
Timber.d("History: $messages")
if (messages.result.size == 50) {
// we loaded at least count messages, try one more to fetch more messages
loadMissingMessages()
}
}
} catch (ex: Exception) {
// TODO - we need to better treat connection problems here, but no let gaps
// on the messages list
Timber.d(ex, "Error fetching channel history")
if (messages.result.isNotEmpty()) {
val models = mapper.map(messages.result, RoomUiModel(
roles = chatRoles,
isBroadcast = chatIsBroadcast,
// FIXME: Why are we fixing isRoom attribute to true here?
isRoom = true
))
messagesRepository.saveAll(messages.result)
//if success - saving last synced time
//assume that BE returns ordered messages, the first message is the latest one
messagesRepository.saveLastSyncDate(chatRoomId, messages.result.first().timestamp)
launchUI(strategy) {
view.showNewMessage(models, true)
}
if (messages.result.size == 50) {
// we loaded at least count messages, try one more to fetch more messages
loadMissingMessages()
}
}
} catch (ex: Exception) {
// TODO - we need to better treat connection problems here, but no let gaps
// on the messages list
Timber.d(ex, "Error fetching channel history")
}
}
}
}
......@@ -944,9 +951,7 @@ class ChatRoomPresenter @Inject constructor(
retryIO("toggleEmoji($messageId, $emoji)") {
client.toggleReaction(messageId, emoji.removeSurrounding(":"))
}
if (analyticsTrackingInteractor.get()) {
logReactionEvent()
}
logReactionEvent()
} catch (ex: RocketChatException) {
Timber.e(ex)
}
......@@ -956,30 +961,30 @@ class ChatRoomPresenter @Inject constructor(
private fun logReactionEvent() {
when {
roomTypeOf(chatRoomType) is RoomType.DirectMessage ->
AnalyticsManager.logReaction(SubscriptionTypeEvent.DirectMessage)
analyticsManager.logReaction(SubscriptionTypeEvent.DirectMessage)
roomTypeOf(chatRoomType) is RoomType.Channel ->
AnalyticsManager.logReaction(SubscriptionTypeEvent.Channel)
else -> AnalyticsManager.logReaction(SubscriptionTypeEvent.Group)
analyticsManager.logReaction(SubscriptionTypeEvent.Channel)
else -> analyticsManager.logReaction(SubscriptionTypeEvent.Group)
}
}
private fun logMediaUploaded(mimeType: String) {
when {
roomTypeOf(chatRoomType) is RoomType.DirectMessage ->
AnalyticsManager.logMediaUploaded(SubscriptionTypeEvent.DirectMessage, mimeType)
analyticsManager.logMediaUploaded(SubscriptionTypeEvent.DirectMessage, mimeType)
roomTypeOf(chatRoomType) is RoomType.Channel ->
AnalyticsManager.logMediaUploaded(SubscriptionTypeEvent.Channel, mimeType)
else -> AnalyticsManager.logMediaUploaded(SubscriptionTypeEvent.Group, mimeType)
analyticsManager.logMediaUploaded(SubscriptionTypeEvent.Channel, mimeType)
else -> analyticsManager.logMediaUploaded(SubscriptionTypeEvent.Group, mimeType)
}
}
private fun logMessageSent(serverUrl: String) {
private fun logMessageSent() {
when {
roomTypeOf(chatRoomType) is RoomType.DirectMessage ->
AnalyticsManager.logMessageSent(SubscriptionTypeEvent.DirectMessage, serverUrl)
analyticsManager.logMessageSent(SubscriptionTypeEvent.DirectMessage)
roomTypeOf(chatRoomType) is RoomType.Channel ->
AnalyticsManager.logMessageSent(SubscriptionTypeEvent.Channel, serverUrl)
else -> AnalyticsManager.logMessageSent(SubscriptionTypeEvent.Group, serverUrl)
analyticsManager.logMessageSent(SubscriptionTypeEvent.Channel)
else -> analyticsManager.logMessageSent(SubscriptionTypeEvent.Group)
}
}
......
......@@ -5,7 +5,6 @@ import android.app.job.JobService
import chat.rocket.android.server.domain.CurrentServerRepository
import chat.rocket.android.server.domain.MessagesRepository
import chat.rocket.android.server.infraestructure.ConnectionManagerFactory
import chat.rocket.common.RocketChatException
import chat.rocket.core.internal.rest.sendMessage
import chat.rocket.core.model.Message
import dagger.android.AndroidInjection
......
......@@ -33,6 +33,8 @@ import androidx.recyclerview.widget.DefaultItemAnimator
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import chat.rocket.android.R
import chat.rocket.android.analytics.AnalyticsManager
import chat.rocket.android.analytics.event.ScreenViewEvent
import chat.rocket.android.chatroom.adapter.ChatRoomAdapter
import chat.rocket.android.chatroom.adapter.CommandSuggestionsAdapter
import chat.rocket.android.chatroom.adapter.PEOPLE
......@@ -60,7 +62,6 @@ import chat.rocket.android.helper.EndlessRecyclerViewScrollListener
import chat.rocket.android.helper.ImageHelper
import chat.rocket.android.helper.KeyboardHelper
import chat.rocket.android.helper.MessageParser
import chat.rocket.android.server.domain.AnalyticsTrackingInteractor
import chat.rocket.android.util.extension.asObservable
import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.extensions.circularRevealOrUnreveal
......@@ -72,8 +73,6 @@ import chat.rocket.android.util.extensions.rotateBy
import chat.rocket.android.util.extensions.showToast
import chat.rocket.android.util.extensions.textContent
import chat.rocket.android.util.extensions.ui
import chat.rocket.android.util.helper.analytics.AnalyticsManager
import chat.rocket.android.util.helper.analytics.event.ScreenViewEvent
import chat.rocket.common.model.RoomType
import chat.rocket.common.model.roomTypeOf
import chat.rocket.core.internal.realtime.socket.model.State
......@@ -148,7 +147,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
@Inject
lateinit var parser: MessageParser
@Inject
lateinit var analyticsTrackingInteractor: AnalyticsTrackingInteractor
lateinit var analyticsManager: AnalyticsManager
private lateinit var adapter: ChatRoomAdapter
internal lateinit var chatRoomId: String
private lateinit var chatRoomName: String
......@@ -219,8 +218,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
requireNotNull(bundle) { "no arguments supplied when the fragment was instantiated" }
}
adapter = ChatRoomAdapter(chatRoomId, chatRoomType, chatRoomName, this,
reactionListener = this)
adapter = ChatRoomAdapter(chatRoomId, chatRoomType, chatRoomName, this, reactionListener = this)
}
override fun onCreateView(
......@@ -246,9 +244,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
it.showToolbarChatRoomIcon(chatRoomType)
}
if (analyticsTrackingInteractor.get()) {
AnalyticsManager.logScreenView(ScreenViewEvent.ChatRoom)
}
analyticsManager.logScreenView(ScreenViewEvent.ChatRoom)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
......@@ -480,23 +476,18 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
override fun showTypingStatus(usernameList: List<String>) {
ui {
when (usernameList.size) {
1 -> {
text_typing_status.text =
1 -> text_typing_status.text =
SpannableStringBuilder()
.bold { append(usernameList[0]) }
.append(getString(R.string.msg_is_typing))
}
2 -> {
text_typing_status.text =
2 -> text_typing_status.text =
SpannableStringBuilder()
.bold { append(usernameList[0]) }
.append(getString(R.string.msg_and))
.bold { append(usernameList[1]) }
.append(getString(R.string.msg_are_typing))
}
else -> {
text_typing_status.text = getString(R.string.msg_several_users_are_typing)
}
else -> text_typing_status.text = getString(R.string.msg_several_users_are_typing)
}
text_typing_status.isVisible = true
}
......@@ -720,16 +711,16 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
text_connection_status.text = getString(R.string.status_connected)
handler.postDelayed(dismissStatus, 2000)
}
is State.Disconnected -> text_connection_status.text =
getString(R.string.status_disconnected)
is State.Connecting -> text_connection_status.text =
getString(R.string.status_connecting)
is State.Authenticating -> text_connection_status.text =
getString(R.string.status_authenticating)
is State.Disconnecting -> text_connection_status.text =
getString(R.string.status_disconnecting)
is State.Waiting -> text_connection_status.text =
getString(R.string.status_waiting, state.seconds)
is State.Disconnected ->
text_connection_status.text = getString(R.string.status_disconnected)
is State.Connecting ->
text_connection_status.text = getString(R.string.status_connecting)
is State.Authenticating ->
text_connection_status.text = getString(R.string.status_authenticating)
is State.Disconnecting ->
text_connection_status.text = getString(R.string.status_disconnecting)
is State.Waiting ->
text_connection_status.text = getString(R.string.status_waiting, state.seconds)
}
}
}
......@@ -791,7 +782,11 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
activity?.supportFragmentManager?.registerFragmentLifecycleCallbacks(
object : FragmentManager.FragmentLifecycleCallbacks() {
override fun onFragmentAttached(fm: FragmentManager, f: Fragment, context: Context) {
override fun onFragmentAttached(
fm: FragmentManager,
f: Fragment,
context: Context
) {
if (f is MessageActionsBottomSheet) {
setReactionButtonIcon(R.drawable.ic_reaction_24dp)
emojiKeyboardPopup.dismiss()
......@@ -802,12 +797,10 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
)
subscribeComposeTextMessage()
emojiKeyboardPopup =
EmojiKeyboardPopup(activity!!, activity!!.findViewById(R.id.fragment_container))
emojiKeyboardPopup = EmojiKeyboardPopup(activity!!, activity!!.findViewById(R.id.fragment_container))
emojiKeyboardPopup.listener = this
text_message.listener = object : ComposerEditText.ComposerEditTextListener {
override fun onKeyboardOpened() {
}
override fun onKeyboardOpened() {}
override fun onKeyboardClosed() {
activity?.let {
......@@ -1005,7 +998,12 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
presenter.messageInfo(id)
}
override fun citeMessage(roomName: String, roomType: String, messageId: String, mentionAuthor: Boolean) {
override fun citeMessage(
roomName: String,
roomType: String,
messageId: String,
mentionAuthor: Boolean
) {
presenter.citeMessage(roomName, roomType, messageId, mentionAuthor)
}
......@@ -1037,10 +1035,15 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
ui {
val builder = AlertDialog.Builder(it)
builder.setTitle(it.getString(R.string.msg_delete_message))
.setMessage(it.getString(R.string.msg_delete_description))
.setPositiveButton(it.getString(R.string.msg_ok)) { _, _ -> presenter.deleteMessage(roomId, id) }
.setNegativeButton(it.getString(R.string.msg_cancel)) { _, _ -> }
.show()
.setMessage(it.getString(R.string.msg_delete_description))
.setPositiveButton(it.getString(R.string.msg_ok)) { _, _ ->
presenter.deleteMessage(
roomId,
id
)
}
.setNegativeButton(it.getString(R.string.msg_cancel)) { _, _ -> }
.show()
}
}
......
package chat.rocket.android.chatroom.ui
import android.graphics.Bitmap
import android.graphics.drawable.Drawable
import android.net.Uri
import androidx.core.view.isVisible
import chat.rocket.android.emoji.internal.GlideApp
import chat.rocket.android.util.extensions.getFileName
import chat.rocket.android.util.extensions.getMimeType
import com.bumptech.glide.request.target.SimpleTarget
import com.bumptech.glide.request.transition.Transition
fun ChatRoomFragment.showFileAttachmentDialog(uri: Uri) {
activity?.let { fragmentActivity ->
uri.getMimeType(fragmentActivity).let { mimeType ->
imagePreview.isVisible = false
audioVideoAttachment.isVisible = false
textFile.isVisible = false
var bitmap: Bitmap? = null
activity?.let { context ->
uri.getMimeType(context).let { mimeType ->
description.text.clear()
when {
mimeType.startsWith("image") -> {
imagePreview.isVisible = true
imagePreview.setImageURI(uri)
}
mimeType.startsWith("video") -> {
audioVideoAttachment.isVisible = true
GlideApp
.with(context)
.asBitmap()
.load(uri)
.override(imagePreview.width, imagePreview.height)
.fitCenter()
.into(object : SimpleTarget<Bitmap>() {
override fun onResourceReady(
resource: Bitmap,
transition: Transition<in Bitmap>?
) {
bitmap = resource
imagePreview.setImageBitmap(resource)
imagePreview.isVisible = true
}
})
}
mimeType.startsWith("video") -> audioVideoAttachment.isVisible = true
else -> {
textFile.isVisible = true
textFile.text = uri.getFileName(fragmentActivity)
textFile.text = uri.getFileName(context)
}
}
}
}
sendButton.setOnClickListener {
presenter.uploadFile(chatRoomId, uri, (citation ?: "") + description.text.toString())
presenter.uploadFile(
chatRoomId,
uri,
(citation ?: "") + description.text.toString(),
bitmap
)
alertDialog.dismiss()
}
cancelButton.setOnClickListener { alertDialog.dismiss() }
......
......@@ -21,6 +21,8 @@ import androidx.lifecycle.ViewModelProviders
import androidx.recyclerview.widget.DefaultItemAnimator
import androidx.recyclerview.widget.LinearLayoutManager
import chat.rocket.android.R
import chat.rocket.android.analytics.AnalyticsManager
import chat.rocket.android.analytics.event.ScreenViewEvent
import chat.rocket.android.chatrooms.adapter.RoomsAdapter
import chat.rocket.android.chatrooms.presentation.ChatRoomsPresenter
import chat.rocket.android.chatrooms.presentation.ChatRoomsView
......@@ -32,15 +34,12 @@ import chat.rocket.android.db.DatabaseManager
import chat.rocket.android.helper.ChatRoomsSortOrder
import chat.rocket.android.helper.Constants
import chat.rocket.android.helper.SharedPreferenceHelper
import chat.rocket.android.server.domain.AnalyticsTrackingInteractor
import chat.rocket.android.util.extension.onQueryTextListener
import chat.rocket.android.util.extensions.fadeIn
import chat.rocket.android.util.extensions.fadeOut
import chat.rocket.android.util.extensions.inflate
import chat.rocket.android.util.extensions.showToast
import chat.rocket.android.util.extensions.ui
import chat.rocket.android.util.helper.analytics.AnalyticsManager
import chat.rocket.android.util.helper.analytics.event.ScreenViewEvent
import chat.rocket.android.widget.DividerItemDecoration
import chat.rocket.core.internal.realtime.socket.model.State
import dagger.android.support.AndroidSupportInjection
......@@ -60,7 +59,7 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
@Inject
lateinit var dbManager: DatabaseManager // TODO - remove when moving ChatRoom screen to DB
@Inject
lateinit var analyticsTrackingInteractor: AnalyticsTrackingInteractor
lateinit var analyticsManager: AnalyticsManager
lateinit var viewModel: ChatRoomsViewModel
private var searchView: SearchView? = null
private var sortView: MenuItem? = null
......@@ -112,9 +111,7 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
setupToolbar()
if (analyticsTrackingInteractor.get()) {
AnalyticsManager.logScreenView(ScreenViewEvent.ChatRooms)
}
analyticsManager.logScreenView(ScreenViewEvent.ChatRooms)
}
private fun subscribeUi() {
......@@ -124,9 +121,13 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
}
recycler_view.layoutManager = LinearLayoutManager(it)
recycler_view.addItemDecoration(DividerItemDecoration(it,
recycler_view.addItemDecoration(
DividerItemDecoration(
it,
resources.getDimensionPixelSize(R.dimen.divider_item_decorator_bound_start),
resources.getDimensionPixelSize(R.dimen.divider_item_decorator_bound_end)))
resources.getDimensionPixelSize(R.dimen.divider_item_decorator_bound_end)
)
)
recycler_view.itemAnimator = DefaultItemAnimator()
recycler_view.adapter = adapter
......@@ -141,7 +142,7 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
})
viewModel.loadingState.observe(viewLifecycleOwner, Observer { state ->
when(state) {
when (state) {
is LoadingState.Loading -> if (state.count == 0L) showLoading()
is LoadingState.Loaded -> {
hideLoading()
......@@ -195,29 +196,41 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
// TODO - simplify this
R.id.action_sort -> {
val dialogLayout = layoutInflater.inflate(R.layout.chatroom_sort_dialog, null)
val sortType = SharedPreferenceHelper.getInt(Constants.CHATROOM_SORT_TYPE_KEY, ChatRoomsSortOrder.ACTIVITY)
val groupByType = SharedPreferenceHelper.getBoolean(Constants.CHATROOM_GROUP_BY_TYPE_KEY, false)
val sortType = SharedPreferenceHelper.getInt(
Constants.CHATROOM_SORT_TYPE_KEY,
ChatRoomsSortOrder.ACTIVITY
)
val groupByType =
SharedPreferenceHelper.getBoolean(Constants.CHATROOM_GROUP_BY_TYPE_KEY, false)
val radioGroup = dialogLayout.findViewById<RadioGroup>(R.id.radio_group_sort)
val groupByTypeCheckBox = dialogLayout.findViewById<CheckBox>(R.id.checkbox_group_by_type)
val groupByTypeCheckBox =
dialogLayout.findViewById<CheckBox>(R.id.checkbox_group_by_type)
radioGroup.check(when (sortType) {
0 -> R.id.radio_sort_alphabetical
else -> R.id.radio_sort_activity
})
radioGroup.check(
when (sortType) {
0 -> R.id.radio_sort_alphabetical
else -> R.id.radio_sort_activity
}
)
radioGroup.setOnCheckedChangeListener { _, checkedId ->
run {
SharedPreferenceHelper.putInt(Constants.CHATROOM_SORT_TYPE_KEY, when (checkedId) {
R.id.radio_sort_alphabetical -> 0
R.id.radio_sort_activity -> 1
else -> 1
})
SharedPreferenceHelper.putInt(
Constants.CHATROOM_SORT_TYPE_KEY, when (checkedId) {
R.id.radio_sort_alphabetical -> 0
R.id.radio_sort_activity -> 1
else -> 1
}
)
}
}
groupByTypeCheckBox.isChecked = groupByType
groupByTypeCheckBox.setOnCheckedChangeListener { _, isChecked ->
SharedPreferenceHelper.putBoolean(Constants.CHATROOM_GROUP_BY_TYPE_KEY, isChecked)
SharedPreferenceHelper.putBoolean(
Constants.CHATROOM_GROUP_BY_TYPE_KEY,
isChecked
)
}
AlertDialog.Builder(context)
......@@ -234,10 +247,13 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
}
private fun updateSort() {
val sortType = SharedPreferenceHelper.getInt(Constants.CHATROOM_SORT_TYPE_KEY, ChatRoomsSortOrder.ACTIVITY)
val sortType = SharedPreferenceHelper.getInt(
Constants.CHATROOM_SORT_TYPE_KEY,
ChatRoomsSortOrder.ACTIVITY
)
val grouped = SharedPreferenceHelper.getBoolean(Constants.CHATROOM_GROUP_BY_TYPE_KEY, false)
val query = when(sortType) {
val query = when (sortType) {
ChatRoomsSortOrder.ALPHABETICAL -> {
Query.ByName(grouped)
}
......@@ -304,11 +320,16 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
text_connection_status.text = getString(R.string.status_connected)
handler.postDelayed(dismissStatus, 2000)
}
is State.Disconnected -> text_connection_status.text = getString(R.string.status_disconnected)
is State.Connecting -> text_connection_status.text = getString(R.string.status_connecting)
is State.Authenticating -> text_connection_status.text = getString(R.string.status_authenticating)
is State.Disconnecting -> text_connection_status.text = getString(R.string.status_disconnecting)
is State.Waiting -> text_connection_status.text = getString(R.string.status_waiting, state.seconds)
is State.Disconnected -> text_connection_status.text =
getString(R.string.status_disconnected)
is State.Connecting -> text_connection_status.text =
getString(R.string.status_connecting)
is State.Authenticating -> text_connection_status.text =
getString(R.string.status_authenticating)
is State.Disconnecting -> text_connection_status.text =
getString(R.string.status_disconnecting)
is State.Waiting -> text_connection_status.text =
getString(R.string.status_waiting, state.seconds)
}
}
}
......
......@@ -17,15 +17,15 @@ import chat.rocket.common.util.ifNull
import chat.rocket.core.internal.realtime.socket.model.State
import chat.rocket.core.internal.rest.spotlight
import chat.rocket.core.model.SpotlightResult
import com.shopify.livedataktx.distinct
import com.shopify.livedataktx.map
import com.shopify.livedataktx.nonNull
import kotlinx.coroutines.experimental.android.UI
import kotlinx.coroutines.experimental.delay
import kotlinx.coroutines.experimental.isActive
import kotlinx.coroutines.experimental.launch
import kotlinx.coroutines.experimental.newSingleThreadContext
import kotlinx.coroutines.experimental.withContext
import me.henrytao.livedataktx.distinct
import me.henrytao.livedataktx.map
import me.henrytao.livedataktx.nonNull
import timber.log.Timber
import java.security.InvalidParameterException
import kotlin.coroutines.experimental.coroutineContext
......
......@@ -15,18 +15,17 @@ import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import chat.rocket.android.R
import chat.rocket.android.analytics.AnalyticsManager
import chat.rocket.android.analytics.event.ScreenViewEvent
import chat.rocket.android.createchannel.presentation.CreateChannelPresenter
import chat.rocket.android.createchannel.presentation.CreateChannelView
import chat.rocket.android.main.ui.MainActivity
import chat.rocket.android.members.adapter.MembersAdapter
import chat.rocket.android.members.uimodel.MemberUiModel
import chat.rocket.android.server.domain.AnalyticsTrackingInteractor
import chat.rocket.android.util.extension.asObservable
import chat.rocket.android.util.extensions.inflate
import chat.rocket.android.util.extensions.showToast
import chat.rocket.android.util.extensions.ui
import chat.rocket.android.util.helper.analytics.AnalyticsManager
import chat.rocket.android.util.helper.analytics.event.ScreenViewEvent
import chat.rocket.common.model.RoomType
import chat.rocket.common.model.roomTypeOf
import com.google.android.material.chip.Chip
......@@ -43,7 +42,7 @@ class CreateChannelFragment : Fragment(), CreateChannelView, ActionMode.Callback
@Inject
lateinit var createChannelPresenter: CreateChannelPresenter
@Inject
lateinit var analyticsTrackingInteractor: AnalyticsTrackingInteractor
lateinit var analyticsManager: AnalyticsManager
private var actionMode: ActionMode? = null
private val adapter: MembersAdapter = MembersAdapter {
if (it.username != null) {
......@@ -77,9 +76,7 @@ class CreateChannelFragment : Fragment(), CreateChannelView, ActionMode.Callback
setupRecyclerView()
subscribeEditTexts()
if (analyticsTrackingInteractor.get()) {
AnalyticsManager.logScreenView(ScreenViewEvent.CreateChannel)
}
analyticsManager.logScreenView(ScreenViewEvent.CreateChannel)
}
override fun onDestroyView() {
......
package chat.rocket.android.dagger
import android.app.Application
import chat.rocket.android.app.AppLifecycleObserver
import chat.rocket.android.app.RocketChatApplication
import chat.rocket.android.chatroom.service.MessageService
import chat.rocket.android.dagger.module.ActivityBuilder
import chat.rocket.android.dagger.module.AndroidWorkerInjectionModule
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.push.FirebaseTokenService
import dagger.BindsInstance
import dagger.Component
import dagger.android.support.AndroidSupportInjectionModule
......@@ -16,7 +15,8 @@ import javax.inject.Singleton
@Singleton
@Component(modules = [AndroidSupportInjectionModule::class,
AppModule::class, ActivityBuilder::class, ServiceBuilder::class, ReceiverBuilder::class])
AppModule::class, ActivityBuilder::class, ServiceBuilder::class, ReceiverBuilder::class,
AndroidWorkerInjectionModule::class])
interface AppComponent {
@Component.Builder
......@@ -29,10 +29,5 @@ interface AppComponent {
fun inject(app: RocketChatApplication)
fun inject(service: FirebaseTokenService)
fun inject(service: MessageService)
/*@Component.Builder
abstract class Builder : AndroidInjector.Builder<RocketChatApplication>()*/
}
package chat.rocket.android.dagger.injector
import androidx.work.Worker
object AndroidWorkerInjection {
fun inject(worker: Worker) {
val application = worker.applicationContext
if (application !is HasWorkerInjector) {
throw RuntimeException("${application.javaClass.canonicalName} does not implement ${HasWorkerInjector::class.java.canonicalName}")
}
val workerInjector = (application as HasWorkerInjector).workerInjector()
checkNotNull(workerInjector) { "${application.javaClass}.workerInjector() return null" }
workerInjector.inject(worker)
}
}
\ No newline at end of file
package chat.rocket.android.dagger.injector
import androidx.work.Worker
import dagger.android.AndroidInjector
interface HasWorkerInjector {
fun workerInjector(): AndroidInjector<Worker>
}
\ No newline at end of file
package chat.rocket.android.dagger.module
import chat.rocket.android.about.di.AboutFragmentProvider
import chat.rocket.android.authentication.di.AuthenticationModule
import chat.rocket.android.authentication.login.di.LoginFragmentProvider
import chat.rocket.android.authentication.loginoptions.di.LoginOptionsFragmentProvider
......@@ -62,6 +63,7 @@ abstract class ActivityBuilder {
CreateChannelProvider::class,
ProfileFragmentProvider::class,
SettingsFragmentProvider::class,
AboutFragmentProvider::class,
PreferencesFragmentProvider::class
]
)
......
package chat.rocket.android.dagger.module
import androidx.work.Worker
import dagger.Module
import dagger.android.AndroidInjector
import dagger.multibindings.Multibinds
@Module
abstract class AndroidWorkerInjectionModule {
@Multibinds
abstract fun workerInjectorFactories(): Map<Class<out Worker>, AndroidInjector.Factory<out Worker>>
}
\ No newline at end of file
......@@ -9,6 +9,10 @@ import android.content.Context
import android.content.SharedPreferences
import chat.rocket.android.BuildConfig
import chat.rocket.android.R
import chat.rocket.android.analytics.Analytics
import chat.rocket.android.analytics.AnalyticsManager
import chat.rocket.android.analytics.AnswersAnalytics
import chat.rocket.android.analytics.GoogleAnalyticsForFirebase
import chat.rocket.android.authentication.infraestructure.SharedPreferencesMultiServerTokenRepository
import chat.rocket.android.authentication.infraestructure.SharedPreferencesTokenRepository
import chat.rocket.android.chatroom.service.MessageService
......@@ -23,10 +27,12 @@ import chat.rocket.android.push.GroupedPush
import chat.rocket.android.push.PushManager
import chat.rocket.android.server.domain.AccountsRepository
import chat.rocket.android.server.domain.ActiveUsersRepository
import chat.rocket.android.server.domain.AnalyticsTrackingInteractor
import chat.rocket.android.server.domain.AnalyticsTrackingRepository
import chat.rocket.android.server.domain.ChatRoomsRepository
import chat.rocket.android.server.domain.CurrentServerRepository
import chat.rocket.android.server.domain.GetAccountInteractor
import chat.rocket.android.server.domain.GetAccountsInteractor
import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.domain.GetSettingsInteractor
import chat.rocket.android.server.domain.JobSchedulerInteractor
......@@ -115,7 +121,10 @@ class AppModule {
@Provides
@Singleton
fun provideImagePipelineConfig(context: Context, okHttpClient: OkHttpClient): ImagePipelineConfig {
fun provideImagePipelineConfig(
context: Context,
okHttpClient: OkHttpClient
): ImagePipelineConfig {
val listeners = setOf(RequestLoggingListener())
return OkHttpImagePipelineConfigFactory.newBuilder(context, okHttpClient)
......@@ -185,7 +194,10 @@ class AppModule {
@Provides
@Singleton
fun providePermissionsRepository(localRepository: LocalRepository, moshi: Moshi): PermissionsRepository {
fun providePermissionsRepository(
localRepository: LocalRepository,
moshi: Moshi
): PermissionsRepository {
return SharedPreferencesPermissionsRepository(localRepository, moshi)
}
......@@ -234,15 +246,20 @@ class AppModule {
@Provides
@Singleton
fun provideMultiServerTokenRepository(repository: LocalRepository, moshi: Moshi): MultiServerTokenRepository {
fun provideMultiServerTokenRepository(
repository: LocalRepository,
moshi: Moshi
): MultiServerTokenRepository {
return SharedPreferencesMultiServerTokenRepository(repository, moshi)
}
@Provides
@Singleton
fun provideMessageRepository(@ForMessages preferences: SharedPreferences,
moshi: Moshi,
currentServerInteractor: GetCurrentServerInteractor): MessagesRepository {
fun provideMessageRepository(
@ForMessages preferences: SharedPreferences,
moshi: Moshi,
currentServerInteractor: GetCurrentServerInteractor
): MessagesRepository {
return SharedPreferencesMessagesRepository(preferences, moshi, currentServerInteractor)
}
......@@ -257,10 +274,12 @@ class AppModule {
fun provideConfiguration(context: Application): SpannableConfiguration {
val res = context.resources
return SpannableConfiguration.builder(context)
.theme(SpannableTheme.builder()
.blockMargin(0)
.linkColor(res.getColor(R.color.colorAccent))
.build())
.theme(
SpannableTheme.builder()
.blockMargin(0)
.linkColor(res.getColor(R.color.colorAccent))
.build()
)
.build()
}
......@@ -277,7 +296,10 @@ class AppModule {
@Provides
@Singleton
fun provideAccountsRepository(preferences: SharedPreferences, moshi: Moshi): AccountsRepository =
fun provideAccountsRepository(
preferences: SharedPreferences,
moshi: Moshi
): AccountsRepository =
SharedPreferencesAccountsRepository(preferences, moshi)
@Provides
......@@ -296,8 +318,16 @@ class AppModule {
manager: NotificationManager,
moshi: Moshi,
getAccountInteractor: GetAccountInteractor,
getSettingsInteractor: GetSettingsInteractor): PushManager {
return PushManager(groupedPushes, manager, moshi, getAccountInteractor, getSettingsInteractor, context)
getSettingsInteractor: GetSettingsInteractor
): PushManager {
return PushManager(
groupedPushes,
manager,
moshi,
getAccountInteractor,
getSettingsInteractor,
context
)
}
@Provides
......@@ -307,14 +337,19 @@ class AppModule {
@Provides
fun provideSendMessageJob(context: Application): JobInfo {
return JobInfo.Builder(MessageService.RETRY_SEND_MESSAGE_ID,
ComponentName(context, MessageService::class.java))
return JobInfo.Builder(
MessageService.RETRY_SEND_MESSAGE_ID,
ComponentName(context, MessageService::class.java)
)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
.build()
}
@Provides
fun provideJobSchedulerInteractor(jobScheduler: JobScheduler, jobInfo: JobInfo): JobSchedulerInteractor {
fun provideJobSchedulerInteractor(
jobScheduler: JobScheduler,
jobInfo: JobInfo
): JobSchedulerInteractor {
return JobSchedulerInteractorImpl(jobScheduler, jobInfo)
}
......@@ -331,4 +366,33 @@ class AppModule {
): DatabaseManager {
return factory.create(currentServer)
}
@Provides
@Singleton
fun provideAnswersAnalytics(): AnswersAnalytics {
return AnswersAnalytics()
}
@Provides
@Singleton
fun provideGoogleAnalyticsForFirebase(context: Application): GoogleAnalyticsForFirebase {
return GoogleAnalyticsForFirebase(context)
}
@Provides
@Singleton
fun provideAnalyticsManager(
analyticsTrackingInteractor: AnalyticsTrackingInteractor,
getCurrentServerInteractor: GetCurrentServerInteractor,
getAccountsInteractor: GetAccountsInteractor,
answersAnalytics: AnswersAnalytics,
googleAnalyticsForFirebase: GoogleAnalyticsForFirebase
): AnalyticsManager {
return AnalyticsManager(
analyticsTrackingInteractor,
getCurrentServerInteractor,
getAccountsInteractor,
listOf(answersAnalytics, googleAnalyticsForFirebase)
)
}
}
package chat.rocket.android.dagger.qualifier
import androidx.work.Worker
import dagger.MapKey
import kotlin.reflect.KClass
@MapKey
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FUNCTION)
annotation class WorkerKey(val value: KClass<out Worker>)
......@@ -10,18 +10,17 @@ import androidx.recyclerview.widget.DefaultItemAnimator
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import chat.rocket.android.R
import chat.rocket.android.analytics.AnalyticsManager
import chat.rocket.android.analytics.event.ScreenViewEvent
import chat.rocket.android.chatroom.adapter.ChatRoomAdapter
import chat.rocket.android.chatroom.ui.ChatRoomActivity
import chat.rocket.android.chatroom.uimodel.BaseUiModel
import chat.rocket.android.favoritemessages.presentation.FavoriteMessagesPresenter
import chat.rocket.android.favoritemessages.presentation.FavoriteMessagesView
import chat.rocket.android.helper.EndlessRecyclerViewScrollListener
import chat.rocket.android.server.domain.AnalyticsTrackingInteractor
import chat.rocket.android.util.extensions.inflate
import chat.rocket.android.util.extensions.showToast
import chat.rocket.android.util.extensions.ui
import chat.rocket.android.util.helper.analytics.AnalyticsManager
import chat.rocket.android.util.helper.analytics.event.ScreenViewEvent
import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_favorite_messages.*
import javax.inject.Inject
......@@ -41,7 +40,7 @@ class FavoriteMessagesFragment : Fragment(), FavoriteMessagesView {
@Inject
lateinit var presenter: FavoriteMessagesPresenter
@Inject
lateinit var analyticsTrackingInteractor: AnalyticsTrackingInteractor
lateinit var analyticsManager: AnalyticsManager
private lateinit var chatRoomId: String
private val adapter = ChatRoomAdapter(enableActions = false)
......@@ -68,9 +67,7 @@ class FavoriteMessagesFragment : Fragment(), FavoriteMessagesView {
setupToolbar()
presenter.loadFavoriteMessages(chatRoomId)
if (analyticsTrackingInteractor.get()) {
AnalyticsManager.logScreenView(ScreenViewEvent.FavoriteMessages)
}
analyticsManager.logScreenView(ScreenViewEvent.FavoriteMessages)
}
override fun showFavoriteMessages(favoriteMessages: List<BaseUiModel<*>>) {
......
......@@ -13,6 +13,8 @@ import androidx.recyclerview.widget.DividerItemDecoration.HORIZONTAL
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import chat.rocket.android.R
import chat.rocket.android.analytics.AnalyticsManager
import chat.rocket.android.analytics.event.ScreenViewEvent
import chat.rocket.android.chatroom.ui.ChatRoomActivity
import chat.rocket.android.files.adapter.FilesAdapter
import chat.rocket.android.files.presentation.FilesPresenter
......@@ -21,12 +23,9 @@ import chat.rocket.android.files.uimodel.FileUiModel
import chat.rocket.android.helper.EndlessRecyclerViewScrollListener
import chat.rocket.android.helper.ImageHelper
import chat.rocket.android.player.PlayerActivity
import chat.rocket.android.server.domain.AnalyticsTrackingInteractor
import chat.rocket.android.util.extensions.inflate
import chat.rocket.android.util.extensions.showToast
import chat.rocket.android.util.extensions.ui
import chat.rocket.android.util.helper.analytics.AnalyticsManager
import chat.rocket.android.util.helper.analytics.event.ScreenViewEvent
import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_files.*
import javax.inject.Inject
......@@ -46,7 +45,7 @@ class FilesFragment : Fragment(), FilesView {
@Inject
lateinit var presenter: FilesPresenter
@Inject
lateinit var analyticsTrackingInteractor: AnalyticsTrackingInteractor
lateinit var analyticsManager: AnalyticsManager
private val adapter: FilesAdapter =
FilesAdapter { fileUiModel -> presenter.openFile(fileUiModel) }
private val linearLayoutManager = LinearLayoutManager(context)
......@@ -75,31 +74,30 @@ class FilesFragment : Fragment(), FilesView {
setupRecyclerView()
presenter.loadFiles(chatRoomId)
if (analyticsTrackingInteractor.get()) {
AnalyticsManager.logScreenView(ScreenViewEvent.Files)
}
analyticsManager.logScreenView(ScreenViewEvent.Files)
}
override fun showFiles(dataSet: List<FileUiModel>, total: Long) {
setupToolbar(total)
if (adapter.itemCount == 0) {
adapter.prependData(dataSet)
if (dataSet.size >= 30) {
recycler_view.addOnScrollListener(object :
EndlessRecyclerViewScrollListener(linearLayoutManager) {
override fun onLoadMore(
page: Int,
totalItemsCount: Int,
recyclerView: RecyclerView
) {
presenter.loadFiles(chatRoomId)
}
})
ui {
setupToolbar(total)
if (adapter.itemCount == 0) {
adapter.prependData(dataSet)
if (dataSet.size >= 30) {
recycler_view.addOnScrollListener(object :
EndlessRecyclerViewScrollListener(linearLayoutManager) {
override fun onLoadMore(
page: Int,
totalItemsCount: Int,
recyclerView: RecyclerView
) {
presenter.loadFiles(chatRoomId)
}
})
}
group_no_file.isVisible = dataSet.isEmpty()
} else {
adapter.appendData(dataSet)
}
group_no_file.isVisible = dataSet.isEmpty()
} else {
adapter.appendData(dataSet)
}
}
......
......@@ -34,8 +34,8 @@ class FileUiModel(
}
private fun getUserDisplayName(): String {
val username = "@${genericAttachment.user?.username}"
val realName = genericAttachment.user?.name
val username = "@${genericAttachment.user.username}"
val realName = genericAttachment.user.name
val uploaderName = if (settings.useRealName()) realName else username
return uploaderName ?: username
}
......
......@@ -131,7 +131,7 @@ class MessageParser @Inject constructor(
private val builder: SpannableBuilder
) : SpannableMarkdownVisitor(configuration, builder) {
private val emojiSize = context.resources.getDimensionPixelSize(R.dimen.radius_mention)
private val emojiSize = context.resources.getDimensionPixelSize(R.dimen.custom_emoji_small)
override fun visit(document: Document) {
val spannable = EmojiParser.parse(context, builder.text())
......
......@@ -27,7 +27,6 @@ import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.server.presentation.CheckServerPresenter
import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.extensions.adminPanelUrl
import chat.rocket.android.util.extensions.registerPushToken
import chat.rocket.android.util.extensions.serverLogoUrl
import chat.rocket.android.util.retryIO
import chat.rocket.common.RocketChatAuthException
......@@ -59,7 +58,7 @@ class MainPresenter @Inject constructor(
private val saveAccountInteractor: SaveAccountInteractor,
private val getAccountsInteractor: GetAccountsInteractor,
private val removeAccountInteractor: RemoveAccountInteractor,
private val factory: RocketChatClientFactory,
factory: RocketChatClientFactory,
private val groupedPush: GroupedPush,
dbManagerFactory: DatabaseManagerFactory,
getSettingsInteractor: GetSettingsInteractor,
......@@ -233,13 +232,6 @@ class MainPresenter @Inject constructor(
}
}
suspend fun refreshToken(token: String?) {
token?.let {
localRepository.save(LocalRepository.KEY_PUSH_TOKEN, token)
client.registerPushToken(token, getAccountsInteractor.get(), factory)
}
}
private suspend fun saveAccount(uiModel: NavHeaderUiModel) {
val icon = settings.favicon()?.let {
currentServer.serverLogoUrl(it)
......
......@@ -18,6 +18,7 @@ import chat.rocket.android.main.adapter.Selector
import chat.rocket.android.main.presentation.MainPresenter
import chat.rocket.android.main.presentation.MainView
import chat.rocket.android.main.uimodel.NavHeaderUiModel
import chat.rocket.android.push.refreshPushToken
import chat.rocket.android.server.domain.PermissionsInteractor
import chat.rocket.android.server.domain.model.Account
import chat.rocket.android.server.ui.INTENT_CHAT_ROOM_ID
......@@ -26,7 +27,6 @@ import chat.rocket.android.util.extensions.fadeOut
import chat.rocket.android.util.extensions.rotateBy
import chat.rocket.android.util.extensions.showToast
import chat.rocket.android.util.invalidateFirebaseToken
import chat.rocket.android.util.refreshFCMToken
import chat.rocket.common.model.UserStatus
import dagger.android.AndroidInjection
import dagger.android.AndroidInjector
......@@ -36,9 +36,6 @@ import dagger.android.support.HasSupportFragmentInjector
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.app_bar.*
import kotlinx.android.synthetic.main.nav_header.view.*
import kotlinx.coroutines.experimental.CommonPool
import kotlinx.coroutines.experimental.launch
import timber.log.Timber
import javax.inject.Inject
private const val CURRENT_STATE = "current_state"
......@@ -64,9 +61,7 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector,
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
launch(CommonPool) {
refreshFCMToken(presenter)
}
refreshPushToken()
chatRoomId = intent.getStringExtra(INTENT_CHAT_ROOM_ID)
......
......@@ -2,6 +2,7 @@ package chat.rocket.android.members.di
import chat.rocket.android.members.ui.MembersFragment
import chat.rocket.android.dagger.scope.PerFragment
import chat.rocket.android.members.ui.MemberBottomSheetFragment
import dagger.Module
import dagger.android.ContributesAndroidInjector
......@@ -11,4 +12,9 @@ abstract class MembersFragmentProvider {
@ContributesAndroidInjector(modules = [MembersFragmentModule::class])
@PerFragment
abstract fun provideMembersFragment(): MembersFragment
@ContributesAndroidInjector()
@PerFragment
abstract fun provideMemberBottomSheetFragment(): MemberBottomSheetFragment
}
\ No newline at end of file
......@@ -6,12 +6,12 @@ import android.view.View
import android.view.ViewGroup
import androidx.core.view.isVisible
import chat.rocket.android.R
import chat.rocket.android.server.domain.AnalyticsTrackingInteractor
import chat.rocket.android.analytics.AnalyticsManager
import chat.rocket.android.analytics.event.ScreenViewEvent
import chat.rocket.android.util.extensions.content
import chat.rocket.android.util.extensions.textContent
import chat.rocket.android.util.helper.analytics.AnalyticsManager
import chat.rocket.android.util.helper.analytics.event.ScreenViewEvent
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_member_bottom_sheet.*
import javax.inject.Inject
......@@ -43,7 +43,7 @@ private const val BUNDLE_UTC_OFFSET = "utc_offset"
class MemberBottomSheetFragment : BottomSheetDialogFragment() {
@Inject
lateinit var analyticsTrackingInteractor: AnalyticsTrackingInteractor
lateinit var analyticsManager: AnalyticsManager
private lateinit var avatarUri: String
private lateinit var realName: String
private lateinit var username: String
......@@ -52,6 +52,7 @@ class MemberBottomSheetFragment : BottomSheetDialogFragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
AndroidSupportInjection.inject(this)
val bundle = arguments
if (bundle != null) {
......@@ -76,9 +77,7 @@ class MemberBottomSheetFragment : BottomSheetDialogFragment() {
super.onViewCreated(view, savedInstanceState)
showMemberDetails()
if (analyticsTrackingInteractor.get()) {
AnalyticsManager.logScreenView(ScreenViewEvent.MemberBottomSheet)
}
analyticsManager.logScreenView(ScreenViewEvent.MemberBottomSheet)
}
private fun showMemberDetails() {
......
package chat.rocket.android.members.ui
import android.os.Bundle
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import chat.rocket.android.R
import chat.rocket.android.analytics.AnalyticsManager
import chat.rocket.android.analytics.event.ScreenViewEvent
import chat.rocket.android.chatroom.ui.ChatRoomActivity
import chat.rocket.android.helper.EndlessRecyclerViewScrollListener
import chat.rocket.android.members.adapter.MembersAdapter
import chat.rocket.android.members.presentation.MembersPresenter
import chat.rocket.android.members.presentation.MembersView
import chat.rocket.android.members.uimodel.MemberUiModel
import chat.rocket.android.server.domain.AnalyticsTrackingInteractor
import chat.rocket.android.util.extensions.inflate
import chat.rocket.android.util.extensions.showToast
import chat.rocket.android.util.extensions.ui
import chat.rocket.android.util.helper.analytics.AnalyticsManager
import chat.rocket.android.util.helper.analytics.event.ScreenViewEvent
import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_members.*
import javax.inject.Inject
......@@ -41,7 +40,7 @@ class MembersFragment : Fragment(), MembersView {
@Inject
lateinit var presenter: MembersPresenter
@Inject
lateinit var analyticsTrackingInteractor: AnalyticsTrackingInteractor
lateinit var analyticsManager: AnalyticsManager
private val adapter: MembersAdapter =
MembersAdapter { memberUiModel -> presenter.toMemberDetails(memberUiModel) }
private val linearLayoutManager = LinearLayoutManager(context)
......@@ -70,9 +69,7 @@ class MembersFragment : Fragment(), MembersView {
setupRecyclerView()
presenter.loadChatRoomsMembers(chatRoomId)
if (analyticsTrackingInteractor.get()) {
AnalyticsManager.logScreenView(ScreenViewEvent.Members)
}
analyticsManager.logScreenView(ScreenViewEvent.Members)
}
override fun showMembers(dataSet: List<MemberUiModel>, total: Long) {
......
......@@ -10,18 +10,17 @@ import androidx.recyclerview.widget.DefaultItemAnimator
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import chat.rocket.android.R
import chat.rocket.android.analytics.AnalyticsManager
import chat.rocket.android.analytics.event.ScreenViewEvent
import chat.rocket.android.chatroom.adapter.ChatRoomAdapter
import chat.rocket.android.chatroom.ui.ChatRoomActivity
import chat.rocket.android.chatroom.uimodel.BaseUiModel
import chat.rocket.android.helper.EndlessRecyclerViewScrollListener
import chat.rocket.android.mentions.presentention.MentionsPresenter
import chat.rocket.android.mentions.presentention.MentionsView
import chat.rocket.android.server.domain.AnalyticsTrackingInteractor
import chat.rocket.android.util.extensions.inflate
import chat.rocket.android.util.extensions.showToast
import chat.rocket.android.util.extensions.ui
import chat.rocket.android.util.helper.analytics.AnalyticsManager
import chat.rocket.android.util.helper.analytics.event.ScreenViewEvent
import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_mentions.*
import javax.inject.Inject
......@@ -44,7 +43,7 @@ class MentionsFragment : Fragment(), MentionsView {
@Inject
lateinit var presenter: MentionsPresenter
@Inject
lateinit var analyticsTrackingInteractor: AnalyticsTrackingInteractor
lateinit var analyticsManager: AnalyticsManager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
......@@ -70,9 +69,7 @@ class MentionsFragment : Fragment(), MentionsView {
setupToolbar()
presenter.loadMentions(chatRoomId)
if (analyticsTrackingInteractor.get()) {
AnalyticsManager.logScreenView(ScreenViewEvent.Mentions)
}
analyticsManager.logScreenView(ScreenViewEvent.Mentions)
}
override fun showMentions(mentions: List<BaseUiModel<*>>) {
......
......@@ -10,6 +10,8 @@ import androidx.recyclerview.widget.DefaultItemAnimator
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import chat.rocket.android.R
import chat.rocket.android.analytics.AnalyticsManager
import chat.rocket.android.analytics.event.ScreenViewEvent
import chat.rocket.android.chatroom.adapter.ChatRoomAdapter
import chat.rocket.android.chatroom.ui.ChatRoomActivity
import chat.rocket.android.chatroom.uimodel.BaseUiModel
......@@ -20,8 +22,6 @@ import chat.rocket.android.server.domain.AnalyticsTrackingInteractor
import chat.rocket.android.util.extensions.inflate
import chat.rocket.android.util.extensions.showToast
import chat.rocket.android.util.extensions.ui
import chat.rocket.android.util.helper.analytics.AnalyticsManager
import chat.rocket.android.util.helper.analytics.event.ScreenViewEvent
import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_pinned_messages.*
import javax.inject.Inject
......@@ -41,7 +41,7 @@ class PinnedMessagesFragment : Fragment(), PinnedMessagesView {
@Inject
lateinit var presenter: PinnedMessagesPresenter
@Inject
lateinit var analyticsTrackingInteractor: AnalyticsTrackingInteractor
lateinit var analyticsManager: AnalyticsManager
private lateinit var chatRoomId: String
private val adapter = ChatRoomAdapter(enableActions = false)
......@@ -69,9 +69,7 @@ class PinnedMessagesFragment : Fragment(), PinnedMessagesView {
setupToolbar()
presenter.loadPinnedMessages(chatRoomId)
if (analyticsTrackingInteractor.get()) {
AnalyticsManager.logScreenView(ScreenViewEvent.PinnedMessages)
}
analyticsManager.logScreenView(ScreenViewEvent.PinnedMessages)
}
override fun showPinnedMessages(pinnedMessages: List<BaseUiModel<*>>) {
......
......@@ -7,12 +7,11 @@ import android.view.ViewGroup
import androidx.fragment.app.Fragment
import chat.rocket.android.BuildConfig
import chat.rocket.android.R
import chat.rocket.android.analytics.AnalyticsManager
import chat.rocket.android.analytics.event.ScreenViewEvent
import chat.rocket.android.main.ui.MainActivity
import chat.rocket.android.preferences.presentation.PreferencesPresenter
import chat.rocket.android.preferences.presentation.PreferencesView
import chat.rocket.android.server.domain.AnalyticsTrackingInteractor
import chat.rocket.android.util.helper.analytics.AnalyticsManager
import chat.rocket.android.util.helper.analytics.event.ScreenViewEvent
import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.app_bar.*
import kotlinx.android.synthetic.main.fragment_preferences.*
......@@ -24,7 +23,7 @@ class PreferencesFragment : Fragment(), PreferencesView {
@Inject
lateinit var presenter: PreferencesPresenter
@Inject
lateinit var analyticsTrackingInteractor: AnalyticsTrackingInteractor
lateinit var analyticsManager: AnalyticsManager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
......@@ -43,17 +42,15 @@ class PreferencesFragment : Fragment(), PreferencesView {
setupListeners()
presenter.loadAnalyticsTrackingInformation()
if (analyticsTrackingInteractor.get()) {
AnalyticsManager.logScreenView(ScreenViewEvent.Preferences)
}
analyticsManager.logScreenView(ScreenViewEvent.Preferences)
}
override fun setupAnalyticsTrackingView(isAnalyticsTrackingEnabled: Boolean) {
if (BuildConfig.FLAVOR == "foss") {
text_analytics_tracking_description.text =
getString(R.string.msg_not_applicable_since_it_is_a_foss_version)
switch_analytics_tracking.isChecked = false
switch_analytics_tracking.isEnabled = false
text_analytics_tracking_description.text =
getString(R.string.msg_not_applicable_since_it_is_a_foss_version)
return
}
......
......@@ -17,10 +17,11 @@ import androidx.core.net.toUri
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import chat.rocket.android.R
import chat.rocket.android.analytics.AnalyticsManager
import chat.rocket.android.analytics.event.ScreenViewEvent
import chat.rocket.android.main.ui.MainActivity
import chat.rocket.android.profile.presentation.ProfilePresenter
import chat.rocket.android.profile.presentation.ProfileView
import chat.rocket.android.server.domain.AnalyticsTrackingInteractor
import chat.rocket.android.util.extension.asObservable
import chat.rocket.android.util.extension.dispatchImageSelection
import chat.rocket.android.util.extension.dispatchTakePicture
......@@ -28,8 +29,6 @@ import chat.rocket.android.util.extensions.inflate
import chat.rocket.android.util.extensions.showToast
import chat.rocket.android.util.extensions.textContent
import chat.rocket.android.util.extensions.ui
import chat.rocket.android.util.helper.analytics.AnalyticsManager
import chat.rocket.android.util.helper.analytics.event.ScreenViewEvent
import com.facebook.drawee.backends.pipeline.Fresco
import dagger.android.support.AndroidSupportInjection
import io.reactivex.disposables.CompositeDisposable
......@@ -48,7 +47,7 @@ class ProfileFragment : Fragment(), ProfileView, ActionMode.Callback {
@Inject
lateinit var presenter: ProfilePresenter
@Inject
lateinit var analyticsTrackingInteractor: AnalyticsTrackingInteractor
lateinit var analyticsManager: AnalyticsManager
private var currentName = ""
private var currentUsername = ""
private var currentEmail = ""
......@@ -81,9 +80,7 @@ class ProfileFragment : Fragment(), ProfileView, ActionMode.Callback {
presenter.loadUserProfile()
subscribeEditTexts()
if (analyticsTrackingInteractor.get()) {
AnalyticsManager.logScreenView(ScreenViewEvent.Profile)
}
analyticsManager.logScreenView(ScreenViewEvent.Profile)
}
override fun onDestroyView() {
......
......@@ -4,7 +4,6 @@ import android.app.NotificationManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import androidx.core.app.NotificationManagerCompat
import androidx.core.app.RemoteInput
import android.widget.Toast
import chat.rocket.android.R
......
......@@ -3,7 +3,7 @@ package chat.rocket.android.server.domain
import chat.rocket.android.server.domain.model.Account
interface AccountsRepository {
suspend fun save(account: Account)
suspend fun load(): List<Account>
suspend fun remove(serverUrl: String)
fun save(account: Account)
fun load(): List<Account>
fun remove(serverUrl: String)
}
\ No newline at end of file
......@@ -3,5 +3,5 @@ package chat.rocket.android.server.domain
import javax.inject.Inject
class GetAccountsInteractor @Inject constructor(val repository: AccountsRepository) {
suspend fun get() = repository.load()
fun get() = repository.load()
}
\ No newline at end of file
......@@ -72,4 +72,22 @@ interface MessagesRepository {
suspend fun getAllUnsent(): List<Message>
suspend fun getUnsentByRoomId(roomId: String): List<Message>
/**
* Save time of the latest room messages sync.
* Call this fun only when the latest messages list being received via /history or /messages network calls
*
* @param rid The id of the room the messages are.
* @param timeMillis time of room messages sync or the latest room message timestamp(which came with /history request)
*/
suspend fun saveLastSyncDate(rid: String, timeMillis: Long)
/**
* Get time when the room chat history has been loaded last time.
*
* @param rid The id of the room the messages are.
*
* @return Last Sync time or Null.
*/
suspend fun getLastSyncDate(rid: String): Long?
}
\ No newline at end of file
......@@ -7,8 +7,16 @@ import kotlinx.coroutines.experimental.withContext
class MemoryMessagesRepository : MessagesRepository {
private var lastSyncDates: HashMap<String, Long> = HashMap()
private val messages: HashMap<String, Message> = HashMap()
override suspend fun saveLastSyncDate(rid: String, timeMillis: Long) {
lastSyncDates[rid] = timeMillis
}
override suspend fun getLastSyncDate(rid: String) = lastSyncDates[rid]
override suspend fun getById(id: String): Message? = withContext(CommonPool) {
return@withContext messages[id]
}
......
......@@ -6,41 +6,37 @@ import chat.rocket.android.server.domain.AccountsRepository
import chat.rocket.android.server.domain.model.Account
import com.squareup.moshi.Moshi
import com.squareup.moshi.Types
import kotlinx.coroutines.experimental.CommonPool
import kotlinx.coroutines.experimental.withContext
private const val ACCOUNTS_KEY = "ACCOUNTS_KEY"
class SharedPreferencesAccountsRepository(
private val preferences: SharedPreferences,
private val moshi: Moshi
) : AccountsRepository {
override suspend fun save(newAccount: Account) {
withContext(CommonPool) {
val accounts = load()
override fun save(newAccount: Account) {
val accounts = load()
val newList = accounts.filter { account -> newAccount.serverUrl != account.serverUrl }
.toMutableList()
newList.add(0, newAccount)
save(newList)
}
val newList = accounts.filter { account -> newAccount.serverUrl != account.serverUrl }
.toMutableList()
newList.add(0, newAccount)
save(newList)
}
override suspend fun load(): List<Account> = withContext(CommonPool) {
override fun load(): List<Account> {
val json = preferences.getString(ACCOUNTS_KEY, "[]")
val type = Types.newParameterizedType(List::class.java, Account::class.java)
val adapter = moshi.adapter<List<Account>>(type)
adapter.fromJson(json) ?: emptyList()
return adapter.fromJson(json) ?: emptyList()
}
override suspend fun remove(serverUrl: String) {
withContext(CommonPool) {
val accounts = load()
override fun remove(serverUrl: String) {
val accounts = load()
val newList = accounts.filter { account -> serverUrl != account.serverUrl }
.toMutableList()
save(newList)
}
val newList = accounts.filter { account -> serverUrl != account.serverUrl }
.toMutableList()
save(newList)
}
private fun save(accounts: List<Account>) {
......@@ -50,6 +46,4 @@ class SharedPreferencesAccountsRepository(
putString(ACCOUNTS_KEY, adapter.toJson(accounts))
}
}
}
private const val ACCOUNTS_KEY = "ACCOUNTS_KEY"
\ No newline at end of file
}
\ No newline at end of file
......@@ -13,6 +13,27 @@ class SharedPreferencesMessagesRepository(
private val moshi: Moshi,
private val currentServerInteractor: GetCurrentServerInteractor
) : MessagesRepository {
private val KEY_LAST_SYNC_DATE = "KEY_LAST_SYNC_DATE"
override suspend fun saveLastSyncDate(rid: String, timeMillis: Long) {
withContext(CommonPool) {
currentServerInteractor.get()?.let {
prefs.edit().putLong(getSyncDateKey(it, rid), timeMillis).apply()
}
}
}
override suspend fun getLastSyncDate(rid: String): Long? = withContext(CommonPool) {
currentServerInteractor.get()?.also { server ->
if (!prefs.contains(getSyncDateKey(server, rid)))
return@withContext null
val time = prefs.getLong(getSyncDateKey(server, rid), -1)
return@withContext if (time == -1L) null else time
}
return@withContext null
}
private fun getSyncDateKey(server: String, rid: String) = "${KEY_LAST_SYNC_DATE}_$server $rid"
override suspend fun getById(id: String): Message? = withContext(CommonPool) {
currentServerInteractor.get()?.also { server ->
......
package chat.rocket.android.server.presentation
import chat.rocket.android.analytics.AnalyticsManager
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.server.domain.AnalyticsTrackingInteractor
import chat.rocket.android.server.domain.GetAccountInteractor
import chat.rocket.android.server.domain.GetAccountsInteractor
import chat.rocket.android.server.domain.GetCurrentServerInteractor
......@@ -11,7 +11,6 @@ import chat.rocket.android.server.domain.SettingsRepository
import chat.rocket.android.server.domain.TokenRepository
import chat.rocket.android.server.infraestructure.ConnectionManagerFactory
import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.helper.analytics.AnalyticsManager
import chat.rocket.common.util.ifNull
import javax.inject.Inject
......@@ -23,7 +22,7 @@ class ChangeServerPresenter @Inject constructor(
private val getCurrentServerInteractor: GetCurrentServerInteractor,
private val getAccountInteractor: GetAccountInteractor,
private val getAccountsInteractor: GetAccountsInteractor,
private val analyticsTrackingInteractor: AnalyticsTrackingInteractor,
private val analyticsManager: AnalyticsManager,
private val settingsRepository: SettingsRepository,
private val tokenRepository: TokenRepository,
private val localRepository: LocalRepository,
......@@ -66,9 +65,7 @@ class ChangeServerPresenter @Inject constructor(
saveCurrentServerInteractor.save(serverUrl)
view.hideProgress()
if (analyticsTrackingInteractor.get()) {
AnalyticsManager.logServerSwitch(serverUrl, accounts.size)
}
analyticsManager.logServerSwitch()
navigator.toChatRooms(chatRoomId)
}.ifNull {
view.hideProgress()
......
......@@ -27,7 +27,7 @@ class PasswordPresenter @Inject constructor(
val me = retryIO("me") { client.me() }
retryIO("updateProfile(${me.id})") {
client.updateProfile(me.id!!, null, null, password, null)
client.updateProfile(me.id, null, null, password, null)
}
view.showPasswordSuccessfullyUpdatedMessage()
......
......@@ -10,15 +10,14 @@ import android.widget.Toast
import androidx.appcompat.view.ActionMode
import androidx.fragment.app.Fragment
import chat.rocket.android.R
import chat.rocket.android.server.domain.AnalyticsTrackingInteractor
import chat.rocket.android.analytics.AnalyticsManager
import chat.rocket.android.analytics.event.ScreenViewEvent
import chat.rocket.android.settings.password.presentation.PasswordPresenter
import chat.rocket.android.settings.password.presentation.PasswordView
import chat.rocket.android.util.extension.asObservable
import chat.rocket.android.util.extensions.inflate
import chat.rocket.android.util.extensions.textContent
import chat.rocket.android.util.extensions.ui
import chat.rocket.android.util.helper.analytics.AnalyticsManager
import chat.rocket.android.util.helper.analytics.event.ScreenViewEvent
import dagger.android.support.AndroidSupportInjection
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.disposables.Disposable
......@@ -32,7 +31,7 @@ class PasswordFragment : Fragment(), PasswordView, ActionMode.Callback {
@Inject
lateinit var presenter: PasswordPresenter
@Inject
lateinit var analyticsTrackingInteractor: AnalyticsTrackingInteractor
lateinit var analyticsManager: AnalyticsManager
private var actionMode: ActionMode? = null
private val disposables = CompositeDisposable()
......@@ -56,9 +55,7 @@ class PasswordFragment : Fragment(), PasswordView, ActionMode.Callback {
disposables.add(listenToChanges())
if (analyticsTrackingInteractor.get()) {
AnalyticsManager.logScreenView(ScreenViewEvent.Password)
}
analyticsManager.logScreenView(ScreenViewEvent.Password)
}
override fun onDestroyView() {
......
......@@ -11,16 +11,16 @@ import androidx.fragment.app.Fragment
import chat.rocket.android.R
import chat.rocket.android.about.ui.AboutFragment
import chat.rocket.android.about.ui.TAG_ABOUT_FRAGMENT
import chat.rocket.android.analytics.AnalyticsManager
import chat.rocket.android.analytics.event.ScreenViewEvent
import chat.rocket.android.main.ui.MainActivity
import chat.rocket.android.preferences.ui.PreferencesFragment
import chat.rocket.android.preferences.ui.TAG_PREFERENCES_FRAGMENT
import chat.rocket.android.server.domain.AnalyticsTrackingInteractor
import chat.rocket.android.settings.password.ui.PasswordActivity
import chat.rocket.android.settings.presentation.SettingsView
import chat.rocket.android.util.extensions.addFragmentBackStack
import chat.rocket.android.util.extensions.inflate
import chat.rocket.android.util.helper.analytics.AnalyticsManager
import chat.rocket.android.util.helper.analytics.event.ScreenViewEvent
import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_settings.*
import javax.inject.Inject
import kotlin.reflect.KClass
......@@ -29,7 +29,12 @@ internal const val TAG_SETTINGS_FRAGMENT = "SettingsFragment"
class SettingsFragment : Fragment(), SettingsView, AdapterView.OnItemClickListener {
@Inject
lateinit var analyticsTrackingInteractor: AnalyticsTrackingInteractor
lateinit var analyticsManager: AnalyticsManager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
AndroidSupportInjection.inject(this)
}
override fun onCreateView(
inflater: LayoutInflater,
......@@ -41,10 +46,7 @@ class SettingsFragment : Fragment(), SettingsView, AdapterView.OnItemClickListen
super.onViewCreated(view, savedInstanceState)
setupToolbar()
setupListView()
if (analyticsTrackingInteractor.get()) {
AnalyticsManager.logScreenView(ScreenViewEvent.Settings)
}
analyticsManager.logScreenView(ScreenViewEvent.Settings)
}
override fun onResume() {
......
......@@ -7,18 +7,18 @@ import chat.rocket.core.RocketChatClient
import chat.rocket.core.internal.rest.registerPushToken
import kotlinx.coroutines.experimental.CommonPool
import kotlinx.coroutines.experimental.launch
import kotlinx.coroutines.experimental.withContext
import timber.log.Timber
suspend fun RocketChatClient.registerPushToken(
suspend fun RocketChatClientFactory.registerPushToken(
token: String,
accounts: List<Account>,
factory: RocketChatClientFactory
accounts: List<Account>
) {
launch(CommonPool) {
withContext(CommonPool) {
accounts.forEach { account ->
try {
retryIO(description = "register push token: ${account.serverUrl}") {
factory.create(account.serverUrl).registerPushToken(token)
create(account.serverUrl).registerPushToken(token)
}
} catch (ex: Exception) {
Timber.d(ex, "Error registering Push token for ${account.serverUrl}")
......
......@@ -10,15 +10,19 @@ import android.provider.DocumentsContract
import android.provider.MediaStore
import android.provider.OpenableColumns
import android.webkit.MimeTypeMap
import java.io.*
import java.io.File
import java.io.FileInputStream
import java.io.FileNotFoundException
import java.io.IOException
import java.io.InputStream
fun Uri.getFileName(context: Context): String? {
val cursor = context.contentResolver.query(this, null, null, null, null, null)
var fileName: String? = null
cursor.use { cursor ->
if (cursor != null && cursor.moveToFirst()) {
fileName = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME))
cursor?.use {
if (it.moveToFirst()) {
fileName = it.getString(it.getColumnIndex(OpenableColumns.DISPLAY_NAME))
}
}
return fileName
......@@ -29,7 +33,7 @@ fun Uri.getFileSize(context: Context): Int {
if (scheme == ContentResolver.SCHEME_CONTENT) {
try {
val fileInputStream = context.contentResolver.openInputStream(this)
fileSize = fileInputStream.available().toString()
fileSize = fileInputStream?.available().toString()
} catch (e: Exception) {
e.printStackTrace()
}
......@@ -67,14 +71,17 @@ fun Uri.isVirtualFile(context: Context): Boolean {
val cursor = context.contentResolver.query(
this,
arrayOf(DocumentsContract.Document.COLUMN_FLAGS),
null, null, null
null,
null,
null
)
var flags = 0
if (cursor.moveToFirst()) {
flags = cursor.getInt(0)
cursor?.use {
if (it.moveToFirst()) {
flags = it.getInt(0)
}
}
cursor.close()
return flags and DocumentsContract.Document.FLAG_VIRTUAL_DOCUMENT != 0
}
......@@ -104,4 +111,4 @@ fun Uri.getInputStream(context: Context): InputStream? {
fun Uri.getBitmpap(context: Context): Bitmap? {
return MediaStore.Images.Media.getBitmap(context.contentResolver, this)
}
}
\ No newline at end of file
......@@ -4,11 +4,8 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Observer
import kotlinx.coroutines.experimental.CommonPool
import kotlinx.coroutines.experimental.CoroutineScope
import kotlinx.coroutines.experimental.Job
import kotlinx.coroutines.experimental.android.UI
import kotlinx.coroutines.experimental.launch
import kotlinx.coroutines.experimental.withContext
import kotlin.coroutines.experimental.CoroutineContext
class WrappedLiveData<Source, Output>(
......
......@@ -11,6 +11,7 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import chat.rocket.android.R
import chat.rocket.android.util.extensions.inflate
import chat.rocket.android.util.extensions.ui
import kotlinx.android.synthetic.main.fragment_admin_panel_web_view.*
private const val BUNDLE_WEB_PAGE_URL = "web_page_url"
......@@ -58,8 +59,10 @@ class AdminPanelWebViewFragment : Fragment() {
web_view.webViewClient = object : WebViewClient() {
override fun onPageFinished(view: WebView, url: String) {
super.onPageFinished(view, url)
view_loading.hide()
web_view.evaluateJavascript("Meteor.loginWithToken('$userToken', function() { })") {}
ui { _ ->
view_loading.hide()
web_view.evaluateJavascript("Meteor.loginWithToken('$userToken', function() { })") {}
}
}
}
web_view.loadUrl(webPageUrl)
......
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="20dp"
android:height="20dp"
android:viewportWidth="20"
android:viewportHeight="20">
<path
android:fillColor="#00000000"
android:fillType="evenOdd"
android:pathData="M11.286,6.143C11.286,7.087 11.136,7.797 10.838,8.273L16.354,13.615L17,15.709L15.708,17L13.62,16.358L13.21,15.949L13.21,14.474L11.735,14.474L11.105,13.844L11.105,12.368L9.629,12.368L8.063,10.796C7.481,11.123 6.84,11.286 6.143,11.286C4.7537,11.286 3.4234,10.7239 2.455,9.7277C1.4867,8.7314 0.9626,7.3858 1.002,5.997C1.077,3.313 3.314,1.077 5.997,1.002C7.3854,0.9628 8.7307,1.4869 9.7267,2.4551C10.7227,3.4232 11.2847,4.753 11.285,6.142L11.286,6.143Z"
android:strokeWidth="1.5"
android:strokeColor="#2F343D" />
<path
android:fillColor="#2F343D"
android:fillType="nonZero"
android:pathData="M5.5,5.5m1.5,0a1.5,1.5 0,1 0,-3 0a1.5,1.5 0,1 0,3 0"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>
......@@ -8,32 +8,19 @@
android:translateX="281.28394"
android:translateY="271.51514">
<path
android:fillColor="#CC3333"
android:fillColor="#FFDB2323"
android:pathData="M491.3,255.3c0,-24.1 -7.2,-47.2 -21.4,-68.7c-12.8,-19.3 -30.7,-36.4 -53.2,-50.7c-43.5,-27.8 -100.6,-43.1 -160.9,-43.1c-20.1,0 -40,1.7 -59.2,5.1c-11.9,-11.2 -25.9,-21.2 -40.7,-29.2c-79,-38.3 -144.6,-0.9 -144.6,-0.9s60.9,50.1 51,93.9c-27.3,27 -42,59.6 -42,93.6c0,0.1 0,0.2 0,0.3c0,0.1 0,0.2 0,0.3c0,33.9 14.8,66.6 42,93.6c9.9,43.9 -51,93.9 -51,93.9s65.5,37.4 144.6,-0.9c14.8,-8 28.8,-18 40.7,-29.2c19.2,3.4 39.1,5.1 59.2,5.1c60.3,0 117.4,-15.3 160.9,-43.1c22.5,-14.4 40.4,-31.5 53.2,-50.7c14.2,-21.5 21.4,-44.6 21.4,-68.7c0,-0.1 0,-0.2 0,-0.3C491.3,255.6 491.3,255.4 491.3,255.3z" />
<path
android:fillColor="#FFFFFF"
android:pathData="M255.9,124.2c113.9,0 206.3,59 206.3,131.8c0,72.8 -92.4,131.8 -206.3,131.8c-25.4,0 -49.7,-2.9 -72.1,-8.3c-22.8,27.4 -73,65.6 -121.7,53.3c15.9,-17 39.4,-45.8 34.3,-93.2c-29.2,-22.7 -46.8,-51.8 -46.8,-83.5C49.6,183.2 142,124.2 255.9,124.2" />
<path
android:fillColor="#CC3333"
android:fillColor="#FFDB2323"
android:pathData="M255.9,256m-27.4,0a27.4,27.4 0,1 1,54.8 0a27.4,27.4 0,1 1,-54.8 0" />
<path
android:fillColor="#CC3333"
android:fillColor="#FFDB2323"
android:pathData="M351.2,256m-27.4,0a27.4,27.4 0,1 1,54.8 0a27.4,27.4 0,1 1,-54.8 0" />
<path
android:fillColor="#CC3333"
android:fillColor="#FFDB2323"
android:pathData="M160.6,256m-27.4,0a27.4,27.4 0,1 1,54.8 0a27.4,27.4 0,1 1,-54.8 0" />
<path
android:fillColor="#CCCCCC"
android:pathData="M255.8,372.8c-25.4,0 -56.2,-4.9 -78.7,-9.5c-20.1,21 -53.7,52.7 -99.6,50.3c-5.7,8.6 -10.2,13.5 -15.5,19.2c48.7,12.3 98.9,-25.8 121.7,-53.3c22.4,5.4 46.7,8.3 72.1,8.3c113,0 204.8,-58.1 206.3,-130C460.7,320.1 368.8,372.8 255.8,372.8z" />
<path
android:fillColor="#00000000"
android:pathData="M172,350.9"
android:strokeColor="#000000"
android:strokeWidth="1" />
<path
android:fillColor="#00000000"
android:pathData="M200.4,422.9"
android:strokeColor="#000000"
android:strokeWidth="1" />
</group>
</vector>
</vector>
\ No newline at end of file
......@@ -770,4 +770,4 @@
android:pathData="M89.876,190C88.289,190.003 87.003,191.286 87,192.87C87.004,193.159 87.053,193.445 87.144,193.719C87.639,193.823 88.14,193.916 88.648,194C88.193,193.513 88.073,192.802 88.341,192.193C88.609,191.584 89.215,191.192 89.882,191.196L107.855,191.196C108.586,190.843 109.305,190.466 110,190.066C109.803,190.023 109.602,190.001 109.401,190L89.876,190Z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>
</vector>
\ No newline at end of file
......@@ -30,7 +30,7 @@
<TextView
android:id="@+id/text_on_boarding_description"
style="@style/Authentication.Description.TextView"
android:text="@string/msg_open_source_communication"
android:text="@string/msg_team_communication"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_on_boarding_title" />
......
<resources>
<!-- Titles -->
<string name="title_sign_in_your_server">Anmelden am Server</string>
<string name="title_log_in">Anmelden</string>
......@@ -75,7 +76,6 @@
<string name="msg_invalid_email">Bitte eine korrekte E-Mail Adresse eingeben</string>
<string name="msg_new_user_agreement">Beim weitergehen akzeptieren Sie usere\n%1$s und %2$s</string>
<string name="msg_2fa_code">2FA Code</string>
<!-- <string name="msg_more_than_ninety_nine_unread_messages" translatable="false">99+</string> -->
<string name="msg_yesterday">Gestern</string>
<string name="msg_today">Heute</string>
<string name="msg_message">Nachricht</string>
......@@ -109,11 +109,8 @@
<string name="msg_no_messages_yet">Noch keine Nachrichten</string>
<string name="msg_ok">OK</string>
<string name="msg_update_app_version_in_order_to_continue">Server Version veraltet. Bitte kontaktieren Sie ihren Server Administrator.</string>
<string name="msg_ver_not_recommended">
Die Server Version scheint älter als die empfolene Version %1$s zu sein.\nSie können sich trotzdem einloggen, aber es kann zu einem unerwartetem Verhalten kommen.</string>
<string name="msg_ver_not_minimum">
Die Server Version scheint älter als die minimale Version %1$s zu sein.\nBitte updaten Sie Ihren Server um sich einloggen zu können!
</string>
<string name="msg_ver_not_recommended">Die Server Version scheint älter als die empfolene Version %1$s zu sein.\nSie können sich trotzdem einloggen, aber es kann zu einem unerwartetem Verhalten kommen.</string>
<string name="msg_ver_not_minimum">Die Server Version scheint älter als die minimale Version %1$s zu sein.\nBitte updaten Sie Ihren Server um sich einloggen zu können!</string>
<string name="msg_no_chat_title">Keine Chat Nachrichten</string>
<string name="msg_no_chat_description">Starte die Konversation um Ihre \nNachrichten hier zu sehen.</string>
<string name="msg_proceed">WEITER</string>
......@@ -136,7 +133,7 @@
<string name="msg_send">Sende</string>
<string name="msg_sent_attachment">Sende Anhang</string>
<string name="msg_welcome_to_rocket_chat">Welcome to Rocket.Chat</string> <!-- TODO Add translation -->
<string name="msg_open_source_communication">Open Source Communication</string> <!-- TODO Add translation -->
<string name="msg_team_communication">Open Source Communication</string> <!-- TODO Add translation -->
<string name="msg_login_with_email">Login with e-mail</string> <!-- TODO Add translation -->
<string name="msg_create_account">Create an account</string> <!-- TODO Add translation -->
<string name="msg_continue_with_facebook">Continue with Facebook</string> <!-- TODO Add translation -->
......
......@@ -133,7 +133,7 @@
<string name="msg_delete_description">Seguro que quieres borrar este mensaje</string>
<string name="msg_no_search_found">No se han encontrado resultados</string>
<string name="msg_welcome_to_rocket_chat">Welcome to Rocket.Chat</string> <!-- TODO Add translation -->
<string name="msg_open_source_communication">Open Source Communication</string> <!-- TODO Add translation -->
<string name="msg_team_communication">Open Source Communication</string> <!-- TODO Add translation -->
<string name="msg_login_with_email">Login with e-mail</string> <!-- TODO Add translation -->
<string name="msg_create_account">Create an account</string> <!-- TODO Add translation -->
<string name="msg_continue_with_facebook">Continue with Facebook</string> <!-- TODO Add translation -->
......
<resources>
<!-- Titles -->
<string name="title_sign_in_your_server">Connectez-vous sur votre serveur</string>
<string name="title_log_in">S\'identifier</string>
<string name="title_register_username">Enregistrer le nom d\'utilisateur</string>
// TODO: Add proper translation.
<string name="title_reset_password">Reset password</string>
<string name="title_reset_password">Réinitialiser mot de passe</string>
<string name="title_sign_up">S\'inscrire</string>
<string name="title_authentication">Authentification</string>
<string name="title_legal_terms">Termes légaux</string>
<string name="title_legal_terms">Termes juridiques</string>
<string name="title_chats">Chats</string>
<string name="title_profile">Profil</string>
<string name="title_members">Membres (%d)</string>
<string name="title_settings">Paramètres</string>
<string name="title_preferences">Preferences</string> <!-- TODO Add translation -->
<string name="title_preferences">Préférences</string>
<string name="title_change_password">Changer le mot de passe</string>
<string name="title_admin_panel">Admin panel</string> <!-- TODO Add translation -->
<string name="title_admin_panel">Administration</string>
<string name="title_password">Changer le mot de passe</string>
<string name="title_update_profile">Update profile</string>
<string name="title_about">Sur</string>
// TODO: Add proper translation.
<string name="title_create_channel">Create Channel</string>
<string name="title_update_profile">Mettre à jour le profil</string>
<string name="title_about">À propos</string>
<string name="title_create_channel">Créer salon</string>
<!-- Actions -->
......@@ -31,8 +30,8 @@
<string name="action_search">Chercher</string>
<string name="action_update">Mettre à jour</string>
<string name="action_settings">Paramètres</string>
<string name="action_create_channel">Create channel</string> <!-- TODO Add translation -->
<string name="action_create">Create</string>
<string name="action_create_channel">Créer salon</string>
<string name="action_create">Créer</string>
<string name="action_logout">Se déconnecter</string>
<string name="action_files">Fichiers</string>
<string name="action_confirm_password">Confirmer le mot de passe</string>
......@@ -43,10 +42,10 @@
<string name="action_busy">Occupé</string>
<string name="action_invisible">Invisible</string>
<string name="action_drawing">Dessin</string>
<string name="action_save_to_gallery">Save to gallery</string> <!-- TODO Add translation -->
<string name="action_select_photo_from_gallery">Select photo from gallery</string> <!-- TODO Add translation -->
<string name="action_take_photo">Select photo from gallery</string> <!-- TODO Add translation -->
<string name="action_reset_avatar">Reset avatar</string> <!-- TODO Add translation -->
<string name="action_save_to_gallery">Sauvegarder vers la gallerie</string>
<string name="action_select_photo_from_gallery">Sélectionner depuis la gallerie</string>
<string name="action_take_photo">Prendre une photo</string>
<string name="action_reset_avatar">Réinitialiser l\'avatar</string>
<string name="action_connect_server">Connect with a server</string> <!-- TODO Add translation -->
<string name="action_join_community">Join in the community</string> <!-- TODO Add translation -->
<string name="action_create_server">Create a new server</string> <!-- TODO Add translation -->
......@@ -55,9 +54,9 @@
<!-- Settings List -->
<string-array name="settings_actions">
<item name="item_preferences">Preferences</item> <!-- TODO Add translation -->
<item name="item_preferences">Préférences</item>
<item name="item_password">Changer le mot de passe</item>
<item name="item_password">Sur</item>
<item name="item_password">À propos</item>
</string-array>
<!-- Regular information messages -->
......@@ -72,20 +71,16 @@
<string name="msg_avatar_url">URL de l\'avatar</string>
<string name="msg_or_continue_using_social_accounts">Ou continuer en utilisant les comptes sociaux</string>
<string name="msg_new_user">Nouvel utilisateur? %1$s</string>
// TODO: Add proper translation.
<string name="msg_forgot_password">Forgot password?</string>
// TODO: Add proper translation.
<string name="msg_reset">Reset</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_forgot_password">Mot de passe oublié ? %1$s</string>
<string name="msg_reset">Réinitialiser</string>
<string name="msg_check_your_email_to_reset_your_password">Email envoyé ! Consultez votre boîte mail pour réinitialiser votre mot de passe.</string>
<string name="msg_invalid_email">Veuillez entrer une adresse valide</string>
<string name="msg_new_user_agreement">En procédant, vous acceptez notre\n%1$s et %2$s</string>
<string name="msg_2fa_code">Code 2FA</string>
<string name="msg_yesterday">Hier</string>
<string name="msg_today">Aujourd\'hui</string>
<string name="msg_message">Message</string>
<string name="msg_this_room_is_read_only">Cette salle est seulement de lecture</string>
<string name="msg_this_room_is_read_only">Ce salon est en lecture seule</string>
<string name="msg_invalid_2fa_code">Code 2FA non valide</string>
<string name="msg_invalid_file">Fichier non valide</string>
<string name="msg_invalid_server_url">URL de serveur non valide</string>
......@@ -96,10 +91,10 @@
<string name="msg_content_description_log_in_using_meteor">Connectez-vous en utilisant Meteor</string>
<string name="msg_content_description_log_in_using_twitter">Connectez-vous en utilisant Twitter</string>
<string name="msg_content_description_log_in_using_gitlab">Connectez-vous en utilisant Gitlab</string>
<string name="msg_content_description_log_in_using_wordpress">Login using WordPress</string> <!-- TODO Translate-->
<string name="msg_content_description_log_in_using_wordpress">Connectez-vous en utilisant WordPress</string>
<string name="msg_content_description_send_message">Envoyer message</string>
<string name="msg_content_description_show_attachment_options">Afficher les options de fichiers</string>
<string name="msg_you">Toi</string>
<string name="msg_you">Vous</string>
<string name="msg_unknown">Inconnu</string>
<string name="msg_email_address">Adresse e-mail</string>
<string name="msg_utc_offset">Décalage UTC</string>
......@@ -109,11 +104,10 @@
<string name="msg_preview_video">Vidéo</string>
<string name="msg_preview_audio">Audio</string>
<string name="msg_preview_photo">Photo</string>
<string name="msg_preview_file">File</string>
<string name="msg_preview_file">Fichier</string>
<string name="msg_no_messages_yet">Aucun message pour le moment</string>
<string name="msg_ok">OK</string>
// TODO: Add proper translation.
<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_update_app_version_in_order_to_continue">La version du serveur est trop ancienne. Veuillez contacter l\'administateur pour mettre à jour le serveur afin de pouvoir vous y connecter.</string>
<string name="msg_ver_not_recommended">
On dirait que la version de votre serveur est en dessous de la version recommandée %1$s.\nVous pouvez toujours vous connecter mais vous pouvez rencontrer des comportements inattendus.</string>
<string name="msg_ver_not_minimum">
......@@ -130,28 +124,21 @@
<string name="msg_no_chat_title">Aucun message de discussion</string>
<string name="msg_no_chat_description">Commencez à converser pour voir\nvos messages ici.</string>
<string name="msg_edited">(édité)</string>
// TODO: Add proper translation.
<string name="msg_and">\u0020and\u0020</string>
// TODO: Add proper translation.
<string name="msg_is_typing">\u0020is 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_and">\u0020et\u0020</string>
<string name="msg_is_typing">\u0020écrit…</string>
<string name="msg_are_typing">\u0020écrivent…</string>
<string name="msg_several_users_are_typing">Plusieurs utilisateurs écrivent…</string>
<string name="msg_no_search_found">Aucun résultat trouvé</string>
// TODO: Add proper translation.
<string name="msg_channel_name">Channel name</string>
// TODO: Add proper translation.
<string name="msg_search">Search</string>
<string name="msg_channel_name">Nom du salon</string>
<string name="msg_search">Rechercher</string>
<string name="msg_message_copied">Message copié</string>
<string name="msg_upload_file">Téléverser un fichier</string>
<string name="msg_file_description">Description du fichier</string>
<string name="msg_send">envoyer</string>
// TODO: Add proper translation.
<string name="msg_delete_message">Delete Message</string>
<string name="msg_delete_description">Are you sure you want to delete this message</string>
<string name="msg_delete_message">Supprimer Message</string>
<string name="msg_delete_description">Êtes-vous sûr de vouloir supprimer ce message</string>
<string name="msg_welcome_to_rocket_chat">Welcome to Rocket.Chat</string> <!-- TODO Add translation -->
<string name="msg_open_source_communication">Open Source Communication</string> <!-- TODO Add translation -->
<string name="msg_team_communication">Open Source Communication</string> <!-- TODO Add translation -->
<string name="msg_login_with_email">Login with e-mail</string> <!-- TODO Add translation -->
<string name="msg_create_account">Create an account</string> <!-- TODO Add translation -->
<string name="msg_continue_with_facebook">Continue with Facebook</string> <!-- TODO Add translation -->
......@@ -163,40 +150,38 @@
<string name="msg__your_2fa_code">What’s your 2FA code?</string> <!-- TODO Add translation -->
<!-- Create channel messages -->
// TODO: Add proper translation.
<string name="msg_private_channel">Private</string>
<string name="msg_private_channel">Privé</string>
<string name="msg_public_channel">Public</string>
<string name="msg_private_channel_description">Only you and invited members can access this channel</string>
<string name="msg_public_channel_description">Everyone can access this channel</string>
<string name="msg_ready_only_channel">Read only channel</string>
<string name="msg_ready_only_channel_description">Only admin can write new messages</string>
<string name="msg_invite_members">Invite members to channel</string>
<string name="msg_member_already_added">You have already selected this user</string>
<string name="msg_member_not_found">Member not found</string>
<string name="msg_channel_created_successfully">Channel created successfully</string>
<string name="msg_private_channel_description">Seul vous et les membres invités peuvent accéder à ce salon</string>
<string name="msg_public_channel_description">Tout le monde peut accéder à ce salon</string>
<string name="msg_ready_only_channel">Salon en lecture seule</string>
<string name="msg_ready_only_channel_description">Seuls les administrateurs peuvent écrire de nouveaux messages</string>
<string name="msg_invite_members">Inviter des membres à rejoindre le salon</string>
<string name="msg_member_already_added">Cet utilisateur est déjà sélectionné</string>
<string name="msg_member_not_found">Membre non trouvé</string>
<string name="msg_channel_created_successfully">Salon créé</string>
<!-- Preferences messages -->
<string name="msg_analytics_tracking">Analytics tracking</string> <!-- TODO Add translation -->
<string name="msg_send_analytics_tracking">Send anonymous statics to help improving this app</string> <!-- TODO Add translation -->
<string name="msg_do_not_send_analytics_tracking">Do not send anonymous statics to help improving this app</string> <!-- TODO Add translation -->
<string name="msg_not_applicable_since_it_is_a_foss_version">Not applicable since it is a FOSS version</string> <!-- TODO Add translation -->
<string name="msg_analytics_tracking">Pistage à des fins d\'analyses</string>
<string name="msg_send_analytics_tracking">Envoyer des statistiques anonymes pour aider à l\'amélioration de l\'application</string>
<string name="msg_do_not_send_analytics_tracking">Ne pas envoyer des statistiques anonymes pour aider à l\'amélioration de l\'application</string>
<string name="msg_not_applicable_since_it_is_a_foss_version">Ne s\'applique pas étant donné qu\'il s\'agit d\'une version FOSS</string>
<!-- System messages -->
<string name="message_room_name_changed">Le nom de le salle a changé à: %1$s par %2$s</string>
<string name="message_room_name_changed">Le nom du salon %1$s a été changé en %2$s</string>
<string name="message_user_added_by">Utilisateur %1$s ajouté par %2$s</string>
<string name="message_user_removed_by">Utilisateur %1$s enlevé par %2$s</string>
<string name="message_user_left">A quitté de la salle.</string>
<string name="message_user_joined_channel">A rejoint la salle.</string>
<string name="message_user_removed_by">Utilisateur %1$s retiré par %2$s</string>
<string name="message_user_left">A quitté le salon.</string>
<string name="message_user_joined_channel">A rejoint le salon.</string>
<string name="message_welcome">Bienvenue %s</string>
<string name="message_removed">Message supprimé</string>
<string name="message_pinned">Épinglé un message:</string>
<string name="message_pinned">Message épinglé:</string>
<string name="message_muted">Utilisateur %1$s mis en sourdine par %2$s</string>
<string name="message_unmuted">Utilisateur %1$s non muté par %2$s</string>
<string name="message_unmuted">Utilisateur %1$s a retrouvé la parole grâce à %2$s</string>
<string name="message_role_add">%1$s a été défini %2$s par %3$s</string>
<string name="message_role_removed">%1$s is no longer %2$s par %3$s</string>
<string name="message_role_removed">%1$s n\'est plus %2$s par %3$s</string>
// TODO:Add proper translation.
<string name="message_credentials_saved_successfully">Credentials saved successfully</string>
<string name="message_credentials_saved_successfully">Certificats sauvegardés</string>
<!-- Message actions -->
<string name="action_msg_reply">Répondre</string>
......@@ -205,8 +190,8 @@
<string name="action_msg_copy">Copier</string>
<string name="action_msg_quote">Citation</string>
<string name="action_msg_delete">Effacer</string>
<string name="action_msg_pin">Épingle message</string>
<string name="action_msg_unpin">Enlever message</string>
<string name="action_msg_pin">Épingler message</string>
<string name="action_msg_unpin">Ne plus épingler message</string>
<string name="action_msg_star">Ajouter aux Favoris</string>
<string name="action_msg_unstar">Retirer des favoris</string>
<string name="action_msg_share">Partager</string>
......@@ -217,26 +202,22 @@
<string name="permission_editing_not_allowed">L\'édition n\'est pas autorisée</string>
<string name="permission_deleting_not_allowed">La suppression n\'est pas autorisée</string>
<string name="permission_pinning_not_allowed">L\'épinglage n\'est pas autorisé</string>
// TODO: Add proper translation.
<string name="permission_starring_not_allowed">Starring is not allowed</string>
<string name="permission_starring_not_allowed">La mise en favori n\'est pas autorisée</string>
<!-- Search message -->
<!-- TODO Add proper translation-->
<string name="title_search_message">Search message</string>
<string name="title_search_message">Rechercher message</string>
<!-- Favorite/Unfavorite chat room -->
<!-- TODO Add proper translation-->
<string name="title_favorite_chat">Favorite chat</string>
<string name="title_unfavorite_chat">Unfavorite chat</string>
<string name="title_favorite_chat">Mettre chat en favori</string>
<string name="title_unfavorite_chat">Retirer chat des favoris</string>
<!-- Members List -->
<string name="title_members_list">Membres</string>
<!-- Mentions -->
<!-- TODO Add proper translation-->
<string name="msg_mentions">Mentions</string>
<string name="msg_no_mention">No mention</string>
<string name="msg_all_the_mentions_appear_here">All the mentions\nappear here</string>
<string name="msg_no_mention">Aucune mention</string>
<string name="msg_all_the_mentions_appear_here">Toutes les mentions\napparaissent ici</string>
<!-- Pinned Messages -->
<string name="title_pinned_messages">Messages épinglés</string>
......@@ -244,32 +225,30 @@
<string name="no_pinned_description">Tous les messages épinglés\napparaissent ici</string>
<!-- Favorite Messages -->
<!-- TODO Add proper translation-->
<string name="title_favorite_messages">Favorite Messages</string>
<string name="no_favorite_messages">No favorite messages</string>
<string name="no_favorite_description">All the favorite messages\nappear here</string>
<string name="title_favorite_messages">Messages favoris</string>
<string name="no_favorite_messages">Aucun message favori</string>
<string name="no_favorite_description">Tous les messages favoris\napparaissent ici</string>
<!-- Files -->
<!-- TODO Add proper translation-->
<string name="title_files">Files</string>
<string name="title_files_total">Files (%d)</string>
<string name="msg_no_files">No files</string>
<string name="msg_all_files_appear_here">All the files appear here</string>
<string name="title_files">Fichiers</string>
<string name="title_files_total">Fichiers (%d)</string>
<string name="msg_no_files">Aucun fichier</string>
<string name="msg_all_files_appear_here">Tous les fichiers apparaissent ici</string>
<!-- Upload Messages -->
<string name="max_file_size_exceeded">Taille du fichier (%1$d bytes) dépassé la taille de téléchargement maximale de %2$d bytes</string>
<string name="max_file_size_exceeded">Taille du fichier (%1$d bytes) dépasse la taille de téléchargement maximale de %2$d bytes</string>
<!-- Socket status -->
<string name="status_connected">Connecté</string>
<string name="status_disconnected">taché</string>
<string name="status_disconnected">connecté</string>
<string name="status_connecting">Connexion</string>
<string name="status_authenticating">Authentification</string>
<string name="status_disconnecting">Déconnexion</string>
<string name="status_waiting">Connexion en %d secondes</string>
<string name="status_waiting">Connexion dans %d secondes</string>
<!--Suggestions-->
<string name="suggest_all_description">Notifier tout dans cette salle</string>
<string name="suggest_here_description">Notifier les utilisateurs actifs dans cette salle</string>
<string name="suggest_all_description">Notifier tous les utilisateurs dans ce salon</string>
<string name="suggest_here_description">Notifier les utilisateurs actifs dans ce salon</string>
<!-- Slash Commands -->
<string name="Slash_Gimme_Description">Affiche ༼ つ ◕_◕ ༽つ avant votre message</string>
......@@ -277,20 +256,20 @@
<string name="Slash_Shrug_Description">Affiche ¯\_(ツ)_/¯ après votre message</string>
<string name="Slash_Tableflip_Description">Affiche (╯°□°)╯︵ ┻━┻</string>
<string name="Slash_TableUnflip_Description">Affiche ┬─┬ ノ( ゜-゜ノ)</string>
<string name="Create_A_New_Channel">Créer une nouvelle salle</string>
<string name="Create_A_New_Channel">Créer un nouveau salon</string>
<string name="Show_the_keyboard_shortcut_list">Afficher la liste des raccourcis clavier</string>
<string name="Invite_user_to_join_channel_all_from">Inviter tous les utilisateurs de [#salle] à rejoindre cette salle</string>
<string name="Invite_user_to_join_channel_all_to">Inviter tous les utilisateurs de cette salle à rejoindre [#salle]</string>
<string name="Invite_user_to_join_channel_all_from">Inviter tous les utilisateurs de [#salle] à rejoindre ce salon</string>
<string name="Invite_user_to_join_channel_all_to">Inviter tous les utilisateurs de ce salon à rejoindre [#salle]</string>
<string name="Archive">Archiver</string>
<string name="Remove_someone_from_room">Retirer quelqu\'un de la salle</string>
<string name="Leave_the_current_channel">Sortir de la salle actuelle</string>
<string name="Remove_someone_from_room">Retirer quelqu\'un du salon</string>
<string name="Leave_the_current_channel">Sortir du salon actuel</string>
<string name="Displays_action_text">Affiche le texte d\'action</string>
<string name="Direct_message_someone">Message direct avec quelqu\'un</string>
<string name="Mute_someone_in_room">Mettre en sourdine une personne dans la salle</string>
<string name="Unmute_someone_in_room">Retirer la sourdine d\'une personne dans la salle</string>
<string name="Invite_user_to_join_channel">Inviter un utilisateur à rejoindre cette salle</string>
<string name="Mute_someone_in_room">Mettre en sourdine une personne dans le salon</string>
<string name="Unmute_someone_in_room">Retirer la sourdine d\'une personne dans le salon</string>
<string name="Invite_user_to_join_channel">Inviter un utilisateur à rejoindre ce salon</string>
<string name="Unarchive">Désarchiver</string>
<string name="Join_the_given_channel">Rejoignez la salle fourni</string>
<string name="Join_the_given_channel">Rejoignez la salon fourni</string>
<string name="Guggy_Command_Description">Génère un gif basé sur le texte fourni</string>
<string name="Slash_Topic_Description">Définir le sujet</string>
......@@ -305,11 +284,11 @@
<string name="dialog_sort_by_activity">Activité</string>
<string name="dialog_group_by_type">Grouper par type</string>
<string name="dialog_group_favourites">Grouper favoris</string>
<string name="dialog_button_done">Done</string><!-- TODO Add translation -->
<string name="dialog_button_done">Ok</string>
<string name="chatroom_header">Entête</string>
<!--ChatRooms Headers-->
<string name="header_channel">Salles</string>
<string name="header_channel">Salons</string>
<string name="header_private_groups">Groupes privés</string>
<string name="header_direct_messages">Messages directs</string>
<string name="header_live_chats">Chats en direct</string>
......@@ -324,4 +303,4 @@
<string name="message_information_title">Informations sur le message</string>
<string name="msg_log_out">Déconnecter…</string>
<string name="msg_sent_attachment">Envoyé un fichier</string>
</resources>
</resources>
\ No newline at end of file
......@@ -12,9 +12,9 @@
<string name="title_profile">प्रोफाइल</string>
<string name="title_members">सदस्य (%d)</string>
<string name="title_settings">सेटिंग्स</string>
<string name="title_preferences">Preferences</string> <!-- TODO Add translation -->
<string name="title_preferences">प्राथमिकताएँ</string>
<string name="title_change_password">पासवर्ड बदलें</string>
<string name="title_admin_panel">Admin panel</string> <!-- TODO Add translation -->
<string name="title_admin_panel">एडमिन पैनल</string>
<string name="title_password">पासवर्ड बदलें</string>
<string name="title_update_profile">प्रोफ़ाइल अपडेट करें</string>
<string name="title_about">परिचय</string>
......@@ -42,9 +42,9 @@
<string name="action_invisible">अदृश्य</string>
<string name="action_save_to_gallery">गैलरी में सहेजें</string>
<string name="action_drawing">चित्रकारी</string>
<string name="action_select_photo_from_gallery">Select photo from gallery</string> <!-- TODO Add translation -->
<string name="action_take_photo">Select photo from gallery</string> <!-- TODO Add translation -->
<string name="action_reset_avatar">Reset avatar</string> <!-- TODO Add translation -->
<string name="action_select_photo_from_gallery">गैलरी से फोटो का चयन करें</string>
<string name="action_take_photo">फोटो खेचिये</string>
<string name="action_reset_avatar">अवतार रीसेट करें</string>
<string name="action_connect_server">सर्वर से कनेक्ट करें</string>
<string name="action_join_community">समुदाय में शामिल हों</string>
<string name="action_create_server">नया सर्वर बनाएं</string>
......@@ -53,7 +53,7 @@
<!-- Settings List -->
<string-array name="settings_actions">
<item name="item_preferences">Preferences</item> <!-- TODO Add translation -->
<item name="item_preferences">प्राथमिकताएँ</item>
<item name="item_password">पासवर्ड बदलें</item>
<item name="item_password">परिचय</item>
</string-array>
......@@ -90,7 +90,7 @@
<string name="msg_content_description_log_in_using_meteor">Meteor द्वारा लॉगिन करें</string>
<string name="msg_content_description_log_in_using_twitter">Twitter द्वारा लॉगिन करें</string>
<string name="msg_content_description_log_in_using_gitlab">Gitlab द्वारा लॉगिन करें</string>
<string name="msg_content_description_log_in_using_wordpress">Login using WordPress</string> <!-- TODO Translate-->
<string name="msg_content_description_log_in_using_wordpress">WordPress द्वारा लॉगिन करें</string>
<string name="msg_content_description_send_message">मेसेज भेजें</string>
<string name="msg_content_description_show_attachment_options">अटैचमेंट विकल्प दिखाएं</string>
<string name="msg_you">आप</string>
......@@ -139,7 +139,7 @@
<string name="msg_delete_message">संदेश को हटाएं</string>
<string name="msg_delete_description">क्या आप निश्चित रूप से यह संदेश हटाना चाहते हैं</string>
<string name="msg_welcome_to_rocket_chat">Rocket.Chat में आपका स्वागत है</string>
<string name="msg_open_source_communication">ओपन सोर्स कम्युनिकेशन</string>
<string name="msg_team_communication">ओपन सोर्स कम्युनिकेशन</string>
<string name="msg_login_with_email">ई-मेल के साथ लॉगिन करें</string>
<string name="msg_create_account">खाता बनाएं</string>
<string name="msg_continue_with_facebook">Facebook के साथ जारी रखें</string>
......@@ -163,10 +163,10 @@
<string name="msg_channel_created_successfully">चैनल सफलतापूर्वक बनाया गया</string>
<!-- Preferences messages -->
<string name="msg_analytics_tracking">Analytics tracking</string> <!-- TODO Add translation -->
<string name="msg_send_analytics_tracking">Send anonymous statics to help improving this app</string> <!-- TODO Add translation -->
<string name="msg_do_not_send_analytics_tracking">Do not send anonymous statics to help improving this app</string> <!-- TODO Add translation -->
<string name="msg_not_applicable_since_it_is_a_foss_version">Not applicable since it is a FOSS version</string> <!-- TODO Add translation -->
<string name="msg_analytics_tracking">एनालिटिक्स ट्रैकिंग</string>
<string name="msg_send_analytics_tracking">इस ऐप को बेहतर बनाने में मदद के लिए अज्ञात स्टेटिक्स भेजें</string>
<string name="msg_do_not_send_analytics_tracking">इस ऐप को बेहतर बनाने में मदद के लिए अनाम स्टेटिक न भेजें</string>
<string name="msg_not_applicable_since_it_is_a_foss_version">लागू नहीं है क्योंकि यह एक FOSS संस्करण है</string>
<!-- System messages -->
<string name="message_room_name_changed">%2$s ने रूम का नाम बदलकर %1$s किया</string>
......@@ -206,8 +206,7 @@
<string name="permission_starring_not_allowed">तारांकित की अनुमति नहीं है</string>
<!-- Search message -->
<!-- TODO Add proper translation-->
<string name="title_search_message">Search message</string>
<string name="title_search_message">संदेश खोजें</string>
<!-- Favorite/Unfavorite chat room -->
<string name="title_favorite_chat">पसंदीदा चैट</string>
......@@ -286,7 +285,7 @@
<string name="dialog_sort_by_activity">गतिविधि</string>
<string name="dialog_group_by_type">प्रकार के आधार पर समूह</string>
<string name="dialog_group_favourites">पसंदीदा समूह</string>
<string name="dialog_button_done">Done</string><!-- TODO Add translation -->
<string name="dialog_button_done">पूर्ण</string>
<string name="chatroom_header">हैडर</string>
<!--ChatRooms Headers-->
......
<?xml version="1.0" encoding="utf-8" ?>
<resources>
<string name="app_name" translatable="false">Rocket.Chat</string>
<!-- Titles -->
<string name="title_sign_in_your_server">サーバーに接続</string>
<string name="title_log_in">ログイン</string>
<string name="title_register_username">ユーザー名を登録する</string>
<string name="title_reset_password">パスワードリセット</string>
<string name="title_sign_up">サインアップ</string>
<string name="title_authentication">認証</string>
<string name="title_legal_terms">Legal Terms</string>
<string name="title_chats">チャット</string>
<string name="title_profile">プロフィール</string>
<string name="title_members">メンバー (%d)</string>
<string name="title_settings">設定</string>
<string name="title_preferences">Preferences</string> <!-- TODO Add translation -->
<string name="title_change_password">Change Password</string> <!-- TODO Add translation -->
<string name="title_admin_panel">Admin panel</string> <!-- TODO Add translation -->
<string name="title_password">パスワードの変更</string>
<string name="title_update_profile">プロフィールの更新</string>
<string name="title_about">About</string>
<string name="title_create_channel">新しいチャネルを作成します</string>
<!-- Actions -->
<string name="action_connect">接続</string>
<string name="action_use_this_username">このユーザー名を使用する</string>
<string name="action_login_or_sign_up">ログインまたはアカウントを作成するにはこのボタンを押してください</string>
<string name="action_terms_of_service">サービス利用規約</string>
<string name="action_privacy_policy">プライバシーポリシー</string>
<string name="action_search">検索</string>
<string name="action_update">更新</string>
<string name="action_settings">設定</string>
<string name="action_create_channel">チャンネル作成</string>
<string name="action_create">作ります</string>
<string name="action_logout">ログアウト</string>
<string name="action_files">ファイル</string>
<string name="action_confirm_password">変更したパスワードの確認</string>
<string name="action_join_chat">チャットに参加</string>
<string name="action_add_account">サーバーの追加</string>
<string name="action_online">オンライン</string>
<string name="action_away">離席中</string>
<string name="action_busy">取り込み中</string>
<string name="action_invisible">状態を隠す</string>
<string name="action_drawing">絵を描く</string>
<string name="action_save_to_gallery">ギャラリーに保存</string>
<string name="action_select_photo_from_gallery">Select photo from gallery</string> <!-- TODO Add translation -->
<string name="action_take_photo">Take photo</string> <!-- TODO Add translation -->
<string name="action_reset_avatar">Reset avatar</string> <!-- TODO Add translation -->
<string name="action_connect_server">Connect with a server</string> <!-- TODO Add translation -->
<string name="action_join_community">Join in the community</string> <!-- TODO Add translation -->
<string name="action_create_server">Create a new server</string> <!-- TODO Add translation -->
<string name="action_register">Register</string> <!-- TODO Add translation -->
<string name="action_confirm">Confirm</string> <!-- TODO Add translation -->
<!-- Settings List -->
<string-array name="settings_actions">
<item name="item_preferences">Preferences</item> <!-- TODO Add translation -->
<item name="item_password">Change Password</item> <!-- TODO Add translation -->
<item name="item_password">About</item> <!-- TODO Add translation -->
</string-array>
<!-- Regular information messages -->
<string name="msg_generic_error">エラーが発生しました。もう一度お試しください。</string>
<string name="msg_no_data_to_display">表示するデータがありません</string>
<string name="msg_profile_update_successfully">プロフィールの更新に成功しました</string>
<string name="msg_username">ユーザー名</string>
<string name="msg_username_or_email">メールアドレスまたはユーザー名</string>
<string name="msg_password">パスワード</string>
<string name="msg_name">名前</string>
<string name="msg_email">メール</string>
<string name="msg_avatar_url">アバター URL</string>
<string name="msg_or_continue_using_social_accounts">またはソーシャルアカウントを使用する</string>
<string name="msg_new_user">新規ユーザー? %1$s</string>
<string name="msg_forgot_password">パスワードをお忘れですか? %1$s</string>
<string name="msg_reset">リセット</string>
<string name="msg_check_your_email_to_reset_your_password">メールが送信されました! パスワードを変更するためには、受信ボックスを確認してください。</string>
<string name="msg_invalid_email">有効なメールアドレスを入力してください</string>
<string name="msg_new_user_agreement">サインアップすることで、\n%1$s と %2$s に同意したとみなします。</string>
<string name="msg_2fa_code">2FA コード</string>
<string name="msg_today">Today</string> <!-- TODO Add translation -->
<string name="msg_yesterday">昨日</string>
<string name="msg_message">メッセージ</string>
<string name="msg_this_room_is_read_only">この部屋は読み取り専用です</string>
<string name="msg_invalid_2fa_code">無効な 2FA コード</string>
<string name="msg_invalid_file">無効なファイル</string>
<string name="msg_invalid_server_url">無効なサーバー URL</string>
<string name="msg_content_description_log_in_using_facebook">Login using Facebook</string>
<string name="msg_content_description_log_in_using_github">Login using Github</string>
<string name="msg_content_description_log_in_using_google">Login using Google</string>
<string name="msg_content_description_log_in_using_linkedin">Login using Linkedin</string>
<string name="msg_content_description_log_in_using_meteor">Login using Meteor</string>
<string name="msg_content_description_log_in_using_twitter">Login using Twitter</string>
<string name="msg_content_description_log_in_using_gitlab">Login using Gitlab</string>
<string name="msg_content_description_log_in_using_wordpress">Login using WordPress</string>
<string name="msg_content_description_send_message">メッセージを送信</string> <!-- TODO Add translation -->
<string name="msg_content_description_show_attachment_options">添付ファイルオプションを表示する</string>
<string name="msg_you">あなた</string>
<string name="msg_unknown">不明</string>
<string name="msg_email_address">電子メールアドレス</string>
<string name="msg_utc_offset">UTC offset</string>
<string name="msg_new_password">新しいパスワード</string>
<string name="msg_confirm_password">新しいパスワードの確認</string>
<string name="msg_channel_name">チャンネル名</string>
<string name="msg_search">検索</string>
<string name="msg_unread_messages">未読メッセージ</string>
<string name="msg_preview_video">ビデオ</string>
<string name="msg_preview_audio">音声</string>
<string name="msg_preview_photo">写真</string>
<string name="msg_preview_file">ファイル</string>
<string name="msg_no_messages_yet">メッセージはまだありません</string>
<string name="msg_ok">OK</string>
<string name="msg_update_app_version_in_order_to_continue">古いバージョンのサーバーを使用しています。続行するには、管理者にサーバーのバージョンを更新するよう連絡してください。</string>
<string name="msg_ver_not_recommended">
ご使用のサーバーのバージョンは推薦バージョン %1$s よりも古いものです。\nログインは可能ですが、予期しない動作が発生する可能性があります。</string>
<string name="msg_ver_not_minimum">
サーバーのバージョンが最低限必要なバージョン %1$s よりも古いものです。\nサーバーのバージョンを更新してからログインしてください!
</string>
<string name="msg_no_chat_title">メッセージがまだありません</string>
<string name="msg_no_chat_description">ここから会話を始めましょう</string>
<string name="msg_proceed">PROCEED</string>
<string name="msg_cancel">キャンセル</string>
<string name="msg_warning">警告</string>
<string name="msg_http_insecure">HTTPを使用している場合、安全ではないサーバーに接続します。安全なサーバーに接続することをお勧めします。</string>
<string name="msg_error_checking_server_version">サーバーのバージョンを確認中にエラーが発生しました。もう一度お試しください。</string>
<string name="msg_invalid_server_protocol">選択したプロトコルはこのサーバーでは使用できません。HTTPSを選択してください。</string>
<string name="msg_image_saved_successfully">画像をギャラリーに保存しました</string>
<string name="msg_image_saved_failed">画像を保存できませんでした</string>
<string name="msg_edited">(編集済)</string>
<string name="msg_and">\u0020and\u0020</string>
<string name="msg_is_typing">\u0020is 入力中…</string>
<string name="msg_are_typing">\u0020are 入力中…</string>
<string name="msg_several_users_are_typing">複数のユーザーが入力中…</string>
<string name="msg_no_search_found">結果が見つかりません</string>
<string name="msg_log_out">ログアウト…</string>
<string name="msg_upload_file">ファイルをアップロード</string>
<string name="msg_file_description">ファイルの説明</string>
<string name="msg_send">送信</string>
<string name="msg_sent_attachment">添付ファイルを送信しました</string>
<string name="msg_welcome_to_rocket_chat">Welcome to Rocket.Chat</string> <!-- TODO Add translation -->
<string name="msg_team_communication">Team Communication</string> <!-- TODO Add translation -->
<string name="msg_login_with_email">Login with e-mail</string> <!-- TODO Add translation -->
<string name="msg_create_account">Create an account</string> <!-- TODO Add translation -->
<string name="msg_continue_with_facebook">Continue with Facebook</string> <!-- TODO Add translation -->
<string name="msg_continue_with_github">Continue with Github</string> <!-- TODO Add translation -->
<string name="msg_continue_with_google">Continue with Google</string> <!-- TODO Add translation -->
<string name="msg_continue_with_linkedin">Continue with Linkedin</string> <!-- TODO Add translation -->
<string name="msg_continue_with_gitlab">Continue with GitLab</string> <!-- TODO Add translation -->
<string name="msg_two_factor_authentication">Two-factor Authentication</string> <!-- TODO Add translation -->
<string name="msg__your_2fa_code">What’s your 2FA code?</string> <!-- TODO Add translation -->
<!-- Create channel messages -->
<string name="msg_private_channel">プライベート</string>
<string name="msg_public_channel">パブリック</string>
<string name="msg_private_channel_description">招待されたユーザーだけがアクセスできます</string>
<string name="msg_public_channel_description">誰もがこのチャンネルにアクセスできます</string>
<string name="msg_ready_only_channel">読み取り専用チャネル</string>
<string name="msg_ready_only_channel_description">許可されたユーザーのみ新しいメッセージを書けます</string>
<string name="msg_invite_members">メンバーを招待する</string>
<string name="msg_member_already_added">あなたは既にこのユーザーを選択しています</string>
<string name="msg_member_not_found">メンバーが見つかりません</string>
<string name="msg_channel_created_successfully">チャンネルが正常に作成されました</string>
<string name="msg_message_copied">メッセージをコピー</string>
<string name="msg_delete_message">メッセージを削除</string>
<string name="msg_delete_description">このメッセージを削除してもよろしいですか?</string>
<!-- Preferences messages -->
<string name="msg_analytics_tracking">Analytics tracking</string> <!-- TODO Add translation -->
<string name="msg_send_analytics_tracking">Send anonymous statics to help improving this app</string> <!-- TODO Add translation -->
<string name="msg_do_not_send_analytics_tracking">Do not send anonymous statics to help improving this app</string> <!-- TODO Add translation -->
<string name="msg_not_applicable_since_it_is_a_foss_version">Not applicable since it is a FOSS version</string> <!-- TODO Add translation -->
<!-- System messages -->
<string name="message_room_name_changed">ルーム名を %1$s から %2$s へ変更しました。</string>
<string name="message_user_added_by">%1$s がユーザー %2$s を追加しました。</string>
<string name="message_user_removed_by">%1$s がユーザー %2$s を削除しました。</string>
<string name="message_user_left">チャンネルを退去しました。</string>
<string name="message_user_joined_channel">チャンネルへ参加しました。</string>
<string name="message_welcome">ようこそ %s</string>
<string name="message_removed">メッセージを削除しました。</string>
<string name="message_pinned">メッセージをピン留めしました:</string>
<string name="message_muted">%2$sでミュートされたユーザー %1$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_removed">%1$s は %3$s で、もう %2$s ではありません</string>
<string name="message_credentials_saved_successfully">資格情報を正常に保存しました</string>
<!-- Message actions -->
<string name="action_msg_reply">返信</string>
<string name="action_msg_info">メッセージ情報</string>
<string name="action_msg_edit">編集</string>
<string name="action_msg_copy">コピー</string>
<string name="action_msg_quote">引用</string>
<string name="action_msg_delete">削除</string>
<string name="action_msg_pin">ピン留めする</string>
<string name="action_msg_unpin">ピン留を外す</string>
<string name="action_msg_star">スターをつける</string>
<string name="action_msg_unstar">スターを外す</string>
<string name="action_msg_share">Share</string>
<string name="action_title_editing">メッセージの編集</string>
<string name="action_msg_add_reaction">リアクションする</string>
<!-- Permission messages -->
<string name="permission_editing_not_allowed">編集は許可されていません</string>
<string name="permission_deleting_not_allowed">削除は許可されていません</string>
<string name="permission_pinning_not_allowed">ピン留めは許可されていません</string>
<string name="permission_starring_not_allowed">閲覧は許可されていません</string>
<!-- Search message -->
<string name="title_search_message">メッセージを検索</string>
<!-- Favorite/Unfavorite chat room -->
<string name="title_favorite_chat">お気に入り</string>
<string name="title_unfavorite_chat">お気に入り解除</string>
<!-- Members List -->
<string name="title_members_list">メンバー</string>
<!-- Mentions -->
<string name="msg_mentions">メンション</string>
<string name="msg_no_mention">メンションされたメッセージはありません</string>
<string name="msg_all_the_mentions_appear_here">全てのメンションされたメッセージが\ここに表示されます</string>
<!-- Pinned Messages -->
<string name="title_pinned_messages">ピン留めされたメッセージ</string>
<string name="no_pinned_messages">ピン留めされたメッセージはありません</string>
<string name="no_pinned_description">ピン留めされたすべてのメッセージが\ここに表示されます</string>
<!-- Favorite Messages -->
<string name="title_favorite_messages">お気に入りのメッセージ</string>
<string name="no_favorite_messages">お気に入りのメッセージはありません</string>
<string name="no_favorite_description">全てのお気に入りメッセージが\ここに表示されます</string>
<!-- Files -->
<string name="title_files">ファイル</string>
<string name="title_files_total">ファイル (%d)</string>
<string name="msg_no_files">ファイルがありません</string>
<string name="msg_all_files_appear_here">全てのファイルがここに表示されます</string>
<!-- Upload Messages -->
<string name="max_file_size_exceeded">ファイルサイズ %1$d バイトが最大アップロードサイズ %2$d バイトとを超えました</string>
<!-- Socket status -->
<string name="status_connected">接続済み</string>
<string name="status_disconnected">接続切断</string>
<string name="status_connecting">接続中</string>
<string name="status_authenticating">認証中</string>
<string name="status_disconnecting">切断</string>
<string name="status_waiting">%d 秒間接続中</string>
<!--Suggestions-->
<string name="suggest_all_description">このルームの全員に通知</string>
<string name="suggest_here_description">この部屋のアクティブユーザーに通知する</string>
<!-- Slash Commands -->
<string name="Slash_Gimme_Description">あなたのメッセージの前に表示(つ◕_◕)つ</string>
<string name="Slash_LennyFace_Description">あなたのメッセージの後に表示(͡°͜ʖ͡°)</string>
<string name="Slash_Shrug_Description">あなたのメッセージの後に表示¯\\ _(ツ)_ /¯</string>
<string name="Slash_Tableflip_Description">表示(╯°□°)╯(┻━┻</string>
<string name="Slash_TableUnflip_Description">表示┬─┬ノ(゜ - ゜ノ)</string>
<string name="Create_A_New_Channel">新しいチャネルを作成します</string>
<string name="Show_the_keyboard_shortcut_list">キーボードショートカットリストを表示する</string>
<string name="Invite_user_to_join_channel_all_from">[#channel]のすべてのユーザーをこのチャンネルに招待する</string>
<string name="Invite_user_to_join_channel_all_to">このチャンネルのすべてのユーザーを[#channel]に招待する</string>
<string name="Archive">アーカイブ</string>
<string name="Remove_someone_from_room">誰かをルームから削除</string>
<string name="Leave_the_current_channel">現在のチャンネルを残す</string>
<string name="Displays_action_text">操作テキストを表示</string>
<string name="Direct_message_someone">誰かへダイレクトメッセージ</string>
<string name="Mute_someone_in_room">誰かをルームでミュートにする</string>
<string name="Unmute_someone_in_room">誰かをルームでミュートを解除されました</string>
<string name="Invite_user_to_join_channel">ユーザーをこのチャンネルへ招待します</string>
<string name="Unarchive">アーカイブを解除</string>
<string name="Join_the_given_channel">チャンネルへ参加する</string>
<string name="Guggy_Command_Description">提供されたテキストに基づいてgifを生成する</string>
<string name="Slash_Topic_Description">設定トピック</string>
<!-- Emoji message-->
<string name="msg_no_recent_emoji">最近の絵文字はありません</string>
<string name="alert_title_default_skin_tone">デフォルトスキントークン</string>
<!-- Sorting and grouping-->
<string name="menu_chatroom_sort">ソート</string>
<string name="dialog_sort_title">ソート</string>
<string name="dialog_sort_by_alphabet">アルファベット順</string>
<string name="dialog_sort_by_activity">アクティビティで並べ替える</string>
<string name="dialog_group_by_type">グループ別</string>
<string name="dialog_group_favourites">お気に入りのグループ</string>
<string name="chatroom_header">ヘッダ</string>
<string name="dialog_button_done">完了</string>
<!--ChatRooms Headers-->
<string name="header_channel">チャンネル</string>
<string name="header_private_groups">プライベートグループ</string>
<string name="header_direct_messages">ダイレクトメッセージ</string>
<string name="header_live_chats">ライブチャット</string>
<string name="header_unknown">不明</string>
<!--Notifications-->
<string name="share_label">Edit shared message</string> <!-- TODO Add translation -->
<string name="notif_action_reply_hint">返信</string>
<string name="notif_error_sending">送信に失敗しました。もう一度お試しください。</string>
<string name="notif_success_sending">メッセージは %1$s に送信されました!</string>
<string name="read_by">読者</string>
<string name="message_information_title">メッセージ情報</string>
</resources>
\ No newline at end of file
......@@ -139,7 +139,7 @@
<string name="msg_delete_message">Delete Message</string>
<string name="msg_delete_description">Are you sure you want to delete this message</string>
<string name="msg_welcome_to_rocket_chat">Welcome to Rocket.Chat</string> <!-- TODO Add translation -->
<string name="msg_open_source_communication">Open Source Communication</string> <!-- TODO Add translation -->
<string name="msg_team_communication">Open Source Communication</string> <!-- TODO Add translation -->
<string name="msg_login_with_email">Login with e-mail</string> <!-- TODO Add translation -->
<string name="msg_create_account">Create an account</string> <!-- TODO Add translation -->
<string name="msg_continue_with_facebook">Continue with Facebook</string> <!-- TODO Add translation -->
......
......@@ -12,9 +12,9 @@
<string name="title_profile">Профиль</string>
<string name="title_members">Пользователи (%d)</string>
<string name="title_settings">Настройки</string>
<string name="title_preferences">Preferences</string> <!-- TODO Add translation -->
<string name="title_preferences">Персональные</string>
<string name="title_change_password">Изменить пароль</string>
<string name="title_admin_panel">Admin panel</string> <!-- TODO Add translation -->
<string name="title_admin_panel">Панель админа</string>
<string name="title_password">Изменить пароль</string>
<string name="title_update_profile">Обновить профиль</string>
<string name="title_about">О программе</string>
......@@ -53,7 +53,7 @@
<!-- Settings List -->
<string-array name="settings_actions">
<item name="item_preferences">Preferences</item> <!-- TODO Add translation -->
<item name="item_preferences">Персональные</item>
<item name="item_password">Изменить пароль</item>
<item name="item_password">О программе</item>
</string-array>
......@@ -136,7 +136,7 @@
<string name="msg_channel_name">Название канала</string>
<string name="msg_search">Поиск</string>
<string name="msg_welcome_to_rocket_chat">Welcome to Rocket.Chat</string> <!-- TODO Add translation -->
<string name="msg_open_source_communication">Open Source Communication</string> <!-- TODO Add translation -->
<string name="msg_team_communication">Open Source Communication</string> <!-- TODO Add translation -->
<string name="msg_login_with_email">Login with e-mail</string> <!-- TODO Add translation -->
<string name="msg_create_account">Create an account</string> <!-- TODO Add translation -->
<string name="msg_continue_with_facebook">Continue with Facebook</string> <!-- TODO Add translation -->
......@@ -160,10 +160,10 @@
<string name="msg_channel_created_successfully">Канал создан успешно</string>
<!-- Preferences messages -->
<string name="msg_analytics_tracking">Analytics tracking</string> <!-- TODO Add translation -->
<string name="msg_send_analytics_tracking">Send anonymous statics to help improving this app</string> <!-- TODO Add translation -->
<string name="msg_do_not_send_analytics_tracking">Do not send anonymous statics to help improving this app</string> <!-- TODO Add translation -->
<string name="msg_not_applicable_since_it_is_a_foss_version">Not applicable since it is a FOSS version</string> <!-- TODO Add translation -->
<string name="msg_analytics_tracking">Отслеживание Analytics</string>
<string name="msg_send_analytics_tracking">Отправлять анонимную статистику для улучшения приложения.</string>
<string name="msg_do_not_send_analytics_tracking">Не отправлять анонимную статистику для улучшения приложения</string>
<string name="msg_not_applicable_since_it_is_a_foss_version">Не применимо, так как это FOSS версия</string>
<!-- System messages -->
<string name="message_room_name_changed">%2$s изменил название канала на %1$s</string>
......
<resources>
<!-- Titles -->
<string name="title_sign_in_your_server">Ваш сервер</string>
<string name="title_log_in">Увійти</string>
<string name="title_register_username">Зареєструвати ім\'я</string>
<string name="title_reset_password">Відновлення паролю</string>
<string name="title_sign_up">Зареєструватися</string>
<string name="title_authentication">Аутентифікація</string>
<string name="title_legal_terms">Юридичні умови</string>
<string name="title_chats">Чати</string>
<string name="title_profile">Профіль</string>
<string name="title_members">Користувачі (%d)</string>
<string name="title_settings">Налаштування</string>
<string name="title_preferences">Персональні</string>
<string name="title_change_password">Змінити пароль</string>
<string name="title_admin_panel">Панель адміністратора</string>
<string name="title_password">Змінити пароль</string>
<string name="title_update_profile">Оновити профіль</string>
<string name="title_about">"Про програму"</string>
<string name="title_create_channel">Створити новий канал</string>
<!-- Actions -->
<string name="action_connect">Підключитися</string>
<string name="action_use_this_username">Використати це ім\'я</string>
<string name="action_login_or_sign_up">Натисніть цю кнопку, щоб увійти до системи або створити аккаунт</string>
<string name="action_terms_of_service">Умови використання</string>
<string name="action_privacy_policy">Політика конфіденційності</string>
<string name="action_search">Пошук</string>
<string name="action_update">Оновити</string>
<string name="action_settings">Налаштування</string>
<string name="action_create_channel">Створити канал</string>
<string name="action_create">Створити</string>
<string name="action_logout">Вийти</string>
<string name="action_files">Файли</string>
<string name="action_confirm_password">Підтвердження зміни пароля</string>
<string name="action_join_chat">Приєднатися до чату</string>
<string name="action_add_account">Додати аккаунт</string>
<string name="action_online">Онлайн</string>
<string name="action_away">Відійшов</string>
<string name="action_busy">Зайнятий</string>
<string name="action_invisible">Невидимий</string>
<string name="action_drawing">Малюнок</string>
<string name="action_save_to_gallery">Зберегти до галереї</string>
<string name="action_select_photo_from_gallery">Вибрати з галереї</string>
<string name="action_take_photo">Зробити знімок</string>
<string name="action_reset_avatar">Відновити аватар</string>
<string name="action_connect_server">Connect with a server</string> <!-- TODO Add translation -->
<string name="action_join_community">Join in the community</string> <!-- TODO Add translation -->
<string name="action_create_server">Create a new server</string> <!-- TODO Add translation -->
<string name="action_register">Register</string> <!-- TODO Add translation -->
<string name="action_confirm">Confirm</string> <!-- TODO Add translation -->
<!-- Settings List -->
<string-array name="settings_actions">
<item name="item_preferences">Preferences</item> <!-- TODO Add translation -->
<item name="item_password">Change Password</item> <!-- TODO Add translation -->
<item name="item_password">About</item> <!-- TODO Add translation -->
</string-array>
<!-- Regular information messages -->
<string name="msg_generic_error">Сталася помилка, спробуйте ще раз.</string>
<string name="msg_no_data_to_display">Немає даних для відображення</string>
<string name="msg_profile_update_successfully">"Профіль оновлено успішно "</string>
<string name="msg_username">Ім\'я користувача</string>
<string name="msg_username_or_email">Ім\'я користувача або e-mail</string>
<string name="msg_password">пароль</string>
<string name="msg_name">ім\'я</string>
<string name="msg_email">e-mail</string>
<string name="msg_avatar_url">URL аватара</string>
<string name="msg_or_continue_using_social_accounts">Або продовжити, за допомогою акаунта у соціальних мережах</string>
<string name="msg_new_user">Новий користувач? %1$s</string>
<string name="msg_forgot_password">Забули пароль? %1$s</string>
<string name="msg_reset">Скинути</string>
<string name="msg_check_your_email_to_reset_your_password">Лист було відправлено! Перевірте свою поштову скриньку, щоб скинути пароль.</string>
<string name="msg_invalid_email">Введіть діючий e-mail</string>
<string name="msg_new_user_agreement">Продовжуючи, ви погоджуєтеся з %1$s і %2$s</string>
<string name="msg_2fa_code">Код 2FA</string>
<string name="msg_yesterday">Вчора</string>
<string name="msg_today">Сьогодні</string>
<string name="msg_message">"Повідомлення "</string>
<string name="msg_this_room_is_read_only">Канал тільки для читання</string>
<string name="msg_invalid_2fa_code">Неправильний код 2FA</string>
<string name="msg_invalid_file">Неправильний файл</string>
<string name="msg_invalid_server_url">Неправильний URL-адреса сервера.</string>
<string name="msg_content_description_log_in_using_facebook">Увійти за допомогою Facebook</string>
<string name="msg_content_description_log_in_using_github">Увійти за допомогою Github</string>
<string name="msg_content_description_log_in_using_google">Увійти за допомогою Google</string>
<string name="msg_content_description_log_in_using_linkedin">Увійти за допомогою Linkedin</string>
<string name="msg_content_description_log_in_using_meteor">Увійти за допомогою Meteor</string>
<string name="msg_content_description_log_in_using_twitter">Увійти за допомогою Twitter</string>
<string name="msg_content_description_log_in_using_gitlab">Увійти за допомогою Gitlab</string>
<string name="msg_content_description_log_in_using_wordpress">Увійти за допомогою Wordpress</string>
<string name="msg_content_description_send_message">Надіслати повідомлення</string>
<string name="msg_content_description_show_attachment_options">Показати параметри долучення</string>
<string name="msg_you">Ви</string>
<string name="msg_unknown">Невідомий</string>
<string name="msg_email_address">E-mail адреса</string>
<string name="msg_utc_offset">UTC зсув</string>
<string name="msg_new_password">Введіть новий пароль</string>
<string name="msg_confirm_password">Підтвердіть новий пароль</string>
<string name="msg_unread_messages">Непрочитані повідомлення</string>
<string name="msg_preview_video">Відео</string>
<string name="msg_preview_audio">Аудіо</string>
<string name="msg_preview_photo">Фото</string>
<string name="msg_preview_file">Файл</string>
<string name="msg_no_messages_yet">"Немає повідомлень "</string>
<string name="msg_build">Збірка %1$d - %2$s - %3$s</string>
<string name="msg_ok">ОК</string>
<string name="msg_update_app_version_in_order_to_continue">Версія сервера застаріла. Будь ласка, зв\'яжіться з адміністратором, щоб оновити сервер.</string>
<string name="msg_ver_not_recommended">Здається, версія сервера менше ніж рекомендована %1$s. \nМожна увійти, але можуть виникнути ті чи інші проблеми під час роботи.</string>
<string name="msg_ver_not_minimum">Здається, версія сервера менше ніж мінімально необхідна %1$s.\nДля роботи потрібно оновити сервер!</string>
<string name="msg_proceed">ПРОДОВЖИТИ</string>
<string name="msg_cancel">ВІДМІНА</string>
<string name="msg_warning">ПОПЕРЕДЖЕННЯ</string>
<string name="msg_http_insecure">При використанні HTTP ви підключаєтеся до потенційно небезпечного сервера. Ми не радимо вам це робити.</string>
<string name="msg_error_checking_server_version">Під час перевірки версії вашого сервера сталася помилка, спробуйте ще раз.</string>
<string name="msg_invalid_server_protocol">Використання обраного протоколу не дозволено на цьому сервері, спробуйте використати HTTPS</string>
<string name="msg_image_saved_successfully">Зображення збережено до галереї</string>
<string name="msg_image_saved_failed">Не вдалося зберегти зображення</string>
<string name="msg_no_chat_title">Повідомлення відсутні</string>
<string name="msg_no_chat_description">Почніть спілкуватися, щоб побачити тут повідомлення.</string>
<string name="msg_edited">(змінено)</string>
<string name="msg_and">\\u0020і\\u0020</string>
<string name="msg_is_typing">\\u0020друкує…</string>
<string name="msg_are_typing">\\u0020друкують…</string>
<string name="msg_several_users_are_typing">Декілька користувачів друкують…</string>
<string name="msg_no_search_found">Результатів не знайдено</string>
<string name="msg_channel_name">Назва каналу</string>
<string name="msg_search">Пошук</string>
<string name="msg_message_copied">Повідомлення було скопійовано</string>
<string name="msg_upload_file">Завантажити файл</string>
<string name="msg_file_description">Опис файлу</string>
<string name="msg_send">Надіслати</string>
<string name="msg_delete_message">Видалити повідомлення</string>
<string name="msg_delete_description">Ви впевнені, що хочете видалити це повідомлення?</string>
<string name="msg_welcome_to_rocket_chat">Welcome to Rocket.Chat</string> <!-- TODO Add translation -->
<string name="msg_team_communication">Team Communication</string> <!-- TODO Add translation -->
<string name="msg_login_with_email">Login with e-mail</string> <!-- TODO Add translation -->
<string name="msg_create_account">Create an account</string> <!-- TODO Add translation -->
<string name="msg_continue_with_facebook">Continue with Facebook</string> <!-- TODO Add translation -->
<string name="msg_continue_with_github">Continue with Github</string> <!-- TODO Add translation -->
<string name="msg_continue_with_google">Continue with Google</string> <!-- TODO Add translation -->
<string name="msg_continue_with_linkedin">Continue with Linkedin</string> <!-- TODO Add translation -->
<string name="msg_continue_with_gitlab">Continue with GitLab</string> <!-- TODO Add translation -->
<string name="msg_two_factor_authentication">Two-factor Authentication</string> <!-- TODO Add translation -->
<string name="msg__your_2fa_code">What’s your 2FA code?</string> <!-- TODO Add translation -->
<!-- Create channel messages -->
<string name="msg_private_channel">Приватний</string>
<string name="msg_public_channel">Публічний</string>
<string name="msg_private_channel_description">Тільки ви та запрошені користувачі можуть отримати доступ до цього каналу</string>
<string name="msg_public_channel_description">Кожен може отримати доступ до цього каналу</string>
<string name="msg_ready_only_channel">Канал тільки для читання</string>
<string name="msg_ready_only_channel_description">Тільки адміністратор може писати нові повідомлення</string>
<string name="msg_invite_members">Запросити користувачів до каналу</string>
<string name="msg_member_already_added">Ви вже обрали цього користувача</string>
<string name="msg_member_not_found">"Користувача не знайдено "</string>
<string name="msg_channel_created_successfully">Канал був створений успішно</string>
<!-- Preferences messages -->
<string name="msg_analytics_tracking">Збір аналітики</string>
<string name="msg_send_analytics_tracking">Надсилати анонімну статистику для покращення роботи програми</string>
<string name="msg_do_not_send_analytics_tracking">Не надсилати анонімну статистику для покращення роботи програми</string>
<string name="msg_not_applicable_since_it_is_a_foss_version">Недоступно у FOSS версії</string>
<!-- System messages -->
<string name="message_room_name_changed">%2$s змінив назву каналу на %1$s</string>
<string name="message_user_added_by">Користувач %2$s додав користувача %1$s</string>
<string name="message_user_removed_by">Користувач %2$s видалив користувача %1$s</string>
<string name="message_user_left">Залишив канал.</string>
<string name="message_user_joined_channel">Приєднався до каналу.</string>
<string name="message_welcome">Привіт, %s</string>
<string name="message_removed">"Повідомлення видалено "</string>
<string name="message_pinned">Зафіксоване повідомлення:</string>
<string name="message_muted">Користувач %2$s позбавив %1$s дару мови</string>
<string name="message_unmuted">Користувачу і%1$s повернули дар мови за рішенням %2$s</string>
<string name="message_role_add">%1$s був призначений як %2$s користувачем %3$s</string>
<string name="message_role_removed">%1$s більше не %2$s за рішенням %3$s</string>
<string name="message_credentials_saved_successfully">Облікові дані було успішно збережено</string>
<!-- Message actions -->
<string name="action_msg_reply">Відповісти</string>
<string name="action_msg_info">Інформація про прочитання</string>
<string name="action_msg_edit">Відредагувати</string>
<string name="action_msg_copy">Скопіювати</string>
<string name="action_msg_quote">Цитувати</string>
<string name="action_msg_delete">Видалити</string>
<string name="action_msg_pin">Зафіксувати повідомлення</string>
<string name="action_msg_unpin">Відмінити фіксацію повідомлення</string>
<string name="action_msg_star">До обраного</string>
<string name="action_msg_unstar">Видалити з обраного</string>
<string name="action_msg_share">Поділитися</string>
<string name="action_title_editing">Редагування повідомлення</string>
<string name="action_msg_add_reaction">Відреагувати</string>
<!-- Permission messages -->
<string name="permission_editing_not_allowed">Редагування заборонено</string>
<string name="permission_deleting_not_allowed">Видалення заборонено</string>
<string name="permission_pinning_not_allowed">Неможливо зафіксувати</string>
<string name="permission_starring_not_allowed">Неможливо додати до обраного</string>
<!-- Search message -->
<string name="title_search_message">Пошук повідомлення</string>
<!-- Favorite/Unfavorite chat room -->
<string name="title_favorite_chat">Додати до обраного</string>
<string name="title_unfavorite_chat">Видалити з обраного</string>
<!-- Members List -->
<string name="title_members_list">Користувачі</string>
<!-- Mentions -->
<string name="msg_mentions">Згадування</string>
<string name="msg_no_mention">Згадування відсутні</string>
<string name="msg_all_the_mentions_appear_here">Згадування відображаються тут</string>
<!-- Pinned Messages -->
<string name="title_pinned_messages">Зафіксовані повідомлення</string>
<string name="no_pinned_messages">Немає зафіксованих повідомлень</string>
<string name="no_pinned_description">Зафіксовані повідомлення відображаються тут</string>
<!-- Favorite Messages -->
<string name="title_favorite_messages">Обрані повідомлення</string>
<string name="no_favorite_messages">Немає обраних повідомлень</string>
<string name="no_favorite_description">Обрані повідомлення відображаються тут</string>
<!-- Files -->
<string name="title_files">Файли</string>
<string name="title_files_total">Файли (%d)</string>
<string name="msg_no_files">Файли відсутні</string>
<string name="msg_all_files_appear_here">Усі файли відображаються тут</string>
<!-- Upload Messages -->
<string name="max_file_size_exceeded">Розмір файлу %1$d перевищив максимально допустимий %2$d байт</string>
<!-- Socket status -->
<string name="status_connected">Підключено</string>
<string name="status_disconnected">Відключено</string>
<string name="status_connecting">Підключаємося…</string>
<string name="status_authenticating">Аутентифікація</string>
<string name="status_disconnecting">Відключаємося…</string>
<string name="status_waiting">З\'єднання через %d секунд</string>
<!--Suggestions-->
<string name="suggest_all_description">Повідомити всіх у цьому чаті</string>
<string name="suggest_here_description">Повідомити всіх активних користувачів у цьому чаті</string>
<!-- Slash Commands -->
<string name="Slash_Gimme_Description">Показує ༼ つ ◕_◕ ༽つ перед вашим повідомленням</string>
<string name="Slash_LennyFace_Description">Показує ( ͡° ͜ʖ ͡°) після Вашого повідомлення</string>
<string name="Slash_Shrug_Description">Показує ¯_(ツ)_/¯ після Вашого повідомлення</string>
<string name="Slash_Tableflip_Description">Показує (╯°□°)╯︵ ┻━┻</string>
<string name="Slash_TableUnflip_Description">Показує ┬─┬ ノ( ゜-゜ノ)</string>
<string name="Create_A_New_Channel">Створити новий канал</string>
<string name="Show_the_keyboard_shortcut_list">Показати список гарячих клавіш</string>
<string name="Invite_user_to_join_channel_all_from">Запросити всіх користувачів з [#channel] приєднатися до цього каналу</string>
<string name="Invite_user_to_join_channel_all_to">Запросити всіх користувачів приєднатися до [#channel]</string>
<string name="Archive">Архівувати</string>
<string name="Remove_someone_from_room">Видалити когось з каналу</string>
<string name="Leave_the_current_channel">Покинути цей канал</string>
<string name="Direct_message_someone">Особисте повідомлення</string>
<string name="Displays_action_text">Відображає текст дії</string>
<string name="Mute_someone_in_room">Позбавити користувача дару мови на каналі</string>
<string name="Unmute_someone_in_room">Повернути користувачу дар мови на каналі</string>
<string name="Invite_user_to_join_channel">Запросити користувача на канал</string>
<string name="Unarchive">Розархівувати</string>
<string name="Join_the_given_channel">Приєднатися до цього каналу</string>
<string name="Guggy_Command_Description">Створює gif використовуючи наданий текст</string>
<string name="Slash_Topic_Description">Встановити тему</string>
<!-- Emoji message-->
<string name="msg_no_recent_emoji">Пусто</string>
<string name="alert_title_default_skin_tone">Тон шкіри за замовчуванням</string>
<!-- Sorting and grouping-->
<string name="menu_chatroom_sort">Сортувати</string>
<string name="dialog_sort_title">Сортувати за</string>
<string name="dialog_sort_by_alphabet">За алфавітом</string>
<string name="dialog_sort_by_activity">За активністю</string>
<string name="dialog_group_by_type">Групувати за типом</string>
<string name="dialog_group_favourites">Групувати обране</string>
<string name="dialog_button_done">Ок</string>
<string name="chatroom_header">Заголовок</string>
<!--ChatRooms Headers-->
<string name="header_channel">Канали</string>
<string name="header_private_groups">Приватні канали</string>
<string name="header_direct_messages">Особисті повідомлення</string>
<string name="header_live_chats">Живі чати</string>
<string name="header_unknown">Невідомі</string>
<!--Notifications-->
<string name="share_label">Редагувати повідомлення</string>
<string name="notif_action_reply_hint">ВІДПОВІСТИ</string>
<string name="notif_error_sending">Не вдалося відповісти. Будь ласка, спробуйте ще раз.</string>
<string name="notif_success_sending">Повідомлення надіслано %1$s!</string>
<string name="read_by">Прочитано</string>
<string name="message_information_title">Інформація про прочитання</string>
<string name="msg_log_out">Виходимо…</string>
<string name="msg_sent_attachment">Долучення відправлено</string>
</resources>
<!--
REMARK:
When adding a new string in this file do not forget to add a proper translation for the
corresponding supported language - if applicable.
Please, follow the rules as shown here (by adding a new string in the correct section).
Coding style:
https://github.com/RocketChat/java-code-styles/blob/master/CODING_STYLE.md#strings
-->
<resources xmlns:tools="http://schemas.android.com/tools">
<string name="app_name" translatable="false">Rocket.Chat</string>
......@@ -85,7 +96,6 @@
<string name="msg_invalid_2fa_code">Invalid 2FA Code</string>
<string name="msg_invalid_file">Invalid file</string>
<string name="msg_invalid_server_url">Invalid server URL</string>
<string name="msg_content_description_onboarding">Onboarding image</string>
<string name="msg_content_description_log_in_using_facebook">Login using Facebook</string>
<string name="msg_content_description_log_in_using_github">Login using Github</string>
<string name="msg_content_description_log_in_using_google">Login using Google</string>
......@@ -140,7 +150,7 @@
<string name="msg_send">Send</string>
<string name="msg_sent_attachment">Sent an attachment</string>
<string name="msg_welcome_to_rocket_chat">Welcome to Rocket.Chat</string>
<string name="msg_open_source_communication">Open Source Communication</string>
<string name="msg_team_communication">Team Communication</string>
<string name="msg_login_with_email">Login with e-mail</string>
<string name="msg_create_account">Create an account</string>
<string name="msg_continue_with_facebook">Continue with Facebook</string>
......
......@@ -2,14 +2,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="chat.rocket.android">
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<permission
android:name="${applicationId}.permission.C2D_MESSAGE"
android:protectionLevel="signature" />
<uses-permission android:name="${applicationId}.permission.C2D_MESSAGE" />
<application
android:name=".app.RocketChatApplication"
android:allowBackup="true"
......@@ -20,25 +12,6 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true">
<receiver
android:name="com.google.android.gms.gcm.GcmReceiver"
android:exported="true"
android:permission="com.google.android.c2dm.permission.SEND">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="${applicationId}" />
</intent-filter>
</receiver>
<service
android:name=".push.FirebaseTokenService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.INSTANCE_ID_EVENT" />
</intent-filter>
</service>
<service
android:name=".push.FirebaseMessagingService"
android:enabled="true"
......
package chat.rocket.android.util.helper.analytics
package chat.rocket.android.analytics
import chat.rocket.android.util.helper.analytics.event.AuthenticationEvent
import chat.rocket.android.util.helper.analytics.event.ScreenViewEvent
import chat.rocket.android.util.helper.analytics.event.SubscriptionTypeEvent
import chat.rocket.android.analytics.event.AuthenticationEvent
import chat.rocket.android.analytics.event.ScreenViewEvent
import chat.rocket.android.analytics.event.SubscriptionTypeEvent
import com.crashlytics.android.answers.Answers
import com.crashlytics.android.answers.CustomEvent
import com.crashlytics.android.answers.LoginEvent
import com.crashlytics.android.answers.SignUpEvent
// TODO inject the and analyticsTrackingInteractor and GetCurrentServerInteractor
object AnalyticsManager : Analytics {
class AnswersAnalytics : Analytics {
override fun logLogin(event: AuthenticationEvent, loginSucceeded: Boolean) =
Answers.getInstance()
......
package chat.rocket.android.analytics
import android.content.Context
import android.os.Bundle
import chat.rocket.android.analytics.event.AuthenticationEvent
import chat.rocket.android.analytics.event.ScreenViewEvent
import chat.rocket.android.analytics.event.SubscriptionTypeEvent
import com.google.firebase.analytics.FirebaseAnalytics
import javax.inject.Inject
class GoogleAnalyticsForFirebase @Inject constructor(val context: Context) :
Analytics {
private val firebaseAnalytics = FirebaseAnalytics.getInstance(context)
override fun logLogin(event: AuthenticationEvent, loginSucceeded: Boolean) {
firebaseAnalytics.logEvent(FirebaseAnalytics.Event.LOGIN, Bundle(1).apply {
putString(FirebaseAnalytics.Param.METHOD, event.methodName)
putLong(FirebaseAnalytics.Param.SUCCESS, if (loginSucceeded) 1 else 0)
})
}
override fun logSignUp(event: AuthenticationEvent, signUpSucceeded: Boolean) {
firebaseAnalytics.logEvent(FirebaseAnalytics.Event.SIGN_UP, Bundle(1).apply {
putString(FirebaseAnalytics.Param.METHOD, event.methodName)
putLong(FirebaseAnalytics.Param.SUCCESS, if (signUpSucceeded) 1 else 0)
})
}
override fun logScreenView(event: ScreenViewEvent) {
firebaseAnalytics.logEvent("screen_view", Bundle(1).apply {
putString("screen", event.screenName)
})
}
override fun logMessageSent(event: SubscriptionTypeEvent, serverUrl: String) {
firebaseAnalytics.logEvent("message_sent", Bundle(1).apply {
putString("subscription_type", event.subscriptionTypeName)
putString("server", serverUrl)
})
}
override fun logMediaUploaded(event: SubscriptionTypeEvent, mimeType: String) {
firebaseAnalytics.logEvent("media_upload", Bundle(1).apply {
putString("subscription_type", event.subscriptionTypeName)
putString("media_type", mimeType)
})
}
override fun logReaction(event: SubscriptionTypeEvent) {
firebaseAnalytics.logEvent("reaction", Bundle(1).apply {
putString("subscription_type", event.subscriptionTypeName)
})
}
override fun logServerSwitch(serverUrl: String, serverCount: Int) {
firebaseAnalytics.logEvent("server_switch", Bundle(1).apply {
putString("server_url", serverUrl)
putInt("server_count", serverCount)
})
}
}
package chat.rocket.android.dagger.module
import androidx.work.Worker
import chat.rocket.android.chatroom.di.MessageServiceProvider
import chat.rocket.android.chatroom.service.MessageService
import chat.rocket.android.dagger.qualifier.WorkerKey
import chat.rocket.android.push.FirebaseMessagingService
import chat.rocket.android.push.FirebaseTokenService
import chat.rocket.android.push.di.FirebaseMessagingServiceProvider
import chat.rocket.android.push.di.FirebaseTokenServiceProvider
import chat.rocket.android.push.di.TokenRegistrationSubComponent
import chat.rocket.android.push.worker.TokenRegistrationWorker
import dagger.Binds
import dagger.Module
import dagger.android.AndroidInjector
import dagger.android.ContributesAndroidInjector
import dagger.multibindings.IntoMap
@Module abstract class ServiceBuilder {
@ContributesAndroidInjector(modules = [FirebaseTokenServiceProvider::class])
abstract fun bindFirebaseTokenService(): FirebaseTokenService
@Module(subcomponents = [TokenRegistrationSubComponent::class])
abstract class ServiceBuilder {
@ContributesAndroidInjector(modules = [FirebaseMessagingServiceProvider::class])
abstract fun bindGcmListenerService(): FirebaseMessagingService
@ContributesAndroidInjector(modules = [MessageServiceProvider::class])
abstract fun bindMessageService(): MessageService
@Binds
@IntoMap
@WorkerKey(TokenRegistrationWorker::class)
abstract fun bindTokenRegistrationWorkerFactory(
builder: TokenRegistrationSubComponent.Builder
): AndroidInjector.Factory<out Worker>
}
\ No newline at end of file
package chat.rocket.android.extensions
import com.google.android.gms.tasks.Task
import kotlin.coroutines.experimental.suspendCoroutine
@JvmName("awaitVoid")
suspend fun Task<Void>.await() = suspendCoroutine<Unit> { continuation ->
addOnSuccessListener { continuation.resume(Unit) }
addOnFailureListener { continuation.resumeWithException(it) }
}
suspend fun <TResult> Task<TResult>.await() = suspendCoroutine<TResult> { continuation ->
addOnSuccessListener { continuation.resume(it) }
addOnFailureListener { continuation.resumeWithException(it) }
}
\ No newline at end of file
package chat.rocket.android.push
import androidx.core.os.bundleOf
import androidx.work.Constraints
import androidx.work.NetworkType
import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.WorkManager
import androidx.work.workDataOf
import chat.rocket.android.push.worker.TokenRegistrationWorker
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
import dagger.android.AndroidInjection
......@@ -17,8 +23,22 @@ class FirebaseMessagingService : FirebaseMessagingService() {
}
override fun onMessageReceived(message: RemoteMessage) {
// XXX - for now this is ok, if we start to do network calls, use a Worker instead
message.data?.let {
pushManager.handle(bundleOf(*(it.map { Pair(it.key, it.value) }).toTypedArray()))
}
}
override fun onNewToken(token: String) {
val data = workDataOf("token" to token)
val constraint =
Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build()
val work = OneTimeWorkRequestBuilder<TokenRegistrationWorker>()
.setInputData(data)
.setConstraints(constraint)
.build()
// Schedule a job since we are using network...
WorkManager.getInstance().enqueue(work)
}
}
\ No newline at end of file
package chat.rocket.android.push
import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.retryIO
import chat.rocket.common.RocketChatException
import chat.rocket.core.internal.rest.registerPushToken
import com.google.firebase.iid.FirebaseInstanceId
import com.google.firebase.iid.FirebaseInstanceIdService
import dagger.android.AndroidInjection
import kotlinx.coroutines.experimental.launch
import timber.log.Timber
import javax.inject.Inject
class FirebaseTokenService : FirebaseInstanceIdService() {
@Inject
lateinit var factory: RocketChatClientFactory
@Inject
lateinit var getCurrentServerInteractor: GetCurrentServerInteractor
@Inject
lateinit var localRepository: LocalRepository
override fun onCreate() {
super.onCreate()
AndroidInjection.inject(this)
}
override fun onTokenRefresh() {
try {
val fcmToken = FirebaseInstanceId.getInstance().token
val currentServer = getCurrentServerInteractor.get()
val client = currentServer?.let { factory.create(currentServer) }
fcmToken?.let {
localRepository.save(LocalRepository.KEY_PUSH_TOKEN, fcmToken)
client?.let {
launch {
try {
Timber.d("Registering push token: $fcmToken for ${client.url}")
retryIO("register push token") { client.registerPushToken(fcmToken) }
} catch (ex: RocketChatException) {
Timber.e(ex, "Error registering push token")
}
}
}
}
} catch (ex: Exception) {
Timber.e(ex, "Error refreshing Firebase TOKEN")
}
}
}
\ No newline at end of file
package chat.rocket.android.push
import androidx.work.Constraints
import androidx.work.NetworkType
import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.WorkManager
import chat.rocket.android.push.worker.TokenRegistrationWorker
fun refreshPushToken() {
val constraint =
Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build()
val work = OneTimeWorkRequestBuilder<TokenRegistrationWorker>()
.setConstraints(constraint)
.build()
// Schedule a job since we are using network...
WorkManager.getInstance().enqueue(work)
}
\ No newline at end of file
package chat.rocket.android.push.di
import chat.rocket.android.dagger.module.AppModule
import chat.rocket.android.push.FirebaseTokenService
import dagger.Module
import dagger.android.ContributesAndroidInjector
@Module abstract class FirebaseTokenServiceProvider {
@ContributesAndroidInjector(modules = [AppModule::class])
abstract fun provideFirebaseTokenService(): FirebaseTokenService
}
\ No newline at end of file
package chat.rocket.android.push.di
import chat.rocket.android.push.worker.TokenRegistrationWorker
import dagger.Subcomponent
import dagger.android.AndroidInjector
@Subcomponent
interface TokenRegistrationSubComponent : AndroidInjector<TokenRegistrationWorker> {
@Subcomponent.Builder
abstract class Builder : AndroidInjector.Builder<TokenRegistrationWorker>()
}
\ No newline at end of file
package chat.rocket.android.push.worker
import androidx.work.Worker
import chat.rocket.android.dagger.injector.AndroidWorkerInjection
import chat.rocket.android.extensions.await
import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.server.domain.GetAccountsInteractor
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.extensions.registerPushToken
import chat.rocket.common.util.ifNull
import com.google.firebase.iid.FirebaseInstanceId
import kotlinx.coroutines.experimental.runBlocking
import timber.log.Timber
import javax.inject.Inject
class TokenRegistrationWorker : Worker() {
@Inject
lateinit var factory: RocketChatClientFactory
@Inject
lateinit var getAccountsInteractor: GetAccountsInteractor
@Inject
lateinit var localRepository: LocalRepository
override fun doWork(): Result {
AndroidWorkerInjection.inject(this)
runBlocking {
val token = inputData.getString("token") ?: refreshToken()
token?.let { fcmToken ->
localRepository.save(LocalRepository.KEY_PUSH_TOKEN, fcmToken)
factory.registerPushToken(fcmToken, getAccountsInteractor.get())
}.ifNull {
Timber.d("Unavailable FCM Token...")
}
}
return Result.SUCCESS
}
private fun refreshToken(): String? {
return runBlocking {
try {
FirebaseInstanceId.getInstance().instanceId.await().token
} catch (ex: Exception) {
Timber.e(ex, "Error refreshing Firebase TOKEN")
null
}
}
}
}
\ No newline at end of file
package chat.rocket.android.util
import chat.rocket.android.main.presentation.MainPresenter
import com.google.firebase.iid.FirebaseInstanceId
import com.google.firebase.messaging.FirebaseMessaging
import timber.log.Timber
suspend fun refreshFCMToken(presenter: MainPresenter) {
try {
val token = FirebaseInstanceId.getInstance().token
Timber.d("FCM token: $token")
presenter.refreshToken(token)
} catch (ex: Exception) {
Timber.d(ex, "Missing play services...")
}
}
fun invalidateFirebaseToken(token: String) {
FirebaseInstanceId.getInstance().deleteToken(token, FirebaseMessaging.INSTANCE_ID_SCOPE)
......
......@@ -16,6 +16,8 @@ buildscript {
classpath 'com.google.gms:google-services:4.0.2'
classpath 'io.fabric.tools:gradle:1.25.4'
classpath "com.github.ben-manes:gradle-versions-plugin:0.20.0"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
......
......@@ -10,28 +10,31 @@ ext {
// For app
kotlin : '1.2.61',
coroutine : '0.24.0',
coroutine : '0.25.0',
appCompat : '1.0.0-beta01',
recyclerview : '1.0.0-beta01',
constraintLayout : '2.0.0-alpha1',
cardview : '1.0.0-beta01',
browser : '1.0.0-beta01',
androidKtx : '1.0.0-beta01',
appCompat : '1.0.0-rc02',
recyclerview : '1.0.0-rc02',
constraintLayout : '2.0.0-alpha2',
cardview : '1.0.0-rc02',
browser : '1.0.0-rc02',
androidKtx : '1.0.0-rc02',
workmanager : '1.0.0-alpha08',
dagger : '2.16',
firebaseCloudMessage : '17.1.0',
firebaseAnalytics : '16.0.1',
playServices : '15.0.1',
firebaseCloudMessage : '17.3.0',
firebaseAnalytics : '16.0.3',
playServices : '16.0.0',
exoPlayer : '2.8.2',
flexbox : '1.0.0',
material : '1.0.0-beta01',
room : '2.0.0-beta01',
lifecycle : '2.0.0-beta01',
room : '2.0.0-rc01',
lifecycle : '2.0.0-rc01',
rxKotlin : '2.2.0',
rxAndroid : '2.0.2',
livedataKtx : '2.0.1',
rxKotlin : '2.3.0',
rxAndroid : '2.1.0',
moshi : '1.6.0',
okhttp : '3.11.0',
......@@ -45,9 +48,9 @@ ext {
kotshi : '1.0.4',
frescoImageViewer : '0.5.1',
markwon : '1.1.0',
markwon : '1.1.1',
aVLoadingIndicatorView: '2.1.3',
glide : '4.8.0-SNAPSHOT',
glide : '4.8.0',
// For wearable
wear : '2.3.0',
......@@ -71,6 +74,9 @@ ext {
cardview : "androidx.cardview:cardview:${versions.cardview}",
browser : "androidx.browser:browser:${versions.browser}",
androidKtx : "androidx.core:core-ktx:${versions.androidKtx}",
fragmentsKtx : "androidx.fragment:fragment-ktx:${versions.androidKtx}",
workmanager : "android.arch.work:work-runtime-ktx:${versions.workmanager}",
workmanagerFirebase : "android.arch.work:work-firebase:${versions.workmanager}",
dagger : "com.google.dagger:dagger:${versions.dagger}",
daggerSupport : "com.google.dagger:dagger-android-support:${versions.dagger}",
......@@ -84,6 +90,9 @@ ext {
roomProcessor : "androidx.room:room-compiler:${versions.room}",
lifecycleExtensions : "androidx.lifecycle:lifecycle-extensions:${versions.lifecycle}",
lifecycleCompiler : "androidx.lifecycle:lifecycle-compiler:${versions.lifecycle}",
viewmodelKtx : "androidx.lifecycle:lifecycle-viewmodel-ktx:${versions.lifecycle}",
livedataKtx : "com.shopify:livedata-ktx:${versions.livedataKtx}",
rxKotlin : "io.reactivex.rxjava2:rxkotlin:${versions.rxKotlin}",
rxAndroid : "io.reactivex.rxjava2:rxandroid:${versions.rxAndroid}",
......
......@@ -139,9 +139,11 @@ class EmojiKeyboardPopup(context: Context, view: View) : OverKeyboardPopupWindow
@ColorInt
private fun getFitzpatrickColor(tone: Fitzpatrick): Int {
val sharedPreferences = context.getSharedPreferences("emoji", Context.MODE_PRIVATE)
sharedPreferences.edit {
putString(PREF_EMOJI_SKIN_TONE, tone.type)
}
return when (tone) {
Fitzpatrick.Default -> ContextCompat.getColor(context, R.color.tone_default)
Fitzpatrick.LightTone -> ContextCompat.getColor(context, R.color.tone_light)
......@@ -194,6 +196,7 @@ class EmojiKeyboardPopup(context: Context, view: View) : OverKeyboardPopupWindow
}
class EmojiTextWatcher(private val editor: EditText) : TextWatcher {
@Volatile
private var emojiToRemove = mutableListOf<EmojiTypefaceSpan>()
......@@ -237,4 +240,4 @@ class EmojiKeyboardPopup(context: Context, view: View) : OverKeyboardPopupWindow
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
}
}
}
\ No newline at end of file
}
......@@ -79,10 +79,10 @@ class EmojiParser {
val px = context.resources.getDimensionPixelSize(R.dimen.custom_emoji_small)
return spannable.also {
return spannable.also { sp ->
regex.findAll(spannable).iterator().forEach { match ->
customEmojis.find { it.shortname.toLowerCase() == match.value.toLowerCase() }?.let {
it.url?.let { url ->
customEmojis.find { it.shortname.toLowerCase() == match.value.toLowerCase() }?.let { emoji ->
emoji.url?.let { url ->
try {
val glideRequest = if (url.endsWith("gif", true)) {
GlideApp.with(context).asGif()
......
......@@ -37,10 +37,12 @@ class EmojiPickerPopup(context: Context) : Dialog(context) {
private fun setSize() {
val lp = WindowManager.LayoutParams()
lp.copyFrom(window.attributes)
val dialogWidth = lp.width
val dialogHeight = context.resources.getDimensionPixelSize(R.dimen.picker_popup_height)
window.setLayout(dialogWidth, dialogHeight)
window?.let {
lp.copyFrom(it.attributes)
val dialogWidth = lp.width
val dialogHeight = context.resources.getDimensionPixelSize(R.dimen.picker_popup_height)
it.setLayout(dialogWidth, dialogHeight)
}
}
private suspend fun setupViewPager() {
......
......@@ -3,11 +3,13 @@ package chat.rocket.android.emoji
import android.content.Context
import android.content.SharedPreferences
import android.graphics.Typeface
import android.util.Log
import chat.rocket.android.emoji.internal.EmojiCategory
import chat.rocket.android.emoji.internal.PREF_EMOJI_RECENTS
import chat.rocket.android.emoji.internal.db.EmojiDatabase
import chat.rocket.android.emoji.internal.isCustom
import com.bumptech.glide.Glide
import com.bumptech.glide.load.engine.GlideException
import kotlinx.coroutines.experimental.CommonPool
import kotlinx.coroutines.experimental.launch
import kotlinx.coroutines.experimental.withContext
......@@ -46,7 +48,11 @@ object EmojiRepository {
this@EmojiRepository.customEmojis = customEmojis
val allEmojis = mutableListOf<Emoji>()
db = EmojiDatabase.getInstance(context)
cachedTypeface = Typeface.createFromAsset(context.assets, "fonts/emojione-android.ttf")
if (!::cachedTypeface.isInitialized) {
cachedTypeface = Typeface.createFromAsset(context.assets, "fonts/emojione-android.ttf")
}
preferences = context.getSharedPreferences("emoji", Context.MODE_PRIVATE)
val stream = context.assets.open(path)
// Load emojis from emojione ttf file temporarily here. We still need to work on them.
......@@ -110,10 +116,17 @@ object EmojiRepository {
val px = context.resources.getDimensionPixelSize(R.dimen.custom_emoji_large)
customEmojis.forEach {
val future = Glide.with(context)
.load(it.url)
.submit(px, px)
future.get()
try {
val future = Glide.with(context)
.load(it.url)
.submit(px, px)
future.get()
} catch (ex: Exception) {
Log.d("EmojiRepository", "Error fetching custom emoji ${it.shortname}", ex)
if (ex is GlideException) {
ex.logRootCauses("EmojiRepository")
}
}
}
}
}
......@@ -300,4 +313,12 @@ object EmojiRepository {
val s2: Int = ((scalar - 0x10000) % 0x400) + 0xDC00
return Pair(s1, s2)
}
fun init(context: Context) {
launch {
db = EmojiDatabase.getInstance(context)
preferences = context.getSharedPreferences("emoji", Context.MODE_PRIVATE)
cachedTypeface = Typeface.createFromAsset(context.assets, "fonts/emojione-android.ttf")
}
}
}
\ No newline at end of file
......@@ -57,9 +57,6 @@ abstract class OverKeyboardPopupWindow(
init {
setBackgroundDrawable(null)
if (BuildConfig.VERSION_CODE >= Build.VERSION_CODES.LOLLIPOP) {
elevation = 0f
}
val view = onCreateView(LayoutInflater.from(context))
onViewCreated(view)
contentView = view
......@@ -89,7 +86,7 @@ abstract class OverKeyboardPopupWindow(
/**
* Call this function to resize the emoji popup according to your soft keyboard size
*/
fun setSizeForSoftKeyboard() {
private fun setSizeForSoftKeyboard() {
val viewTreeObserver = rootView.viewTreeObserver
viewTreeObserver.addOnGlobalLayoutListener(this)
}
......
......@@ -147,10 +147,11 @@ internal class EmojiPagerAdapter(private val listener: EmojiKeyboardListener) :
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): EmojiRowViewHolder {
val inflater = LayoutInflater.from(parent.context)
val view = if (viewType == CUSTOM) {
LayoutInflater.from(parent.context).inflate(R.layout.emoji_image_row_item, parent, false)
inflater.inflate(R.layout.emoji_image_row_item, parent, false)
} else {
LayoutInflater.from(parent.context).inflate(R.layout.emoji_row_item, parent, false)
inflater.inflate(R.layout.emoji_row_item, parent, false)
}
return EmojiRowViewHolder(view, listener)
}
......
......@@ -6,18 +6,16 @@
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/emoji_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp" />
android:layout_height="match_parent" />
<TextView
android:id="@+id/text_no_recent_emoji"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/msg_no_recent_emoji"
android:layout_gravity="center"
android:elevation="10dp"
android:text="@string/msg_no_recent_emoji"
android:textSize="16sp"
android:visibility="gone"/>
android:visibility="gone" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
\ No newline at end of file
</androidx.coordinatorlayout.widget.CoordinatorLayout>
......@@ -20,8 +20,6 @@
android:id="@+id/pager_categories"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:background="@color/colorWhite" />
</LinearLayout>
\ No newline at end of file
</LinearLayout>
......@@ -2,11 +2,11 @@
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/emoji_view"
style="@style/TextAppearance.AppCompat.Title"
android:layout_width="48dp"
android:layout_height="48dp"
android:foreground="?selectableItemBackground"
android:layout_gravity="center"
android:gravity="center"
android:textColor="#000000"
android:textSize="26sp"
android:textSize="24sp"
tools:text="😀" />
......@@ -23,20 +23,6 @@ android {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
flavorDimensions "type"
productFlavors {
// includes proprietary libs
play {
dimension "type"
}
// only foss
foss {
dimension "type"
}
}
}
dependencies {
......@@ -55,9 +41,4 @@ dependencies {
// TODO This is a dependency from the core module, but the util module are unable to get that dependencies. Check why it is occurring since transitive is enable by default
implementation libraries.lifecycleExtensions
kapt libraries.lifecycleCompiler
// Proprietary libraries
playImplementation('com.crashlytics.sdk.android:answers:1.4.2@aar') {
transitive = true
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment