Commit c5bda436 authored by divyanshu's avatar divyanshu

Merge branch 'develop-2.x' into redesignOnboarding

# Conflicts:
#	app/src/main/java/chat/rocket/android/authentication/login/ui/LoginFragment.kt
#	app/src/main/java/chat/rocket/android/authentication/presentation/AuthenticationNavigator.kt
#	app/src/main/java/chat/rocket/android/authentication/twofactor/ui/TwoFAFragment.kt
#	app/src/main/java/chat/rocket/android/authentication/ui/AuthenticationActivity.kt
#	app/src/main/java/chat/rocket/android/profile/ui/ProfileFragment.kt
#	app/src/main/res/layout/fragment_authentication_log_in.xml
#	app/src/main/res/layout/fragment_profile.xml
#	app/src/main/res/menu/navigation.xml
#	app/src/main/res/values-de/strings.xml
#	app/src/main/res/values-es/strings.xml
#	app/src/main/res/values-fr/strings.xml
#	app/src/main/res/values-hi-rIN/strings.xml
#	app/src/main/res/values-pt-rBR/strings.xml
#	app/src/main/res/values-ru/strings.xml
#	app/src/main/res/values/strings.xml
#	build.gradle
parents e199faa5 14861132
...@@ -64,7 +64,7 @@ jobs: ...@@ -64,7 +64,7 @@ jobs:
- store_artifacts: - store_artifacts:
path: app/build/reports/ path: app/build/reports/
destination: reports destination: reports
build-apk: build-play-apk:
docker: docker:
- image: circleci/android:api-27-alpha - image: circleci/android:api-27-alpha
environment: environment:
...@@ -93,7 +93,40 @@ jobs: ...@@ -93,7 +93,40 @@ jobs:
- run: - run:
name: Build APK name: Build APK
command: | command: |
./gradlew assembleRelease --info --console=plain --stacktrace ./gradlew assemblePlayRelease --info --console=plain --stacktrace
- store_artifacts:
path: app/build/outputs/apk
destination: apks
build-foss-apk:
docker:
- image: circleci/android:api-27-alpha
environment:
JVM_OPTS: -Xmx3200m
steps:
- checkout
- run:
name: restore files from ENV
command: |
echo $ROCKET_JKS_BASE64 | base64 --decode > Rocket.jks
echo $ROCKET_PLAY_JSON | base64 --decode > app/rocket-chat.json
- run:
name: checkout Rocket.Chat.Kotlin.SDK
command: git clone https://github.com/RocketChat/Rocket.Chat.Kotlin.SDK.git ../Rocket.Chat.Kotlin.SDK
- restore_cache:
key: kotlin-sdk-{{ .Revision }}
- restore_cache:
key: jars-{{ checksum "build.gradle" }}-{{ checksum "app/build.gradle" }}-{{ checksum "player/build.gradle" }}
- run:
name: Download Dependencies
command: ./gradlew androidDependencies --quiet --console=plain
- save_cache:
paths:
- ~/.gradle
key: jars-{{ checksum "build.gradle" }}-{{ checksum "app/build.gradle" }}-{{ checksum "player/build.gradle" }}
- run:
name: Build APK
command: |
./gradlew assembleFossRelease --info --console=plain --stacktrace
- store_artifacts: - store_artifacts:
path: app/build/outputs/apk path: app/build/outputs/apk
destination: apks destination: apks
...@@ -112,6 +145,9 @@ workflows: ...@@ -112,6 +145,9 @@ workflows:
- develop - develop
- develop-2.x - develop-2.x
- master - master
- build-apk: - build-play-apk:
requires:
- build-kotlin-sdk
- build-foss-apk:
requires: requires:
- build-kotlin-sdk - build-kotlin-sdk
#!/bin/bash #!/bin/bash
CURRENT_DIR=$(pwd) CURRENT_DIR=$(pwd)
# The SDK dir should be 2 directories up in the tree, so we use dirname 2 times
# to get the common parent dir of the SDK and the app
GIT=$(which git) GIT=$(which git)
cd ../..
tmp=$(pwd) if [ "$#" -eq 1 ] && [ ! -z "$1" ]; then
SDK_DIR="$tmp/Rocket.Chat.Kotlin.SDK" # if in an argument is given this is the (relative) path to SDK_DIR
cd "${CURRENT_DIR}" SDK_DIR=$(readlink -f $1)
else
# The SDK dir should be 2 directories up in the tree, so we use dirname 2 times
# to get the common parent dir of the SDK and the app
cd ../..
tmp=$(pwd)
SDK_DIR="$tmp/Rocket.Chat.Kotlin.SDK"
cd "${CURRENT_DIR}"
fi
echo "CURRENT DIR: $CURRENT_DIR" echo "CURRENT DIR: $CURRENT_DIR"
echo "SDK DIR: $SDK_DIR" echo "SDK DIR: $SDK_DIR"
......
def taskRequests = getGradle().getStartParameter().getTaskRequests().toString()
def isPlay = !(taskRequests.contains("Foss") || taskRequests.contains("foss"))
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
apply plugin: 'io.fabric' if (isPlay) { apply plugin: 'io.fabric' }
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions' apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt' apply plugin: 'kotlin-kapt'
...@@ -17,6 +20,10 @@ android { ...@@ -17,6 +20,10 @@ android {
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
multiDexEnabled true multiDexEnabled true
def gitSha = 'git rev-parse --short HEAD'.execute([], project.rootDir).text.trim()
def buildTime = new GregorianCalendar().format("MM-dd-yyyy' 'h:mm:ss a z")
buildConfigField "String", "GIT_SHA", "\"${gitSha}\""
javaCompileOptions { javaCompileOptions {
annotationProcessorOptions { annotationProcessorOptions {
arguments = ["room.schemaLocation": "$projectDir/schemas".toString()] arguments = ["room.schemaLocation": "$projectDir/schemas".toString()]
...@@ -57,6 +64,20 @@ android { ...@@ -57,6 +64,20 @@ android {
} }
} }
flavorDimensions "type"
productFlavors {
// includes proprietary libs
play {
dimension "type"
}
// only foss
foss {
dimension "type"
}
}
packagingOptions { packagingOptions {
exclude 'META-INF/core.kotlin_module' exclude 'META-INF/core.kotlin_module'
exclude 'META-INF/main.kotlin_module' exclude 'META-INF/main.kotlin_module'
...@@ -78,10 +99,8 @@ dependencies { ...@@ -78,10 +99,8 @@ dependencies {
implementation libraries.appCompat implementation libraries.appCompat
implementation libraries.recyclerview implementation libraries.recyclerview
implementation libraries.material
implementation libraries.constraintlayout implementation libraries.constraintlayout
implementation libraries.cardview implementation libraries.cardview
implementation libraries.flexbox
implementation libraries.browser implementation libraries.browser
implementation libraries.androidKtx implementation libraries.androidKtx
...@@ -91,8 +110,8 @@ dependencies { ...@@ -91,8 +110,8 @@ dependencies {
kapt libraries.daggerProcessor kapt libraries.daggerProcessor
kapt libraries.daggerAndroidApt kapt libraries.daggerAndroidApt
implementation libraries.fcm implementation libraries.flexbox
implementation libraries.playServicesAuth implementation libraries.material
implementation libraries.room implementation libraries.room
kapt libraries.roomProcessor kapt libraries.roomProcessor
...@@ -126,9 +145,12 @@ dependencies { ...@@ -126,9 +145,12 @@ dependencies {
implementation "com.github.luciofm:livedata-ktx:b1e8bbc25a" implementation "com.github.luciofm:livedata-ktx:b1e8bbc25a"
implementation('com.crashlytics.sdk.android:crashlytics:2.9.4@aar') { // Proprietary libraries
transitive = true 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 }
testImplementation libraries.junit testImplementation libraries.junit
testImplementation libraries.truth testImplementation libraries.truth
...@@ -148,13 +170,16 @@ androidExtensions { ...@@ -148,13 +170,16 @@ androidExtensions {
// FIXME - build and install the sdk into the app/libs directory // FIXME - build and install the sdk into the app/libs directory
// We were having some issues with the kapt generated files from the sdk when importing as a module // We were having some issues with the kapt generated files from the sdk when importing as a module
def sdk_location=project.properties['sdk_location'] ?: ""
task compileSdk(type:Exec) { task compileSdk(type:Exec) {
if (System.getProperty('os.name').toLowerCase(Locale.ROOT).contains('windows')) { if (System.getProperty('os.name').toLowerCase(Locale.ROOT).contains('windows')) {
commandLine 'cmd', '/c', 'build-sdk.sh' commandLine 'cmd', '/c', 'build-sdk.sh', sdk_location
} else { } else {
commandLine './build-sdk.sh' commandLine './build-sdk.sh', sdk_location
} }
} }
preBuild.dependsOn compileSdk preBuild.dependsOn compileSdk
if (isPlay) {
apply plugin: 'com.google.gms.google-services' apply plugin: 'com.google.gms.google-services'
}
...@@ -24,35 +24,44 @@ class ChatRoomFragmentTest { ...@@ -24,35 +24,44 @@ class ChatRoomFragmentTest {
@Before @Before
fun stubAllExternalIntents() { fun stubAllExternalIntents() {
val activityIntent = InstrumentationRegistry.getTargetContext().chatRoomIntent("id", "name", "type", false, 0L) val activityIntent = InstrumentationRegistry.getTargetContext()
.chatRoomIntent("id", "name", "type", false, 0L)
activityRule.launchActivity(activityIntent) activityRule.launchActivity(activityIntent)
intending(not(isInternal())).respondWith(ActivityResult(Activity.RESULT_OK, null)) intending(not(isInternal())).respondWith(ActivityResult(Activity.RESULT_OK, null))
} }
@Test @Test
fun showFileSelection_nonNullFiltersAreApplied() { fun showFileSelection_nonNullFiltersAreApplied() {
val fragment = activityRule.activity.supportFragmentManager.findFragmentByTag(ChatRoomActivity.TAG_CHAT_ROOM_FRAGMENT) as ChatRoomFragment val fragment =
activityRule.activity.supportFragmentManager.findFragmentByTag("ChatRoomFragment") as ChatRoomFragment
val filters = arrayOf("image/*") val filters = arrayOf("image/*")
fragment.showFileSelection(filters) fragment.showFileSelection(filters)
intended(allOf( intended(
allOf(
hasAction(Intent.ACTION_GET_CONTENT), hasAction(Intent.ACTION_GET_CONTENT),
hasType("*/*"), hasType("*/*"),
hasCategories(setOf(Intent.CATEGORY_OPENABLE)), hasCategories(setOf(Intent.CATEGORY_OPENABLE)),
hasExtra(Intent.EXTRA_MIME_TYPES, filters))) hasExtra(Intent.EXTRA_MIME_TYPES, filters)
)
)
} }
@Test @Test
fun showFileSelection_nullFiltersAreNotApplied() { fun showFileSelection_nullFiltersAreNotApplied() {
val fragment = activityRule.activity.supportFragmentManager.findFragmentByTag(ChatRoomActivity.TAG_CHAT_ROOM_FRAGMENT) as ChatRoomFragment val fragment =
activityRule.activity.supportFragmentManager.findFragmentByTag("ChatRoomFragment") as ChatRoomFragment
fragment.showFileSelection(null) fragment.showFileSelection(null)
intended(allOf( intended(
allOf(
hasAction(Intent.ACTION_GET_CONTENT), hasAction(Intent.ACTION_GET_CONTENT),
hasType("*/*"), hasType("*/*"),
hasCategories(setOf(Intent.CATEGORY_OPENABLE)), hasCategories(setOf(Intent.CATEGORY_OPENABLE)),
not(hasExtraWithKey(Intent.EXTRA_MIME_TYPES)))) not(hasExtraWithKey(Intent.EXTRA_MIME_TYPES))
)
)
} }
} }
\ No newline at end of file
package chat.rocket.android.dagger.module
import chat.rocket.android.chatroom.di.MessageServiceProvider
import chat.rocket.android.chatroom.service.MessageService
import dagger.Module
import dagger.android.ContributesAndroidInjector
@Module abstract class ServiceBuilder {
@ContributesAndroidInjector(modules = [MessageServiceProvider::class])
abstract fun bindMessageService(): MessageService
}
package chat.rocket.android.helper
import android.app.Activity
import android.content.Intent
import androidx.fragment.app.FragmentActivity
fun FragmentActivity.saveCredentials(id: String, password: String) {
}
fun Activity.requestStoredCredentials(): Pair<String, String>? = null
fun getCredentials(data: Intent): Pair<String, String>? = null
fun hasCredentialsSupport() = false
\ No newline at end of file
package chat.rocket.android.helper
import timber.log.Timber
import android.util.Log
class CrashlyticsTree : Timber.Tree() {
override fun log(priority: Int, tag: String?, message: String, throwable: Throwable?) {
Log.println(priority, tag, message)
if (throwable != null) {
Log.e(tag,throwable.toString())
}
}
}
package chat.rocket.android.push
class FirebaseTokenService {
}
\ No newline at end of file
package chat.rocket.android.util
import android.content.Context
fun setupFabric(context: Context) {
//Do absolutely nothing
}
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
...@@ -95,23 +95,6 @@ ...@@ -95,23 +95,6 @@
</intent-filter> </intent-filter>
</receiver> </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"
android:exported="true">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
<service <service
android:name=".chatroom.service.MessageService" android:name=".chatroom.service.MessageService"
android:exported="true" android:exported="true"
......
...@@ -8,16 +8,21 @@ import androidx.fragment.app.Fragment ...@@ -8,16 +8,21 @@ import androidx.fragment.app.Fragment
import chat.rocket.android.BuildConfig import chat.rocket.android.BuildConfig
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.main.ui.MainActivity 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 kotlinx.android.synthetic.main.app_bar.* import kotlinx.android.synthetic.main.app_bar.*
import kotlinx.android.synthetic.main.fragment_about.* import kotlinx.android.synthetic.main.fragment_about.*
import javax.inject.Inject
class AboutFragment : Fragment() { internal const val TAG_ABOUT_FRAGMENT = "AboutFragment"
companion object { class AboutFragment : Fragment() {
fun newInstance() = AboutFragment() @Inject
} lateinit var analyticsTrackingInteractor: AnalyticsTrackingInteractor
override fun onCreateView(inflater: LayoutInflater, override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?
): View? = inflater.inflate(R.layout.fragment_about, container, false) ): View? = inflater.inflate(R.layout.fragment_about, container, false)
...@@ -26,24 +31,27 @@ class AboutFragment : Fragment() { ...@@ -26,24 +31,27 @@ class AboutFragment : Fragment() {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
setupToolbar() setupToolbar()
setupViews() setupViews()
if (analyticsTrackingInteractor.get()) {
AnalyticsManager.logScreenView(ScreenViewEvent.About)
}
} }
private fun setupViews() { private fun setupViews() {
text_version_name.text = getString(R.string.msg_version, BuildConfig.VERSION_NAME) text_version_name.text = BuildConfig.VERSION_NAME
text_build_number.text = getString(R.string.msg_build, BuildConfig.VERSION_CODE) text_build_number.text = getString(R.string.msg_build, BuildConfig.VERSION_CODE,
BuildConfig.GIT_SHA, BuildConfig.FLAVOR)
} }
private fun setupToolbar() { private fun setupToolbar() {
val toolbar = (activity as MainActivity).toolbar with((activity as MainActivity).toolbar) {
toolbar.title = getString(R.string.title_about) title = getString(R.string.title_about)
toolbar.setNavigationIcon(R.drawable.ic_arrow_back_white_24dp) setNavigationIcon(R.drawable.ic_arrow_back_white_24dp)
toolbar.setNavigationOnClickListener { setNavigationOnClickListener { activity?.onBackPressed() }
this.activity?.onBackPressed()
} }
} }
override fun onStop() { companion object {
super.onStop() fun newInstance() = AboutFragment()
(activity as MainActivity).toolbar.setNavigationIcon(R.drawable.ic_menu_white_24dp)
} }
} }
...@@ -22,6 +22,9 @@ class AppLifecycleObserver @Inject constructor( ...@@ -22,6 +22,9 @@ class AppLifecycleObserver @Inject constructor(
@OnLifecycleEvent(Lifecycle.Event.ON_START) @OnLifecycleEvent(Lifecycle.Event.ON_START)
fun onEnterForeground() { fun onEnterForeground() {
changeTemporaryStatus(UserStatus.Online()) changeTemporaryStatus(UserStatus.Online())
serverInteractor.get()?.let { currentServer ->
factory.create(currentServer).resetReconnectionTimer()
}
} }
@OnLifecycleEvent(Lifecycle.Event.ON_STOP) @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
......
...@@ -13,15 +13,13 @@ import chat.rocket.android.dagger.DaggerAppComponent ...@@ -13,15 +13,13 @@ import chat.rocket.android.dagger.DaggerAppComponent
import chat.rocket.android.dagger.qualifier.ForMessages import chat.rocket.android.dagger.qualifier.ForMessages
import chat.rocket.android.helper.CrashlyticsTree import chat.rocket.android.helper.CrashlyticsTree
import chat.rocket.android.infrastructure.LocalRepository import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.infrastructure.installCrashlyticsWrapper
import chat.rocket.android.server.domain.AccountsRepository import chat.rocket.android.server.domain.AccountsRepository
import chat.rocket.android.server.domain.GetCurrentServerInteractor import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.domain.GetSettingsInteractor import chat.rocket.android.server.domain.GetSettingsInteractor
import chat.rocket.android.server.domain.SITE_URL import chat.rocket.android.server.domain.SITE_URL
import chat.rocket.android.server.domain.TokenRepository import chat.rocket.android.server.domain.TokenRepository
import chat.rocket.android.emoji.EmojiRepository import chat.rocket.android.emoji.EmojiRepository
import com.crashlytics.android.Crashlytics import chat.rocket.android.util.setupFabric
import com.crashlytics.android.core.CrashlyticsCore
import com.facebook.drawee.backends.pipeline.DraweeConfig import com.facebook.drawee.backends.pipeline.DraweeConfig
import com.facebook.drawee.backends.pipeline.Fresco import com.facebook.drawee.backends.pipeline.Fresco
import com.facebook.imagepipeline.core.ImagePipelineConfig import com.facebook.imagepipeline.core.ImagePipelineConfig
...@@ -31,7 +29,6 @@ import dagger.android.DispatchingAndroidInjector ...@@ -31,7 +29,6 @@ import dagger.android.DispatchingAndroidInjector
import dagger.android.HasActivityInjector import dagger.android.HasActivityInjector
import dagger.android.HasBroadcastReceiverInjector import dagger.android.HasBroadcastReceiverInjector
import dagger.android.HasServiceInjector import dagger.android.HasServiceInjector
import io.fabric.sdk.android.Fabric
import timber.log.Timber import timber.log.Timber
import java.lang.ref.WeakReference import java.lang.ref.WeakReference
import javax.inject.Inject import javax.inject.Inject
...@@ -89,7 +86,7 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje ...@@ -89,7 +86,7 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje
AndroidThreeTen.init(this) AndroidThreeTen.init(this)
EmojiRepository.load(this) EmojiRepository.load(this)
setupCrashlytics() setupFabric(this)
setupFresco() setupFresco()
setupTimber() setupTimber()
...@@ -125,15 +122,6 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje ...@@ -125,15 +122,6 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje
} }
} }
private fun setupCrashlytics() {
val core = CrashlyticsCore.Builder().disabled(BuildConfig.DEBUG).build()
Fabric.with(this, Crashlytics.Builder().core(core).build())
installCrashlyticsWrapper(this@RocketChatApplication,
getCurrentServerInteractor, settingsInteractor,
accountRepository, localRepository)
}
private fun setupFresco() { private fun setupFresco() {
Fresco.initialize(this, imagePipelineConfig, draweeConfig) Fresco.initialize(this, imagePipelineConfig, draweeConfig)
} }
......
...@@ -5,30 +5,8 @@ import chat.rocket.android.authentication.presentation.AuthenticationNavigator ...@@ -5,30 +5,8 @@ import chat.rocket.android.authentication.presentation.AuthenticationNavigator
import chat.rocket.android.core.lifecycle.CancelStrategy import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.helper.OauthHelper import chat.rocket.android.helper.OauthHelper
import chat.rocket.android.infrastructure.LocalRepository import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.server.domain.GetAccountsInteractor 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.isWordpressAuthenticationEnabled
import chat.rocket.android.server.domain.isPasswordResetEnabled
import chat.rocket.android.server.domain.isRegistrationEnabledForNewUsers
import chat.rocket.android.server.domain.wordpressUrl
import chat.rocket.android.server.domain.model.Account import chat.rocket.android.server.domain.model.Account
import chat.rocket.android.server.domain.wideTile
import chat.rocket.android.server.infraestructure.RocketChatClientFactory import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.extension.launchUI import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.extensions.avatarUrl import chat.rocket.android.util.extensions.avatarUrl
...@@ -40,6 +18,8 @@ import chat.rocket.android.util.extensions.parseColor ...@@ -40,6 +18,8 @@ import chat.rocket.android.util.extensions.parseColor
import chat.rocket.android.util.extensions.registerPushToken import chat.rocket.android.util.extensions.registerPushToken
import chat.rocket.android.util.extensions.samlUrl import chat.rocket.android.util.extensions.samlUrl
import chat.rocket.android.util.extensions.serverLogoUrl 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.android.util.retryIO
import chat.rocket.common.RocketChatAuthException import chat.rocket.common.RocketChatAuthException
import chat.rocket.common.RocketChatException import chat.rocket.common.RocketChatException
...@@ -82,6 +62,7 @@ class LoginPresenter @Inject constructor( ...@@ -82,6 +62,7 @@ class LoginPresenter @Inject constructor(
private val localRepository: LocalRepository, private val localRepository: LocalRepository,
private val getAccountsInteractor: GetAccountsInteractor, private val getAccountsInteractor: GetAccountsInteractor,
private val settingsInteractor: GetSettingsInteractor, private val settingsInteractor: GetSettingsInteractor,
private val analyticsTrackingInteractor: AnalyticsTrackingInteractor,
serverInteractor: GetConnectingServerInteractor, serverInteractor: GetConnectingServerInteractor,
private val saveCurrentServer: SaveCurrentServerInteractor, private val saveCurrentServer: SaveCurrentServerInteractor,
private val saveAccountInteractor: SaveAccountInteractor, private val saveAccountInteractor: SaveAccountInteractor,
...@@ -97,6 +78,7 @@ class LoginPresenter @Inject constructor( ...@@ -97,6 +78,7 @@ class LoginPresenter @Inject constructor(
private lateinit var credentialSecret: String private lateinit var credentialSecret: String
private lateinit var deepLinkUserId: String private lateinit var deepLinkUserId: String
private lateinit var deepLinkToken: String private lateinit var deepLinkToken: String
private lateinit var loginMethod: AuthenticationEvent
fun setupView() { fun setupView() {
setupConnectionInfo(currentServer) setupConnectionInfo(currentServer)
...@@ -117,6 +99,7 @@ class LoginPresenter @Inject constructor( ...@@ -117,6 +99,7 @@ class LoginPresenter @Inject constructor(
else -> { else -> {
this.usernameOrEmail = usernameOrEmail this.usernameOrEmail = usernameOrEmail
this.password = password this.password = password
loginMethod = AuthenticationEvent.AuthenticationWithUserAndPassword
doAuthentication(TYPE_LOGIN_USER_EMAIL) doAuthentication(TYPE_LOGIN_USER_EMAIL)
} }
} }
...@@ -124,17 +107,20 @@ class LoginPresenter @Inject constructor( ...@@ -124,17 +107,20 @@ class LoginPresenter @Inject constructor(
fun authenticateWithCas(casToken: String) { fun authenticateWithCas(casToken: String) {
credentialToken = casToken credentialToken = casToken
loginMethod = AuthenticationEvent.AuthenticationWithCas
doAuthentication(TYPE_LOGIN_CAS) doAuthentication(TYPE_LOGIN_CAS)
} }
fun authenticateWithSaml(samlToken: String) { fun authenticateWithSaml(samlToken: String) {
credentialToken = samlToken credentialToken = samlToken
loginMethod = AuthenticationEvent.AuthenticationWithSaml
doAuthentication(TYPE_LOGIN_SAML) doAuthentication(TYPE_LOGIN_SAML)
} }
fun authenticateWithOauth(oauthToken: String, oauthSecret: String) { fun authenticateWithOauth(oauthToken: String, oauthSecret: String) {
credentialToken = oauthToken credentialToken = oauthToken
credentialSecret = oauthSecret credentialSecret = oauthSecret
loginMethod = AuthenticationEvent.AuthenticationWithOauth
doAuthentication(TYPE_LOGIN_OAUTH) doAuthentication(TYPE_LOGIN_OAUTH)
} }
...@@ -145,6 +131,7 @@ class LoginPresenter @Inject constructor( ...@@ -145,6 +131,7 @@ class LoginPresenter @Inject constructor(
deepLinkUserId = deepLinkInfo.userId deepLinkUserId = deepLinkInfo.userId
deepLinkToken = deepLinkInfo.token deepLinkToken = deepLinkInfo.token
tokenRepository.save(serverUrl, Token(deepLinkUserId, deepLinkToken)) tokenRepository.save(serverUrl, Token(deepLinkUserId, deepLinkToken))
loginMethod = AuthenticationEvent.AuthenticationWithDeeplink
doAuthentication(TYPE_LOGIN_DEEP_LINK) doAuthentication(TYPE_LOGIN_DEEP_LINK)
} else { } else {
// If we don't have the login credentials, just go through normal setup and user input. // If we don't have the login credentials, just go through normal setup and user input.
...@@ -304,7 +291,7 @@ class LoginPresenter @Inject constructor( ...@@ -304,7 +291,7 @@ class LoginPresenter @Inject constructor(
if (myself.id == deepLinkUserId) { if (myself.id == deepLinkUserId) {
Token(deepLinkUserId, deepLinkToken) Token(deepLinkUserId, deepLinkToken)
} else { } else {
throw RocketChatAuthException("Invalid Authentication Deep Link Credentials...") throw RocketChatAuthException("Invalid AuthenticationEvent Deep Link Credentials...")
} }
} }
else -> { else -> {
...@@ -323,11 +310,14 @@ class LoginPresenter @Inject constructor( ...@@ -323,11 +310,14 @@ class LoginPresenter @Inject constructor(
username = myself.username, username = myself.username,
utcOffset = myself.utcOffset utcOffset = myself.utcOffset
) )
localRepository.saveCurrentUser(url = currentServer, user = user) localRepository.saveCurrentUser(currentServer, user)
saveCurrentServer.save(currentServer) saveCurrentServer.save(currentServer)
saveAccount(myself.username!!) saveAccount(myself.username!!)
saveToken(token) saveToken(token)
registerPushToken() registerPushToken()
if (analyticsTrackingInteractor.get()) {
AnalyticsManager.logLogin(loginMethod, true)
}
if (loginType == TYPE_LOGIN_USER_EMAIL) { if (loginType == TYPE_LOGIN_USER_EMAIL) {
view.saveSmartLockCredentials(usernameOrEmail, password) view.saveSmartLockCredentials(usernameOrEmail, password)
} }
...@@ -341,6 +331,9 @@ class LoginPresenter @Inject constructor( ...@@ -341,6 +331,9 @@ class LoginPresenter @Inject constructor(
navigator.toTwoFA(usernameOrEmail, password) navigator.toTwoFA(usernameOrEmail, password)
} }
else -> { else -> {
if (analyticsTrackingInteractor.get()) {
AnalyticsManager.logLogin(loginMethod, false)
}
exception.message?.let { exception.message?.let {
view.showMessage(it) view.showMessage(it)
}.ifNull { }.ifNull {
......
...@@ -2,7 +2,6 @@ package chat.rocket.android.authentication.login.presentation ...@@ -2,7 +2,6 @@ package chat.rocket.android.authentication.login.presentation
import chat.rocket.android.core.behaviours.LoadingView import chat.rocket.android.core.behaviours.LoadingView
import chat.rocket.android.core.behaviours.MessageView import chat.rocket.android.core.behaviours.MessageView
import com.google.android.gms.auth.api.credentials.Credential
interface LoginView : LoadingView, MessageView { interface LoginView : LoadingView, MessageView {
......
...@@ -11,26 +11,36 @@ import android.text.style.ClickableSpan ...@@ -11,26 +11,36 @@ import android.text.style.ClickableSpan
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.ViewTreeObserver
import android.widget.Button import android.widget.Button
import android.widget.ImageButton
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.ScrollView
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.core.view.postDelayed
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.authentication.domain.model.LoginDeepLinkInfo import chat.rocket.android.authentication.domain.model.LoginDeepLinkInfo
import chat.rocket.android.authentication.login.presentation.LoginPresenter import chat.rocket.android.authentication.login.presentation.LoginPresenter
import chat.rocket.android.authentication.login.presentation.LoginView import chat.rocket.android.authentication.login.presentation.LoginView
import chat.rocket.android.helper.* import chat.rocket.android.helper.*
import chat.rocket.android.server.domain.AnalyticsTrackingInteractor
import chat.rocket.android.util.extensions.* 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.INTENT_SSO_TOKEN
import chat.rocket.android.webview.sso.ui.ssoWebViewIntent import chat.rocket.android.webview.sso.ui.ssoWebViewIntent
import chat.rocket.android.webview.oauth.ui.INTENT_OAUTH_CREDENTIAL_SECRET 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.INTENT_OAUTH_CREDENTIAL_TOKEN
import chat.rocket.android.webview.oauth.ui.oauthWebViewIntent import chat.rocket.android.webview.oauth.ui.oauthWebViewIntent
import chat.rocket.common.util.ifNull import chat.rocket.common.util.ifNull
import com.google.android.gms.auth.api.credentials.*
import dagger.android.support.AndroidSupportInjection import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_authentication_log_in.* import kotlinx.android.synthetic.main.fragment_authentication_log_in.*
import javax.inject.Inject import javax.inject.Inject
internal const val TAG_LOGIN_FRAGMENT = "LoginFragment"
internal const val REQUEST_CODE_FOR_SIGN_IN_REQUIRED = 1
internal const val REQUEST_CODE_FOR_MULTIPLE_ACCOUNTS_RESOLUTION = 2
internal const val REQUEST_CODE_FOR_SAVE_RESOLUTION = 3
internal const val REQUEST_CODE_FOR_CAS = 4 internal const val REQUEST_CODE_FOR_CAS = 4
internal const val REQUEST_CODE_FOR_SAML = 5 internal const val REQUEST_CODE_FOR_SAML = 5
internal const val REQUEST_CODE_FOR_OAUTH = 6 internal const val REQUEST_CODE_FOR_OAUTH = 6
...@@ -38,8 +48,14 @@ internal const val REQUEST_CODE_FOR_OAUTH = 6 ...@@ -38,8 +48,14 @@ internal const val REQUEST_CODE_FOR_OAUTH = 6
class LoginFragment : Fragment(), LoginView { class LoginFragment : Fragment(), LoginView {
@Inject @Inject
lateinit var presenter: LoginPresenter lateinit var presenter: LoginPresenter
@Inject
lateinit var analyticsTrackingInteractor: AnalyticsTrackingInteractor
private var isOauthViewEnable = false
private val layoutListener = ViewTreeObserver.OnGlobalLayoutListener {
areLoginOptionsNeeded()
}
private var isGlobalLayoutListenerSetUp = false
private var deepLinkInfo: LoginDeepLinkInfo? = null private var deepLinkInfo: LoginDeepLinkInfo? = null
private val credentialsClient by lazy { Credentials.getClient(requireActivity()) }
companion object { companion object {
private const val DEEP_LINK_INFO = "DeepLinkInfo" private const val DEEP_LINK_INFO = "DeepLinkInfo"
...@@ -76,6 +92,22 @@ class LoginFragment : Fragment(), LoginView { ...@@ -76,6 +92,22 @@ class LoginFragment : Fragment(), LoginView {
}.ifNull { }.ifNull {
presenter.setupView() presenter.setupView()
} }
if (!hasCredentialsSupport()) {
image_key.isVisible = false
}
if (analyticsTrackingInteractor.get()) {
AnalyticsManager.logScreenView(ScreenViewEvent.Login)
}
}
override fun onDestroyView() {
super.onDestroyView()
if (isGlobalLayoutListenerSetUp) {
scroll_view.viewTreeObserver.removeOnGlobalLayoutListener(layoutListener)
isGlobalLayoutListenerSetUp = false
}
} }
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
...@@ -83,13 +115,15 @@ class LoginFragment : Fragment(), LoginView { ...@@ -83,13 +115,15 @@ class LoginFragment : Fragment(), LoginView {
if (data != null) { if (data != null) {
when (requestCode) { when (requestCode) {
REQUEST_CODE_FOR_MULTIPLE_ACCOUNTS_RESOLUTION -> { REQUEST_CODE_FOR_MULTIPLE_ACCOUNTS_RESOLUTION -> {
onCredentialRetrieved(data.getParcelableExtra(Credential.EXTRA_KEY)) getCredentials(data)?.let {
onCredentialRetrieved(it.first, it.second)
}
} }
REQUEST_CODE_FOR_SIGN_IN_REQUIRED -> { REQUEST_CODE_FOR_SIGN_IN_REQUIRED -> {
//use the hints to autofill sign in forms to reduce the info to be filled. getCredentials(data)?.let { credential ->
val credential: Credential = data.getParcelableExtra(Credential.EXTRA_KEY) text_username_or_email.setText(credential.first)
text_username_or_email.setText(credential.id) text_password.setText(credential.second)
text_password.setText(credential.password) }
} }
REQUEST_CODE_FOR_SAVE_RESOLUTION -> { REQUEST_CODE_FOR_SAVE_RESOLUTION -> {
showMessage(getString(R.string.message_credentials_saved_successfully)) showMessage(getString(R.string.message_credentials_saved_successfully))
...@@ -137,19 +171,19 @@ class LoginFragment : Fragment(), LoginView { ...@@ -137,19 +171,19 @@ class LoginFragment : Fragment(), LoginView {
private fun requestStoredCredentials() { private fun requestStoredCredentials() {
activity?.let { activity?.let {
SmartLockHelper.requestStoredCredentials(credentialsClient, it)?.let { it.requestStoredCredentials()?.let { credentials ->
onCredentialRetrieved(it) onCredentialRetrieved(credentials.first, credentials.second)
} }
} }
} }
private fun onCredentialRetrieved(credential: Credential) { private fun onCredentialRetrieved(id: String, password: String) {
presenter.authenticateWithUserAndPassword(credential.id, credential.password.toString()) presenter.authenticateWithUserAndPassword(id, password)
} }
override fun saveSmartLockCredentials(id: String, password: String) { override fun saveSmartLockCredentials(id: String, password: String) {
activity?.let { activity?.let {
SmartLockHelper.save(credentialsClient, it, id, password) it.saveCredentials(id, password)
} }
} }
...@@ -247,6 +281,27 @@ class LoginFragment : Fragment(), LoginView { ...@@ -247,6 +281,27 @@ class LoginFragment : Fragment(), LoginView {
} }
} }
override fun showSignUpView() {
ui {
text_new_to_rocket_chat.isVisible = true
}
}
override fun setupSignUpView() {
ui {
val signUp = getString(R.string.title_sign_up)
val newToRocketChat = String.format(getString(R.string.msg_new_user), signUp)
text_new_to_rocket_chat.text = newToRocketChat
val signUpListener = object : ClickableSpan() {
override fun onClick(view: View) = presenter.signup()
}
TextHelper.addLink(text_new_to_rocket_chat, arrayOf(signUp), arrayOf(signUpListener))
}
}
override fun showForgotPasswordView() { override fun showForgotPasswordView() {
ui { ui {
text_forgot_your_password.isVisible = true text_forgot_your_password.isVisible = true
...@@ -255,10 +310,170 @@ class LoginFragment : Fragment(), LoginView { ...@@ -255,10 +310,170 @@ class LoginFragment : Fragment(), LoginView {
override fun setupForgotPasswordView() { override fun setupForgotPasswordView() {
ui { ui {
val forgotPassword = String.format(getString(R.string.msg_forgot_password)) val reset = getString(R.string.msg_reset)
val forgotPassword = String.format(getString(R.string.msg_forgot_password), reset)
text_forgot_your_password.text = forgotPassword text_forgot_your_password.text = forgotPassword
text_forgot_your_password.setOnClickListener {
presenter.forgotPassword() val resetListener = object : ClickableSpan() {
override fun onClick(view: View) = presenter.forgotPassword()
}
TextHelper.addLink(text_forgot_your_password, arrayOf(reset), arrayOf(resetListener))
}
}
override fun hideSignUpView() {
ui {
text_new_to_rocket_chat.isVisible = false
}
}
override fun enableOauthView() {
ui {
isOauthViewEnable = true
showThreeSocialAccountsMethods()
social_accounts_container.isVisible = true
}
}
override fun disableOauthView() {
ui {
isOauthViewEnable = false
social_accounts_container.isVisible = false
}
}
override fun showLoginButton() {
ui {
button_log_in.isVisible = true
}
}
override fun hideLoginButton() {
ui {
button_log_in.isVisible = false
}
}
override fun enableLoginByFacebook() {
ui {
button_facebook.isClickable = true
}
}
override fun setupFacebookButtonListener(facebookOauthUrl: String, state: String) {
ui { activity ->
button_facebook.setOnClickListener {
startActivityForResult(
activity.oauthWebViewIntent(facebookOauthUrl, state),
REQUEST_CODE_FOR_OAUTH
)
activity.overridePendingTransition(R.anim.slide_up, R.anim.hold)
}
}
}
override fun enableLoginByGithub() {
ui {
button_github.isClickable = true
}
}
override fun setupGithubButtonListener(githubUrl: String, state: String) {
ui { activity ->
button_github.setOnClickListener {
startActivityForResult(
activity.oauthWebViewIntent(githubUrl, state),
REQUEST_CODE_FOR_OAUTH
)
activity.overridePendingTransition(R.anim.slide_up, R.anim.hold)
}
}
}
override fun enableLoginByGoogle() {
ui {
button_google.isClickable = true
}
}
// TODO: Use custom tabs instead of web view.
// See https://github.com/RocketChat/Rocket.Chat.Android/issues/968
override fun setupGoogleButtonListener(googleUrl: String, state: String) {
ui { activity ->
button_google.setOnClickListener {
startActivityForResult(
activity.oauthWebViewIntent(googleUrl, state),
REQUEST_CODE_FOR_OAUTH
)
activity.overridePendingTransition(R.anim.slide_up, R.anim.hold)
}
}
}
override fun enableLoginByLinkedin() {
ui {
button_linkedin.isClickable = true
}
}
override fun setupLinkedinButtonListener(linkedinUrl: String, state: String) {
ui { activity ->
button_linkedin.setOnClickListener {
startActivityForResult(
activity.oauthWebViewIntent(linkedinUrl, state),
REQUEST_CODE_FOR_OAUTH
)
activity.overridePendingTransition(R.anim.slide_up, R.anim.hold)
}
}
}
override fun enableLoginByMeteor() {
ui {
button_meteor.isClickable = true
}
}
override fun enableLoginByTwitter() {
ui {
button_twitter.isClickable = true
}
}
override fun enableLoginByGitlab() {
ui {
button_gitlab.isClickable = true
}
}
override fun setupGitlabButtonListener(gitlabUrl: String, state: String) {
ui { activity ->
button_gitlab.setOnClickListener {
startActivityForResult(
activity.oauthWebViewIntent(gitlabUrl, state),
REQUEST_CODE_FOR_OAUTH
)
activity.overridePendingTransition(R.anim.slide_up, R.anim.hold)
}
}
}
override fun enableLoginByWordpress() {
ui {
button_wordpress.isClickable = true
}
}
override fun setupWordpressButtonListener(wordpressUrl: String, state: String) {
ui { activity ->
button_wordpress.setOnClickListener {
startActivityForResult(
activity.oauthWebViewIntent(wordpressUrl, state),
REQUEST_CODE_FOR_OAUTH
)
activity.overridePendingTransition(R.anim.slide_up, R.anim.hold)
} }
} }
} }
...@@ -272,6 +487,7 @@ class LoginFragment : Fragment(), LoginView { ...@@ -272,6 +487,7 @@ class LoginFragment : Fragment(), LoginView {
) { ) {
ui { activity -> ui { activity ->
val button = getCustomServiceButton(serviceName, serviceNameColor, buttonColor) val button = getCustomServiceButton(serviceName, serviceNameColor, buttonColor)
social_accounts_container.addView(button)
button.setOnClickListener { button.setOnClickListener {
startActivityForResult( startActivityForResult(
...@@ -292,6 +508,7 @@ class LoginFragment : Fragment(), LoginView { ...@@ -292,6 +508,7 @@ class LoginFragment : Fragment(), LoginView {
) { ) {
ui { activity -> ui { activity ->
val button = getCustomServiceButton(serviceName, serviceNameColor, buttonColor) val button = getCustomServiceButton(serviceName, serviceNameColor, buttonColor)
social_accounts_container.addView(button)
button.setOnClickListener { button.setOnClickListener {
startActivityForResult( startActivityForResult(
...@@ -303,6 +520,27 @@ class LoginFragment : Fragment(), LoginView { ...@@ -303,6 +520,27 @@ class LoginFragment : Fragment(), LoginView {
} }
} }
override fun setupFabListener() {
ui {
button_fab.isVisible = true
button_fab.setOnClickListener {
button_fab.hide()
showRemainingSocialAccountsView()
scrollToBottom()
}
}
}
override fun setupGlobalListener() {
// We need to setup the layout to hide and show the oauth interface when the soft keyboard
// is shown (which means that the user has touched the text_username_or_email or
// text_password EditText to fill that respective fields).
if (!isGlobalLayoutListenerSetUp) {
scroll_view.viewTreeObserver.addOnGlobalLayoutListener(layoutListener)
isGlobalLayoutListenerSetUp = true
}
}
override fun alertWrongUsernameOrEmail() { override fun alertWrongUsernameOrEmail() {
ui { ui {
vibrateSmartPhone() vibrateSmartPhone()
...@@ -319,11 +557,84 @@ class LoginFragment : Fragment(), LoginView { ...@@ -319,11 +557,84 @@ class LoginFragment : Fragment(), LoginView {
} }
} }
private fun showRemainingSocialAccountsView() {
social_accounts_container.postDelayed(300) {
ui {
(0..social_accounts_container.childCount)
.mapNotNull { social_accounts_container.getChildAt(it) as? ImageButton }
.filter { it.isClickable }
.forEach { it.isVisible = true }
}
}
}
// Scrolling to the bottom of the screen.
private fun scrollToBottom() {
scroll_view.postDelayed(1250) {
ui {
scroll_view.fullScroll(ScrollView.FOCUS_DOWN)
}
}
}
private fun areLoginOptionsNeeded() {
if (!isEditTextEmpty() || KeyboardHelper.isSoftKeyboardShown(scroll_view.rootView)) {
hideSignUpView()
hideOauthView()
showLoginButton()
} else {
showSignUpView()
showOauthView()
hideLoginButton()
}
}
// Returns true if *all* EditTexts are empty. // Returns true if *all* EditTexts are empty.
private fun isEditTextEmpty(): Boolean { private fun isEditTextEmpty(): Boolean {
return text_username_or_email.textContent.isBlank() && text_password.textContent.isEmpty() return text_username_or_email.textContent.isBlank() && text_password.textContent.isEmpty()
} }
private fun showThreeSocialAccountsMethods() {
(0..social_accounts_container.childCount)
.mapNotNull { social_accounts_container.getChildAt(it) as? ImageButton }
.filter { it.isClickable }
.take(3)
.forEach { it.isVisible = true }
}
private fun showOauthView() {
if (isOauthViewEnable) {
social_accounts_container.isVisible = true
if (enabledSocialAccounts() > 3) {
button_fab.isVisible = true
}
}
}
private fun hideOauthView() {
if (isOauthViewEnable) {
social_accounts_container.isVisible = false
button_fab.isVisible = false
}
}
private fun enabledSocialAccounts(): Int {
return enabledOauthAccountsImageButtons() + enabledServicesAccountsButtons()
}
private fun enabledOauthAccountsImageButtons(): Int {
return (0..social_accounts_container.childCount)
.mapNotNull { social_accounts_container.getChildAt(it) as? ImageButton }
.filter { it.isClickable }
.size
}
private fun enabledServicesAccountsButtons(): Int {
return (0..social_accounts_container.childCount)
.mapNotNull { social_accounts_container.getChildAt(it) as? Button }
.size
}
/** /**
* Gets a stylized custom service button. * Gets a stylized custom service button.
......
package chat.rocket.android.authentication.presentation package chat.rocket.android.authentication.presentation
import android.content.Intent import android.content.Intent
import androidx.appcompat.widget.DialogTitle
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.authentication.domain.model.LoginDeepLinkInfo import chat.rocket.android.authentication.domain.model.LoginDeepLinkInfo
import chat.rocket.android.authentication.login.ui.LoginFragment import chat.rocket.android.authentication.login.ui.LoginFragment
import chat.rocket.android.authentication.loginoptions.ui.LoginOptionsFragment import chat.rocket.android.authentication.login.ui.TAG_LOGIN_FRAGMENT
import chat.rocket.android.authentication.registerusername.ui.RegisterUsernameFragment import chat.rocket.android.authentication.registerusername.ui.RegisterUsernameFragment
import chat.rocket.android.authentication.registerusername.ui.TAG_REGISTER_USERNAME_FRAGMENT
import chat.rocket.android.authentication.resetpassword.ui.ResetPasswordFragment import chat.rocket.android.authentication.resetpassword.ui.ResetPasswordFragment
import chat.rocket.android.authentication.resetpassword.ui.TAG_RESET_PASSWORD_FRAGMENT
import chat.rocket.android.authentication.signup.ui.SignupFragment import chat.rocket.android.authentication.signup.ui.SignupFragment
import chat.rocket.android.authentication.signup.ui.TAG_SIGNUP_FRAGMENT
import chat.rocket.android.authentication.twofactor.ui.TAG_TWO_FA_FRAGMENT
import chat.rocket.android.authentication.twofactor.ui.TwoFAFragment import chat.rocket.android.authentication.twofactor.ui.TwoFAFragment
import chat.rocket.android.authentication.ui.AuthenticationActivity import chat.rocket.android.authentication.ui.AuthenticationActivity
import chat.rocket.android.authentication.ui.newServerIntent import chat.rocket.android.authentication.ui.newServerIntent
...@@ -20,20 +23,14 @@ import chat.rocket.android.webview.ui.webViewIntent ...@@ -20,20 +23,14 @@ import chat.rocket.android.webview.ui.webViewIntent
class AuthenticationNavigator(internal val activity: AuthenticationActivity) { class AuthenticationNavigator(internal val activity: AuthenticationActivity) {
fun toLoginOptions(server: String) {
activity.addFragmentBackStack("LoginOptionFragment", R.id.fragment_container) {
LoginOptionsFragment.newInstance(server)
}
}
fun toLogin() { fun toLogin() {
activity.addFragmentBackStack("LoginFragment", R.id.fragment_container) { activity.addFragmentBackStack(TAG_LOGIN_FRAGMENT, R.id.fragment_container) {
LoginFragment.newInstance() LoginFragment.newInstance()
} }
} }
fun toLogin(deepLinkInfo: LoginDeepLinkInfo) { fun toLogin(deepLinkInfo: LoginDeepLinkInfo) {
activity.addFragmentBackStack("LoginFragment", R.id.fragment_container) { activity.addFragmentBackStack(TAG_LOGIN_FRAGMENT, R.id.fragment_container) {
LoginFragment.newInstance(deepLinkInfo) LoginFragment.newInstance(deepLinkInfo)
} }
} }
...@@ -43,40 +40,34 @@ class AuthenticationNavigator(internal val activity: AuthenticationActivity) { ...@@ -43,40 +40,34 @@ class AuthenticationNavigator(internal val activity: AuthenticationActivity) {
} }
fun toTwoFA(username: String, password: String) { fun toTwoFA(username: String, password: String) {
activity.addFragmentBackStack("TwoFAFragment", R.id.fragment_container) { activity.addFragmentBackStack(TAG_TWO_FA_FRAGMENT, R.id.fragment_container) {
TwoFAFragment.newInstance(username, password) TwoFAFragment.newInstance(username, password)
} }
} }
fun toSignUp() { fun toSignUp() {
activity.addFragmentBackStack("SignupFragment", R.id.fragment_container) { activity.addFragmentBackStack(TAG_SIGNUP_FRAGMENT, R.id.fragment_container) {
SignupFragment.newInstance() SignupFragment.newInstance()
} }
} }
fun toForgotPassword() { fun toForgotPassword() {
activity.addFragmentBackStack("ResetPasswordFragment", R.id.fragment_container) { activity.addFragmentBackStack(TAG_RESET_PASSWORD_FRAGMENT, R.id.fragment_container) {
ResetPasswordFragment.newInstance() ResetPasswordFragment.newInstance()
} }
} }
fun toWebPage(url: String, toolbarTitle: String? = null) { fun toWebPage(url: String) {
activity.startActivity(activity.webViewIntent(url, toolbarTitle)) activity.startActivity(activity.webViewIntent(url))
activity.overridePendingTransition(R.anim.slide_up, R.anim.hold) activity.overridePendingTransition(R.anim.slide_up, R.anim.hold)
} }
fun toRegisterUsername(userId: String, authToken: String) { fun toRegisterUsername(userId: String, authToken: String) {
activity.addFragmentBackStack("RegisterUsernameFragment", R.id.fragment_container) { activity.addFragmentBackStack(TAG_REGISTER_USERNAME_FRAGMENT, R.id.fragment_container) {
RegisterUsernameFragment.newInstance(userId, authToken) RegisterUsernameFragment.newInstance(userId, authToken)
} }
} }
fun toCreateAccount() {
activity.addFragmentBackStack("SignUpFragment", R.id.fragment_container){
SignupFragment.newInstance()
}
}
fun toChatList() { fun toChatList() {
activity.startActivity(Intent(activity, MainActivity::class.java)) activity.startActivity(Intent(activity, MainActivity::class.java))
activity.finish() activity.finish()
......
...@@ -3,13 +3,24 @@ package chat.rocket.android.authentication.registerusername.presentation ...@@ -3,13 +3,24 @@ package chat.rocket.android.authentication.registerusername.presentation
import chat.rocket.android.authentication.presentation.AuthenticationNavigator import chat.rocket.android.authentication.presentation.AuthenticationNavigator
import chat.rocket.android.core.lifecycle.CancelStrategy import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.infrastructure.LocalRepository import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.server.domain.* 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
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.model.Account
import chat.rocket.android.server.domain.wideTile
import chat.rocket.android.server.infraestructure.RocketChatClientFactory import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.extensions.avatarUrl
import chat.rocket.android.util.extension.launchUI 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.registerPushToken
import chat.rocket.android.util.extensions.serverLogoUrl 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.android.util.retryIO
import chat.rocket.common.RocketChatException import chat.rocket.common.RocketChatException
import chat.rocket.common.model.Token import chat.rocket.common.model.Token
...@@ -27,6 +38,7 @@ class RegisterUsernamePresenter @Inject constructor( ...@@ -27,6 +38,7 @@ class RegisterUsernamePresenter @Inject constructor(
private val factory: RocketChatClientFactory, private val factory: RocketChatClientFactory,
private val saveAccountInteractor: SaveAccountInteractor, private val saveAccountInteractor: SaveAccountInteractor,
private val getAccountsInteractor: GetAccountsInteractor, private val getAccountsInteractor: GetAccountsInteractor,
private val analyticsTrackingInteractor: AnalyticsTrackingInteractor,
serverInteractor: GetConnectingServerInteractor, serverInteractor: GetConnectingServerInteractor,
private val saveCurrentServer: SaveCurrentServerInteractor, private val saveCurrentServer: SaveCurrentServerInteractor,
settingsInteractor: GetSettingsInteractor settingsInteractor: GetSettingsInteractor
...@@ -51,9 +63,15 @@ class RegisterUsernamePresenter @Inject constructor( ...@@ -51,9 +63,15 @@ class RegisterUsernamePresenter @Inject constructor(
saveCurrentServer.save(currentServer) saveCurrentServer.save(currentServer)
tokenRepository.save(currentServer, Token(userId, authToken)) tokenRepository.save(currentServer, Token(userId, authToken))
registerPushToken() registerPushToken()
if (analyticsTrackingInteractor.get()) {
AnalyticsManager.logSignUp(AuthenticationEvent.AuthenticationWithOauth, true)
}
navigator.toChatList() navigator.toChatList()
} }
} catch (exception: RocketChatException) { } catch (exception: RocketChatException) {
if (analyticsTrackingInteractor.get()) {
AnalyticsManager.logSignUp(AuthenticationEvent.AuthenticationWithOauth, false)
}
exception.message?.let { exception.message?.let {
view.showMessage(it) view.showMessage(it)
}.ifNull { }.ifNull {
......
...@@ -10,14 +10,21 @@ import android.view.ViewGroup ...@@ -10,14 +10,21 @@ import android.view.ViewGroup
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.authentication.registerusername.presentation.RegisterUsernamePresenter import chat.rocket.android.authentication.registerusername.presentation.RegisterUsernamePresenter
import chat.rocket.android.authentication.registerusername.presentation.RegisterUsernameView 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.extensions.*
import chat.rocket.android.util.helper.analytics.AnalyticsManager
import chat.rocket.android.util.helper.analytics.event.ScreenViewEvent
import dagger.android.support.AndroidSupportInjection import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_authentication_register_username.* import kotlinx.android.synthetic.main.fragment_authentication_register_username.*
import javax.inject.Inject import javax.inject.Inject
internal const val TAG_REGISTER_USERNAME_FRAGMENT = "RegisterUsernameFragment"
class RegisterUsernameFragment : Fragment(), RegisterUsernameView { class RegisterUsernameFragment : Fragment(), RegisterUsernameView {
@Inject @Inject
lateinit var presenter: RegisterUsernamePresenter lateinit var presenter: RegisterUsernamePresenter
@Inject
lateinit var analyticsTrackingInteractor: AnalyticsTrackingInteractor
private lateinit var userId: String private lateinit var userId: String
private lateinit var authToken: String private lateinit var authToken: String
...@@ -61,6 +68,10 @@ class RegisterUsernameFragment : Fragment(), RegisterUsernameView { ...@@ -61,6 +68,10 @@ class RegisterUsernameFragment : Fragment(), RegisterUsernameView {
} }
setupOnClickListener() setupOnClickListener()
if (analyticsTrackingInteractor.get()) {
AnalyticsManager.logScreenView(ScreenViewEvent.RegisterUsername)
}
} }
override fun alertBlankUsername() { override fun alertBlankUsername() {
......
...@@ -11,18 +11,21 @@ import android.widget.Toast ...@@ -11,18 +11,21 @@ import android.widget.Toast
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.authentication.resetpassword.presentation.ResetPasswordPresenter import chat.rocket.android.authentication.resetpassword.presentation.ResetPasswordPresenter
import chat.rocket.android.authentication.resetpassword.presentation.ResetPasswordView 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.extensions.*
import chat.rocket.android.util.helper.analytics.AnalyticsManager
import chat.rocket.android.util.helper.analytics.event.ScreenViewEvent
import dagger.android.support.AndroidSupportInjection import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_authentication_reset_password.* import kotlinx.android.synthetic.main.fragment_authentication_reset_password.*
import javax.inject.Inject import javax.inject.Inject
internal const val TAG_RESET_PASSWORD_FRAGMENT = "ResetPasswordFragment"
class ResetPasswordFragment : Fragment(), ResetPasswordView { class ResetPasswordFragment : Fragment(), ResetPasswordView {
@Inject @Inject
lateinit var presenter: ResetPasswordPresenter lateinit var presenter: ResetPasswordPresenter
@Inject
companion object { lateinit var analyticsTrackingInteractor: AnalyticsTrackingInteractor
fun newInstance() = ResetPasswordFragment()
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
...@@ -48,6 +51,10 @@ class ResetPasswordFragment : Fragment(), ResetPasswordView { ...@@ -48,6 +51,10 @@ class ResetPasswordFragment : Fragment(), ResetPasswordView {
} }
setupOnClickListener() setupOnClickListener()
if (analyticsTrackingInteractor.get()) {
AnalyticsManager.logScreenView(ScreenViewEvent.ResetPassword)
}
} }
override fun alertBlankEmail() { override fun alertBlankEmail() {
...@@ -131,4 +138,8 @@ class ResetPasswordFragment : Fragment(), ResetPasswordView { ...@@ -131,4 +138,8 @@ class ResetPasswordFragment : Fragment(), ResetPasswordView {
presenter.resetPassword(text_email.textContent) presenter.resetPassword(text_email.textContent)
} }
} }
companion object {
fun newInstance() = ResetPasswordFragment()
}
} }
\ No newline at end of file
...@@ -18,7 +18,10 @@ import chat.rocket.android.authentication.server.presentation.ServerPresenter ...@@ -18,7 +18,10 @@ import chat.rocket.android.authentication.server.presentation.ServerPresenter
import chat.rocket.android.authentication.server.presentation.ServerView import chat.rocket.android.authentication.server.presentation.ServerView
import chat.rocket.android.authentication.ui.AuthenticationActivity import chat.rocket.android.authentication.ui.AuthenticationActivity
import chat.rocket.android.helper.KeyboardHelper import chat.rocket.android.helper.KeyboardHelper
import chat.rocket.android.server.domain.AnalyticsTrackingInteractor
import chat.rocket.android.util.extensions.* 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.common.util.ifNull import chat.rocket.common.util.ifNull
import dagger.android.support.AndroidSupportInjection import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.app_bar_chat_room.* import kotlinx.android.synthetic.main.app_bar_chat_room.*
...@@ -26,24 +29,17 @@ import kotlinx.android.synthetic.main.fragment_authentication_server.* ...@@ -26,24 +29,17 @@ import kotlinx.android.synthetic.main.fragment_authentication_server.*
import okhttp3.HttpUrl import okhttp3.HttpUrl
import javax.inject.Inject import javax.inject.Inject
internal const val TAG_SERVER_FRAGMENT = "ServerFragment"
class ServerFragment : Fragment(), ServerView { class ServerFragment : Fragment(), ServerView {
@Inject @Inject
lateinit var presenter: ServerPresenter lateinit var presenter: ServerPresenter
@Inject
lateinit var analyticsTrackingInteractor: AnalyticsTrackingInteractor
private var deepLinkInfo: LoginDeepLinkInfo? = null private var deepLinkInfo: LoginDeepLinkInfo? = null
private val layoutListener = ViewTreeObserver.OnGlobalLayoutListener { private val layoutListener = ViewTreeObserver.OnGlobalLayoutListener {
text_server_url.isCursorVisible = KeyboardHelper.isSoftKeyboardShown(constraint_layout.rootView) text_server_url.isCursorVisible = KeyboardHelper.isSoftKeyboardShown(constraint_layout.rootView)
} }
companion object {
private const val DEEP_LINK_INFO = "DeepLinkInfo"
fun newInstance(deepLinkInfo: LoginDeepLinkInfo?) = ServerFragment().apply {
arguments = Bundle().apply {
putParcelable(DEEP_LINK_INFO, deepLinkInfo)
}
}
}
private var protocol = "https://" private var protocol = "https://"
private var ignoreChange = false private var ignoreChange = false
...@@ -104,6 +100,10 @@ class ServerFragment : Fragment(), ServerView { ...@@ -104,6 +100,10 @@ class ServerFragment : Fragment(), ServerView {
override fun onNothingSelected(parent: AdapterView<*>?) { override fun onNothingSelected(parent: AdapterView<*>?) {
} }
} }
if (analyticsTrackingInteractor.get()) {
AnalyticsManager.logScreenView(ScreenViewEvent.Server)
}
} }
private fun setupToobar() { private fun setupToobar() {
...@@ -227,4 +227,14 @@ class ServerFragment : Fragment(), ServerView { ...@@ -227,4 +227,14 @@ class ServerFragment : Fragment(), ServerView {
} }
} }
} }
companion object {
private const val DEEP_LINK_INFO = "DeepLinkInfo"
fun newInstance(deepLinkInfo: LoginDeepLinkInfo?) = ServerFragment().apply {
arguments = Bundle().apply {
putParcelable(DEEP_LINK_INFO, deepLinkInfo)
}
}
}
} }
\ No newline at end of file
...@@ -3,11 +3,25 @@ package chat.rocket.android.authentication.signup.presentation ...@@ -3,11 +3,25 @@ package chat.rocket.android.authentication.signup.presentation
import chat.rocket.android.authentication.presentation.AuthenticationNavigator import chat.rocket.android.authentication.presentation.AuthenticationNavigator
import chat.rocket.android.core.lifecycle.CancelStrategy import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.infrastructure.LocalRepository import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.server.domain.* 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
import chat.rocket.android.server.domain.SaveAccountInteractor
import chat.rocket.android.server.domain.SaveCurrentServerInteractor
import chat.rocket.android.server.domain.favicon
import chat.rocket.android.server.domain.model.Account import chat.rocket.android.server.domain.model.Account
import chat.rocket.android.server.domain.wideTile
import chat.rocket.android.server.infraestructure.RocketChatClientFactory import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.extension.launchUI import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.extensions.* 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.android.util.retryIO
import chat.rocket.common.RocketChatException import chat.rocket.common.RocketChatException
import chat.rocket.common.util.ifNull import chat.rocket.common.util.ifNull
...@@ -25,6 +39,7 @@ class SignupPresenter @Inject constructor( ...@@ -25,6 +39,7 @@ class SignupPresenter @Inject constructor(
private val localRepository: LocalRepository, private val localRepository: LocalRepository,
private val serverInteractor: GetConnectingServerInteractor, private val serverInteractor: GetConnectingServerInteractor,
private val saveCurrentServerInteractor: SaveCurrentServerInteractor, private val saveCurrentServerInteractor: SaveCurrentServerInteractor,
private val analyticsTrackingInteractor: AnalyticsTrackingInteractor,
private val factory: RocketChatClientFactory, private val factory: RocketChatClientFactory,
private val saveAccountInteractor: SaveAccountInteractor, private val saveAccountInteractor: SaveAccountInteractor,
private val getAccountsInteractor: GetAccountsInteractor, private val getAccountsInteractor: GetAccountsInteractor,
...@@ -66,9 +81,21 @@ class SignupPresenter @Inject constructor( ...@@ -66,9 +81,21 @@ class SignupPresenter @Inject constructor(
localRepository.save(LocalRepository.CURRENT_USERNAME_KEY, me.username) localRepository.save(LocalRepository.CURRENT_USERNAME_KEY, me.username)
saveAccount(me) saveAccount(me)
registerPushToken() registerPushToken()
if (analyticsTrackingInteractor.get()) {
AnalyticsManager.logSignUp(
AuthenticationEvent.AuthenticationWithUserAndPassword,
true
)
}
view.saveSmartLockCredentials(username, password) view.saveSmartLockCredentials(username, password)
navigator.toChatList() navigator.toChatList()
} catch (exception: RocketChatException) { } catch (exception: RocketChatException) {
if (analyticsTrackingInteractor.get()) {
AnalyticsManager.logSignUp(
AuthenticationEvent.AuthenticationWithUserAndPassword,
false
)
}
exception.message?.let { exception.message?.let {
view.showMessage(it) view.showMessage(it)
}.ifNull { }.ifNull {
......
...@@ -2,7 +2,6 @@ package chat.rocket.android.authentication.signup.presentation ...@@ -2,7 +2,6 @@ package chat.rocket.android.authentication.signup.presentation
import chat.rocket.android.core.behaviours.LoadingView import chat.rocket.android.core.behaviours.LoadingView
import chat.rocket.android.core.behaviours.MessageView import chat.rocket.android.core.behaviours.MessageView
import com.google.android.gms.auth.api.credentials.Credential
interface SignupView : LoadingView, MessageView { interface SignupView : LoadingView, MessageView {
......
...@@ -5,30 +5,40 @@ import android.app.Activity ...@@ -5,30 +5,40 @@ import android.app.Activity
import android.content.Intent import android.content.Intent
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import androidx.fragment.app.Fragment
import android.text.style.ClickableSpan import android.text.style.ClickableSpan
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.ViewTreeObserver import android.view.ViewTreeObserver
import androidx.fragment.app.Fragment
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.R.string.message_credentials_saved_successfully import chat.rocket.android.R.string.message_credentials_saved_successfully
import chat.rocket.android.authentication.signup.presentation.SignupPresenter import chat.rocket.android.authentication.signup.presentation.SignupPresenter
import chat.rocket.android.authentication.signup.presentation.SignupView import chat.rocket.android.authentication.signup.presentation.SignupView
import chat.rocket.android.helper.KeyboardHelper import chat.rocket.android.helper.KeyboardHelper
import chat.rocket.android.helper.SmartLockHelper
import chat.rocket.android.helper.TextHelper import chat.rocket.android.helper.TextHelper
import chat.rocket.android.util.extensions.* import chat.rocket.android.helper.saveCredentials
import com.google.android.gms.auth.api.credentials.Credentials 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 dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_authentication_sign_up.* import kotlinx.android.synthetic.main.fragment_authentication_sign_up.*
import javax.inject.Inject import javax.inject.Inject
internal const val TAG_SIGNUP_FRAGMENT = "SignupFragment"
internal const val SAVE_CREDENTIALS = 1 internal const val SAVE_CREDENTIALS = 1
class SignupFragment : Fragment(), SignupView { class SignupFragment : Fragment(), SignupView {
@Inject @Inject
lateinit var presenter: SignupPresenter lateinit var presenter: SignupPresenter
@Inject
lateinit var analyticsTrackingInteractor: AnalyticsTrackingInteractor
private val layoutListener = ViewTreeObserver.OnGlobalLayoutListener { private val layoutListener = ViewTreeObserver.OnGlobalLayoutListener {
if (KeyboardHelper.isSoftKeyboardShown(constraint_layout.rootView)) { if (KeyboardHelper.isSoftKeyboardShown(constraint_layout.rootView)) {
text_new_user_agreement.setVisible(false) text_new_user_agreement.setVisible(false)
...@@ -41,10 +51,6 @@ class SignupFragment : Fragment(), SignupView { ...@@ -41,10 +51,6 @@ class SignupFragment : Fragment(), SignupView {
} }
} }
companion object {
fun newInstance() = SignupFragment()
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
AndroidSupportInjection.inject(this) AndroidSupportInjection.inject(this)
...@@ -75,6 +81,10 @@ class SignupFragment : Fragment(), SignupView { ...@@ -75,6 +81,10 @@ class SignupFragment : Fragment(), SignupView {
text_email.textContent text_email.textContent
) )
} }
if (analyticsTrackingInteractor.get()) {
AnalyticsManager.logScreenView(ScreenViewEvent.SignUp)
}
} }
override fun onDestroyView() { override fun onDestroyView() {
...@@ -155,9 +165,7 @@ class SignupFragment : Fragment(), SignupView { ...@@ -155,9 +165,7 @@ class SignupFragment : Fragment(), SignupView {
} }
override fun saveSmartLockCredentials(id: String, password: String) { override fun saveSmartLockCredentials(id: String, password: String) {
activity?.let { activity?.saveCredentials(id, password)
SmartLockHelper.save(Credentials.getClient(it), it, id, password)
}
} }
private fun tintEditTextDrawableStart() { private fun tintEditTextDrawableStart() {
...@@ -216,4 +224,8 @@ class SignupFragment : Fragment(), SignupView { ...@@ -216,4 +224,8 @@ class SignupFragment : Fragment(), SignupView {
text_password.isEnabled = value text_password.isEnabled = value
text_email.isEnabled = value text_email.isEnabled = value
} }
companion object {
fun newInstance() = SignupFragment()
}
} }
...@@ -6,10 +6,12 @@ import chat.rocket.android.infrastructure.LocalRepository ...@@ -6,10 +6,12 @@ import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.server.domain.* import chat.rocket.android.server.domain.*
import chat.rocket.android.server.domain.model.Account import chat.rocket.android.server.domain.model.Account
import chat.rocket.android.server.infraestructure.RocketChatClientFactory import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.extensions.avatarUrl
import chat.rocket.android.util.extension.launchUI 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.registerPushToken
import chat.rocket.android.util.extensions.serverLogoUrl 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.android.util.retryIO
import chat.rocket.common.RocketChatAuthException import chat.rocket.common.RocketChatAuthException
import chat.rocket.common.RocketChatException import chat.rocket.common.RocketChatException
...@@ -20,23 +22,30 @@ import chat.rocket.core.internal.rest.me ...@@ -20,23 +22,30 @@ import chat.rocket.core.internal.rest.me
import chat.rocket.core.model.Myself import chat.rocket.core.model.Myself
import javax.inject.Inject import javax.inject.Inject
class TwoFAPresenter @Inject constructor(private val view: TwoFAView, class TwoFAPresenter @Inject constructor(
private val view: TwoFAView,
private val strategy: CancelStrategy, private val strategy: CancelStrategy,
private val navigator: AuthenticationNavigator, private val navigator: AuthenticationNavigator,
private val tokenRepository: TokenRepository, private val tokenRepository: TokenRepository,
private val localRepository: LocalRepository, private val localRepository: LocalRepository,
private val serverInteractor: GetConnectingServerInteractor, private val serverInteractor: GetConnectingServerInteractor,
private val saveCurrentServerInteractor: SaveCurrentServerInteractor, private val saveCurrentServerInteractor: SaveCurrentServerInteractor,
private val analyticsTrackingInteractor: AnalyticsTrackingInteractor,
private val factory: RocketChatClientFactory, private val factory: RocketChatClientFactory,
private val saveAccountInteractor: SaveAccountInteractor, private val saveAccountInteractor: SaveAccountInteractor,
private val getAccountsInteractor: GetAccountsInteractor, private val getAccountsInteractor: GetAccountsInteractor,
settingsInteractor: GetSettingsInteractor) { settingsInteractor: GetSettingsInteractor
) {
private val currentServer = serverInteractor.get()!! private val currentServer = serverInteractor.get()!!
private val client: RocketChatClient = factory.create(currentServer) private val client: RocketChatClient = factory.create(currentServer)
private var settings: PublicSettings = settingsInteractor.get(serverInteractor.get()!!) private var settings: PublicSettings = settingsInteractor.get(serverInteractor.get()!!)
// TODO: If the usernameOrEmail and password was informed by the user on the previous screen, then we should pass only the pin, like this: fun authenticate(pin: EditText) // TODO: If the usernameOrEmail and password was informed by the user on the previous screen, then we should pass only the pin, like this: fun authenticate(pin: EditText)
fun authenticate(usernameOrEmail: String, password: String, twoFactorAuthenticationCode: String) { fun authenticate(
usernameOrEmail: String,
password: String,
twoFactorAuthenticationCode: String
) {
val server = serverInteractor.get() val server = serverInteractor.get()
when { when {
server == null -> { server == null -> {
...@@ -59,11 +68,23 @@ class TwoFAPresenter @Inject constructor(private val view: TwoFAView, ...@@ -59,11 +68,23 @@ class TwoFAPresenter @Inject constructor(private val view: TwoFAView,
saveCurrentServerInteractor.save(currentServer) saveCurrentServerInteractor.save(currentServer)
tokenRepository.save(server, token) tokenRepository.save(server, token)
registerPushToken() registerPushToken()
if (analyticsTrackingInteractor.get()) {
AnalyticsManager.logLogin(
AuthenticationEvent.AuthenticationWithUserAndPassword,
true
)
}
navigator.toChatList() navigator.toChatList()
} catch (exception: RocketChatException) { } catch (exception: RocketChatException) {
if (exception is RocketChatAuthException) { if (exception is RocketChatAuthException) {
view.alertInvalidTwoFactorAuthenticationCode() view.alertInvalidTwoFactorAuthenticationCode()
} else { } else {
if (analyticsTrackingInteractor.get()) {
AnalyticsManager.logLogin(
AuthenticationEvent.AuthenticationWithUserAndPassword,
false
)
}
exception.message?.let { exception.message?.let {
view.showMessage(it) view.showMessage(it)
}.ifNull { }.ifNull {
......
...@@ -4,37 +4,38 @@ import DrawableHelper ...@@ -4,37 +4,38 @@ import DrawableHelper
import android.content.Context import android.content.Context
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.inputmethod.InputMethodManager import android.view.inputmethod.InputMethodManager
import androidx.fragment.app.Fragment
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.authentication.twofactor.presentation.TwoFAPresenter import chat.rocket.android.authentication.twofactor.presentation.TwoFAPresenter
import chat.rocket.android.authentication.twofactor.presentation.TwoFAView import chat.rocket.android.authentication.twofactor.presentation.TwoFAView
import chat.rocket.android.util.extensions.* 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
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 dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_authentication_two_fa.* import kotlinx.android.synthetic.main.fragment_authentication_two_fa.*
import javax.inject.Inject import javax.inject.Inject
internal const val TAG_TWO_FA_FRAGMENT = "TwoFAFragment"
class TwoFAFragment : Fragment(), TwoFAView { class TwoFAFragment : Fragment(), TwoFAView {
@Inject lateinit var presenter: TwoFAPresenter @Inject
lateinit var presenter: TwoFAPresenter
@Inject
lateinit var analyticsTrackingInteractor: AnalyticsTrackingInteractor
lateinit var username: String lateinit var username: String
lateinit var password: String lateinit var password: String
// TODO - we could create an in memory repository to save username and password.
companion object {
private const val USERNAME = "username"
private const val PASSWORD = "password"
fun newInstance(username: String, password: String) = TwoFAFragment().apply {
arguments = Bundle(2).apply {
putString(USERNAME, username)
putString(PASSWORD, password)
}
}
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
AndroidSupportInjection.inject(this) AndroidSupportInjection.inject(this)
...@@ -44,7 +45,8 @@ class TwoFAFragment : Fragment(), TwoFAView { ...@@ -44,7 +45,8 @@ class TwoFAFragment : Fragment(), TwoFAView {
password = arguments?.getString(PASSWORD) ?: "" password = arguments?.getString(PASSWORD) ?: ""
} }
override fun onCreateView(inflater: LayoutInflater, override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?
): View? = container?.inflate(R.layout.fragment_authentication_two_fa) ): View? = container?.inflate(R.layout.fragment_authentication_two_fa)
...@@ -63,6 +65,10 @@ class TwoFAFragment : Fragment(), TwoFAView { ...@@ -63,6 +65,10 @@ class TwoFAFragment : Fragment(), TwoFAView {
} }
setupOnClickListener() setupOnClickListener()
if (analyticsTrackingInteractor.get()) {
AnalyticsManager.logScreenView(ScreenViewEvent.TwoFa)
}
} }
override fun alertBlankTwoFactorAuthenticationCode() { override fun alertBlankTwoFactorAuthenticationCode() {
...@@ -106,7 +112,8 @@ class TwoFAFragment : Fragment(), TwoFAView { ...@@ -106,7 +112,8 @@ class TwoFAFragment : Fragment(), TwoFAView {
private fun tintEditTextDrawableStart() { private fun tintEditTextDrawableStart() {
ui { ui {
val lockDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_vpn_key_black_24dp, it) val lockDrawable =
DrawableHelper.getDrawableFromId(R.drawable.ic_vpn_key_black_24dp, it)
DrawableHelper.wrapDrawable(lockDrawable) DrawableHelper.wrapDrawable(lockDrawable)
DrawableHelper.tintDrawable(lockDrawable, it, R.color.colorDrawableTintGrey) DrawableHelper.tintDrawable(lockDrawable, it, R.color.colorDrawableTintGrey)
DrawableHelper.compoundDrawable(text_two_factor_auth, lockDrawable) DrawableHelper.compoundDrawable(text_two_factor_auth, lockDrawable)
...@@ -123,4 +130,17 @@ class TwoFAFragment : Fragment(), TwoFAView { ...@@ -123,4 +130,17 @@ class TwoFAFragment : Fragment(), TwoFAView {
presenter.authenticate(username, password, text_two_factor_auth.textContent) presenter.authenticate(username, password, text_two_factor_auth.textContent)
} }
} }
// TODO - we could create an in memory repository to save username and password.
companion object {
private const val USERNAME = "username"
private const val PASSWORD = "password"
fun newInstance(username: String, password: String) = TwoFAFragment().apply {
arguments = Bundle(2).apply {
putString(USERNAME, username)
putString(PASSWORD, password)
}
}
}
} }
...@@ -3,19 +3,18 @@ package chat.rocket.android.authentication.ui ...@@ -3,19 +3,18 @@ package chat.rocket.android.authentication.ui
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.authentication.onboarding.ui.OnBoardingFragment import chat.rocket.android.authentication.domain.model.LoginDeepLinkInfo
import chat.rocket.android.authentication.domain.model.getLoginDeepLinkInfo
import chat.rocket.android.authentication.presentation.AuthenticationPresenter import chat.rocket.android.authentication.presentation.AuthenticationPresenter
import chat.rocket.android.authentication.server.ui.ServerFragment
import chat.rocket.android.util.extensions.addFragment import chat.rocket.android.util.extensions.addFragment
import dagger.android.AndroidInjection import dagger.android.AndroidInjection
import dagger.android.AndroidInjector import dagger.android.AndroidInjector
import dagger.android.DispatchingAndroidInjector import dagger.android.DispatchingAndroidInjector
import dagger.android.support.HasSupportFragmentInjector import dagger.android.support.HasSupportFragmentInjector
import kotlinx.android.synthetic.main.app_bar_chat_room.*
import kotlinx.coroutines.experimental.Job import kotlinx.coroutines.experimental.Job
import kotlinx.coroutines.experimental.android.UI import kotlinx.coroutines.experimental.android.UI
import kotlinx.coroutines.experimental.launch import kotlinx.coroutines.experimental.launch
...@@ -31,49 +30,21 @@ class AuthenticationActivity : AppCompatActivity(), HasSupportFragmentInjector { ...@@ -31,49 +30,21 @@ class AuthenticationActivity : AppCompatActivity(), HasSupportFragmentInjector {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
AndroidInjection.inject(this) AndroidInjection.inject(this)
setContentView(R.layout.activity_authentication) setContentView(R.layout.activity_authentication)
setTheme(R.style.AuthenticationTheme)
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setupToolbar()
}
private fun setupToolbar() {
setSupportActionBar(toolbar)
supportActionBar?.setDisplayShowTitleEnabled(false)
toolbar.setNavigationIcon(R.drawable.ic_arrow_back_white_24dp)
toolbar.setNavigationOnClickListener {
onBackPressed()
}
} }
override fun onStart() { override fun onStart() {
super.onStart() super.onStart()
val deepLinkInfo = intent.getLoginDeepLinkInfo()
launch(UI + job) { launch(UI + job) {
val newServer = intent.getBooleanExtra(INTENT_ADD_NEW_SERVER, false) val newServer = intent.getBooleanExtra(INTENT_ADD_NEW_SERVER, false)
// if we got authenticateWithDeepLink information, pass true to newServer also // if we got authenticateWithDeepLink information, pass true to newServer also
presenter.loadCredentials(newServer) { authenticated -> presenter.loadCredentials(newServer || deepLinkInfo != null) { authenticated ->
if (!authenticated) { if (!authenticated) {
showOnBoarding() showServerInput(deepLinkInfo)
}
}
}
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
val inflater = menuInflater
inflater.inflate(R.menu.legal, menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem?): Boolean {
return when(item?.itemId){
R.id.action_terms_of_Service -> {
presenter.termsOfService(getString(R.string.title_legal_terms))
true
} }
R.id.action_privacy_policy -> {
presenter.privacyPolicy(getString(R.string.title_legal_terms))
true
} }
else -> super.onOptionsItemSelected(item)
} }
} }
...@@ -84,7 +55,6 @@ class AuthenticationActivity : AppCompatActivity(), HasSupportFragmentInjector { ...@@ -84,7 +55,6 @@ class AuthenticationActivity : AppCompatActivity(), HasSupportFragmentInjector {
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data) super.onActivityResult(requestCode, resultCode, data)
supportFragmentManager.findFragmentById(R.id.fragment_container)?.onActivityResult(requestCode, resultCode, data)
val currentFragment = supportFragmentManager.findFragmentById(R.id.fragment_container) val currentFragment = supportFragmentManager.findFragmentById(R.id.fragment_container)
currentFragment?.onActivityResult(requestCode, resultCode, data) currentFragment?.onActivityResult(requestCode, resultCode, data)
} }
...@@ -93,9 +63,9 @@ class AuthenticationActivity : AppCompatActivity(), HasSupportFragmentInjector { ...@@ -93,9 +63,9 @@ class AuthenticationActivity : AppCompatActivity(), HasSupportFragmentInjector {
return fragmentDispatchingAndroidInjector return fragmentDispatchingAndroidInjector
} }
private fun showOnBoarding() { fun showServerInput(deepLinkInfo: LoginDeepLinkInfo?) {
addFragment("OnBoardingFragment", R.id.fragment_container, allowStateLoss = true) { addFragment("ServerFragment", R.id.fragment_container, allowStateLoss = true) {
OnBoardingFragment.newInstance() ServerFragment.newInstance(deepLinkInfo)
} }
} }
} }
......
...@@ -6,7 +6,6 @@ import android.os.Bundle ...@@ -6,7 +6,6 @@ import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.chatinformation.ui.MessageInfoFragment.Companion.TAG_MESSAGE_INFO_FRAGMENT
import chat.rocket.android.util.extensions.addFragment import chat.rocket.android.util.extensions.addFragment
import chat.rocket.android.util.extensions.textContent import chat.rocket.android.util.extensions.textContent
import dagger.android.AndroidInjection import dagger.android.AndroidInjection
......
...@@ -13,10 +13,11 @@ import chat.rocket.android.chatinformation.adapter.ReadReceiptAdapter ...@@ -13,10 +13,11 @@ import chat.rocket.android.chatinformation.adapter.ReadReceiptAdapter
import chat.rocket.android.chatinformation.presentation.MessageInfoPresenter import chat.rocket.android.chatinformation.presentation.MessageInfoPresenter
import chat.rocket.android.chatinformation.presentation.MessageInfoView import chat.rocket.android.chatinformation.presentation.MessageInfoView
import chat.rocket.android.chatinformation.viewmodel.ReadReceiptViewModel import chat.rocket.android.chatinformation.viewmodel.ReadReceiptViewModel
import chat.rocket.android.helper.EndlessRecyclerViewScrollListener import chat.rocket.android.server.domain.AnalyticsTrackingInteractor
import chat.rocket.android.util.extensions.setVisible import chat.rocket.android.util.extensions.setVisible
import chat.rocket.android.util.extensions.showToast import chat.rocket.android.util.extensions.showToast
import chat.rocket.core.model.ReadReceipt import chat.rocket.android.util.helper.analytics.AnalyticsManager
import chat.rocket.android.util.helper.analytics.event.ScreenViewEvent
import dagger.android.support.AndroidSupportInjection import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_message_info.* import kotlinx.android.synthetic.main.fragment_message_info.*
import javax.inject.Inject import javax.inject.Inject
...@@ -29,13 +30,14 @@ fun newInstance(messageId: String): Fragment { ...@@ -29,13 +30,14 @@ fun newInstance(messageId: String): Fragment {
} }
} }
internal const val TAG_MESSAGE_INFO_FRAGMENT = "MessageInfoFragment"
private const val BUNDLE_MESSAGE_ID = "message_id" private const val BUNDLE_MESSAGE_ID = "message_id"
class MessageInfoFragment : Fragment(), MessageInfoView { class MessageInfoFragment : Fragment(), MessageInfoView {
@Inject @Inject
lateinit var presenter: MessageInfoPresenter lateinit var presenter: MessageInfoPresenter
@Inject
lateinit var analyticsTrackingInteractor: AnalyticsTrackingInteractor
private lateinit var adapter: ReadReceiptAdapter private lateinit var adapter: ReadReceiptAdapter
private lateinit var messageId: String private lateinit var messageId: String
...@@ -64,6 +66,10 @@ class MessageInfoFragment : Fragment(), MessageInfoView { ...@@ -64,6 +66,10 @@ class MessageInfoFragment : Fragment(), MessageInfoView {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
setupRecyclerView() setupRecyclerView()
presenter.loadReadReceipts(messageId = messageId) presenter.loadReadReceipts(messageId = messageId)
if (analyticsTrackingInteractor.get()) {
AnalyticsManager.logScreenView(ScreenViewEvent.MessageInfo)
}
} }
private fun setupRecyclerView() { private fun setupRecyclerView() {
...@@ -93,8 +99,4 @@ class MessageInfoFragment : Fragment(), MessageInfoView { ...@@ -93,8 +99,4 @@ class MessageInfoFragment : Fragment(), MessageInfoView {
override fun showReadReceipts(messageReceipts: List<ReadReceiptViewModel>) { override fun showReadReceipts(messageReceipts: List<ReadReceiptViewModel>) {
adapter.addAll(messageReceipts) adapter.addAll(messageReceipts)
} }
companion object {
const val TAG_MESSAGE_INFO_FRAGMENT = "MessageInfoFragment"
}
} }
...@@ -4,37 +4,42 @@ import chat.rocket.android.R ...@@ -4,37 +4,42 @@ import chat.rocket.android.R
import chat.rocket.android.chatinformation.ui.messageInformationIntent import chat.rocket.android.chatinformation.ui.messageInformationIntent
import chat.rocket.android.chatroom.ui.ChatRoomActivity import chat.rocket.android.chatroom.ui.ChatRoomActivity
import chat.rocket.android.chatroom.ui.chatRoomIntent import chat.rocket.android.chatroom.ui.chatRoomIntent
import chat.rocket.android.favoritemessages.ui.TAG_FAVORITE_MESSAGES_FRAGMENT
import chat.rocket.android.files.ui.TAG_FILES_FRAGMENT
import chat.rocket.android.members.ui.TAG_MEMBERS_FRAGMENT
import chat.rocket.android.mentions.ui.TAG_MENTIONS_FRAGMENT
import chat.rocket.android.pinnedmessages.ui.TAG_PINNED_MESSAGES_FRAGMENT
import chat.rocket.android.server.ui.changeServerIntent import chat.rocket.android.server.ui.changeServerIntent
import chat.rocket.android.util.extensions.addFragmentBackStack import chat.rocket.android.util.extensions.addFragmentBackStack
class ChatRoomNavigator(internal val activity: ChatRoomActivity) { class ChatRoomNavigator(internal val activity: ChatRoomActivity) {
fun toMembersList(chatRoomId: String) { fun toMembersList(chatRoomId: String) {
activity.addFragmentBackStack("MembersFragment", R.id.fragment_container) { activity.addFragmentBackStack(TAG_MEMBERS_FRAGMENT, R.id.fragment_container) {
chat.rocket.android.members.ui.newInstance(chatRoomId) chat.rocket.android.members.ui.newInstance(chatRoomId)
} }
} }
fun toMentions(chatRoomId: String) { fun toMentions(chatRoomId: String) {
activity.addFragmentBackStack("MentionsFragment", R.id.fragment_container) { activity.addFragmentBackStack(TAG_MENTIONS_FRAGMENT, R.id.fragment_container) {
chat.rocket.android.mentions.ui.newInstance(chatRoomId) chat.rocket.android.mentions.ui.newInstance(chatRoomId)
} }
} }
fun toPinnedMessageList(chatRoomId: String) { fun toPinnedMessageList(chatRoomId: String) {
activity.addFragmentBackStack("PinnedMessages", R.id.fragment_container) { activity.addFragmentBackStack(TAG_PINNED_MESSAGES_FRAGMENT, R.id.fragment_container) {
chat.rocket.android.pinnedmessages.ui.newInstance(chatRoomId) chat.rocket.android.pinnedmessages.ui.newInstance(chatRoomId)
} }
} }
fun toFavoriteMessageList(chatRoomId: String) { fun toFavoriteMessageList(chatRoomId: String) {
activity.addFragmentBackStack("FavoriteMessages", R.id.fragment_container) { activity.addFragmentBackStack(TAG_FAVORITE_MESSAGES_FRAGMENT, R.id.fragment_container) {
chat.rocket.android.favoritemessages.ui.newInstance(chatRoomId) chat.rocket.android.favoritemessages.ui.newInstance(chatRoomId)
} }
} }
fun toFileList(chatRoomId: String) { fun toFileList(chatRoomId: String) {
activity.addFragmentBackStack("Files", R.id.fragment_container) { activity.addFragmentBackStack(TAG_FILES_FRAGMENT, R.id.fragment_container) {
chat.rocket.android.files.ui.newInstance(chatRoomId) chat.rocket.android.files.ui.newInstance(chatRoomId)
} }
} }
......
...@@ -17,6 +17,7 @@ import chat.rocket.android.db.DatabaseManager ...@@ -17,6 +17,7 @@ import chat.rocket.android.db.DatabaseManager
import chat.rocket.android.helper.MessageHelper import chat.rocket.android.helper.MessageHelper
import chat.rocket.android.helper.UserHelper import chat.rocket.android.helper.UserHelper
import chat.rocket.android.infrastructure.LocalRepository 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.GetCurrentServerInteractor
import chat.rocket.android.server.domain.GetSettingsInteractor import chat.rocket.android.server.domain.GetSettingsInteractor
import chat.rocket.android.server.domain.JobSchedulerInteractor import chat.rocket.android.server.domain.JobSchedulerInteractor
...@@ -33,6 +34,8 @@ import chat.rocket.android.server.infraestructure.state ...@@ -33,6 +34,8 @@ import chat.rocket.android.server.infraestructure.state
import chat.rocket.android.util.extension.compressImageAndGetInputStream import chat.rocket.android.util.extension.compressImageAndGetInputStream
import chat.rocket.android.util.extension.launchUI import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.extensions.avatarUrl 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.android.util.retryIO
import chat.rocket.common.RocketChatException import chat.rocket.common.RocketChatException
import chat.rocket.common.model.RoomType import chat.rocket.common.model.RoomType
...@@ -90,6 +93,7 @@ class ChatRoomPresenter @Inject constructor( ...@@ -90,6 +93,7 @@ class ChatRoomPresenter @Inject constructor(
private val usersRepository: UsersRepository, private val usersRepository: UsersRepository,
private val roomsRepository: RoomRepository, private val roomsRepository: RoomRepository,
private val localRepository: LocalRepository, private val localRepository: LocalRepository,
private val analyticsTrackingInteractor: AnalyticsTrackingInteractor,
private val userHelper: UserHelper, private val userHelper: UserHelper,
private val mapper: UiModelMapper, private val mapper: UiModelMapper,
private val jobSchedulerInteractor: JobSchedulerInteractor, private val jobSchedulerInteractor: JobSchedulerInteractor,
...@@ -107,7 +111,7 @@ class ChatRoomPresenter @Inject constructor( ...@@ -107,7 +111,7 @@ class ChatRoomPresenter @Inject constructor(
private val messagesChannel = Channel<Message>() private val messagesChannel = Channel<Message>()
private var chatRoomId: String? = null private var chatRoomId: String? = null
private var chatRoomType: String? = null private lateinit var chatRoomType: String
private var chatIsBroadcast: Boolean = false private var chatIsBroadcast: Boolean = false
private var chatRoles = emptyList<ChatRoomRole>() private var chatRoles = emptyList<ChatRoomRole>()
private val stateChannel = Channel<State>() private val stateChannel = Channel<State>()
...@@ -261,7 +265,7 @@ class ChatRoomPresenter @Inject constructor( ...@@ -261,7 +265,7 @@ class ChatRoomPresenter @Inject constructor(
try { try {
// ignore message for now, will receive it on the stream // ignore message for now, will receive it on the stream
val id = UUID.randomUUID().toString() val id = UUID.randomUUID().toString()
val message = if (messageId == null) { if (messageId == null) {
val username = userHelper.username() val username = userHelper.username()
val newMessage = Message( val newMessage = Message(
id = id, id = id,
...@@ -296,6 +300,9 @@ class ChatRoomPresenter @Inject constructor( ...@@ -296,6 +300,9 @@ class ChatRoomPresenter @Inject constructor(
), false ), false
) )
client.sendMessage(id, chatRoomId, text) client.sendMessage(id, chatRoomId, text)
if (analyticsTrackingInteractor.get()) {
logMessageSent(currentServer)
}
} catch (ex: Exception) { } catch (ex: Exception) {
// Ok, not very beautiful, but the backend sends us a not valid response // 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 // When someone sends a message on a read-only channel, so we just ignore it
...@@ -338,12 +345,9 @@ class ChatRoomPresenter @Inject constructor( ...@@ -338,12 +345,9 @@ class ChatRoomPresenter @Inject constructor(
val maxFileSizeAllowed = settings.uploadMaxFileSize() val maxFileSizeAllowed = settings.uploadMaxFileSize()
when { when {
fileName.isEmpty() -> { fileName.isEmpty() -> view.showInvalidFileMessage()
view.showInvalidFileMessage() fileSize > maxFileSizeAllowed && maxFileSizeAllowed !in -1..0 ->
}
fileSize > maxFileSizeAllowed -> {
view.showInvalidFileSize(fileSize, maxFileSizeAllowed) view.showInvalidFileSize(fileSize, maxFileSizeAllowed)
}
else -> { else -> {
var inputStream: InputStream? = uriInteractor.getInputStream(uri) var inputStream: InputStream? = uriInteractor.getInputStream(uri)
...@@ -366,6 +370,9 @@ class ChatRoomPresenter @Inject constructor( ...@@ -366,6 +370,9 @@ class ChatRoomPresenter @Inject constructor(
inputStream inputStream
} }
} }
if (analyticsTrackingInteractor.get()) {
logMediaUploaded(mimeType)
}
} }
} }
} }
...@@ -392,9 +399,8 @@ class ChatRoomPresenter @Inject constructor( ...@@ -392,9 +399,8 @@ class ChatRoomPresenter @Inject constructor(
val maxFileSizeAllowed = settings.uploadMaxFileSize() val maxFileSizeAllowed = settings.uploadMaxFileSize()
when { when {
fileSize > maxFileSizeAllowed -> { fileSize > maxFileSizeAllowed && maxFileSizeAllowed !in -1..0 ->
view.showInvalidFileSize(fileSize, maxFileSizeAllowed) view.showInvalidFileSize(fileSize, maxFileSizeAllowed)
}
else -> { else -> {
retryIO("uploadFile($roomId, $fileName, $mimeType") { retryIO("uploadFile($roomId, $fileName, $mimeType") {
client.uploadFile( client.uploadFile(
...@@ -938,12 +944,45 @@ class ChatRoomPresenter @Inject constructor( ...@@ -938,12 +944,45 @@ class ChatRoomPresenter @Inject constructor(
retryIO("toggleEmoji($messageId, $emoji)") { retryIO("toggleEmoji($messageId, $emoji)") {
client.toggleReaction(messageId, emoji.removeSurrounding(":")) client.toggleReaction(messageId, emoji.removeSurrounding(":"))
} }
if (analyticsTrackingInteractor.get()) {
logReactionEvent()
}
} catch (ex: RocketChatException) { } catch (ex: RocketChatException) {
Timber.e(ex) Timber.e(ex)
} }
} }
} }
private fun logReactionEvent() {
when {
roomTypeOf(chatRoomType) is RoomType.DirectMessage ->
AnalyticsManager.logReaction(SubscriptionTypeEvent.DirectMessage)
roomTypeOf(chatRoomType) is RoomType.Channel ->
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)
roomTypeOf(chatRoomType) is RoomType.Channel ->
AnalyticsManager.logMediaUploaded(SubscriptionTypeEvent.Channel, mimeType)
else -> AnalyticsManager.logMediaUploaded(SubscriptionTypeEvent.Group, mimeType)
}
}
private fun logMessageSent(serverUrl: String) {
when {
roomTypeOf(chatRoomType) is RoomType.DirectMessage ->
AnalyticsManager.logMessageSent(SubscriptionTypeEvent.DirectMessage, serverUrl)
roomTypeOf(chatRoomType) is RoomType.Channel ->
AnalyticsManager.logMessageSent(SubscriptionTypeEvent.Channel, serverUrl)
else -> AnalyticsManager.logMessageSent(SubscriptionTypeEvent.Group, serverUrl)
}
}
fun showReactions(messageId: String) { fun showReactions(messageId: String) {
view.showReactionsPopup(messageId) view.showReactionsPopup(messageId)
} }
......
...@@ -6,8 +6,6 @@ import android.content.Intent ...@@ -6,8 +6,6 @@ import android.content.Intent
import android.os.Bundle import android.os.Bundle
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import android.text.SpannableStringBuilder
import androidx.core.view.isVisible
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.chatroom.presentation.ChatRoomNavigator import chat.rocket.android.chatroom.presentation.ChatRoomNavigator
import chat.rocket.android.server.domain.GetCurrentServerInteractor import chat.rocket.android.server.domain.GetCurrentServerInteractor
...@@ -169,8 +167,4 @@ class ChatRoomActivity : AppCompatActivity(), HasSupportFragmentInjector { ...@@ -169,8 +167,4 @@ class ChatRoomActivity : AppCompatActivity(), HasSupportFragmentInjector {
super.onBackPressed() super.onBackPressed()
overridePendingTransition(R.anim.close_enter, R.anim.close_exit) overridePendingTransition(R.anim.close_enter, R.anim.close_exit)
} }
companion object {
const val TAG_CHAT_ROOM_FRAGMENT = "ChatRoomFragment"
}
} }
\ No newline at end of file
...@@ -55,6 +55,7 @@ import chat.rocket.android.helper.EndlessRecyclerViewScrollListener ...@@ -55,6 +55,7 @@ import chat.rocket.android.helper.EndlessRecyclerViewScrollListener
import chat.rocket.android.helper.ImageHelper import chat.rocket.android.helper.ImageHelper
import chat.rocket.android.helper.KeyboardHelper import chat.rocket.android.helper.KeyboardHelper
import chat.rocket.android.helper.MessageParser 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.asObservable
import chat.rocket.android.util.extensions.circularRevealOrUnreveal import chat.rocket.android.util.extensions.circularRevealOrUnreveal
import chat.rocket.android.util.extensions.fadeIn import chat.rocket.android.util.extensions.fadeIn
...@@ -65,6 +66,8 @@ import chat.rocket.android.util.extensions.rotateBy ...@@ -65,6 +66,8 @@ import chat.rocket.android.util.extensions.rotateBy
import chat.rocket.android.util.extensions.showToast import chat.rocket.android.util.extensions.showToast
import chat.rocket.android.util.extensions.textContent import chat.rocket.android.util.extensions.textContent
import chat.rocket.android.util.extensions.ui 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.RoomType
import chat.rocket.common.model.roomTypeOf import chat.rocket.common.model.roomTypeOf
import chat.rocket.core.internal.realtime.socket.model.State import chat.rocket.core.internal.realtime.socket.model.State
...@@ -107,6 +110,8 @@ fun newInstance( ...@@ -107,6 +110,8 @@ fun newInstance(
} }
} }
internal const val TAG_CHAT_ROOM_FRAGMENT = "ChatRoomFragment"
private const val BUNDLE_CHAT_ROOM_ID = "chat_room_id" private const val BUNDLE_CHAT_ROOM_ID = "chat_room_id"
private const val BUNDLE_CHAT_ROOM_NAME = "chat_room_name" private const val BUNDLE_CHAT_ROOM_NAME = "chat_room_name"
private const val BUNDLE_CHAT_ROOM_TYPE = "chat_room_type" private const val BUNDLE_CHAT_ROOM_TYPE = "chat_room_type"
...@@ -132,6 +137,8 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -132,6 +137,8 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
lateinit var presenter: ChatRoomPresenter lateinit var presenter: ChatRoomPresenter
@Inject @Inject
lateinit var parser: MessageParser lateinit var parser: MessageParser
@Inject
lateinit var analyticsTrackingInteractor: AnalyticsTrackingInteractor
private lateinit var adapter: ChatRoomAdapter private lateinit var adapter: ChatRoomAdapter
internal lateinit var chatRoomId: String internal lateinit var chatRoomId: String
private lateinit var chatRoomName: String private lateinit var chatRoomName: String
...@@ -228,6 +235,10 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -228,6 +235,10 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
it.showToolbarTitle(chatRoomName) it.showToolbarTitle(chatRoomName)
it.showToolbarChatRoomIcon(chatRoomType) it.showToolbarChatRoomIcon(chatRoomType)
} }
if (analyticsTrackingInteractor.get()) {
AnalyticsManager.logScreenView(ScreenViewEvent.ChatRoom)
}
} }
override fun onActivityCreated(savedInstanceState: Bundle?) { override fun onActivityCreated(savedInstanceState: Bundle?) {
......
...@@ -32,12 +32,15 @@ import chat.rocket.android.db.DatabaseManager ...@@ -32,12 +32,15 @@ import chat.rocket.android.db.DatabaseManager
import chat.rocket.android.helper.ChatRoomsSortOrder import chat.rocket.android.helper.ChatRoomsSortOrder
import chat.rocket.android.helper.Constants import chat.rocket.android.helper.Constants
import chat.rocket.android.helper.SharedPreferenceHelper 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.extension.onQueryTextListener
import chat.rocket.android.util.extensions.fadeIn import chat.rocket.android.util.extensions.fadeIn
import chat.rocket.android.util.extensions.fadeOut import chat.rocket.android.util.extensions.fadeOut
import chat.rocket.android.util.extensions.inflate import chat.rocket.android.util.extensions.inflate
import chat.rocket.android.util.extensions.showToast import chat.rocket.android.util.extensions.showToast
import chat.rocket.android.util.extensions.ui 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.android.widget.DividerItemDecoration
import chat.rocket.core.internal.realtime.socket.model.State import chat.rocket.core.internal.realtime.socket.model.State
import dagger.android.support.AndroidSupportInjection import dagger.android.support.AndroidSupportInjection
...@@ -45,6 +48,8 @@ import kotlinx.android.synthetic.main.fragment_chat_rooms.* ...@@ -45,6 +48,8 @@ import kotlinx.android.synthetic.main.fragment_chat_rooms.*
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
internal const val TAG_CHAT_ROOMS_FRAGMENT = "ChatRoomsFragment"
private const val BUNDLE_CHAT_ROOM_ID = "BUNDLE_CHAT_ROOM_ID" private const val BUNDLE_CHAT_ROOM_ID = "BUNDLE_CHAT_ROOM_ID"
class ChatRoomsFragment : Fragment(), ChatRoomsView { class ChatRoomsFragment : Fragment(), ChatRoomsView {
...@@ -54,14 +59,12 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView { ...@@ -54,14 +59,12 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
lateinit var factory: ChatRoomsViewModelFactory lateinit var factory: ChatRoomsViewModelFactory
@Inject @Inject
lateinit var dbManager: DatabaseManager // TODO - remove when moving ChatRoom screen to DB lateinit var dbManager: DatabaseManager // TODO - remove when moving ChatRoom screen to DB
@Inject
lateinit var analyticsTrackingInteractor: AnalyticsTrackingInteractor
lateinit var viewModel: ChatRoomsViewModel lateinit var viewModel: ChatRoomsViewModel
private var searchView: SearchView? = null private var searchView: SearchView? = null
private var sortView: MenuItem? = null private var sortView: MenuItem? = null
private val handler = Handler() private val handler = Handler()
private var chatRoomId: String? = null private var chatRoomId: String? = null
private var progressDialog: ProgressDialog? = null private var progressDialog: ProgressDialog? = null
...@@ -108,6 +111,10 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView { ...@@ -108,6 +111,10 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
subscribeUi() subscribeUi()
setupToolbar() setupToolbar()
if (analyticsTrackingInteractor.get()) {
AnalyticsManager.logScreenView(ScreenViewEvent.ChatRooms)
}
} }
private fun subscribeUi() { private fun subscribeUi() {
...@@ -216,7 +223,7 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView { ...@@ -216,7 +223,7 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
AlertDialog.Builder(context) AlertDialog.Builder(context)
.setTitle(R.string.dialog_sort_title) .setTitle(R.string.dialog_sort_title)
.setView(dialogLayout) .setView(dialogLayout)
.setPositiveButton("Done") { dialog, _ -> .setPositiveButton(R.string.dialog_button_done) { dialog, _ ->
invalidateQueryOnSearch() invalidateQueryOnSearch()
updateSort() updateSort()
dialog.dismiss() dialog.dismiss()
......
...@@ -20,10 +20,13 @@ import chat.rocket.android.createchannel.presentation.CreateChannelView ...@@ -20,10 +20,13 @@ import chat.rocket.android.createchannel.presentation.CreateChannelView
import chat.rocket.android.main.ui.MainActivity import chat.rocket.android.main.ui.MainActivity
import chat.rocket.android.members.adapter.MembersAdapter import chat.rocket.android.members.adapter.MembersAdapter
import chat.rocket.android.members.uimodel.MemberUiModel 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.extension.asObservable
import chat.rocket.android.util.extensions.inflate import chat.rocket.android.util.extensions.inflate
import chat.rocket.android.util.extensions.showToast import chat.rocket.android.util.extensions.showToast
import chat.rocket.android.util.extensions.ui 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.RoomType
import chat.rocket.common.model.roomTypeOf import chat.rocket.common.model.roomTypeOf
import com.google.android.material.chip.Chip import com.google.android.material.chip.Chip
...@@ -34,9 +37,13 @@ import kotlinx.android.synthetic.main.fragment_create_channel.* ...@@ -34,9 +37,13 @@ import kotlinx.android.synthetic.main.fragment_create_channel.*
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import javax.inject.Inject import javax.inject.Inject
internal const val TAG_CREATE_CHANNEL_FRAGMENT = "CreateChannelFragment"
class CreateChannelFragment : Fragment(), CreateChannelView, ActionMode.Callback { class CreateChannelFragment : Fragment(), CreateChannelView, ActionMode.Callback {
@Inject @Inject
lateinit var createChannelPresenter: CreateChannelPresenter lateinit var createChannelPresenter: CreateChannelPresenter
@Inject
lateinit var analyticsTrackingInteractor: AnalyticsTrackingInteractor
private var actionMode: ActionMode? = null private var actionMode: ActionMode? = null
private val adapter: MembersAdapter = MembersAdapter { private val adapter: MembersAdapter = MembersAdapter {
if (it.username != null) { if (it.username != null) {
...@@ -69,6 +76,10 @@ class CreateChannelFragment : Fragment(), CreateChannelView, ActionMode.Callback ...@@ -69,6 +76,10 @@ class CreateChannelFragment : Fragment(), CreateChannelView, ActionMode.Callback
setupViewListeners() setupViewListeners()
setupRecyclerView() setupRecyclerView()
subscribeEditTexts() subscribeEditTexts()
if (analyticsTrackingInteractor.get()) {
AnalyticsManager.logScreenView(ScreenViewEvent.CreateChannel)
}
} }
override fun onDestroyView() { override fun onDestroyView() {
...@@ -161,7 +172,7 @@ class CreateChannelFragment : Fragment(), CreateChannelView, ActionMode.Callback ...@@ -161,7 +172,7 @@ class CreateChannelFragment : Fragment(), CreateChannelView, ActionMode.Callback
override fun prepareToShowChatList() { override fun prepareToShowChatList() {
with(activity as MainActivity) { with(activity as MainActivity) {
setCheckedNavDrawerItem(R.id.action_chat_rooms) setCheckedNavDrawerItem(R.id.menu_action_chats)
openDrawer() openDrawer()
getDrawerLayout().postDelayed(1000) { getDrawerLayout().postDelayed(1000) {
closeDrawer() closeDrawer()
......
...@@ -27,6 +27,7 @@ import chat.rocket.android.main.ui.MainActivity ...@@ -27,6 +27,7 @@ import chat.rocket.android.main.ui.MainActivity
import chat.rocket.android.members.di.MembersFragmentProvider import chat.rocket.android.members.di.MembersFragmentProvider
import chat.rocket.android.mentions.di.MentionsFragmentProvider import chat.rocket.android.mentions.di.MentionsFragmentProvider
import chat.rocket.android.pinnedmessages.di.PinnedMessagesFragmentProvider import chat.rocket.android.pinnedmessages.di.PinnedMessagesFragmentProvider
import chat.rocket.android.preferences.di.PreferencesFragmentProvider
import chat.rocket.android.profile.di.ProfileFragmentProvider import chat.rocket.android.profile.di.ProfileFragmentProvider
import chat.rocket.android.server.di.ChangeServerModule import chat.rocket.android.server.di.ChangeServerModule
import chat.rocket.android.server.ui.ChangeServerActivity import chat.rocket.android.server.ui.ChangeServerActivity
...@@ -60,7 +61,8 @@ abstract class ActivityBuilder { ...@@ -60,7 +61,8 @@ abstract class ActivityBuilder {
ChatRoomsFragmentProvider::class, ChatRoomsFragmentProvider::class,
CreateChannelProvider::class, CreateChannelProvider::class,
ProfileFragmentProvider::class, ProfileFragmentProvider::class,
SettingsFragmentProvider::class SettingsFragmentProvider::class,
PreferencesFragmentProvider::class
] ]
) )
abstract fun bindMainActivity(): MainActivity abstract fun bindMainActivity(): MainActivity
......
...@@ -23,6 +23,7 @@ import chat.rocket.android.push.GroupedPush ...@@ -23,6 +23,7 @@ import chat.rocket.android.push.GroupedPush
import chat.rocket.android.push.PushManager import chat.rocket.android.push.PushManager
import chat.rocket.android.server.domain.AccountsRepository import chat.rocket.android.server.domain.AccountsRepository
import chat.rocket.android.server.domain.ActiveUsersRepository import chat.rocket.android.server.domain.ActiveUsersRepository
import chat.rocket.android.server.domain.AnalyticsTrackingRepository
import chat.rocket.android.server.domain.ChatRoomsRepository import chat.rocket.android.server.domain.ChatRoomsRepository
import chat.rocket.android.server.domain.CurrentServerRepository import chat.rocket.android.server.domain.CurrentServerRepository
import chat.rocket.android.server.domain.GetAccountInteractor import chat.rocket.android.server.domain.GetAccountInteractor
...@@ -45,6 +46,7 @@ import chat.rocket.android.server.infraestructure.SharedPreferencesAccountsRepos ...@@ -45,6 +46,7 @@ import chat.rocket.android.server.infraestructure.SharedPreferencesAccountsRepos
import chat.rocket.android.server.infraestructure.SharedPreferencesMessagesRepository import chat.rocket.android.server.infraestructure.SharedPreferencesMessagesRepository
import chat.rocket.android.server.infraestructure.SharedPreferencesPermissionsRepository import chat.rocket.android.server.infraestructure.SharedPreferencesPermissionsRepository
import chat.rocket.android.server.infraestructure.SharedPreferencesSettingsRepository import chat.rocket.android.server.infraestructure.SharedPreferencesSettingsRepository
import chat.rocket.android.server.infraestructure.SharedPrefsAnalyticsTrackingRepository
import chat.rocket.android.server.infraestructure.SharedPrefsConnectingServerRepository import chat.rocket.android.server.infraestructure.SharedPrefsConnectingServerRepository
import chat.rocket.android.server.infraestructure.SharedPrefsCurrentServerRepository import chat.rocket.android.server.infraestructure.SharedPrefsCurrentServerRepository
import chat.rocket.android.util.AppJsonAdapterFactory import chat.rocket.android.util.AppJsonAdapterFactory
...@@ -163,6 +165,12 @@ class AppModule { ...@@ -163,6 +165,12 @@ class AppModule {
return SharedPrefsCurrentServerRepository(prefs) return SharedPrefsCurrentServerRepository(prefs)
} }
@Provides
@Singleton
fun provideAnalyticsTrackingRepository(prefs: SharedPreferences): AnalyticsTrackingRepository {
return SharedPrefsAnalyticsTrackingRepository(prefs)
}
@Provides @Provides
@ForAuthentication @ForAuthentication
fun provideConnectingServerRepository(prefs: SharedPreferences): CurrentServerRepository { fun provideConnectingServerRepository(prefs: SharedPreferences): CurrentServerRepository {
......
...@@ -330,7 +330,7 @@ class DatabaseManager(val context: Application, ...@@ -330,7 +330,7 @@ class DatabaseManager(val context: Application,
id = room.id, id = room.id,
subscriptionId = subscription.id, subscriptionId = subscription.id,
type = room.type.toString(), type = room.type.toString(),
name = room.name ?: subscription.name ?: throw NullPointerException(),// this should be filtered on the SDK name = room.name ?: subscription.name ?: throw NullPointerException(), // this should be filtered on the SDK
fullname = subscription.fullName ?: room.fullName, fullname = subscription.fullName ?: room.fullName,
userId = userId, userId = userId,
ownerId = room.user?.id, ownerId = room.user?.id,
......
...@@ -16,9 +16,12 @@ import chat.rocket.android.chatroom.uimodel.BaseUiModel ...@@ -16,9 +16,12 @@ import chat.rocket.android.chatroom.uimodel.BaseUiModel
import chat.rocket.android.favoritemessages.presentation.FavoriteMessagesPresenter import chat.rocket.android.favoritemessages.presentation.FavoriteMessagesPresenter
import chat.rocket.android.favoritemessages.presentation.FavoriteMessagesView import chat.rocket.android.favoritemessages.presentation.FavoriteMessagesView
import chat.rocket.android.helper.EndlessRecyclerViewScrollListener 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.inflate
import chat.rocket.android.util.extensions.showToast import chat.rocket.android.util.extensions.showToast
import chat.rocket.android.util.extensions.ui 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 dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_favorite_messages.* import kotlinx.android.synthetic.main.fragment_favorite_messages.*
import javax.inject.Inject import javax.inject.Inject
...@@ -31,13 +34,16 @@ fun newInstance(chatRoomId: String): Fragment { ...@@ -31,13 +34,16 @@ fun newInstance(chatRoomId: String): Fragment {
} }
} }
internal const val TAG_FAVORITE_MESSAGES_FRAGMENT = "FavoriteMessagesFragment"
private const val INTENT_CHAT_ROOM_ID = "chat_room_id" private const val INTENT_CHAT_ROOM_ID = "chat_room_id"
class FavoriteMessagesFragment : Fragment(), FavoriteMessagesView { class FavoriteMessagesFragment : Fragment(), FavoriteMessagesView {
private lateinit var chatRoomId: String
private val adapter = ChatRoomAdapter(enableActions = false)
@Inject @Inject
lateinit var presenter: FavoriteMessagesPresenter lateinit var presenter: FavoriteMessagesPresenter
@Inject
lateinit var analyticsTrackingInteractor: AnalyticsTrackingInteractor
private lateinit var chatRoomId: String
private val adapter = ChatRoomAdapter(enableActions = false)
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
...@@ -61,6 +67,10 @@ class FavoriteMessagesFragment : Fragment(), FavoriteMessagesView { ...@@ -61,6 +67,10 @@ class FavoriteMessagesFragment : Fragment(), FavoriteMessagesView {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
setupToolbar() setupToolbar()
presenter.loadFavoriteMessages(chatRoomId) presenter.loadFavoriteMessages(chatRoomId)
if (analyticsTrackingInteractor.get()) {
AnalyticsManager.logScreenView(ScreenViewEvent.FavoriteMessages)
}
} }
override fun showFavoriteMessages(favoriteMessages: List<BaseUiModel<*>>) { override fun showFavoriteMessages(favoriteMessages: List<BaseUiModel<*>>) {
......
...@@ -21,9 +21,12 @@ import chat.rocket.android.files.uimodel.FileUiModel ...@@ -21,9 +21,12 @@ import chat.rocket.android.files.uimodel.FileUiModel
import chat.rocket.android.helper.EndlessRecyclerViewScrollListener import chat.rocket.android.helper.EndlessRecyclerViewScrollListener
import chat.rocket.android.helper.ImageHelper import chat.rocket.android.helper.ImageHelper
import chat.rocket.android.player.PlayerActivity 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.inflate
import chat.rocket.android.util.extensions.showToast import chat.rocket.android.util.extensions.showToast
import chat.rocket.android.util.extensions.ui 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 dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_files.* import kotlinx.android.synthetic.main.fragment_files.*
import javax.inject.Inject import javax.inject.Inject
...@@ -36,11 +39,14 @@ fun newInstance(chatRoomId: String): Fragment { ...@@ -36,11 +39,14 @@ fun newInstance(chatRoomId: String): Fragment {
} }
} }
internal const val TAG_FILES_FRAGMENT = "FilesFragment"
private const val BUNDLE_CHAT_ROOM_ID = "chat_room_id" private const val BUNDLE_CHAT_ROOM_ID = "chat_room_id"
class FilesFragment : Fragment(), FilesView { class FilesFragment : Fragment(), FilesView {
@Inject @Inject
lateinit var presenter: FilesPresenter lateinit var presenter: FilesPresenter
@Inject
lateinit var analyticsTrackingInteractor: AnalyticsTrackingInteractor
private val adapter: FilesAdapter = private val adapter: FilesAdapter =
FilesAdapter { fileUiModel -> presenter.openFile(fileUiModel) } FilesAdapter { fileUiModel -> presenter.openFile(fileUiModel) }
private val linearLayoutManager = LinearLayoutManager(context) private val linearLayoutManager = LinearLayoutManager(context)
...@@ -68,6 +74,10 @@ class FilesFragment : Fragment(), FilesView { ...@@ -68,6 +74,10 @@ class FilesFragment : Fragment(), FilesView {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
setupRecyclerView() setupRecyclerView()
presenter.loadFiles(chatRoomId) presenter.loadFiles(chatRoomId)
if (analyticsTrackingInteractor.get()) {
AnalyticsManager.logScreenView(ScreenViewEvent.Files)
}
} }
override fun showFiles(dataSet: List<FileUiModel>, total: Long) { override fun showFiles(dataSet: List<FileUiModel>, total: Long) {
......
...@@ -2,7 +2,6 @@ package chat.rocket.android.helper ...@@ -2,7 +2,6 @@ package chat.rocket.android.helper
import chat.rocket.android.infrastructure.LocalRepository import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.server.domain.GetCurrentServerInteractor import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.domain.PublicSettings
import chat.rocket.android.server.domain.SettingsRepository import chat.rocket.android.server.domain.SettingsRepository
import chat.rocket.android.server.domain.useRealName import chat.rocket.android.server.domain.useRealName
import chat.rocket.common.model.SimpleUser import chat.rocket.common.model.SimpleUser
...@@ -12,43 +11,13 @@ import javax.inject.Inject ...@@ -12,43 +11,13 @@ import javax.inject.Inject
class UserHelper @Inject constructor( class UserHelper @Inject constructor(
private val localRepository: LocalRepository, private val localRepository: LocalRepository,
private val getCurrentServerInteractor: GetCurrentServerInteractor, private val getCurrentServerInteractor: GetCurrentServerInteractor,
settingsRepository: SettingsRepository private val settingsRepository: SettingsRepository
) { ) {
private val settings: PublicSettings = settingsRepository.get(getCurrentServerInteractor.get()!!)
/**
* Return the display name for the given [user].
* If setting 'Use_Real_Name' is true then the real name will be given, or else
* the username without the '@' is yielded. The fallback for any case is the username, which
* could be null.
*/
fun displayName(user: User): String? {
return if (settings.useRealName()) user.name ?: user.username else user.username
}
fun displayName(user: SimpleUser): String {
return if (settings.useRealName()) user.name ?: user.username ?: "" else user.username ?: ""
}
/**
* Return current logged user's display name.
*
* @see displayName
*/
fun displayName(): String? {
user()?.let {
return displayName(it)
}
return null
}
/** /**
* Return current logged [User]. * Return current logged [User].
*/ */
fun user(): User? { fun user(): User? = getCurrentServerInteractor.get()?.let { localRepository.getCurrentUser(it) }
return localRepository.getCurrentUser(serverUrl())
}
/** /**
* Return the username for the current logged [User]. * Return the username for the current logged [User].
...@@ -56,13 +25,20 @@ class UserHelper @Inject constructor( ...@@ -56,13 +25,20 @@ class UserHelper @Inject constructor(
fun username(): String? = localRepository.get(LocalRepository.CURRENT_USERNAME_KEY, null) fun username(): String? = localRepository.get(LocalRepository.CURRENT_USERNAME_KEY, null)
/** /**
* Whether current [User] is admin on the current server. * Return the display name for the given [user].
* If setting 'Use_Real_Name' is true then the real name will be given, otherwise the username
* without the '@' is yielded.
*/ */
fun isAdmin(): Boolean { fun displayName(user: SimpleUser) = getCurrentServerInteractor.get()?.let {
return user()?.roles?.find { it.equals("admin", ignoreCase = true) } != null if (settingsRepository.get(it).useRealName()) {
user.name
} else {
user.username
} }
}.orEmpty()
private fun serverUrl(): String { /**
return getCurrentServerInteractor.get()!! * Whether current [User] is admin on the current server.
} */
fun isAdmin(): Boolean = user()?.roles?.find { it.equals("admin", true) } != null
} }
...@@ -4,39 +4,50 @@ import chat.rocket.android.R ...@@ -4,39 +4,50 @@ import chat.rocket.android.R
import chat.rocket.android.authentication.ui.newServerIntent import chat.rocket.android.authentication.ui.newServerIntent
import chat.rocket.android.chatroom.ui.chatRoomIntent import chat.rocket.android.chatroom.ui.chatRoomIntent
import chat.rocket.android.chatrooms.ui.ChatRoomsFragment import chat.rocket.android.chatrooms.ui.ChatRoomsFragment
import chat.rocket.android.chatrooms.ui.TAG_CHAT_ROOMS_FRAGMENT
import chat.rocket.android.createchannel.ui.CreateChannelFragment import chat.rocket.android.createchannel.ui.CreateChannelFragment
import chat.rocket.android.createchannel.ui.TAG_CREATE_CHANNEL_FRAGMENT
import chat.rocket.android.main.ui.MainActivity import chat.rocket.android.main.ui.MainActivity
import chat.rocket.android.profile.ui.ProfileFragment import chat.rocket.android.profile.ui.ProfileFragment
import chat.rocket.android.profile.ui.TAG_PROFILE_FRAGMENT
import chat.rocket.android.server.ui.changeServerIntent import chat.rocket.android.server.ui.changeServerIntent
import chat.rocket.android.settings.ui.SettingsFragment import chat.rocket.android.settings.ui.SettingsFragment
import chat.rocket.android.settings.ui.TAG_SETTINGS_FRAGMENT
import chat.rocket.android.util.extensions.addFragment import chat.rocket.android.util.extensions.addFragment
import chat.rocket.android.webview.adminpanel.ui.AdminPanelWebViewFragment
class MainNavigator(internal val activity: MainActivity) { class MainNavigator(internal val activity: MainActivity) {
fun toChatList(chatRoomId: String? = null) { fun toChatList(chatRoomId: String? = null) {
activity.addFragment("ChatRoomsFragment", R.id.fragment_container) { activity.addFragment(TAG_CHAT_ROOMS_FRAGMENT, R.id.fragment_container) {
ChatRoomsFragment.newInstance(chatRoomId) ChatRoomsFragment.newInstance(chatRoomId)
} }
} }
fun toCreateChannel() { fun toCreateChannel() {
activity.addFragment("CreateChannelFragment", R.id.fragment_container) { activity.addFragment(TAG_CREATE_CHANNEL_FRAGMENT, R.id.fragment_container) {
CreateChannelFragment.newInstance() CreateChannelFragment.newInstance()
} }
} }
fun toUserProfile() { fun toUserProfile() {
activity.addFragment("ProfileFragment", R.id.fragment_container) { activity.addFragment(TAG_PROFILE_FRAGMENT, R.id.fragment_container) {
ProfileFragment.newInstance() ProfileFragment.newInstance()
} }
} }
fun toSettings() { fun toSettings() {
activity.addFragment("SettingsFragment", R.id.fragment_container) { activity.addFragment(TAG_SETTINGS_FRAGMENT, R.id.fragment_container) {
SettingsFragment.newInstance() SettingsFragment.newInstance()
} }
} }
fun toAdminPanel(webPageUrl: String, userToken: String) {
activity.addFragment("AdminPanelWebViewFragment", R.id.fragment_container) {
AdminPanelWebViewFragment.newInstance(webPageUrl, userToken)
}
}
fun toChatRoom( fun toChatRoom(
chatRoomId: String, chatRoomId: String,
chatRoomName: String, chatRoomName: String,
...@@ -62,7 +73,17 @@ class MainNavigator(internal val activity: MainActivity) { ...@@ -62,7 +73,17 @@ class MainNavigator(internal val activity: MainActivity) {
activity.overridePendingTransition(R.anim.open_enter, R.anim.open_exit) activity.overridePendingTransition(R.anim.open_enter, R.anim.open_exit)
} }
fun toNewServer(serverUrl: String? = null) { /**
* Switches to a server, given a [serverUrl] or adds a new server (navigating to the
* AuthenticationActivity) if the user server list only contains one server and the
* user logs out from this server.
* NOTE: If the user has more than one server and logs out from the current server, then it will
* switch to the first server in the server list.
*
* @param serverUrl The server URL to switch from, or null in case user logs out from the
* current server.
*/
fun switchOrAddNewServer(serverUrl: String? = null) {
activity.startActivity(activity.changeServerIntent(serverUrl = serverUrl)) activity.startActivity(activity.changeServerIntent(serverUrl = serverUrl))
activity.finish() activity.finish()
} }
......
...@@ -11,6 +11,7 @@ import chat.rocket.android.server.domain.GetCurrentServerInteractor ...@@ -11,6 +11,7 @@ import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.domain.GetSettingsInteractor import chat.rocket.android.server.domain.GetSettingsInteractor
import chat.rocket.android.server.domain.PublicSettings import chat.rocket.android.server.domain.PublicSettings
import chat.rocket.android.server.domain.RefreshSettingsInteractor import chat.rocket.android.server.domain.RefreshSettingsInteractor
import chat.rocket.android.server.domain.RefreshPermissionsInteractor
import chat.rocket.android.server.domain.RemoveAccountInteractor import chat.rocket.android.server.domain.RemoveAccountInteractor
import chat.rocket.android.server.domain.SaveAccountInteractor import chat.rocket.android.server.domain.SaveAccountInteractor
import chat.rocket.android.server.domain.TokenRepository import chat.rocket.android.server.domain.TokenRepository
...@@ -20,6 +21,7 @@ import chat.rocket.android.server.infraestructure.ConnectionManagerFactory ...@@ -20,6 +21,7 @@ import chat.rocket.android.server.infraestructure.ConnectionManagerFactory
import chat.rocket.android.server.infraestructure.RocketChatClientFactory import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.server.presentation.CheckServerPresenter import chat.rocket.android.server.presentation.CheckServerPresenter
import chat.rocket.android.util.extension.launchUI 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.registerPushToken
import chat.rocket.android.util.extensions.serverLogoUrl import chat.rocket.android.util.extensions.serverLogoUrl
import chat.rocket.android.util.retryIO import chat.rocket.android.util.retryIO
...@@ -28,14 +30,12 @@ import chat.rocket.common.RocketChatException ...@@ -28,14 +30,12 @@ import chat.rocket.common.RocketChatException
import chat.rocket.common.model.UserStatus import chat.rocket.common.model.UserStatus
import chat.rocket.common.util.ifNull import chat.rocket.common.util.ifNull
import chat.rocket.core.RocketChatClient import chat.rocket.core.RocketChatClient
import chat.rocket.core.internal.realtime.setDefaultStatus
import chat.rocket.core.internal.rest.logout import chat.rocket.core.internal.rest.logout
import chat.rocket.core.internal.rest.me import chat.rocket.core.internal.rest.me
import chat.rocket.core.internal.rest.unregisterPushToken import chat.rocket.core.internal.rest.unregisterPushToken
import chat.rocket.core.model.Myself import chat.rocket.core.model.Myself
import kotlinx.coroutines.experimental.CommonPool import kotlinx.coroutines.experimental.CommonPool
import kotlinx.coroutines.experimental.channels.Channel import kotlinx.coroutines.experimental.channels.Channel
import kotlinx.coroutines.experimental.launch
import kotlinx.coroutines.experimental.withContext import kotlinx.coroutines.experimental.withContext
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
...@@ -47,6 +47,7 @@ class MainPresenter @Inject constructor( ...@@ -47,6 +47,7 @@ class MainPresenter @Inject constructor(
private val tokenRepository: TokenRepository, private val tokenRepository: TokenRepository,
private val serverInteractor: GetCurrentServerInteractor, private val serverInteractor: GetCurrentServerInteractor,
private val refreshSettingsInteractor: RefreshSettingsInteractor, private val refreshSettingsInteractor: RefreshSettingsInteractor,
private val refreshPermissionsInteractor: RefreshPermissionsInteractor,
private val localRepository: LocalRepository, private val localRepository: LocalRepository,
private val navHeaderMapper: NavHeaderUiModelMapper, private val navHeaderMapper: NavHeaderUiModelMapper,
private val saveAccountInteractor: SaveAccountInteractor, private val saveAccountInteractor: SaveAccountInteractor,
...@@ -63,7 +64,6 @@ class MainPresenter @Inject constructor( ...@@ -63,7 +64,6 @@ class MainPresenter @Inject constructor(
private val dbManager = dbManagerFactory.create(currentServer) private val dbManager = dbManagerFactory.create(currentServer)
private val client: RocketChatClient = factory.create(currentServer) private val client: RocketChatClient = factory.create(currentServer)
private var settings: PublicSettings = getSettingsInteractor.get(serverInteractor.get()!!) private var settings: PublicSettings = getSettingsInteractor.get(serverInteractor.get()!!)
private val userDataChannel = Channel<Myself>() private val userDataChannel = Channel<Myself>()
fun toChatList(chatRoomId: String? = null) = navigator.toChatList(chatRoomId) fun toChatList(chatRoomId: String? = null) = navigator.toChatList(chatRoomId)
...@@ -72,6 +72,10 @@ class MainPresenter @Inject constructor( ...@@ -72,6 +72,10 @@ class MainPresenter @Inject constructor(
fun toSettings() = navigator.toSettings() fun toSettings() = navigator.toSettings()
fun toAdminPanel() = tokenRepository.get(currentServer)?.let {
navigator.toAdminPanel(currentServer.adminPanelUrl(), it.authToken)
}
fun toCreateChannel() = navigator.toCreateChannel() fun toCreateChannel() = navigator.toCreateChannel()
fun loadServerAccounts() { fun loadServerAccounts() {
...@@ -145,7 +149,7 @@ class MainPresenter @Inject constructor( ...@@ -145,7 +149,7 @@ class MainPresenter @Inject constructor(
tokenRepository.remove(currentServer) tokenRepository.remove(currentServer)
withContext(CommonPool) { dbManager.logout() } withContext(CommonPool) { dbManager.logout() }
navigator.toNewServer() navigator.switchOrAddNewServer()
} catch (ex: Exception) { } catch (ex: Exception) {
Timber.d(ex, "Error cleaning up the session...") Timber.d(ex, "Error cleaning up the session...")
} }
...@@ -155,6 +159,7 @@ class MainPresenter @Inject constructor( ...@@ -155,6 +159,7 @@ class MainPresenter @Inject constructor(
fun connect() { fun connect() {
refreshSettingsInteractor.refreshAsync(currentServer) refreshSettingsInteractor.refreshAsync(currentServer)
refreshPermissionsInteractor.refreshAsync(currentServer)
manager.connect() manager.connect()
} }
...@@ -165,7 +170,7 @@ class MainPresenter @Inject constructor( ...@@ -165,7 +170,7 @@ class MainPresenter @Inject constructor(
fun changeServer(serverUrl: String) { fun changeServer(serverUrl: String) {
if (currentServer != serverUrl) { if (currentServer != serverUrl) {
navigator.toNewServer(serverUrl) navigator.switchOrAddNewServer(serverUrl)
} else { } else {
view.closeServerSelection() view.closeServerSelection()
} }
......
...@@ -5,10 +5,9 @@ import android.app.Activity ...@@ -5,10 +5,9 @@ import android.app.Activity
import android.app.AlertDialog import android.app.AlertDialog
import android.app.ProgressDialog import android.app.ProgressDialog
import android.os.Bundle import android.os.Bundle
import android.view.Gravity
import android.view.MenuItem
import androidx.annotation.IdRes import androidx.annotation.IdRes
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.GravityCompat
import androidx.drawerlayout.widget.DrawerLayout import androidx.drawerlayout.widget.DrawerLayout
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
...@@ -19,15 +18,16 @@ import chat.rocket.android.main.adapter.Selector ...@@ -19,15 +18,16 @@ import chat.rocket.android.main.adapter.Selector
import chat.rocket.android.main.presentation.MainPresenter import chat.rocket.android.main.presentation.MainPresenter
import chat.rocket.android.main.presentation.MainView import chat.rocket.android.main.presentation.MainView
import chat.rocket.android.main.uimodel.NavHeaderUiModel import chat.rocket.android.main.uimodel.NavHeaderUiModel
import chat.rocket.android.server.domain.PermissionsInteractor
import chat.rocket.android.server.domain.model.Account import chat.rocket.android.server.domain.model.Account
import chat.rocket.android.server.ui.INTENT_CHAT_ROOM_ID import chat.rocket.android.server.ui.INTENT_CHAT_ROOM_ID
import chat.rocket.android.util.extensions.fadeIn import chat.rocket.android.util.extensions.fadeIn
import chat.rocket.android.util.extensions.fadeOut import chat.rocket.android.util.extensions.fadeOut
import chat.rocket.android.util.extensions.rotateBy import chat.rocket.android.util.extensions.rotateBy
import chat.rocket.android.util.extensions.showToast import chat.rocket.android.util.extensions.showToast
import chat.rocket.android.util.invalidateFirebaseToken
import chat.rocket.android.util.refreshFCMToken
import chat.rocket.common.model.UserStatus import chat.rocket.common.model.UserStatus
import com.google.firebase.iid.FirebaseInstanceId
import com.google.firebase.messaging.FirebaseMessaging
import dagger.android.AndroidInjection import dagger.android.AndroidInjection
import dagger.android.AndroidInjector import dagger.android.AndroidInjector
import dagger.android.DispatchingAndroidInjector import dagger.android.DispatchingAndroidInjector
...@@ -51,6 +51,8 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector, ...@@ -51,6 +51,8 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector,
lateinit var fragmentDispatchingAndroidInjector: DispatchingAndroidInjector<Fragment> lateinit var fragmentDispatchingAndroidInjector: DispatchingAndroidInjector<Fragment>
@Inject @Inject
lateinit var presenter: MainPresenter lateinit var presenter: MainPresenter
@Inject
lateinit var permissions: PermissionsInteractor
private var isFragmentAdded: Boolean = false private var isFragmentAdded: Boolean = false
private var expanded = false private var expanded = false
private val headerLayout by lazy { view_navigation.getHeaderView(0) } private val headerLayout by lazy { view_navigation.getHeaderView(0) }
...@@ -63,13 +65,7 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector, ...@@ -63,13 +65,7 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector,
setContentView(R.layout.activity_main) setContentView(R.layout.activity_main)
launch(CommonPool) { launch(CommonPool) {
try { refreshFCMToken(presenter)
val token = FirebaseInstanceId.getInstance().token
Timber.d("FCM token: $token")
presenter.refreshToken(token)
} catch (ex: Exception) {
Timber.d(ex, "Missing play services...")
}
} }
chatRoomId = intent.getStringExtra(INTENT_CHAT_ROOM_ID) chatRoomId = intent.getStringExtra(INTENT_CHAT_ROOM_ID)
...@@ -135,7 +131,7 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector, ...@@ -135,7 +131,7 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector,
text_user_name.text = userDisplayName text_user_name.text = userDisplayName
} }
if (userAvatar != null) { if (userAvatar != null) {
image_avatar.setImageURI(userAvatar) setAvatar(userAvatar)
} }
if (serverLogo != null) { if (serverLogo != null) {
server_logo.setImageURI(serverLogo) server_logo.setImageURI(serverLogo)
...@@ -172,9 +168,9 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector, ...@@ -172,9 +168,9 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector,
} }
headerLayout.image_avatar.setOnClickListener { headerLayout.image_avatar.setOnClickListener {
view_navigation.menu.findItem(R.id.action_profile).isChecked = true view_navigation.menu.findItem(R.id.menu_action_profile).isChecked = true
presenter.toUserProfile() presenter.toUserProfile()
drawer_layout.closeDrawer(Gravity.START) drawer_layout.closeDrawer(GravityCompat.START)
} }
} }
...@@ -211,7 +207,7 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector, ...@@ -211,7 +207,7 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector,
} }
override fun invalidateToken(token: String) = override fun invalidateToken(token: String) =
FirebaseInstanceId.getInstance().deleteToken(token, FirebaseMessaging.INSTANCE_ID_SCOPE) invalidateFirebaseToken(token)
override fun showMessage(resId: Int) = showToast(resId) override fun showMessage(resId: Int) = showToast(resId)
...@@ -224,44 +220,31 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector, ...@@ -224,44 +220,31 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector,
} }
fun setupNavigationView() { fun setupNavigationView() {
view_navigation.setNavigationItemSelectedListener { menuItem -> with (view_navigation.menu) {
menuItem.isChecked = true clear()
setupMenu(this)
}
view_navigation.setNavigationItemSelectedListener {
it.isChecked = true
closeDrawer() closeDrawer()
onNavDrawerItemSelected(menuItem) onNavDrawerItemSelected(it)
true true
} }
toolbar.setNavigationIcon(R.drawable.ic_menu_white_24dp) toolbar.setNavigationIcon(R.drawable.ic_menu_white_24dp)
toolbar.setNavigationOnClickListener { toolbar.setNavigationOnClickListener { openDrawer() }
openDrawer()
}
} }
private fun onNavDrawerItemSelected(menuItem: MenuItem) { fun setAvatar(avatarUrl: String) {
when (menuItem.itemId) { headerLayout.image_avatar.setImageURI(avatarUrl)
R.id.action_chat_rooms -> {
presenter.toChatList()
}
R.id.action_profile -> {
presenter.toUserProfile()
}
R.id.action_channel -> {
presenter.toCreateChannel()
}
R.id.action_settings -> {
presenter.toSettings()
}
R.id.action_logout -> {
presenter.logout()
}
}
} }
fun getDrawerLayout(): DrawerLayout = drawer_layout fun getDrawerLayout(): DrawerLayout = drawer_layout
fun openDrawer() = drawer_layout.openDrawer(Gravity.START) fun openDrawer() = drawer_layout.openDrawer(GravityCompat.START)
fun closeDrawer() = drawer_layout.closeDrawer(Gravity.START) fun closeDrawer() = drawer_layout.closeDrawer(GravityCompat.START)
fun setCheckedNavDrawerItem(@IdRes item: Int) = view_navigation.setCheckedItem(item) fun setCheckedNavDrawerItem(@IdRes item: Int) = view_navigation.setCheckedItem(item)
......
package chat.rocket.android.main.ui
import android.view.Menu
import android.view.MenuItem
import chat.rocket.android.R
internal fun MainActivity.setupMenu(menu: Menu) {
with(menu) {
add(
R.id.menu_section_one,
R.id.menu_action_chats,
Menu.NONE,
R.string.title_chats
).setIcon(R.drawable.ic_chat_bubble_black_24dp)
.isChecked = true
add(
R.id.menu_section_one,
R.id.menu_action_create_channel,
Menu.NONE,
R.string.action_create_channel
).setIcon(R.drawable.ic_create_black_24dp)
add(
R.id.menu_section_two,
R.id.menu_action_profile,
Menu.NONE,
R.string.title_profile
).setIcon(R.drawable.ic_person_black_24dp)
add(
R.id.menu_section_two,
R.id.menu_action_settings,
Menu.NONE,
R.string.title_settings
).setIcon(R.drawable.ic_settings_black_24dp)
if (permissions.canSeeTheAdminPanel()) {
add(
R.id.menu_section_two,
R.id.menu_action_admin_panel,
Menu.NONE,
R.string.title_admin_panel
).setIcon(R.drawable.ic_settings_black_24dp)
}
add(
R.id.menu_section_three,
R.id.menu_action_logout,
Menu.NONE,
R.string.action_logout
).setIcon(R.drawable.ic_logout_black_24dp)
setGroupCheckable(R.id.menu_section_one, true, true)
setGroupCheckable(R.id.menu_section_two, true, true)
setGroupCheckable(R.id.menu_section_three, true, true)
}
}
internal fun MainActivity.onNavDrawerItemSelected(menuItem: MenuItem) {
when (menuItem.itemId) {
R.id.menu_action_chats-> presenter.toChatList()
R.id.menu_action_create_channel -> presenter.toCreateChannel()
R.id.menu_action_profile -> presenter.toUserProfile()
R.id.menu_action_settings -> presenter.toSettings()
R.id.menu_action_admin_panel -> presenter.toAdminPanel()
R.id.menu_action_logout -> presenter.logout()
}
}
package chat.rocket.android.members.presentation package chat.rocket.android.members.presentation
import chat.rocket.android.chatroom.ui.ChatRoomActivity import chat.rocket.android.chatroom.ui.ChatRoomActivity
import chat.rocket.android.members.ui.TAG_MEMBER_BOTTOM_SHEET_FRAGMENT
import chat.rocket.android.members.ui.newInstance import chat.rocket.android.members.ui.newInstance
class MembersNavigator(internal val activity: ChatRoomActivity) { class MembersNavigator(internal val activity: ChatRoomActivity) {
...@@ -8,7 +9,7 @@ class MembersNavigator(internal val activity: ChatRoomActivity) { ...@@ -8,7 +9,7 @@ class MembersNavigator(internal val activity: ChatRoomActivity) {
fun toMemberDetails(avatarUri: String, realName: String, username: String, email: String, utcOffset: String) { fun toMemberDetails(avatarUri: String, realName: String, username: String, email: String, utcOffset: String) {
activity.apply { activity.apply {
newInstance(avatarUri, realName, username, email, utcOffset) newInstance(avatarUri, realName, username, email, utcOffset)
.show(supportFragmentManager, "MemberBottomSheetFragment") .show(supportFragmentManager, TAG_MEMBER_BOTTOM_SHEET_FRAGMENT)
} }
} }
} }
package chat.rocket.android.members.ui package chat.rocket.android.members.ui
import android.os.Bundle import android.os.Bundle
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.view.isVisible import androidx.core.view.isVisible
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.server.domain.AnalyticsTrackingInteractor
import chat.rocket.android.util.extensions.content import chat.rocket.android.util.extensions.content
import chat.rocket.android.util.extensions.textContent 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 kotlinx.android.synthetic.main.fragment_member_bottom_sheet.* import kotlinx.android.synthetic.main.fragment_member_bottom_sheet.*
import javax.inject.Inject
fun newInstance(avatarUri: String, fun newInstance(
avatarUri: String,
realName: String, realName: String,
username: String, username: String,
email: String, email: String,
utcOffset: String): BottomSheetDialogFragment { utcOffset: String
): BottomSheetDialogFragment {
return MemberBottomSheetFragment().apply { return MemberBottomSheetFragment().apply {
arguments = Bundle(1).apply { arguments = Bundle(1).apply {
putString(BUNDLE_AVATAR_URI, avatarUri) putString(BUNDLE_AVATAR_URI, avatarUri)
...@@ -27,13 +33,17 @@ fun newInstance(avatarUri: String, ...@@ -27,13 +33,17 @@ fun newInstance(avatarUri: String,
} }
} }
internal const val TAG_MEMBER_BOTTOM_SHEET_FRAGMENT = "MemberBottomSheetFragment"
private const val BUNDLE_AVATAR_URI = "avatar_uri" private const val BUNDLE_AVATAR_URI = "avatar_uri"
private const val BUNDLE_REAL_NAME = "real_name" private const val BUNDLE_REAL_NAME = "real_name"
private const val BUNDLE_USERNAME = "username" private const val BUNDLE_USERNAME = "username"
private const val BUNDLE_EMAIL = "email" private const val BUNDLE_EMAIL = "email"
private const val BUNDLE_UTC_OFFSET = "utc_offset" private const val BUNDLE_UTC_OFFSET = "utc_offset"
class MemberBottomSheetFragment: BottomSheetDialogFragment() { class MemberBottomSheetFragment : BottomSheetDialogFragment() {
@Inject
lateinit var analyticsTrackingInteractor: AnalyticsTrackingInteractor
private lateinit var avatarUri: String private lateinit var avatarUri: String
private lateinit var realName: String private lateinit var realName: String
private lateinit var username: String private lateinit var username: String
...@@ -54,12 +64,21 @@ class MemberBottomSheetFragment: BottomSheetDialogFragment() { ...@@ -54,12 +64,21 @@ class MemberBottomSheetFragment: BottomSheetDialogFragment() {
requireNotNull(bundle) { "no arguments supplied when the fragment was instantiated" } requireNotNull(bundle) { "no arguments supplied when the fragment was instantiated" }
} }
} }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? =
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? =
inflater.inflate(R.layout.fragment_member_bottom_sheet, container, false) inflater.inflate(R.layout.fragment_member_bottom_sheet, container, false)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
showMemberDetails() showMemberDetails()
if (analyticsTrackingInteractor.get()) {
AnalyticsManager.logScreenView(ScreenViewEvent.MemberBottomSheet)
}
} }
private fun showMemberDetails() { private fun showMemberDetails() {
...@@ -74,7 +93,7 @@ class MemberBottomSheetFragment: BottomSheetDialogFragment() { ...@@ -74,7 +93,7 @@ class MemberBottomSheetFragment: BottomSheetDialogFragment() {
text_member_email_address.isVisible = false text_member_email_address.isVisible = false
} }
if (utcOffset.isNotEmpty()){ if (utcOffset.isNotEmpty()) {
text_member_utc.content = utcOffset text_member_utc.content = utcOffset
} else { } else {
text_utc.isVisible = false text_utc.isVisible = false
......
...@@ -16,9 +16,12 @@ import chat.rocket.android.members.adapter.MembersAdapter ...@@ -16,9 +16,12 @@ import chat.rocket.android.members.adapter.MembersAdapter
import chat.rocket.android.members.presentation.MembersPresenter import chat.rocket.android.members.presentation.MembersPresenter
import chat.rocket.android.members.presentation.MembersView import chat.rocket.android.members.presentation.MembersView
import chat.rocket.android.members.uimodel.MemberUiModel 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.inflate
import chat.rocket.android.util.extensions.showToast import chat.rocket.android.util.extensions.showToast
import chat.rocket.android.util.extensions.ui 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 dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_members.* import kotlinx.android.synthetic.main.fragment_members.*
import javax.inject.Inject import javax.inject.Inject
...@@ -31,11 +34,14 @@ fun newInstance(chatRoomId: String): Fragment { ...@@ -31,11 +34,14 @@ fun newInstance(chatRoomId: String): Fragment {
} }
} }
internal const val TAG_MEMBERS_FRAGMENT = "MembersFragment"
private const val BUNDLE_CHAT_ROOM_ID = "chat_room_id" private const val BUNDLE_CHAT_ROOM_ID = "chat_room_id"
class MembersFragment : Fragment(), MembersView { class MembersFragment : Fragment(), MembersView {
@Inject @Inject
lateinit var presenter: MembersPresenter lateinit var presenter: MembersPresenter
@Inject
lateinit var analyticsTrackingInteractor: AnalyticsTrackingInteractor
private val adapter: MembersAdapter = private val adapter: MembersAdapter =
MembersAdapter { memberUiModel -> presenter.toMemberDetails(memberUiModel) } MembersAdapter { memberUiModel -> presenter.toMemberDetails(memberUiModel) }
private val linearLayoutManager = LinearLayoutManager(context) private val linearLayoutManager = LinearLayoutManager(context)
...@@ -63,6 +69,10 @@ class MembersFragment : Fragment(), MembersView { ...@@ -63,6 +69,10 @@ class MembersFragment : Fragment(), MembersView {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
setupRecyclerView() setupRecyclerView()
presenter.loadChatRoomsMembers(chatRoomId) presenter.loadChatRoomsMembers(chatRoomId)
if (analyticsTrackingInteractor.get()) {
AnalyticsManager.logScreenView(ScreenViewEvent.Members)
}
} }
override fun showMembers(dataSet: List<MemberUiModel>, total: Long) { override fun showMembers(dataSet: List<MemberUiModel>, total: Long) {
......
...@@ -16,9 +16,12 @@ import chat.rocket.android.chatroom.uimodel.BaseUiModel ...@@ -16,9 +16,12 @@ import chat.rocket.android.chatroom.uimodel.BaseUiModel
import chat.rocket.android.helper.EndlessRecyclerViewScrollListener import chat.rocket.android.helper.EndlessRecyclerViewScrollListener
import chat.rocket.android.mentions.presentention.MentionsPresenter import chat.rocket.android.mentions.presentention.MentionsPresenter
import chat.rocket.android.mentions.presentention.MentionsView 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.inflate
import chat.rocket.android.util.extensions.showToast import chat.rocket.android.util.extensions.showToast
import chat.rocket.android.util.extensions.ui 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 dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_mentions.* import kotlinx.android.synthetic.main.fragment_mentions.*
import javax.inject.Inject import javax.inject.Inject
...@@ -31,6 +34,7 @@ fun newInstance(chatRoomId: String): Fragment { ...@@ -31,6 +34,7 @@ fun newInstance(chatRoomId: String): Fragment {
} }
} }
internal const val TAG_MENTIONS_FRAGMENT = "MentionsFragment"
private const val BUNDLE_CHAT_ROOM_ID = "chat_room_id" private const val BUNDLE_CHAT_ROOM_ID = "chat_room_id"
class MentionsFragment : Fragment(), MentionsView { class MentionsFragment : Fragment(), MentionsView {
...@@ -39,6 +43,8 @@ class MentionsFragment : Fragment(), MentionsView { ...@@ -39,6 +43,8 @@ class MentionsFragment : Fragment(), MentionsView {
private val adapter = ChatRoomAdapter(enableActions = false) private val adapter = ChatRoomAdapter(enableActions = false)
@Inject @Inject
lateinit var presenter: MentionsPresenter lateinit var presenter: MentionsPresenter
@Inject
lateinit var analyticsTrackingInteractor: AnalyticsTrackingInteractor
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
...@@ -63,6 +69,10 @@ class MentionsFragment : Fragment(), MentionsView { ...@@ -63,6 +69,10 @@ class MentionsFragment : Fragment(), MentionsView {
setupToolbar() setupToolbar()
presenter.loadMentions(chatRoomId) presenter.loadMentions(chatRoomId)
if (analyticsTrackingInteractor.get()) {
AnalyticsManager.logScreenView(ScreenViewEvent.Mentions)
}
} }
override fun showMentions(mentions: List<BaseUiModel<*>>) { override fun showMentions(mentions: List<BaseUiModel<*>>) {
......
package chat.rocket.android.pinnedmessages.ui package chat.rocket.android.pinnedmessages.ui
import android.os.Bundle import android.os.Bundle
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.DefaultItemAnimator
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.DefaultItemAnimator
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.chatroom.adapter.ChatRoomAdapter import chat.rocket.android.chatroom.adapter.ChatRoomAdapter
import chat.rocket.android.chatroom.ui.ChatRoomActivity import chat.rocket.android.chatroom.ui.ChatRoomActivity
...@@ -16,9 +16,12 @@ import chat.rocket.android.chatroom.uimodel.BaseUiModel ...@@ -16,9 +16,12 @@ import chat.rocket.android.chatroom.uimodel.BaseUiModel
import chat.rocket.android.helper.EndlessRecyclerViewScrollListener import chat.rocket.android.helper.EndlessRecyclerViewScrollListener
import chat.rocket.android.pinnedmessages.presentation.PinnedMessagesPresenter import chat.rocket.android.pinnedmessages.presentation.PinnedMessagesPresenter
import chat.rocket.android.pinnedmessages.presentation.PinnedMessagesView import chat.rocket.android.pinnedmessages.presentation.PinnedMessagesView
import chat.rocket.android.server.domain.AnalyticsTrackingInteractor
import chat.rocket.android.util.extensions.inflate import chat.rocket.android.util.extensions.inflate
import chat.rocket.android.util.extensions.showToast import chat.rocket.android.util.extensions.showToast
import chat.rocket.android.util.extensions.ui 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 dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_pinned_messages.* import kotlinx.android.synthetic.main.fragment_pinned_messages.*
import javax.inject.Inject import javax.inject.Inject
...@@ -31,14 +34,16 @@ fun newInstance(chatRoomId: String): Fragment { ...@@ -31,14 +34,16 @@ fun newInstance(chatRoomId: String): Fragment {
} }
} }
internal const val TAG_PINNED_MESSAGES_FRAGMENT = "PinnedMessagesFragment"
private const val BUNDLE_CHAT_ROOM_ID = "chat_room_id" private const val BUNDLE_CHAT_ROOM_ID = "chat_room_id"
class PinnedMessagesFragment : Fragment(), PinnedMessagesView { class PinnedMessagesFragment : Fragment(), PinnedMessagesView {
private lateinit var chatRoomId: String
private val adapter = ChatRoomAdapter(enableActions = false)
@Inject @Inject
lateinit var presenter: PinnedMessagesPresenter lateinit var presenter: PinnedMessagesPresenter
@Inject
lateinit var analyticsTrackingInteractor: AnalyticsTrackingInteractor
private lateinit var chatRoomId: String
private val adapter = ChatRoomAdapter(enableActions = false)
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
...@@ -63,6 +68,10 @@ class PinnedMessagesFragment : Fragment(), PinnedMessagesView { ...@@ -63,6 +68,10 @@ class PinnedMessagesFragment : Fragment(), PinnedMessagesView {
setupToolbar() setupToolbar()
presenter.loadPinnedMessages(chatRoomId) presenter.loadPinnedMessages(chatRoomId)
if (analyticsTrackingInteractor.get()) {
AnalyticsManager.logScreenView(ScreenViewEvent.PinnedMessages)
}
} }
override fun showPinnedMessages(pinnedMessages: List<BaseUiModel<*>>) { override fun showPinnedMessages(pinnedMessages: List<BaseUiModel<*>>) {
......
package chat.rocket.android.preferences.di
import chat.rocket.android.dagger.scope.PerFragment
import chat.rocket.android.preferences.presentation.PreferencesView
import chat.rocket.android.preferences.ui.PreferencesFragment
import dagger.Module
import dagger.Provides
@Module
class PreferencesFragmentModule {
@Provides
@PerFragment
fun preferencesView(frag: PreferencesFragment): PreferencesView {
return frag
}
}
\ No newline at end of file
package chat.rocket.android.preferences.di
import chat.rocket.android.dagger.scope.PerFragment
import chat.rocket.android.preferences.ui.PreferencesFragment
import dagger.Module
import dagger.android.ContributesAndroidInjector
@Module
abstract class PreferencesFragmentProvider {
@ContributesAndroidInjector(modules = [PreferencesFragmentModule::class])
@PerFragment
abstract fun providePreferencesFragment(): PreferencesFragment
}
\ No newline at end of file
package chat.rocket.android.preferences.presentation
import chat.rocket.android.server.domain.AnalyticsTrackingInteractor
import javax.inject.Inject
class PreferencesPresenter @Inject constructor(
private val view: PreferencesView,
private val analyticsTrackingInteractor: AnalyticsTrackingInteractor
) {
fun loadAnalyticsTrackingInformation() {
view.setupAnalyticsTrackingView(analyticsTrackingInteractor.get())
}
fun enableAnalyticsTracking() {
analyticsTrackingInteractor.save(true)
}
fun disableAnalyticsTracking() {
analyticsTrackingInteractor.save(false)
}
}
\ No newline at end of file
package chat.rocket.android.preferences.presentation
interface PreferencesView {
/**
* Setups the analytics tracking view.
*
* @param isAnalyticsTrackingEnabled Whether the analytics tracking is enabled
*/
fun setupAnalyticsTrackingView(isAnalyticsTrackingEnabled: Boolean)
}
\ No newline at end of file
package chat.rocket.android.preferences.ui
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import chat.rocket.android.BuildConfig
import chat.rocket.android.R
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.*
import javax.inject.Inject
internal const val TAG_PREFERENCES_FRAGMENT = "PreferencesFragment"
class PreferencesFragment : Fragment(), PreferencesView {
@Inject
lateinit var presenter: PreferencesPresenter
@Inject
lateinit var analyticsTrackingInteractor: AnalyticsTrackingInteractor
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
AndroidSupportInjection.inject(this)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? = inflater.inflate(R.layout.fragment_preferences, container, false)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setupToolbar()
setupListeners()
presenter.loadAnalyticsTrackingInformation()
if (analyticsTrackingInteractor.get()) {
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
return
}
if (isAnalyticsTrackingEnabled) {
text_analytics_tracking_description.text =
getString(R.string.msg_send_analytics_tracking)
} else {
text_analytics_tracking_description.text =
getString(R.string.msg_do_not_send_analytics_tracking)
}
switch_analytics_tracking.isChecked = isAnalyticsTrackingEnabled
}
private fun setupToolbar() {
with((activity as MainActivity).toolbar) {
title = getString(R.string.title_preferences)
setNavigationIcon(R.drawable.ic_arrow_back_white_24dp)
setNavigationOnClickListener { activity?.onBackPressed() }
}
}
private fun setupListeners() {
switch_analytics_tracking.setOnCheckedChangeListener { _, isChecked ->
if (isChecked) {
text_analytics_tracking_description.text =
getString(R.string.msg_send_analytics_tracking)
presenter.enableAnalyticsTracking()
} else {
text_analytics_tracking_description.text =
getString(R.string.msg_do_not_send_analytics_tracking)
presenter.disableAnalyticsTracking()
}
}
}
companion object {
fun newInstance() = PreferencesFragment()
}
}
package chat.rocket.android.profile.presentation package chat.rocket.android.profile.presentation
import android.graphics.Bitmap
import android.net.Uri
import chat.rocket.android.chatroom.domain.UriInteractor
import chat.rocket.android.core.behaviours.showMessage import chat.rocket.android.core.behaviours.showMessage
import chat.rocket.android.core.lifecycle.CancelStrategy import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.helper.UserHelper
import chat.rocket.android.server.domain.GetCurrentServerInteractor import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.infraestructure.RocketChatClientFactory import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.extension.compressImageAndGetByteArray
import chat.rocket.android.util.extension.launchUI import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.extensions.avatarUrl import chat.rocket.android.util.extensions.avatarUrl
import chat.rocket.android.util.retryIO import chat.rocket.android.util.retryIO
import chat.rocket.common.RocketChatException import chat.rocket.common.RocketChatException
import chat.rocket.common.util.ifNull import chat.rocket.common.util.ifNull
import chat.rocket.core.RocketChatClient import chat.rocket.core.RocketChatClient
import chat.rocket.core.internal.rest.me import chat.rocket.core.internal.rest.resetAvatar
import chat.rocket.core.internal.rest.setAvatar import chat.rocket.core.internal.rest.setAvatar
import chat.rocket.core.internal.rest.updateProfile import chat.rocket.core.internal.rest.updateProfile
import java.util.*
import javax.inject.Inject import javax.inject.Inject
class ProfilePresenter @Inject constructor( class ProfilePresenter @Inject constructor(
private val view: ProfileView, private val view: ProfileView,
private val strategy: CancelStrategy, private val strategy: CancelStrategy,
private val uriInteractor: UriInteractor,
val userHelper: UserHelper,
serverInteractor: GetCurrentServerInteractor, serverInteractor: GetCurrentServerInteractor,
factory: RocketChatClientFactory factory: RocketChatClientFactory
) { ) {
private val serverUrl = serverInteractor.get()!! private val serverUrl = serverInteractor.get()!!
private val client: RocketChatClient = factory.create(serverUrl) private val client: RocketChatClient = factory.create(serverUrl)
private lateinit var myselfId: String private val myselfId = userHelper.user()?.id ?: ""
private var myselfName = userHelper.user()?.name ?: ""
private var myselfUsername = userHelper.username() ?: ""
private var myselfEmailAddress = userHelper.user()?.emails?.getOrNull(0)?.address ?: ""
fun loadUserProfile() { fun loadUserProfile() {
launchUI(strategy) { launchUI(strategy) {
view.showLoading() view.showLoading()
try { try {
val myself = retryIO("me") { client.me() }
val id = myself.id
val username = myself.username
if (id == null || username == null) {
view.showGenericErrorMessage()
} else {
myselfId = id
val avatarUrl = serverUrl.avatarUrl(username)
val email = myself.emails?.getOrNull(0)?.address
view.showProfile( view.showProfile(
avatarUrl, serverUrl.avatarUrl(myselfUsername),
myself.name ?: "", myselfName,
myself.username ?: "", myselfUsername,
email myselfEmailAddress
) )
}
} catch (exception: RocketChatException) { } catch (exception: RocketChatException) {
view.showMessage(exception) view.showMessage(exception)
} finally { } finally {
...@@ -53,18 +54,16 @@ class ProfilePresenter @Inject constructor( ...@@ -53,18 +54,16 @@ class ProfilePresenter @Inject constructor(
} }
} }
fun updateUserProfile(email: String, name: String, username: String, avatarUrl: String = "") { fun updateUserProfile(email: String, name: String, username: String) {
launchUI(strategy) { launchUI(strategy) {
view.showLoading() view.showLoading()
try { try {
if (avatarUrl != "") { retryIO { client.updateProfile(myselfId, email, name, username) }
retryIO { client.setAvatar(avatarUrl) }
} myselfEmailAddress = email
val user = retryIO { myselfName = name
client.updateProfile( myselfUsername = username
userId = myselfId, email = email, name = name, username = username
)
}
view.showProfileUpdateSuccessfullyMessage() view.showProfileUpdateSuccessfullyMessage()
loadUserProfile() loadUserProfile()
} catch (exception: RocketChatException) { } catch (exception: RocketChatException) {
...@@ -78,4 +77,74 @@ class ProfilePresenter @Inject constructor( ...@@ -78,4 +77,74 @@ class ProfilePresenter @Inject constructor(
} }
} }
} }
fun updateAvatar(uri: Uri) {
launchUI(strategy) {
view.showLoading()
try {
retryIO {
client.setAvatar(
uriInteractor.getFileName(uri) ?: uri.toString(),
uriInteractor.getMimeType(uri)
) {
uriInteractor.getInputStream(uri)
}
}
view.reloadUserAvatar(serverUrl.avatarUrl(myselfUsername))
} catch (exception: RocketChatException) {
exception.message?.let {
view.showMessage(it)
}.ifNull {
view.showGenericErrorMessage()
}
} finally {
view.hideLoading()
}
}
}
fun preparePhotoAndUpdateAvatar(bitmap: Bitmap) {
launchUI(strategy) {
view.showLoading()
try {
val byteArray = bitmap.compressImageAndGetByteArray("image/png")
retryIO {
client.setAvatar(
UUID.randomUUID().toString() + ".png",
"image/png"
) {
byteArray?.inputStream()
}
}
view.reloadUserAvatar(serverUrl.avatarUrl(myselfUsername))
} catch (exception: RocketChatException) {
exception.message?.let {
view.showMessage(it)
}.ifNull {
view.showGenericErrorMessage()
}
} finally {
view.hideLoading()
}
}
}
fun resetAvatar() {
launchUI(strategy) {
view.showLoading()
try {
retryIO { client.resetAvatar(myselfId) }
view.reloadUserAvatar(serverUrl.avatarUrl(myselfUsername))
} catch (exception: RocketChatException) {
exception.message?.let {
view.showMessage(it)
}.ifNull {
view.showGenericErrorMessage()
}
} finally {
view.hideLoading()
}
}
}
} }
\ No newline at end of file
...@@ -15,6 +15,13 @@ interface ProfileView : LoadingView, MessageView { ...@@ -15,6 +15,13 @@ interface ProfileView : LoadingView, MessageView {
*/ */
fun showProfile(avatarUrl: String, name: String, username: String, email: String?) fun showProfile(avatarUrl: String, name: String, username: String, email: String?)
/**
* Reloads the user avatar (after successfully updating it).
*
* @param avatarUrl The user avatar URL.
*/
fun reloadUserAvatar(avatarUrl: String)
/** /**
* Shows a profile update successfully message * Shows a profile update successfully message
*/ */
......
package chat.rocket.android.profile.ui package chat.rocket.android.profile.ui
import DrawableHelper import DrawableHelper
import android.app.Activity
import android.content.Intent
import android.graphics.Bitmap
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import androidx.fragment.app.Fragment import android.view.LayoutInflater
import androidx.appcompat.view.ActionMode import android.view.Menu
import android.view.* import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.view.ActionMode
import androidx.core.net.toUri
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.main.ui.MainActivity import chat.rocket.android.main.ui.MainActivity
import chat.rocket.android.profile.presentation.ProfilePresenter import chat.rocket.android.profile.presentation.ProfilePresenter
import chat.rocket.android.profile.presentation.ProfileView 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.asObservable
import chat.rocket.android.util.extensions.* import chat.rocket.android.util.extension.dispatchImageSelection
import chat.rocket.android.util.extension.dispatchTakePicture
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 dagger.android.support.AndroidSupportInjection
import io.reactivex.disposables.CompositeDisposable import io.reactivex.disposables.CompositeDisposable
import io.reactivex.disposables.Disposable
import io.reactivex.rxkotlin.Observables import io.reactivex.rxkotlin.Observables
import kotlinx.android.synthetic.main.avatar_profile.* import kotlinx.android.synthetic.main.avatar_profile.*
import kotlinx.android.synthetic.main.fragment_profile.* import kotlinx.android.synthetic.main.fragment_profile.*
import kotlinx.android.synthetic.main.update_avatar_options.*
import javax.inject.Inject import javax.inject.Inject
internal const val TAG_PROFILE_FRAGMENT = "ProfileFragment"
private const val REQUEST_CODE_FOR_PERFORM_SAF = 1
private const val REQUEST_CODE_FOR_PERFORM_CAMERA = 2
class ProfileFragment : Fragment(), ProfileView, ActionMode.Callback { class ProfileFragment : Fragment(), ProfileView, ActionMode.Callback {
@Inject @Inject
lateinit var presenter: ProfilePresenter lateinit var presenter: ProfilePresenter
private lateinit var currentName: String @Inject
private lateinit var currentUsername: String lateinit var analyticsTrackingInteractor: AnalyticsTrackingInteractor
private lateinit var currentEmail: String private var currentName = ""
private lateinit var currentAvatar: String private var currentUsername = ""
private var currentEmail = ""
private var actionMode: ActionMode? = null private var actionMode: ActionMode? = null
private val editTextsDisposable = CompositeDisposable() private val editTextsDisposable = CompositeDisposable()
...@@ -50,11 +74,16 @@ class ProfileFragment : Fragment(), ProfileView, ActionMode.Callback { ...@@ -50,11 +74,16 @@ class ProfileFragment : Fragment(), ProfileView, ActionMode.Callback {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
setupToolbar() setupToolbar()
setupListeners()
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) { if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) {
tintEditTextDrawableStart() tintEditTextDrawableStart()
} }
presenter.loadUserProfile() presenter.loadUserProfile()
subscribeEditTexts()
if (analyticsTrackingInteractor.get()) {
AnalyticsManager.logScreenView(ScreenViewEvent.Profile)
}
} }
override fun onDestroyView() { override fun onDestroyView() {
...@@ -62,24 +91,35 @@ class ProfileFragment : Fragment(), ProfileView, ActionMode.Callback { ...@@ -62,24 +91,35 @@ class ProfileFragment : Fragment(), ProfileView, ActionMode.Callback {
unsubscribeEditTexts() unsubscribeEditTexts()
} }
override fun onActivityResult(requestCode: Int, resultCode: Int, resultData: Intent?) {
if (resultData != null && resultCode == Activity.RESULT_OK) {
if (requestCode == REQUEST_CODE_FOR_PERFORM_SAF) {
presenter.updateAvatar(resultData.data)
} else if (requestCode == REQUEST_CODE_FOR_PERFORM_CAMERA) {
presenter.preparePhotoAndUpdateAvatar(resultData.extras["data"] as Bitmap)
}
}
}
override fun showProfile(avatarUrl: String, name: String, username: String, email: String?) { override fun showProfile(avatarUrl: String, name: String, username: String, email: String?) {
ui { ui {
image_avatar.setImageURI(avatarUrl) image_avatar.setImageURI(avatarUrl)
text_name.textContent = name text_name.textContent = name
text_username.textContent = username text_username.textContent = username
text_email.textContent = email ?: "" text_email.textContent = email ?: ""
text_avatar_url.textContent = ""
currentName = name currentName = name
currentUsername = username currentUsername = username
currentEmail = email ?: "" currentEmail = email ?: ""
currentAvatar = avatarUrl
profile_container.setVisible(true) profile_container.isVisible = true
}
subscribeEditTexts()
} }
override fun reloadUserAvatar(avatarUrl: String) {
Fresco.getImagePipeline().evictFromCache(avatarUrl.toUri())
image_avatar.setImageURI(avatarUrl)
(activity as MainActivity).setAvatar(avatarUrl)
} }
override fun showProfileUpdateSuccessfullyMessage() { override fun showProfileUpdateSuccessfullyMessage() {
...@@ -88,30 +128,24 @@ class ProfileFragment : Fragment(), ProfileView, ActionMode.Callback { ...@@ -88,30 +128,24 @@ class ProfileFragment : Fragment(), ProfileView, ActionMode.Callback {
override fun showLoading() { override fun showLoading() {
enableUserInput(false) enableUserInput(false)
ui { ui { view_loading.isVisible = true }
view_loading.setVisible(true)
}
} }
override fun hideLoading() { override fun hideLoading() {
ui { ui {
if (view_loading != null) { if (view_loading != null) {
view_loading.setVisible(false) view_loading.isVisible = false
} }
} }
enableUserInput(true) enableUserInput(true)
} }
override fun showMessage(resId: Int) { override fun showMessage(resId: Int) {
ui { ui { showToast(resId) }
showToast(resId)
}
} }
override fun showMessage(message: String) { override fun showMessage(message: String) {
ui { ui { showToast(message) }
showToast(message)
}
} }
override fun showGenericErrorMessage() = showMessage(getString(R.string.msg_generic_error)) override fun showGenericErrorMessage() = showMessage(getString(R.string.msg_generic_error))
...@@ -130,8 +164,7 @@ class ProfileFragment : Fragment(), ProfileView, ActionMode.Callback { ...@@ -130,8 +164,7 @@ class ProfileFragment : Fragment(), ProfileView, ActionMode.Callback {
presenter.updateUserProfile( presenter.updateUserProfile(
text_email.textContent, text_email.textContent,
text_name.textContent, text_name.textContent,
text_username.textContent, text_username.textContent
text_avatar_url.textContent
) )
mode.finish() mode.finish()
true true
...@@ -151,21 +184,50 @@ class ProfileFragment : Fragment(), ProfileView, ActionMode.Callback { ...@@ -151,21 +184,50 @@ class ProfileFragment : Fragment(), ProfileView, ActionMode.Callback {
getString(R.string.title_profile) getString(R.string.title_profile)
} }
private fun setupListeners() {
image_avatar.setOnClickListener { showUpdateAvatarOptions() }
view_dim.setOnClickListener { hideUpdateAvatarOptions() }
button_open_gallery.setOnClickListener {
dispatchImageSelection(REQUEST_CODE_FOR_PERFORM_SAF)
hideUpdateAvatarOptions()
}
button_take_photo.setOnClickListener {
dispatchTakePicture(REQUEST_CODE_FOR_PERFORM_CAMERA)
hideUpdateAvatarOptions()
}
button_reset_avatar.setOnClickListener {
hideUpdateAvatarOptions()
presenter.resetAvatar()
}
}
private fun showUpdateAvatarOptions() {
view_dim.isVisible = true
layout_update_avatar_options.isVisible = true
}
private fun hideUpdateAvatarOptions() {
layout_update_avatar_options.isVisible = false
view_dim.isVisible = false
}
private fun tintEditTextDrawableStart() { private fun tintEditTextDrawableStart() {
(activity as MainActivity).apply { (activity as MainActivity).apply {
val personDrawable = val personDrawable =
DrawableHelper.getDrawableFromId(R.drawable.ic_person_black_20dp, this) DrawableHelper.getDrawableFromId(R.drawable.ic_person_black_24dp, this)
val atDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_at_black_20dp, this) val atDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_at_black_24dp, this)
val emailDrawable = val emailDrawable =
DrawableHelper.getDrawableFromId(R.drawable.ic_email_black_20dp, this) DrawableHelper.getDrawableFromId(R.drawable.ic_email_black_24dp, this)
val linkDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_link_black_24dp, this)
val drawables = arrayOf(personDrawable, atDrawable, emailDrawable, linkDrawable) val drawables = arrayOf(personDrawable, atDrawable, emailDrawable)
DrawableHelper.wrapDrawables(drawables) DrawableHelper.wrapDrawables(drawables)
DrawableHelper.tintDrawables(drawables, this, R.color.colorDrawableTintGrey) DrawableHelper.tintDrawables(drawables, this, R.color.colorDrawableTintGrey)
DrawableHelper.compoundDrawables( DrawableHelper.compoundDrawables(
arrayOf(text_name, text_username, text_email, text_avatar_url), arrayOf(text_name, text_username, text_email), drawables
drawables
) )
} }
} }
...@@ -174,13 +236,11 @@ class ProfileFragment : Fragment(), ProfileView, ActionMode.Callback { ...@@ -174,13 +236,11 @@ class ProfileFragment : Fragment(), ProfileView, ActionMode.Callback {
editTextsDisposable.add(Observables.combineLatest( editTextsDisposable.add(Observables.combineLatest(
text_name.asObservable(), text_name.asObservable(),
text_username.asObservable(), text_username.asObservable(),
text_email.asObservable(), text_email.asObservable()
text_avatar_url.asObservable() ) { text_name, text_username, text_email ->
) { text_name, text_username, text_email, text_avatar_url ->
return@combineLatest (text_name.toString() != currentName || return@combineLatest (text_name.toString() != currentName ||
text_username.toString() != currentUsername || text_username.toString() != currentUsername ||
text_email.toString() != currentEmail || text_email.toString() != currentEmail)
(text_avatar_url.toString() != "" && text_avatar_url.toString() != currentAvatar))
}.subscribe { isValid -> }.subscribe { isValid ->
if (isValid) { if (isValid) {
startActionMode() startActionMode()
...@@ -190,9 +250,7 @@ class ProfileFragment : Fragment(), ProfileView, ActionMode.Callback { ...@@ -190,9 +250,7 @@ class ProfileFragment : Fragment(), ProfileView, ActionMode.Callback {
}) })
} }
private fun unsubscribeEditTexts() { private fun unsubscribeEditTexts() = editTextsDisposable.clear()
editTextsDisposable.clear()
}
private fun startActionMode() { private fun startActionMode() {
if (actionMode == null) { if (actionMode == null) {
...@@ -207,7 +265,6 @@ class ProfileFragment : Fragment(), ProfileView, ActionMode.Callback { ...@@ -207,7 +265,6 @@ class ProfileFragment : Fragment(), ProfileView, ActionMode.Callback {
text_username.isEnabled = value text_username.isEnabled = value
text_username.isEnabled = value text_username.isEnabled = value
text_email.isEnabled = value text_email.isEnabled = value
text_avatar_url.isEnabled = value
} }
} }
} }
package chat.rocket.android.server.domain
import javax.inject.Inject
class AnalyticsTrackingInteractor @Inject constructor(val repository: AnalyticsTrackingRepository) {
fun save(isAnalyticsTrackingEnable: Boolean) = repository.save(isAnalyticsTrackingEnable)
fun get(): Boolean = repository.get()
}
\ No newline at end of file
package chat.rocket.android.server.domain
interface AnalyticsTrackingRepository {
fun save(isAnalyticsTrackingEnable: Boolean)
fun get(): Boolean
}
\ No newline at end of file
package chat.rocket.android.server.domain package chat.rocket.android.server.domain
import chat.rocket.android.helper.UserHelper import chat.rocket.android.helper.UserHelper
import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.core.model.Permission
import javax.inject.Inject import javax.inject.Inject
// Creating rooms // Creating rooms
const val CREATE_PUBLIC_CHANNELS = "create-c" private const val CREATE_PUBLIC_CHANNELS = "create-c"
const val CREATE_DIRECT_MESSAGES = "create-d" private const val CREATE_DIRECT_MESSAGES = "create-d"
const val CREATE_PRIVATE_CHANNELS = "create-p" private const val CREATE_PRIVATE_CHANNELS = "create-p"
// Messages // Messages
const val DELETE_MESSAGE = "delete-message" private const val DELETE_MESSAGE = "delete-message"
const val FORCE_DELETE_MESSAGE = "force-delete-message" private const val FORCE_DELETE_MESSAGE = "force-delete-message"
const val EDIT_MESSAGE = "edit-message" private const val EDIT_MESSAGE = "edit-message"
const val PIN_MESSAGE = "pin-message" private const val PIN_MESSAGE = "pin-message"
const val POST_READONLY = "post-readonly" private const val POST_READONLY = "post-readonly"
private const val VIEW_STATISTICS = "view-statistics"
private const val VIEW_ROOM_ADMINISTRATION = "view-room-administration"
private const val VIEW_USER_ADMINISTRATION = "view-user-administration"
private const val VIEW_PRIVILEGED_SETTING = "view-privileged-setting"
class PermissionsInteractor @Inject constructor( class PermissionsInteractor @Inject constructor(
private val settingsRepository: SettingsRepository, private val settingsRepository: SettingsRepository,
...@@ -23,14 +26,8 @@ class PermissionsInteractor @Inject constructor( ...@@ -23,14 +26,8 @@ class PermissionsInteractor @Inject constructor(
private val getCurrentServerInteractor: GetCurrentServerInteractor, private val getCurrentServerInteractor: GetCurrentServerInteractor,
private val userHelper: UserHelper private val userHelper: UserHelper
) { ) {
private fun publicSettings(): PublicSettings? = settingsRepository.get(currentServerUrl()!!) private fun publicSettings(): PublicSettings? = settingsRepository.get(currentServerUrl()!!)
fun saveAll(permissions: List<Permission>) {
val url = currentServerUrl()!!
permissions.forEach { permissionsRepository.save(url, it) }
}
/** /**
* Check whether the user is allowed to delete a message. * Check whether the user is allowed to delete a message.
*/ */
...@@ -71,6 +68,28 @@ class PermissionsInteractor @Inject constructor( ...@@ -71,6 +68,28 @@ class PermissionsInteractor @Inject constructor(
} == true || userHelper.isAdmin() } == true || userHelper.isAdmin()
} }
fun canSeeTheAdminPanel(): Boolean {
currentServerUrl()?.let { serverUrl ->
val viewStatistics =
permissionsRepository.get(serverUrl, VIEW_STATISTICS)
val viewRoomAdministration =
permissionsRepository.get(serverUrl, VIEW_ROOM_ADMINISTRATION)
val viewUserAdministration =
permissionsRepository.get(serverUrl, VIEW_USER_ADMINISTRATION)
val viewPrivilegedSetting =
permissionsRepository.get(serverUrl, VIEW_PRIVILEGED_SETTING)
userHelper.user()?.roles?.let { userRolesList ->
return viewStatistics?.roles?.any { userRolesList.contains(it) } == true ||
viewRoomAdministration?.roles?.any { userRolesList.contains(it) } == true ||
viewUserAdministration?.roles?.any { userRolesList.contains(it) } == true ||
viewPrivilegedSetting?.roles?.any { userRolesList.contains(it) } == true
}
}
return false
}
private fun currentServerUrl(): String? { private fun currentServerUrl(): String? {
return getCurrentServerInteractor.get() return getCurrentServerInteractor.get()
} }
......
...@@ -5,20 +5,20 @@ import chat.rocket.core.model.Permission ...@@ -5,20 +5,20 @@ import chat.rocket.core.model.Permission
interface PermissionsRepository { interface PermissionsRepository {
/** /**
* Store [permission] locally. * Stores a list of [Permission] locally.
* *
* @param url The server url from where we're interest to store the permission. * @param url The server url to store the permission.
* @param permission The permission to store. * @param permissionList The permission list to store.
*/ */
fun save(url: String, permission: Permission) fun save(url: String, permissionList: List<Permission>)
/** /**
* Get permission given by the [permissionId] and for the server [url]. * Gets permission given by the [permissionId] and for the server [url].
* *
* @param url The server url from where we're interested on getting the permissions. * @param url The server url to get the permissions from.
* @param permissionId the id of the permission to get. * @param permissionId the ID of the permission to get.
* *
* @return The interested [Permission] or null if not found. * @return The [Permission] or null if not found.
*/ */
fun get(url: String, permissionId: String): Permission? fun get(url: String, permissionId: String): Permission?
} }
\ No newline at end of file
package chat.rocket.android.server.domain
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.retryIO
import chat.rocket.core.internal.rest.permissions
import kotlinx.coroutines.experimental.CommonPool
import kotlinx.coroutines.experimental.launch
import timber.log.Timber
import javax.inject.Inject
/**
* This class reloads the current logged server permission whenever its used.
*/
class RefreshPermissionsInteractor @Inject constructor(
private val factory: RocketChatClientFactory,
private val repository: PermissionsRepository
) {
fun refreshAsync(server: String) {
launch(CommonPool) {
try {
factory.create(server).let { client ->
val permissions = retryIO(
description = "permissions",
times = 5,
maxDelay = 5000,
initialDelay = 300
) {
client.permissions()
}
repository.save(server, permissions)
}
} catch (ex: Exception) {
Timber.e(ex, "Error refreshing permissions for: $server")
}
}
}
}
\ No newline at end of file
...@@ -9,6 +9,9 @@ import kotlinx.coroutines.experimental.withContext ...@@ -9,6 +9,9 @@ import kotlinx.coroutines.experimental.withContext
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
/**
* This class reloads the current logged server settings whenever needed.
*/
class RefreshSettingsInteractor @Inject constructor( class RefreshSettingsInteractor @Inject constructor(
private val factory: RocketChatClientFactory, private val factory: RocketChatClientFactory,
private val repository: SettingsRepository private val repository: SettingsRepository
......
package chat.rocket.android.server.domain
import chat.rocket.core.model.Value
import javax.inject.Inject
class SaveSettingsInteractor @Inject constructor(private val repository: SettingsRepository) {
fun save(url: String, settings: Map<String, Value<Any>>) = repository.save(url, settings)
}
\ No newline at end of file
...@@ -193,6 +193,14 @@ class ConnectionManager( ...@@ -193,6 +193,14 @@ class ConnectionManager(
client.setTemporaryStatus(userStatus) client.setTemporaryStatus(userStatus)
} }
fun resetReconnectionTimer() {
// if we are waiting to reconnect, immediately try to reconnect
// and reset the reconnection counter
if (client.state is State.Waiting) {
client.connect(resetCounter = true)
}
}
private fun resubscribeRooms() { private fun resubscribeRooms() {
roomMessagesChannels.toList().map { (roomId, channel) -> roomMessagesChannels.toList().map { (roomId, channel) ->
client.subscribeRoomMessages(roomId) { _, id -> client.subscribeRoomMessages(roomId) { _, id ->
......
...@@ -9,12 +9,13 @@ class SharedPreferencesPermissionsRepository( ...@@ -9,12 +9,13 @@ class SharedPreferencesPermissionsRepository(
private val localRepository: LocalRepository, private val localRepository: LocalRepository,
moshi: Moshi moshi: Moshi
) : PermissionsRepository { ) : PermissionsRepository {
private val adapter = moshi.adapter(Permission::class.java) private val adapter = moshi.adapter(Permission::class.java)
override fun save(url: String, permission: Permission) { override fun save(url: String, permissionList: List<Permission>) {
for (permission in permissionList) {
localRepository.save(getPermissionKey(url, permission.id), adapter.toJson(permission)) localRepository.save(getPermissionKey(url, permission.id), adapter.toJson(permission))
} }
}
override fun get(url: String, permissionId: String): Permission? { override fun get(url: String, permissionId: String): Permission? {
return localRepository.get(getPermissionKey(url, permissionId))?.let { return localRepository.get(getPermissionKey(url, permissionId))?.let {
......
package chat.rocket.android.server.infraestructure
import android.content.SharedPreferences
import chat.rocket.android.server.domain.AnalyticsTrackingRepository
private const val ANALYTICS_TRACKING_KEY = "ANALYTICS_TRACKING_KEY"
class SharedPrefsAnalyticsTrackingRepository(private val preferences: SharedPreferences) :
AnalyticsTrackingRepository {
override fun save(isAnalyticsTrackingEnable: Boolean) =
preferences.edit().putBoolean(ANALYTICS_TRACKING_KEY, isAnalyticsTrackingEnable).apply()
override fun get() = preferences.getBoolean(ANALYTICS_TRACKING_KEY, true)
}
\ No newline at end of file
...@@ -7,6 +7,7 @@ import chat.rocket.android.server.ui.ChangeServerActivity ...@@ -7,6 +7,7 @@ import chat.rocket.android.server.ui.ChangeServerActivity
import chat.rocket.android.server.ui.INTENT_CHAT_ROOM_ID import chat.rocket.android.server.ui.INTENT_CHAT_ROOM_ID
class ChangeServerNavigator (internal val activity: ChangeServerActivity) { class ChangeServerNavigator (internal val activity: ChangeServerActivity) {
fun toServerScreen() { fun toServerScreen() {
activity.startActivity(activity.newServerIntent()) activity.startActivity(activity.newServerIntent())
activity.finish() activity.finish()
......
...@@ -2,9 +2,16 @@ package chat.rocket.android.server.presentation ...@@ -2,9 +2,16 @@ package chat.rocket.android.server.presentation
import chat.rocket.android.core.lifecycle.CancelStrategy import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.infrastructure.LocalRepository import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.server.domain.* 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
import chat.rocket.android.server.domain.SaveCurrentServerInteractor
import chat.rocket.android.server.domain.SettingsRepository
import chat.rocket.android.server.domain.TokenRepository
import chat.rocket.android.server.infraestructure.ConnectionManagerFactory import chat.rocket.android.server.infraestructure.ConnectionManagerFactory
import chat.rocket.android.util.extension.launchUI import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.helper.analytics.AnalyticsManager
import chat.rocket.common.util.ifNull import chat.rocket.common.util.ifNull
import javax.inject.Inject import javax.inject.Inject
...@@ -16,17 +23,20 @@ class ChangeServerPresenter @Inject constructor( ...@@ -16,17 +23,20 @@ class ChangeServerPresenter @Inject constructor(
private val getCurrentServerInteractor: GetCurrentServerInteractor, private val getCurrentServerInteractor: GetCurrentServerInteractor,
private val getAccountInteractor: GetAccountInteractor, private val getAccountInteractor: GetAccountInteractor,
private val getAccountsInteractor: GetAccountsInteractor, private val getAccountsInteractor: GetAccountsInteractor,
private val analyticsTrackingInteractor: AnalyticsTrackingInteractor,
private val settingsRepository: SettingsRepository, private val settingsRepository: SettingsRepository,
private val tokenRepository: TokenRepository, private val tokenRepository: TokenRepository,
private val localRepository: LocalRepository, private val localRepository: LocalRepository,
private val connectionManager: ConnectionManagerFactory private val connectionManager: ConnectionManagerFactory
) { ) {
fun loadServer(newUrl: String?, chatRoomId: String? = null) { fun loadServer(newUrl: String?, chatRoomId: String? = null) {
launchUI(strategy) { launchUI(strategy) {
view.showProgress() view.showProgress()
var url = newUrl var url = newUrl
if (url == null) { // Try to load next server on the list...
val accounts = getAccountsInteractor.get() val accounts = getAccountsInteractor.get()
if (url == null) {
// Try to load next server on the list...
url = accounts.firstOrNull()?.serverUrl url = accounts.firstOrNull()?.serverUrl
} }
...@@ -56,6 +66,9 @@ class ChangeServerPresenter @Inject constructor( ...@@ -56,6 +66,9 @@ class ChangeServerPresenter @Inject constructor(
saveCurrentServerInteractor.save(serverUrl) saveCurrentServerInteractor.save(serverUrl)
view.hideProgress() view.hideProgress()
if (analyticsTrackingInteractor.get()) {
AnalyticsManager.logServerSwitch(serverUrl, accounts.size)
}
navigator.toChatRooms(chatRoomId) navigator.toChatRooms(chatRoomId)
}.ifNull { }.ifNull {
view.hideProgress() view.hideProgress()
......
...@@ -11,6 +11,18 @@ import chat.rocket.android.util.extensions.showToast ...@@ -11,6 +11,18 @@ import chat.rocket.android.util.extensions.showToast
import dagger.android.AndroidInjection import dagger.android.AndroidInjection
import javax.inject.Inject import javax.inject.Inject
private const val INTENT_SERVER_URL = "INTENT_SERVER_URL"
const val INTENT_CHAT_ROOM_ID = "INTENT_CHAT_ROOM_ID"
fun Context.changeServerIntent(serverUrl: String? = null, chatRoomId: String? = ""): Intent {
return Intent(this, ChangeServerActivity::class.java).apply {
serverUrl?.let { url ->
putExtra(INTENT_SERVER_URL, url)
putExtra(INTENT_CHAT_ROOM_ID, chatRoomId)
}
flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_CLEAR_TASK
}
}
class ChangeServerActivity : AppCompatActivity(), ChangeServerView { class ChangeServerActivity : AppCompatActivity(), ChangeServerView {
@Inject lateinit var presenter: ChangeServerPresenter @Inject lateinit var presenter: ChangeServerPresenter
...@@ -37,18 +49,3 @@ class ChangeServerActivity : AppCompatActivity(), ChangeServerView { ...@@ -37,18 +49,3 @@ class ChangeServerActivity : AppCompatActivity(), ChangeServerView {
progress?.dismiss() progress?.dismiss()
} }
} }
private const val INTENT_SERVER_URL = "INTENT_SERVER_URL"
private const val INTENT_CHAT_ROOM_NAME = "INTENT_CHAT_ROOM_NAME"
private const val INTENT_CHAT_ROOM_TYPE = "INTENT_CHAT_ROOM_TYPE"
const val INTENT_CHAT_ROOM_ID = "INTENT_CHAT_ROOM_ID"
fun Context.changeServerIntent(serverUrl: String? = null, chatRoomId: String? = ""): Intent {
return Intent(this, ChangeServerActivity::class.java).apply {
serverUrl?.let { url ->
putExtra(INTENT_SERVER_URL, url)
putExtra(INTENT_CHAT_ROOM_ID, chatRoomId)
}
flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_CLEAR_TASK
}
}
\ No newline at end of file
...@@ -4,7 +4,6 @@ import android.os.Bundle ...@@ -4,7 +4,6 @@ import android.os.Bundle
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.chatrooms.ui.ChatRoomsFragment
import chat.rocket.android.util.extensions.addFragment import chat.rocket.android.util.extensions.addFragment
import chat.rocket.android.util.extensions.textContent import chat.rocket.android.util.extensions.textContent
import dagger.android.AndroidInjection import dagger.android.AndroidInjection
...@@ -23,7 +22,7 @@ class PasswordActivity : AppCompatActivity(), HasSupportFragmentInjector { ...@@ -23,7 +22,7 @@ class PasswordActivity : AppCompatActivity(), HasSupportFragmentInjector {
setContentView(R.layout.activity_password) setContentView(R.layout.activity_password)
setupToolbar() setupToolbar()
addFragment("PasswordFragment") addFragment(TAG_PASSWORD_FRAGMENT)
} }
override fun onBackPressed() { override fun onBackPressed() {
...@@ -47,6 +46,6 @@ class PasswordActivity : AppCompatActivity(), HasSupportFragmentInjector { ...@@ -47,6 +46,6 @@ class PasswordActivity : AppCompatActivity(), HasSupportFragmentInjector {
private fun setupToolbar() { private fun setupToolbar() {
setSupportActionBar(toolbar) setSupportActionBar(toolbar)
text_change_password.textContent = resources.getString(R.string.title_password) text_change_password.textContent = resources.getString(R.string.title_change_password)
} }
} }
package chat.rocket.android.settings.password.ui package chat.rocket.android.settings.password.ui
import android.os.Bundle import android.os.Bundle
import androidx.fragment.app.Fragment import android.view.LayoutInflater
import android.view.* import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.view.ActionMode
import androidx.fragment.app.Fragment
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.server.domain.AnalyticsTrackingInteractor
import chat.rocket.android.settings.password.presentation.PasswordPresenter import chat.rocket.android.settings.password.presentation.PasswordPresenter
import chat.rocket.android.settings.password.presentation.PasswordView import chat.rocket.android.settings.password.presentation.PasswordView
import chat.rocket.android.util.extensions.inflate
import androidx.appcompat.view.ActionMode
import chat.rocket.android.util.extension.asObservable 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.textContent
import chat.rocket.android.util.extensions.ui 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 dagger.android.support.AndroidSupportInjection
import io.reactivex.disposables.CompositeDisposable import io.reactivex.disposables.CompositeDisposable
import io.reactivex.disposables.Disposable import io.reactivex.disposables.Disposable
...@@ -19,8 +26,13 @@ import io.reactivex.rxkotlin.Observables ...@@ -19,8 +26,13 @@ import io.reactivex.rxkotlin.Observables
import kotlinx.android.synthetic.main.fragment_password.* import kotlinx.android.synthetic.main.fragment_password.*
import javax.inject.Inject import javax.inject.Inject
class PasswordFragment: Fragment(), PasswordView, ActionMode.Callback { internal const val TAG_PASSWORD_FRAGMENT = "PasswordFragment"
@Inject lateinit var presenter: PasswordPresenter
class PasswordFragment : Fragment(), PasswordView, ActionMode.Callback {
@Inject
lateinit var presenter: PasswordPresenter
@Inject
lateinit var analyticsTrackingInteractor: AnalyticsTrackingInteractor
private var actionMode: ActionMode? = null private var actionMode: ActionMode? = null
private val disposables = CompositeDisposable() private val disposables = CompositeDisposable()
...@@ -33,12 +45,20 @@ class PasswordFragment: Fragment(), PasswordView, ActionMode.Callback { ...@@ -33,12 +45,20 @@ class PasswordFragment: Fragment(), PasswordView, ActionMode.Callback {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
} }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? = container?.inflate(R.layout.fragment_password) override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? = container?.inflate(R.layout.fragment_password)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
disposables.add(listenToChanges()) disposables.add(listenToChanges())
if (analyticsTrackingInteractor.get()) {
AnalyticsManager.logScreenView(ScreenViewEvent.Password)
}
} }
override fun onDestroyView() { override fun onDestroyView() {
...@@ -98,12 +118,17 @@ class PasswordFragment: Fragment(), PasswordView, ActionMode.Callback { ...@@ -98,12 +118,17 @@ class PasswordFragment: Fragment(), PasswordView, ActionMode.Callback {
private fun finishActionMode() = actionMode?.finish() private fun finishActionMode() = actionMode?.finish()
private fun listenToChanges(): Disposable { private fun listenToChanges(): Disposable {
return Observables.combineLatest(text_new_password.asObservable(), return Observables.combineLatest(
text_confirm_password.asObservable()).subscribe { text_new_password.asObservable(),
text_confirm_password.asObservable()
).subscribe {
val textPassword = text_new_password.textContent val textPassword = text_new_password.textContent
val textConfirmPassword = text_confirm_password.textContent val textConfirmPassword = text_confirm_password.textContent
if (textPassword.length > 5 && textConfirmPassword.length > 5 && textPassword.equals(textConfirmPassword)) if (textPassword.length > 5 && textConfirmPassword.length > 5 && textPassword.equals(
textConfirmPassword
)
)
startActionMode() startActionMode()
else else
finishActionMode() finishActionMode()
......
...@@ -2,26 +2,34 @@ package chat.rocket.android.settings.ui ...@@ -2,26 +2,34 @@ package chat.rocket.android.settings.ui
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import androidx.fragment.app.Fragment
import androidx.appcompat.app.AppCompatActivity
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.AdapterView import android.widget.AdapterView
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.about.ui.AboutFragment import chat.rocket.android.about.ui.AboutFragment
import chat.rocket.android.about.ui.TAG_ABOUT_FRAGMENT
import chat.rocket.android.main.ui.MainActivity 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.password.ui.PasswordActivity
import chat.rocket.android.settings.presentation.SettingsView import chat.rocket.android.settings.presentation.SettingsView
import chat.rocket.android.util.extensions.addFragmentBackStack import chat.rocket.android.util.extensions.addFragmentBackStack
import chat.rocket.android.util.extensions.inflate 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 kotlinx.android.synthetic.main.fragment_settings.* import kotlinx.android.synthetic.main.fragment_settings.*
import javax.inject.Inject
import kotlin.reflect.KClass import kotlin.reflect.KClass
internal const val TAG_SETTINGS_FRAGMENT = "SettingsFragment"
class SettingsFragment : Fragment(), SettingsView, AdapterView.OnItemClickListener { class SettingsFragment : Fragment(), SettingsView, AdapterView.OnItemClickListener {
companion object { @Inject
fun newInstance() = SettingsFragment() lateinit var analyticsTrackingInteractor: AnalyticsTrackingInteractor
}
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
...@@ -33,23 +41,35 @@ class SettingsFragment : Fragment(), SettingsView, AdapterView.OnItemClickListen ...@@ -33,23 +41,35 @@ class SettingsFragment : Fragment(), SettingsView, AdapterView.OnItemClickListen
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
setupToolbar() setupToolbar()
setupListView() setupListView()
if (analyticsTrackingInteractor.get()) {
AnalyticsManager.logScreenView(ScreenViewEvent.Settings)
}
} }
override fun onResume() { override fun onResume() {
// FIXME - gambiarra ahead. will fix when moving to new androidx Navigation // FIXME - gambiarra ahead. will fix when moving to new androidx Navigation
(activity as? MainActivity)?.let { (activity as? MainActivity)?.setupNavigationView()
it.setupNavigationView()
}
super.onResume() super.onResume()
} }
override fun onItemClick(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { override fun onItemClick(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
when (parent?.getItemAtPosition(position).toString()) { when (parent?.getItemAtPosition(position).toString()) {
resources.getString(R.string.title_password) -> { resources.getString(R.string.title_preferences) -> {
startNewActivity(PasswordActivity::class) (activity as AppCompatActivity).addFragmentBackStack(
TAG_PREFERENCES_FRAGMENT,
R.id.fragment_container
) {
PreferencesFragment.newInstance()
} }
}
resources.getString(R.string.title_change_password) ->
startNewActivity(PasswordActivity::class)
resources.getString(R.string.title_about) -> { resources.getString(R.string.title_about) -> {
(activity as AppCompatActivity).addFragmentBackStack("AboutFragmnet", R.id.fragment_container){ (activity as AppCompatActivity).addFragmentBackStack(
TAG_ABOUT_FRAGMENT,
R.id.fragment_container
) {
AboutFragment.newInstance() AboutFragment.newInstance()
} }
} }
...@@ -69,4 +89,8 @@ class SettingsFragment : Fragment(), SettingsView, AdapterView.OnItemClickListen ...@@ -69,4 +89,8 @@ class SettingsFragment : Fragment(), SettingsView, AdapterView.OnItemClickListen
startActivity(Intent(activity, classType.java)) startActivity(Intent(activity, classType.java))
activity?.overridePendingTransition(R.anim.open_enter, R.anim.open_exit) activity?.overridePendingTransition(R.anim.open_enter, R.anim.open_exit)
} }
companion object {
fun newInstance() = SettingsFragment()
}
} }
...@@ -51,6 +51,8 @@ fun String.termsOfServiceUrl() = "${removeTrailingSlash()}/terms-of-service" ...@@ -51,6 +51,8 @@ fun String.termsOfServiceUrl() = "${removeTrailingSlash()}/terms-of-service"
fun String.privacyPolicyUrl() = "${removeTrailingSlash()}/privacy-policy" fun String.privacyPolicyUrl() = "${removeTrailingSlash()}/privacy-policy"
fun String.adminPanelUrl() = "${removeTrailingSlash()}/admin/info?layout=embedded"
fun String.isValidUrl(): Boolean = Patterns.WEB_URL.matcher(this).matches() fun String.isValidUrl(): Boolean = Patterns.WEB_URL.matcher(this).matches()
fun String.parseColor(): Int { fun String.parseColor(): Int {
......
package chat.rocket.android.webview.adminpanel.ui
import android.annotation.SuppressLint
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import chat.rocket.android.R
import chat.rocket.android.util.extensions.inflate
import kotlinx.android.synthetic.main.fragment_admin_panel_web_view.*
private const val BUNDLE_WEB_PAGE_URL = "web_page_url"
private const val BUNDLE_USER_TOKEN = "user_token"
class AdminPanelWebViewFragment : Fragment() {
private lateinit var webPageUrl: String
private lateinit var userToken: String
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val bundle = arguments
if (bundle != null) {
webPageUrl = bundle.getString(BUNDLE_WEB_PAGE_URL)
userToken = bundle.getString(BUNDLE_USER_TOKEN)
} else {
requireNotNull(bundle) { "no arguments supplied when the fragment was instantiated" }
}
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? = container?.inflate(R.layout.fragment_admin_panel_web_view)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setupToolbar()
setupWebView()
}
private fun setupToolbar() {
(activity as AppCompatActivity?)?.supportActionBar?.title =
getString(R.string.title_admin_panel)
}
@SuppressLint("SetJavaScriptEnabled")
private fun setupWebView() {
with(web_view.settings) {
javaScriptEnabled = true
domStorageEnabled = true
}
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() { })") {}
}
}
web_view.loadUrl(webPageUrl)
}
companion object {
fun newInstance(webPageUrl: String, userToken: String) = AdminPanelWebViewFragment().apply {
arguments = Bundle(2).apply {
putString(BUNDLE_WEB_PAGE_URL, webPageUrl)
putString(BUNDLE_USER_TOKEN, userToken)
}
}
}
}
\ No newline at end of file
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M21,19V5c0,-1.1 -0.9,-2 -2,-2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2zM8.5,13.5l2.5,3.01L14.5,12l4.5,6H5l3.5,-4.5z" />
</vector>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M3.9,12c0,-1.71 1.39,-3.1 3.1,-3.1h4L11,7L7,7c-2.76,0 -5,2.24 -5,5s2.24,5 5,5h4v-1.9L7,15.1c-1.71,0 -3.1,-1.39 -3.1,-3.1zM8,13h8v-2L8,11v2zM17,7h-4v1.9h4c1.71,0 3.1,1.39 3.1,3.1s-1.39,3.1 -3.1,3.1h-4L13,17h4c2.76,0 5,-2.24 5,-5s-2.24,-5 -5,-5z"/>
</vector>
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp" android:width="24dp"
android:height="24dp" android:height="24dp"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:viewportWidth="24.0"> android:viewportHeight="24.0">
<path <path
android:fillColor="#FF000000" android:fillColor="#FF000000"
......
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M12,12m-3.2,0a3.2,3.2 0,1 1,6.4 0a3.2,3.2 0,1 1,-6.4 0" />
<path
android:fillColor="#FF000000"
android:pathData="M9,2L7.17,4L4,4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,6c0,-1.1 -0.9,-2 -2,-2h-3.17L15,2L9,2zM12,17c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5z" />
</vector>
...@@ -33,8 +33,7 @@ ...@@ -33,8 +33,7 @@
android:id="@+id/view_navigation" android:id="@+id/view_navigation"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="match_parent" android:layout_height="match_parent"
app:headerLayout="@layout/nav_header" app:headerLayout="@layout/nav_header" />
app:menu="@menu/navigation" />
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/accounts_list" android:id="@+id/accounts_list"
......
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
android:textColor="@color/colorWhite" android:textColor="@color/colorWhite"
android:textSize="18sp" android:textSize="18sp"
android:textStyle="bold" android:textStyle="bold"
tools:text="@string/title_password" /> tools:text="@string/title_change_password" />
</RelativeLayout> </RelativeLayout>
</androidx.appcompat.widget.Toolbar> </androidx.appcompat.widget.Toolbar>
......
...@@ -35,7 +35,8 @@ ...@@ -35,7 +35,8 @@
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/image_app_name" app:layout_constraintTop_toBottomOf="@+id/image_app_name"
android:layout_marginTop="16dp" android:layout_marginTop="16dp"
android:textColor="@color/colorSecondaryText" /> android:textColor="@color/colorSecondaryText"
android:textAppearance="@style/TextAppearance.AppCompat.Medium"/>
<TextView <TextView
android:id="@+id/text_build_number" android:id="@+id/text_build_number"
......
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".webview.adminpanel.ui.AdminPanelWebViewFragment">
<WebView
android:id="@+id/web_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.wang.avi.AVLoadingIndicatorView
android:id="@+id/view_loading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:indicatorColor="@color/colorBlack"
app:indicatorName="BallPulseIndicator"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/scroll_view"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:id="@+id/constraint_layout" android:fillViewport="true"
tools:context=".authentication.login.ui.LoginFragment"> tools:context=".authentication.login.ui.LoginFragment">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView <TextView
android:id="@+id/text_headline" android:id="@+id/text_headline"
style="@style/Authentication.Headline.TextView" style="@style/Authentication.Headline.TextView"
android:text="@string/title_log_in" android:text="@string/title_log_in"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintRight_toRightOf="parent"
android:layout_marginStart="21dp" app:layout_constraintTop_toTopOf="parent" />
android:layout_marginTop="30dp" />
<EditText <EditText
android:id="@+id/text_username_or_email" android:id="@+id/text_username_or_email"
style="@style/Authentication.EditText" style="@style/Authentication.EditText"
android:layout_marginTop="16dp" android:layout_marginTop="32dp"
android:drawableStart="@drawable/ic_at_black_20dp" android:drawableStart="@drawable/ic_assignment_ind_black_24dp"
android:hint="@string/msg_username_or_email" android:hint="@string/msg_username_or_email"
android:imeOptions="actionNext" android:imeOptions="actionNext"
android:inputType="textEmailAddress|text" android:inputType="textEmailAddress|text"
...@@ -35,7 +39,8 @@ ...@@ -35,7 +39,8 @@
android:layout_marginEnd="10dp" android:layout_marginEnd="10dp"
android:src="@drawable/ic_vpn_key_black_24dp" android:src="@drawable/ic_vpn_key_black_24dp"
android:tint="@color/colorDrawableTintGrey" android:tint="@color/colorDrawableTintGrey"
android:visibility="visible" android:visibility="gone"
tools:visibility="visible"
app:layout_constraintBottom_toBottomOf="@+id/text_username_or_email" app:layout_constraintBottom_toBottomOf="@+id/text_username_or_email"
app:layout_constraintEnd_toEndOf="@+id/text_username_or_email" app:layout_constraintEnd_toEndOf="@+id/text_username_or_email"
app:layout_constraintTop_toTopOf="@+id/text_username_or_email" /> app:layout_constraintTop_toTopOf="@+id/text_username_or_email" />
...@@ -43,61 +48,216 @@ ...@@ -43,61 +48,216 @@
<EditText <EditText
android:id="@+id/text_password" android:id="@+id/text_password"
style="@style/Authentication.EditText" style="@style/Authentication.EditText"
android:layout_marginTop="10dp" android:layout_marginTop="16dp"
android:drawableStart="@drawable/ic_key_black_20dp" android:drawableStart="@drawable/ic_lock_black_24dp"
android:hint="@string/msg_password" android:hint="@string/msg_password"
android:imeOptions="actionDone" android:imeOptions="actionDone"
android:inputType="textPassword" android:inputType="textPassword"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_username_or_email" /> app:layout_constraintTop_toBottomOf="@+id/text_username_or_email" />
<Button <Button
android:id="@+id/button_log_in" android:id="@+id/button_cas"
style="@style/Authentication.Button" style="@style/Authentication.Button"
android:text="@string/title_log_in" android:layout_marginStart="@dimen/screen_edge_left_and_right_margins"
android:visibility="visible" android:layout_marginTop="16dp"
app:layout_constraintStart_toStartOf="parent" android:layout_marginEnd="@dimen/screen_edge_left_and_right_margins"
app:layout_constraintEnd_toEndOf="parent" android:text="@string/action_login_or_sign_up"
app:layout_constraintTop_toBottomOf="@id/text_password" android:visibility="gone"
android:layout_marginTop="20dp" /> app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_password"
tools:visibility="visible" />
<TextView
android:id="@+id/text_new_to_rocket_chat"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/screen_edge_left_and_right_margins"
android:layout_marginTop="16dp"
android:layout_marginEnd="@dimen/screen_edge_left_and_right_margins"
android:gravity="center"
android:textColorLink="@color/colorAccent"
android:visibility="gone"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/button_cas"
tools:visibility="visible" />
<TextView <TextView
android:id="@+id/text_forgot_your_password" android:id="@+id/text_forgot_your_password"
android:layout_width="wrap_content" android:layout_width="wrap_content"
style="?borderlessButtonStyle" android:layout_height="wrap_content"
android:layout_height="48dp" android:layout_marginStart="@dimen/screen_edge_left_and_right_margins"
android:text="@string/msg_forgot_password" android:layout_marginTop="8dp"
android:textSize="18sp" android:layout_marginEnd="@dimen/screen_edge_left_and_right_margins"
android:textStyle="bold" android:gravity="center"
android:textColor="@color/colorAccent" android:textColorLink="@color/colorAccent"
android:textAllCaps="false" android:visibility="gone"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/button_log_in" app:layout_constraintTop_toBottomOf="@+id/text_new_to_rocket_chat"
android:layout_marginTop="10dp"/> tools:visibility="visible" />
<com.wang.avi.AVLoadingIndicatorView <com.wang.avi.AVLoadingIndicatorView
android:id="@+id/view_loading" android:id="@+id/view_loading"
style="@style/Authentication.AVLoadingIndicatorView" android:layout_width="wrap_content"
app:layout_constraintStart_toStartOf="parent" android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent" android:layout_marginTop="16dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:visibility="gone" android:visibility="gone"
app:indicatorName="BallPulseIndicator"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/button_cas"
tools:visibility="visible" /> tools:visibility="visible" />
<Button <LinearLayout
android:id="@+id/button_cas" android:id="@+id/social_accounts_container"
style="@style/Authentication.Button" android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/screen_edge_left_and_right_margins" android:layout_marginStart="@dimen/screen_edge_left_and_right_margins"
android:layout_marginTop="16dp" android:layout_marginTop="20dp"
android:layout_marginEnd="@dimen/screen_edge_left_and_right_margins" android:layout_marginEnd="@dimen/screen_edge_left_and_right_margins"
android:text="@string/action_login_or_sign_up" android:background="@color/colorPrimaryDark"
android:gravity="center"
android:orientation="vertical"
android:paddingTop="@dimen/screen_edge_left_and_right_margins"
android:paddingBottom="32dp"
android:visibility="gone" android:visibility="gone"
app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_forgot_your_password" app:layout_constraintTop_toBottomOf="@+id/text_forgot_your_password"
tools:visibility="visible">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="@string/msg_or_continue_using_social_accounts" />
<ImageButton
android:id="@+id/button_facebook"
android:layout_width="290dp"
android:layout_height="40dp"
android:layout_marginTop="16dp"
android:clickable="false"
android:contentDescription="@string/msg_content_description_log_in_using_facebook"
android:foreground="?android:attr/selectableItemBackgroundBorderless"
android:src="@drawable/ic_facebook"
android:visibility="gone"
tools:visibility="visible" /> tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout> <ImageButton
\ No newline at end of file android:id="@+id/button_github"
android:layout_width="290dp"
android:layout_height="40dp"
android:layout_marginTop="16dp"
android:clickable="false"
android:contentDescription="@string/msg_content_description_log_in_using_github"
android:foreground="?android:attr/selectableItemBackgroundBorderless"
android:src="@drawable/ic_github"
android:visibility="gone"
tools:visibility="visible" />
<ImageButton
android:id="@+id/button_google"
android:layout_width="290dp"
android:layout_height="40dp"
android:layout_marginTop="16dp"
android:clickable="false"
android:contentDescription="@string/msg_content_description_log_in_using_google"
android:foreground="?android:attr/selectableItemBackground"
android:src="@drawable/ic_google"
android:visibility="gone"
tools:visibility="visible" />
<ImageButton
android:id="@+id/button_linkedin"
android:layout_width="290dp"
android:layout_height="40dp"
android:layout_marginTop="16dp"
android:clickable="false"
android:contentDescription="@string/msg_content_description_log_in_using_linkedin"
android:foreground="?android:attr/selectableItemBackgroundBorderless"
android:src="@drawable/ic_linkedin"
android:visibility="gone"
tools:visibility="gone" />
<ImageButton
android:id="@+id/button_meteor"
android:layout_width="290dp"
android:layout_height="40dp"
android:layout_marginTop="16dp"
android:clickable="false"
android:contentDescription="@string/msg_content_description_log_in_using_meteor"
android:foreground="?android:attr/selectableItemBackgroundBorderless"
android:src="@drawable/ic_meteor"
android:visibility="gone"
tools:visibility="gone" />
<ImageButton
android:id="@+id/button_twitter"
android:layout_width="290dp"
android:layout_height="40dp"
android:layout_marginTop="16dp"
android:clickable="false"
android:contentDescription="@string/msg_content_description_log_in_using_twitter"
android:foreground="?android:attr/selectableItemBackgroundBorderless"
android:src="@drawable/ic_twitter"
android:visibility="gone"
tools:visibility="gone" />
<ImageButton
android:id="@+id/button_gitlab"
android:layout_width="290dp"
android:layout_height="40dp"
android:layout_marginTop="16dp"
android:clickable="false"
android:contentDescription="@string/msg_content_description_log_in_using_gitlab"
android:foreground="?android:attr/selectableItemBackgroundBorderless"
android:src="@drawable/ic_gitlab"
android:visibility="gone"
tools:visibility="gone" />
<ImageButton
android:id="@+id/button_wordpress"
android:layout_width="290dp"
android:layout_height="40dp"
android:layout_marginTop="16dp"
android:clickable="false"
android:contentDescription="@string/msg_content_description_log_in_using_gitlab"
android:foreground="?android:attr/selectableItemBackgroundBorderless"
android:src="@drawable/ic_wordpress"
android:visibility="gone"
tools:visibility="gone" />
</LinearLayout>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/button_fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_expand_more_black_24dp"
android:theme="@style/Theme.AppCompat"
android:tint="@color/colorWhite"
android:visibility="gone"
app:backgroundTint="@color/colorAccent"
app:elevation="@dimen/fab_elevation"
app:fabSize="mini"
app:layout_constraintBottom_toBottomOf="@+id/social_accounts_container"
app:layout_constraintLeft_toLeftOf="@+id/social_accounts_container"
app:layout_constraintRight_toRightOf="@+id/social_accounts_container"
app:layout_constraintTop_toBottomOf="@+id/social_accounts_container"
tools:visibility="visible" />
<Button
android:id="@+id/button_log_in"
style="@style/Authentication.Button"
android:text="@string/title_log_in"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>
\ No newline at end of file
...@@ -6,7 +6,8 @@ ...@@ -6,7 +6,8 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:paddingBottom="16dp" android:paddingBottom="16dp"
app:layout_behavior=" com.google.android.material.bottomsheet.BottomSheetBehavior"> app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior"
tools:context=".members.ui.MemberBottomSheetFragment">
<com.facebook.drawee.view.SimpleDraweeView <com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/image_bottom_sheet_avatar" android:id="@+id/image_bottom_sheet_avatar"
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
android:id="@+id/root_layout" android:id="@+id/root_layout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
tools:context=".chatinformation.ui.MessageInfoActivity"> tools:context=".chatinformation.ui.MessageInfoFragment">
<com.wang.avi.AVLoadingIndicatorView <com.wang.avi.AVLoadingIndicatorView
android:id="@+id/view_loading" android:id="@+id/view_loading"
......
...@@ -3,7 +3,8 @@ ...@@ -3,7 +3,8 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"> xmlns:tools="http://schemas.android.com/tools"
tools:context=".settings.password.ui.PasswordFragment">
<com.google.android.material.textfield.TextInputLayout <com.google.android.material.textfield.TextInputLayout
android:id="@+id/layout_new_password" android:id="@+id/layout_new_password"
......
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="16dp"
tools:context="preferences.ui.PreferencesFragment">
<com.wang.avi.AVLoadingIndicatorView
android:id="@+id/view_loading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
app:indicatorColor="@color/colorBlack"
app:indicatorName="BallPulseIndicator"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:visibility="visible" />
<TextView
android:id="@+id/text_analytics_tracking"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/msg_analytics_tracking"
android:textColor="@color/colorPrimaryText"
android:textSize="16sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/text_analytics_tracking_description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/msg_send_analytics_tracking"
android:textColor="@color/colorSecondaryText"
android:textSize="12sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_analytics_tracking" />
<Switch
android:id="@+id/switch_analytics_tracking"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="true"
app:layout_constraintBottom_toBottomOf="@+id/text_analytics_tracking_description"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/text_analytics_tracking" />
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/relative_layout"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/relative_layout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:focusableInTouchMode="true" android:focusableInTouchMode="true"
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
android:id="@+id/text_name" android:id="@+id/text_name"
style="@style/Profile.EditText" style="@style/Profile.EditText"
android:layout_marginTop="32dp" android:layout_marginTop="32dp"
android:drawableStart="@drawable/ic_person_black_20dp" android:drawableStart="@drawable/ic_person_black_24dp"
android:hint="@string/msg_name" android:hint="@string/msg_name"
android:inputType="textCapWords" /> android:inputType="textCapWords" />
...@@ -40,7 +40,7 @@ ...@@ -40,7 +40,7 @@
android:id="@+id/text_username" android:id="@+id/text_username"
style="@style/Profile.EditText" style="@style/Profile.EditText"
android:layout_marginTop="16dp" android:layout_marginTop="16dp"
android:drawableStart="@drawable/ic_at_black_20dp" android:drawableStart="@drawable/ic_at_black_24dp"
android:hint="@string/msg_username" android:hint="@string/msg_username"
android:inputType="text" /> android:inputType="text" />
...@@ -48,20 +48,11 @@ ...@@ -48,20 +48,11 @@
android:id="@+id/text_email" android:id="@+id/text_email"
style="@style/Profile.EditText" style="@style/Profile.EditText"
android:layout_marginTop="16dp" android:layout_marginTop="16dp"
android:drawableStart="@drawable/ic_email_black_20dp" android:layout_marginBottom="16dp"
android:drawableStart="@drawable/ic_email_black_24dp"
android:hint="@string/msg_email" android:hint="@string/msg_email"
android:inputType="textEmailAddress" /> android:inputType="textEmailAddress" />
<EditText
android:id="@+id/text_avatar_url"
style="@style/Profile.EditText"
android:layout_marginTop="16dp"
android:drawableStart="@drawable/ic_link_black_24dp"
android:hint="@string/msg_avatar_url"
android:inputType="text"
android:layout_marginBottom="16dp"/>
</LinearLayout> </LinearLayout>
</ScrollView> </ScrollView>
<com.wang.avi.AVLoadingIndicatorView <com.wang.avi.AVLoadingIndicatorView
...@@ -72,4 +63,19 @@ ...@@ -72,4 +63,19 @@
app:indicatorColor="@color/colorBlack" app:indicatorColor="@color/colorBlack"
app:indicatorName="BallPulseIndicator" /> app:indicatorName="BallPulseIndicator" />
<View
android:id="@+id/view_dim"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorDim"
android:visibility="gone" />
<include
android:id="@+id/layout_update_avatar_options"
layout="@layout/update_avatar_options"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:visibility="gone" />
</RelativeLayout> </RelativeLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" xmlns:tools="http://schemas.android.com/tools"
android:layout_height="match_parent"> android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".settings.ui.SettingsFragment">
<ListView <ListView
android:id="@+id/settings_list" android:id="@+id/settings_list"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:entries="@array/settings_actions"/> android:entries="@array/settings_actions" />
</RelativeLayout> </RelativeLayout>
...@@ -88,15 +88,6 @@ ...@@ -88,15 +88,6 @@
android:background="@color/colorRed" /> android:background="@color/colorRed" />
</LinearLayout> </LinearLayout>
<include
android:id="@+id/layout_avatar"
layout="@layout/avatar"
android:layout_width="38dp"
android:layout_height="40dp"
android:layout_marginTop="5dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/new_messages_notif" />
<TextView <TextView
android:id="@+id/text_sender" android:id="@+id/text_sender"
style="@style/Sender.Name.TextView" style="@style/Sender.Name.TextView"
......
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/style_attachment_options"
android:orientation="vertical">
<Button
android:id="@+id/button_open_gallery"
style="?android:attr/borderlessButtonStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawableStart="@drawable/ic_image_black_24dp"
android:drawablePadding="20dp"
android:gravity="start|center"
android:text="@string/action_select_photo_from_gallery" />
<Button
android:id="@+id/button_take_photo"
style="?android:attr/borderlessButtonStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawableStart="@drawable/ic_photo_camera_black_24dp"
android:drawablePadding="20dp"
android:gravity="start|center"
android:text="@string/action_take_photo" />
<Button
android:id="@+id/button_reset_avatar"
style="?android:attr/borderlessButtonStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawableStart="@drawable/ic_close_black_24dp"
android:drawablePadding="20dp"
android:gravity="start|center"
android:text="@string/action_reset_avatar" />
</LinearLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<group
android:id="@+id/menu_section_1"
android:checkableBehavior="single">
<item
android:id="@+id/action_chat_rooms"
android:checked="true"
android:icon="@drawable/ic_chat_bubble_black_24dp"
android:title="@string/title_chats" />
<item
android:id="@+id/action_channel"
android:icon="@drawable/ic_create_black_24dp"
android:title="@string/action_create_channel" />
</group>
<group
android:id="@+id/menu_section_2"
android:checkableBehavior="single">
<item
android:id="@+id/action_profile"
android:icon="@drawable/ic_person_black_20dp"
android:title="@string/title_profile" />
<item
android:id="@+id/action_settings"
android:icon="@drawable/ic_settings_black_24dp"
android:title="@string/title_settings" />
</group>
<group
android:id="@+id/menu_section_3"
android:checkableBehavior="single">
<item
android:id="@+id/action_logout"
android:icon="@drawable/ic_exit_to_app_black_24dp"
android:title="@string/action_logout" />
</group>
</menu>
\ No newline at end of file
...@@ -11,6 +11,9 @@ ...@@ -11,6 +11,9 @@
<string name="title_profile">Profil</string> <string name="title_profile">Profil</string>
<string name="title_members">Benutzer (%d)</string> <string name="title_members">Benutzer (%d)</string>
<string name="title_settings">Einstellungen</string> <string name="title_settings">Einstellungen</string>
<string name="title_preferences">Preferences</string> <!-- TODO Add translation -->
<string name="title_change_password">Ändere Passwort</string>
<string name="title_admin_panel">Admin panel</string> <!-- TODO Add translation -->
<string name="title_password">Ändere Passwort</string> <string name="title_password">Ändere Passwort</string>
<string name="title_update_profile">Update Profil</string> <string name="title_update_profile">Update Profil</string>
<string name="title_about">Über</string> <string name="title_about">Über</string>
...@@ -38,15 +41,13 @@ ...@@ -38,15 +41,13 @@
<string name="action_invisible">Unsichtbar</string> <string name="action_invisible">Unsichtbar</string>
<string name="action_drawing">Zeichnung</string> <string name="action_drawing">Zeichnung</string>
<string name="action_save_to_gallery">Sichern in Gallerie</string> <string name="action_save_to_gallery">Sichern in Gallerie</string>
// TODO: Verify translations <string name="action_select_photo_from_gallery">Select photo from gallery</string> <!-- TODO Add translation -->
<string name="action_connect_server">Verbinde dich mit einem Server</string> <string name="action_take_photo">Select photo from gallery</string> <!-- TODO Add translation -->
<string name="action_join_community">Trete der Community bei</string> <string name="action_reset_avatar">Reset avatar</string> <!-- TODO Add translation -->
<string name="action_create_server">Erstelle einen neuen Server</string>
<string name="action_register">Registrieren</string>
<string name="action_confirm">bestätigen</string>
<!-- Settings List --> <!-- Settings List -->
<string-array name="settings_actions"> <string-array name="settings_actions">
<item name="item_preferences">Preferences</item> <!-- TODO Add translation -->
<item name="item_password">Ändere Passwort</item> <item name="item_password">Ändere Passwort</item>
<item name="item_password">Über</item> <item name="item_password">Über</item>
</string-array> </string-array>
...@@ -63,8 +64,7 @@ ...@@ -63,8 +64,7 @@
<string name="msg_avatar_url">Avatar URL</string> <string name="msg_avatar_url">Avatar URL</string>
<string name="msg_or_continue_using_social_accounts">Oder weiter mit einem Social Account</string> <string name="msg_or_continue_using_social_accounts">Oder weiter mit einem Social Account</string>
<string name="msg_new_user">Neuer Benutzer? %1$s</string> <string name="msg_new_user">Neuer Benutzer? %1$s</string>
// TODO: Verify transaltion <string name="msg_forgot_password">Passwort vergessen? %1$s</string>
<string name="msg_forgot_password">Haben Sie Ihr Passwort vergessen?</string>
<string name="msg_reset">Zurücksetzen</string> <string name="msg_reset">Zurücksetzen</string>
<string name="msg_check_your_email_to_reset_your_password">E-Mail gesendet! Prüfe dein E-Mail Posteingang um dein Passwort zurückzusetzen.</string> <string name="msg_check_your_email_to_reset_your_password">E-Mail gesendet! Prüfe dein E-Mail Posteingang um dein Passwort zurückzusetzen.</string>
<string name="msg_invalid_email">Bitte eine korrekte E-Mail Adresse eingeben</string> <string name="msg_invalid_email">Bitte eine korrekte E-Mail Adresse eingeben</string>
...@@ -85,6 +85,7 @@ ...@@ -85,6 +85,7 @@
<string name="msg_content_description_log_in_using_meteor">Login mit Meteor</string> <string name="msg_content_description_log_in_using_meteor">Login mit Meteor</string>
<string name="msg_content_description_log_in_using_twitter">Login mit Twitter</string> <string name="msg_content_description_log_in_using_twitter">Login mit Twitter</string>
<string name="msg_content_description_log_in_using_gitlab">Login mit Gitlab</string> <string name="msg_content_description_log_in_using_gitlab">Login mit Gitlab</string>
<string name="msg_content_description_log_in_using_wordpress">Login mit WordPress</string>
<string name="msg_content_description_send_message">Sende Nachricht</string> <string name="msg_content_description_send_message">Sende Nachricht</string>
<string name="msg_content_description_show_attachment_options">Zeige Anhang Optionen</string> <string name="msg_content_description_show_attachment_options">Zeige Anhang Optionen</string>
<string name="msg_you">Du</string> <string name="msg_you">Du</string>
...@@ -101,8 +102,6 @@ ...@@ -101,8 +102,6 @@
<string name="msg_preview_photo">Bild</string> <string name="msg_preview_photo">Bild</string>
<string name="msg_preview_file">Datei</string> <string name="msg_preview_file">Datei</string>
<string name="msg_no_messages_yet">Noch keine Nachrichten</string> <string name="msg_no_messages_yet">Noch keine Nachrichten</string>
<string name="msg_version">Version %1$s</string>
<string name="msg_build">Build %1$d</string>
<string name="msg_ok">OK</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_update_app_version_in_order_to_continue">Server Version veraltet. Bitte kontaktieren Sie ihren Server Administrator.</string>
<string name="msg_ver_not_recommended"> <string name="msg_ver_not_recommended">
...@@ -131,18 +130,6 @@ ...@@ -131,18 +130,6 @@
<string name="msg_file_description">Datei Beschreibung</string> <string name="msg_file_description">Datei Beschreibung</string>
<string name="msg_send">Sende</string> <string name="msg_send">Sende</string>
<string name="msg_sent_attachment">Sende Anhang</string> <string name="msg_sent_attachment">Sende Anhang</string>
// TODO: Verify translations
<string name="msg_welcome_to_rocket_chat">Willkommen bei Rocket.Chat</string>
<string name="msg_open_source_communication">Open Source Kommunikation</string>
<string name="msg_login_with_email">Einloggen mit E-Mail</string>
<string name="msg_create_account">Ein Konto erstellen</string>
<string name="msg_continue_with_facebook">Weiter mit Facebook</string>
<string name="msg_continue_with_github">Weiter mit Github</string>
<string name="msg_continue_with_google">Weiter mit Google</string>
<string name="msg_continue_with_linkedin">Weiter mit Linkedin</string>
<string name="msg_continue_with_gitlab">Weiter mit GitLab</string>
<string name="msg_two_factor_authentication">Zwei-Faktor-Authentifizierung</string>
<string name="msg__your_2fa_code">Was ist dein 2FA-Code?</string>
<!-- Create channel messages --> <!-- Create channel messages -->
<string name="msg_private_channel">Privat</string> <string name="msg_private_channel">Privat</string>
...@@ -159,6 +146,12 @@ ...@@ -159,6 +146,12 @@
<string name="msg_delete_message">Lösche Nachricht</string> <string name="msg_delete_message">Lösche Nachricht</string>
<string name="msg_delete_description">Sind Sie sicher, dass Sie diese Nachricht löschen wollen?</string> <string name="msg_delete_description">Sind Sie sicher, dass Sie diese Nachricht löschen wollen?</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 --> <!-- System messages -->
<string name="message_room_name_changed">Raum Namen geändert zu: %1$s von %2$s</string> <string name="message_room_name_changed">Raum Namen geändert zu: %1$s von %2$s</string>
<string name="message_user_added_by">Benutzer %1$s hinzugefügt von %2$s</string> <string name="message_user_added_by">Benutzer %1$s hinzugefügt von %2$s</string>
...@@ -188,7 +181,6 @@ ...@@ -188,7 +181,6 @@
<string name="action_msg_share">Teilen</string> <string name="action_msg_share">Teilen</string>
<string name="action_title_editing">Nachricht bearbeiten</string> <string name="action_title_editing">Nachricht bearbeiten</string>
<string name="action_msg_add_reaction">Reaktion hinzufügen</string> <string name="action_msg_add_reaction">Reaktion hinzufügen</string>
<string name="action_share">Teilen</string>
<!-- Permission messages --> <!-- Permission messages -->
<string name="permission_editing_not_allowed">Bearbeiten nicht erlaubt</string> <string name="permission_editing_not_allowed">Bearbeiten nicht erlaubt</string>
...@@ -276,6 +268,7 @@ ...@@ -276,6 +268,7 @@
<string name="dialog_sort_by_activity">Aktivität</string> <string name="dialog_sort_by_activity">Aktivität</string>
<string name="dialog_group_by_type">Räume nach Typ</string> <string name="dialog_group_by_type">Räume nach Typ</string>
<string name="dialog_group_favourites">Räume nach Favoriten</string> <string name="dialog_group_favourites">Räume nach Favoriten</string>
<string name="dialog_button_done">Done</string><!-- TODO Add translation -->
<string name="chatroom_header">Kopf</string> <string name="chatroom_header">Kopf</string>
<!--ChatRooms Headers--> <!--ChatRooms Headers-->
......
...@@ -11,6 +11,9 @@ ...@@ -11,6 +11,9 @@
<string name="title_profile">Perfil</string> <string name="title_profile">Perfil</string>
<string name="title_members">Miembros (%d)</string> <string name="title_members">Miembros (%d)</string>
<string name="title_settings">Configuraciones</string> <string name="title_settings">Configuraciones</string>
<string name="title_preferences">Preferences</string> <!-- TODO Add translation -->
<string name="title_change_password">Cambia la contraseña</string>
<string name="title_admin_panel">Admin panel</string> <!-- TODO Add translation -->
<string name="title_password">Cambia la contraseña</string> <string name="title_password">Cambia la contraseña</string>
<string name="title_update_profile">Actualización del perfil</string> <string name="title_update_profile">Actualización del perfil</string>
<string name="title_about">Acerca de</string> <string name="title_about">Acerca de</string>
...@@ -36,17 +39,15 @@ ...@@ -36,17 +39,15 @@
<string name="action_away">Ausente</string> <string name="action_away">Ausente</string>
<string name="action_busy">Ocupado</string> <string name="action_busy">Ocupado</string>
<string name="action_invisible">Invisible</string> <string name="action_invisible">Invisible</string>
<string name="action_drawing">dibujo</string> <string name="action_drawing">Dibujo</string>
<string name="action_save_to_gallery">Guardar en la galería</string> <string name="action_save_to_gallery">Guardar en la galería</string>
// TODO: Verify translations <string name="action_select_photo_from_gallery">Select photo from gallery</string> <!-- TODO Add translation -->
<string name="action_connect_server">Conéctate con un servidor</string> <string name="action_take_photo">Select photo from gallery</string> <!-- TODO Add translation -->
<string name="action_join_community">Únete a la comunidad</string> <string name="action_reset_avatar">Reset avatar</string> <!-- TODO Add translation -->
<string name="action_create_server">Crea un nuevo servidor</string>
<string name="action_register">Registro</string>
<string name="action_confirm">confirmar</string>
<!-- Settings List --> <!-- Settings List -->
<string-array name="settings_actions"> <string-array name="settings_actions">
<item name="item_preferences">Preferences</item> <!-- TODO Add translation -->
<item name="item_password">Cambia la contraseña</item> <item name="item_password">Cambia la contraseña</item>
<item name="item_password">Acerca de</item> <item name="item_password">Acerca de</item>
</string-array> </string-array>
...@@ -63,8 +64,7 @@ ...@@ -63,8 +64,7 @@
<string name="msg_avatar_url">URL del avatar</string> <string name="msg_avatar_url">URL del avatar</string>
<string name="msg_or_continue_using_social_accounts">O continuar usando cuentas sociales</string> <string name="msg_or_continue_using_social_accounts">O continuar usando cuentas sociales</string>
<string name="msg_new_user">Nuevo usuario? %1$s</string> <string name="msg_new_user">Nuevo usuario? %1$s</string>
// TODO: Verify transaltion <string name="msg_forgot_password">Se te olvidó tu contraseña? %1$s</string>
<string name="msg_forgot_password">Mot de passe oublié?</string>
<string name="msg_reset">reiniciar</string> <string name="msg_reset">reiniciar</string>
<string name="msg_check_your_email_to_reset_your_password">¡Email enviado! Verifique su bandeja de entrada para restablecer su contraseña.</string> <string name="msg_check_your_email_to_reset_your_password">¡Email enviado! Verifique su bandeja de entrada para restablecer su contraseña.</string>
<string name="msg_invalid_email">Por favor escriba un correo electrónico válido</string> <string name="msg_invalid_email">Por favor escriba un correo electrónico válido</string>
...@@ -84,7 +84,7 @@ ...@@ -84,7 +84,7 @@
<string name="msg_content_description_log_in_using_meteor">Inicia sesión usando Meteor</string> <string name="msg_content_description_log_in_using_meteor">Inicia sesión usando Meteor</string>
<string name="msg_content_description_log_in_using_twitter">Inicia sesión usando Twitter</string> <string name="msg_content_description_log_in_using_twitter">Inicia sesión usando Twitter</string>
<string name="msg_content_description_log_in_using_gitlab">Inicia sesión usando Gitlab</string> <string name="msg_content_description_log_in_using_gitlab">Inicia sesión usando Gitlab</string>
<string name="msg_content_description_log_in_using_wordprees">Login using WordPress</string> <!-- TODO Translate--> <string name="msg_content_description_log_in_using_wordpress">Login using WordPress</string> <!-- TODO Translate-->
<string name="msg_content_description_send_message">Enviar mensaje</string> <string name="msg_content_description_send_message">Enviar mensaje</string>
<string name="msg_content_description_show_attachment_options">Mostrar opciones de archivo adjunto</string> <string name="msg_content_description_show_attachment_options">Mostrar opciones de archivo adjunto</string>
<string name="msg_you"></string> <string name="msg_you"></string>
...@@ -99,8 +99,6 @@ ...@@ -99,8 +99,6 @@
<string name="msg_preview_photo">Foto</string> <string name="msg_preview_photo">Foto</string>
<string name="msg_preview_file">Fichero</string> <string name="msg_preview_file">Fichero</string>
<string name="msg_no_messages_yet">Aún no hay mensajes</string> <string name="msg_no_messages_yet">Aún no hay mensajes</string>
<string name="msg_version">Versión %1$s</string>
<string name="msg_build">Build %1$d</string>
<string name="msg_ok">OK</string> <string name="msg_ok">OK</string>
<string name="msg_update_app_version_in_order_to_continue">Versión del servidor actualizada. Póngase en contacto con el administrador del servidor para actualizar la versión del servidor y continuar.</string> <string name="msg_update_app_version_in_order_to_continue">Versión del servidor actualizada. Póngase en contacto con el administrador del servidor para actualizar la versión del servidor y continuar.</string>
<string name="msg_ver_not_recommended"> <string name="msg_ver_not_recommended">
...@@ -129,18 +127,6 @@ ...@@ -129,18 +127,6 @@
<string name="msg_delete_message">Borrar mensaje</string> <string name="msg_delete_message">Borrar mensaje</string>
<string name="msg_delete_description">Seguro que quieres borrar este mensaje</string> <string name="msg_delete_description">Seguro que quieres borrar este mensaje</string>
<string name="msg_no_search_found">No se han encontrado resultados</string> <string name="msg_no_search_found">No se han encontrado resultados</string>
// TODO: Verify translations
<string name="msg_welcome_to_rocket_chat">Bienvenido a Rocket.Chat</string>
<string name="msg_open_source_communication">Comunicación de código abierto</string>
<string name="msg_login_with_email">Iniciar sesión con correo electrónico</string>
<string name="msg_create_account">Crea una cuenta</string>
<string name="msg_continue_with_facebook">Continuar con Facebook</string>
<string name="msg_continue_with_github">Continuar con Github</string>
<string name="msg_continue_with_google">Continuar con Google</string>
<string name="msg_continue_with_linkedin">Continuar con Linkedin</string>
<string name="msg_continue_with_gitlab">Continuar con GitLab</string>
<string name="msg_two_factor_authentication">Autenticación de dos factores</string>
<string name="msg__your_2fa_code">¿Cuál es tu código 2FA?</string>
<!-- Create channel messages --> <!-- Create channel messages -->
<string name="msg_private_channel">Privado</string> <string name="msg_private_channel">Privado</string>
...@@ -154,6 +140,11 @@ ...@@ -154,6 +140,11 @@
<string name="msg_member_not_found">Miembro no encontrado</string> <string name="msg_member_not_found">Miembro no encontrado</string>
<string name="msg_channel_created_successfully">Canal creado con éxito</string> <string name="msg_channel_created_successfully">Canal creado con éxito</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 --> <!-- System messages -->
<string name="message_room_name_changed">Nombre de la sala cambiado para: %1$s por %2$s</string> <string name="message_room_name_changed">Nombre de la sala cambiado para: %1$s por %2$s</string>
...@@ -272,6 +263,7 @@ ...@@ -272,6 +263,7 @@
<string name="dialog_sort_by_activity">Actividad</string> <string name="dialog_sort_by_activity">Actividad</string>
<string name="dialog_group_by_type">Agrupar por tipo</string> <string name="dialog_group_by_type">Agrupar por tipo</string>
<string name="dialog_group_favourites">Agrupar favoritos</string> <string name="dialog_group_favourites">Agrupar favoritos</string>
<string name="dialog_button_done">Done</string><!-- TODO Add translation -->
<string name="chatroom_header">Cabezazo</string> <string name="chatroom_header">Cabezazo</string>
<!--ChatRooms Headers--> <!--ChatRooms Headers-->
......
...@@ -12,6 +12,9 @@ ...@@ -12,6 +12,9 @@
<string name="title_profile">Profil</string> <string name="title_profile">Profil</string>
<string name="title_members">Membres (%d)</string> <string name="title_members">Membres (%d)</string>
<string name="title_settings">Paramètres</string> <string name="title_settings">Paramètres</string>
<string name="title_preferences">Preferences</string> <!-- TODO Add translation -->
<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_password">Changer le mot de passe</string> <string name="title_password">Changer le mot de passe</string>
<string name="title_update_profile">Update profile</string> <string name="title_update_profile">Update profile</string>
<string name="title_about">Sur</string> <string name="title_about">Sur</string>
...@@ -27,8 +30,7 @@ ...@@ -27,8 +30,7 @@
<string name="action_search">Chercher</string> <string name="action_search">Chercher</string>
<string name="action_update">Mettre à jour</string> <string name="action_update">Mettre à jour</string>
<string name="action_settings">Paramètres</string> <string name="action_settings">Paramètres</string>
// TODO: Add proper translation. <string name="action_create_channel">Create channel</string> <!-- TODO Add translation -->
<string name="action_create_channel">Create channel</string>
<string name="action_create">Create</string> <string name="action_create">Create</string>
<string name="action_logout">Se déconnecter</string> <string name="action_logout">Se déconnecter</string>
<string name="action_files">Fichiers</string> <string name="action_files">Fichiers</string>
...@@ -40,17 +42,14 @@ ...@@ -40,17 +42,14 @@
<string name="action_busy">Occupé</string> <string name="action_busy">Occupé</string>
<string name="action_invisible">Invisible</string> <string name="action_invisible">Invisible</string>
<string name="action_drawing">Dessin</string> <string name="action_drawing">Dessin</string>
// TODO: Add proper translation. <string name="action_save_to_gallery">Save to gallery</string> <!-- TODO Add translation -->
<string name="action_save_to_gallery">Save to gallery</string> <string name="action_select_photo_from_gallery">Select photo from gallery</string> <!-- TODO Add translation -->
// TODO: Verify translations <string name="action_take_photo">Select photo from gallery</string> <!-- TODO Add translation -->
<string name="action_connect_server">Se connecter avec un serveur</string> <string name="action_reset_avatar">Reset avatar</string> <!-- TODO Add translation -->
<string name="action_join_community">Rejoindre la communauté</string>
<string name="action_create_server">Créer un nouveau serveur</string>
<string name="action_register">registre</string>
<string name="action_confirm">confirmer</string>
<!-- Settings List --> <!-- Settings List -->
<string-array name="settings_actions"> <string-array name="settings_actions">
<item name="item_preferences">Preferences</item> <!-- TODO Add translation -->
<item name="item_password">Changer le mot de passe</item> <item name="item_password">Changer le mot de passe</item>
<item name="item_password">Sur</item> <item name="item_password">Sur</item>
</string-array> </string-array>
...@@ -67,8 +66,8 @@ ...@@ -67,8 +66,8 @@
<string name="msg_avatar_url">URL de l\'avatar</string> <string name="msg_avatar_url">URL de l\'avatar</string>
<string name="msg_or_continue_using_social_accounts">Ou continuer en utilisant les comptes sociaux</string> <string name="msg_or_continue_using_social_accounts">Ou continuer en utilisant les comptes sociaux</string>
<string name="msg_new_user">Nouvel utilisateur? %1$s</string> <string name="msg_new_user">Nouvel utilisateur? %1$s</string>
// TODO: Verify translation. // TODO: Add proper translation.
<string name="msg_forgot_password">Mot de passe oublié?</string> <string name="msg_forgot_password">Forgot password? %1$s</string>
// TODO: Add proper translation. // TODO: Add proper translation.
<string name="msg_reset">Reset</string> <string name="msg_reset">Reset</string>
// TODO: Add proper translation. // TODO: Add proper translation.
...@@ -91,7 +90,7 @@ ...@@ -91,7 +90,7 @@
<string name="msg_content_description_log_in_using_meteor">Connectez-vous en utilisant Meteor</string> <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_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_gitlab">Connectez-vous en utilisant Gitlab</string>
<string name="msg_content_description_log_in_using_wordprees">Login using WordPress</string> <!-- TODO Translate--> <string name="msg_content_description_log_in_using_wordpress">Login using WordPress</string> <!-- TODO Translate-->
<string name="msg_content_description_send_message">Envoyer message</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_content_description_show_attachment_options">Afficher les options de fichiers</string>
<string name="msg_you">Toi</string> <string name="msg_you">Toi</string>
...@@ -106,8 +105,6 @@ ...@@ -106,8 +105,6 @@
<string name="msg_preview_photo">Photo</string> <string name="msg_preview_photo">Photo</string>
<string name="msg_preview_file">File</string> <string name="msg_preview_file">File</string>
<string name="msg_no_messages_yet">Aucun message pour le moment</string> <string name="msg_no_messages_yet">Aucun message pour le moment</string>
<string name="msg_version">Version %1$s</string>
<string name="msg_build">Build %1$d</string>
<string name="msg_ok">OK</string> <string name="msg_ok">OK</string>
// TODO: Add proper translation. // 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">Out to date server version. Please contact the server admin to update the server version in order to continue.</string>
...@@ -147,18 +144,6 @@ ...@@ -147,18 +144,6 @@
// TODO: Add proper translation. // TODO: Add proper translation.
<string name="msg_delete_message">Delete Message</string> <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_description">Are you sure you want to delete this message</string>
// TODO: Verify translations
<string name="msg_welcome_to_rocket_chat">Bienvenue sur Rocket.Chat</string>
<string name="msg_open_source_communication">Communication Open Source</string>
<string name="msg_login_with_email">Connexion avec e-mail</string>
<string name="msg_create_account">Créer un compte</string>
<string name="msg_continue_with_facebook">Continuer avec Facebook</string>
<string name="msg_continue_with_github">Continuer avec Github</string>
<string name="msg_continue_with_google">Continuer avec Google</string>
<string name="msg_continue_with_linkedin">Continuer avec Linkedin</string>
<string name="msg_continue_with_gitlab">Continuer avec GitLab</string>
<string name="msg_two_factor_authentication">Authentification à deux facteurs</string>
<string name="msg__your_2fa_code">Quel est votre code 2FA?</string>
<!-- Create channel messages --> <!-- Create channel messages -->
// TODO: Add proper translation. // TODO: Add proper translation.
...@@ -173,6 +158,12 @@ ...@@ -173,6 +158,12 @@
<string name="msg_member_not_found">Member not found</string> <string name="msg_member_not_found">Member not found</string>
<string name="msg_channel_created_successfully">Channel created successfully</string> <string name="msg_channel_created_successfully">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 -->
<!-- System messages --> <!-- 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 de le salle a changé à: %1$s par %2$s</string>
<string name="message_user_added_by">Utilisateur %1$s ajouté par %2$s</string> <string name="message_user_added_by">Utilisateur %1$s ajouté par %2$s</string>
...@@ -204,7 +195,6 @@ ...@@ -204,7 +195,6 @@
<string name="action_msg_share">Partager</string> <string name="action_msg_share">Partager</string>
<string name="action_title_editing">Modification du message</string> <string name="action_title_editing">Modification du message</string>
<string name="action_msg_add_reaction">Ajouter une réaction</string> <string name="action_msg_add_reaction">Ajouter une réaction</string>
<string name="action_share">Partager</string>
<!-- Permission messages --> <!-- Permission messages -->
<string name="permission_editing_not_allowed">L\'édition n\'est pas autorisée</string> <string name="permission_editing_not_allowed">L\'édition n\'est pas autorisée</string>
...@@ -298,6 +288,7 @@ ...@@ -298,6 +288,7 @@
<string name="dialog_sort_by_activity">Activité</string> <string name="dialog_sort_by_activity">Activité</string>
<string name="dialog_group_by_type">Grouper par type</string> <string name="dialog_group_by_type">Grouper par type</string>
<string name="dialog_group_favourites">Grouper favoris</string> <string name="dialog_group_favourites">Grouper favoris</string>
<string name="dialog_button_done">Done</string><!-- TODO Add translation -->
<string name="chatroom_header">Entête</string> <string name="chatroom_header">Entête</string>
<!--ChatRooms Headers--> <!--ChatRooms Headers-->
......
...@@ -12,6 +12,9 @@ ...@@ -12,6 +12,9 @@
<string name="title_profile">प्रोफाइल</string> <string name="title_profile">प्रोफाइल</string>
<string name="title_members">सदस्य (%d)</string> <string name="title_members">सदस्य (%d)</string>
<string name="title_settings">सेटिंग्स</string> <string name="title_settings">सेटिंग्स</string>
<string name="title_preferences">Preferences</string> <!-- TODO Add translation -->
<string name="title_change_password">पासवर्ड बदलें</string>
<string name="title_admin_panel">Admin panel</string> <!-- TODO Add translation -->
<string name="title_password">पासवर्ड बदलें</string> <string name="title_password">पासवर्ड बदलें</string>
<string name="title_update_profile">प्रोफ़ाइल अपडेट करें</string> <string name="title_update_profile">प्रोफ़ाइल अपडेट करें</string>
<string name="title_about">परिचय</string> <string name="title_about">परिचय</string>
...@@ -39,15 +42,13 @@ ...@@ -39,15 +42,13 @@
<string name="action_invisible">अदृश्य</string> <string name="action_invisible">अदृश्य</string>
<string name="action_save_to_gallery">गैलरी में सहेजें</string> <string name="action_save_to_gallery">गैलरी में सहेजें</string>
<string name="action_drawing">चित्रकारी</string> <string name="action_drawing">चित्रकारी</string>
<string name="action_share">शेयर</string> <string name="action_select_photo_from_gallery">Select photo from gallery</string> <!-- TODO Add translation -->
<string name="action_connect_server">सर्वर से कनेक्ट करें</string> <string name="action_take_photo">Select photo from gallery</string> <!-- TODO Add translation -->
<string name="action_join_community">समुदाय में शामिल हों</string> <string name="action_reset_avatar">Reset avatar</string> <!-- TODO Add translation -->
<string name="action_create_server">नया सर्वर बनाएं</string>
<string name="action_register">रजिस्टर</string>
<string name="action_confirm">पुष्टि करें</string>
<!-- Settings List --> <!-- Settings List -->
<string-array name="settings_actions"> <string-array name="settings_actions">
<item name="item_preferences">Preferences</item> <!-- TODO Add translation -->
<item name="item_password">पासवर्ड बदलें</item> <item name="item_password">पासवर्ड बदलें</item>
<item name="item_password">परिचय</item> <item name="item_password">परिचय</item>
</string-array> </string-array>
...@@ -64,7 +65,7 @@ ...@@ -64,7 +65,7 @@
<string name="msg_avatar_url">अवतार यूआरएल</string> <string name="msg_avatar_url">अवतार यूआरएल</string>
<string name="msg_or_continue_using_social_accounts">या सामाजिक खाते का उपयोग करना जारी रखें</string> <string name="msg_or_continue_using_social_accounts">या सामाजिक खाते का उपयोग करना जारी रखें</string>
<string name="msg_new_user">नया उपयोगकर्ता? %1$s</string> <string name="msg_new_user">नया उपयोगकर्ता? %1$s</string>
<string name="msg_forgot_password">क्या आप अपना पासवर्ड भूल गए?</string> <string name="msg_forgot_password">पासवर्ड भूल गए? %1$s</string>
<string name="msg_reset">रीसेट करें</string> <string name="msg_reset">रीसेट करें</string>
<string name="msg_check_your_email_to_reset_your_password">ईमेल गया गया है! अपना पासवर्ड रीसेट करने के लिए अपने इनबॉक्स की जांच करें।</string> <string name="msg_check_your_email_to_reset_your_password">ईमेल गया गया है! अपना पासवर्ड रीसेट करने के लिए अपने इनबॉक्स की जांच करें।</string>
<string name="msg_invalid_email">कृपया एक वैध ई-मेल टाइप करें</string> <string name="msg_invalid_email">कृपया एक वैध ई-मेल टाइप करें</string>
...@@ -84,7 +85,7 @@ ...@@ -84,7 +85,7 @@
<string name="msg_content_description_log_in_using_meteor">Meteor द्वारा लॉगिन करें</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_twitter">Twitter द्वारा लॉगिन करें</string>
<string name="msg_content_description_log_in_using_gitlab">Gitlab द्वारा लॉगिन करें</string> <string name="msg_content_description_log_in_using_gitlab">Gitlab द्वारा लॉगिन करें</string>
<string name="msg_content_description_log_in_using_wordprees">Login using WordPress</string> <!-- TODO Translate--> <string name="msg_content_description_log_in_using_wordpress">Login using WordPress</string> <!-- TODO Translate-->
<string name="msg_content_description_send_message">मेसेज भेजें</string> <string name="msg_content_description_send_message">मेसेज भेजें</string>
<string name="msg_content_description_show_attachment_options">अटैचमेंट विकल्प दिखाएं</string> <string name="msg_content_description_show_attachment_options">अटैचमेंट विकल्प दिखाएं</string>
<string name="msg_you">आप</string> <string name="msg_you">आप</string>
...@@ -99,8 +100,6 @@ ...@@ -99,8 +100,6 @@
<string name="msg_preview_file">फ़ाइल</string> <string name="msg_preview_file">फ़ाइल</string>
<string name="msg_unread_messages">अपठित संदेश</string> <string name="msg_unread_messages">अपठित संदेश</string>
<string name="msg_no_messages_yet">अभी तक कोई पोस्ट नहीं</string> <string name="msg_no_messages_yet">अभी तक कोई पोस्ट नहीं</string>
<string name="msg_version">वर्शन %1$s</string>
<string name="msg_build">बिल्ड %1$d</string>
<string name="msg_update_app_version_in_order_to_continue">पुराना सर्वर वर्शन। जारी रखने के लिए सर्वर वर्शन को अद्यतन करने के लिए कृपया सर्वर व्यवस्थापक से संपर्क करें।</string> <string name="msg_update_app_version_in_order_to_continue">पुराना सर्वर वर्शन। जारी रखने के लिए सर्वर वर्शन को अद्यतन करने के लिए कृपया सर्वर व्यवस्थापक से संपर्क करें।</string>
<string name="msg_ok">ठीक है</string> <string name="msg_ok">ठीक है</string>
<string name="msg_ver_not_recommended"> <string name="msg_ver_not_recommended">
...@@ -134,17 +133,6 @@ ...@@ -134,17 +133,6 @@
<string name="msg_sent_attachment">एक अनुलग्नक भेजा</string> <string name="msg_sent_attachment">एक अनुलग्नक भेजा</string>
<string name="msg_delete_message">संदेश को हटाएं</string> <string name="msg_delete_message">संदेश को हटाएं</string>
<string name="msg_delete_description">क्या आप निश्चित रूप से यह संदेश हटाना चाहते हैं</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_login_with_email">ई-मेल के साथ लॉगिन करें</string>
<string name="msg_create_account">खाता बनाएं</string>
<string name="msg_continue_with_facebook">Facebook के साथ जारी रखें</string>
<string name="msg_continue_with_github">Github के साथ जारी रखें</string>
<string name="msg_continue_with_google">Google के साथ जारी रखें</string>
<string name="msg_continue_with_linkedin">Linkedin के साथ जारी रखें</string>
<string name="msg_continue_with_gitlab">GitLab के साथ जारी रखें</string>
<string name="msg_two_factor_authentication">दो तरीकों से प्रमाणीकरण</string>
<string name="msg__your_2fa_code">आपका 2 एफए कोड क्या है?</string>
<!-- Create channel messages --> <!-- Create channel messages -->
<string name="msg_private_channel">प्राइवेट</string> <string name="msg_private_channel">प्राइवेट</string>
...@@ -158,6 +146,12 @@ ...@@ -158,6 +146,12 @@
<string name="msg_member_not_found">सदस्य नहीं मिला</string> <string name="msg_member_not_found">सदस्य नहीं मिला</string>
<string name="msg_channel_created_successfully">चैनल सफलतापूर्वक बनाया गया</string> <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 -->
<!-- System messages --> <!-- System messages -->
<string name="message_room_name_changed">%2$s ने रूम का नाम बदलकर %1$s किया</string> <string name="message_room_name_changed">%2$s ने रूम का नाम बदलकर %1$s किया</string>
<string name="message_user_added_by">उपयोगकर्ता %1$s द्वारा %2$s को जोड़ा गया</string> <string name="message_user_added_by">उपयोगकर्ता %1$s द्वारा %2$s को जोड़ा गया</string>
...@@ -276,6 +270,7 @@ ...@@ -276,6 +270,7 @@
<string name="dialog_sort_by_activity">गतिविधि</string> <string name="dialog_sort_by_activity">गतिविधि</string>
<string name="dialog_group_by_type">प्रकार के आधार पर समूह</string> <string name="dialog_group_by_type">प्रकार के आधार पर समूह</string>
<string name="dialog_group_favourites">पसंदीदा समूह</string> <string name="dialog_group_favourites">पसंदीदा समूह</string>
<string name="dialog_button_done">Done</string><!-- TODO Add translation -->
<string name="chatroom_header">हैडर</string> <string name="chatroom_header">हैडर</string>
<!--ChatRooms Headers--> <!--ChatRooms Headers-->
......
...@@ -12,6 +12,9 @@ ...@@ -12,6 +12,9 @@
<string name="title_profile">Perfil</string> <string name="title_profile">Perfil</string>
<string name="title_members">Membros (%d)</string> <string name="title_members">Membros (%d)</string>
<string name="title_settings">Configurações</string> <string name="title_settings">Configurações</string>
<string name="title_preferences">Preferencias</string>
<string name="title_change_password">Alterar senha</string>
<string name="title_admin_panel">Painel administrativo</string>
<string name="title_password">Alterar senha</string> <string name="title_password">Alterar senha</string>
<string name="title_update_profile">Editar perfil</string> <string name="title_update_profile">Editar perfil</string>
<string name="title_about">Sobre</string> <string name="title_about">Sobre</string>
...@@ -39,15 +42,13 @@ ...@@ -39,15 +42,13 @@
<string name="action_invisible">Invisível</string> <string name="action_invisible">Invisível</string>
<string name="action_drawing">Desenhando</string> <string name="action_drawing">Desenhando</string>
<string name="action_save_to_gallery">Salvar na galeria</string> <string name="action_save_to_gallery">Salvar na galeria</string>
// TODO: Verify translations <string name="action_select_photo_from_gallery">Escolher foto da galeria</string>
<string name="action_connect_server">Conecte-se com um servidor</string> <string name="action_take_photo">Tirar foto</string>
<string name="action_join_community">Junte-se à comunidade</string> <string name="action_reset_avatar">Resetar avatar</string>
<string name="action_create_server">Crie um novo servidor</string>
<string name="action_register">registo</string>
<string name="action_confirm">confirme</string>
<!-- Settings List --> <!-- Settings List -->
<string-array name="settings_actions"> <string-array name="settings_actions">
<item name="item_preferences">Preferencias</item>
<item name="item_password">Alterar senha</item> <item name="item_password">Alterar senha</item>
<item name="item_password">Sobre</item> <item name="item_password">Sobre</item>
</string-array> </string-array>
...@@ -64,8 +65,7 @@ ...@@ -64,8 +65,7 @@
<string name="msg_avatar_url">URL do avatar</string> <string name="msg_avatar_url">URL do avatar</string>
<string name="msg_or_continue_using_social_accounts">Ou continue através de contas sociais</string> <string name="msg_or_continue_using_social_accounts">Ou continue através de contas sociais</string>
<string name="msg_new_user">Novo usuário? %1$s</string> <string name="msg_new_user">Novo usuário? %1$s</string>
// TODO: Verify transaltion <string name="msg_forgot_password">Esqueceu a senha? %1$s</string>
<string name="msg_forgot_password">Esqueceu sua senha?</string>
<string name="msg_reset">Redefinir</string> <string name="msg_reset">Redefinir</string>
<string name="msg_check_your_email_to_reset_your_password">Email enviado! Verifique sua caixa de entrada para redefinir sua senha.</string> <string name="msg_check_your_email_to_reset_your_password">Email enviado! Verifique sua caixa de entrada para redefinir sua senha.</string>
<string name="msg_invalid_email">Por favor informe um e-mail válido</string> <string name="msg_invalid_email">Por favor informe um e-mail válido</string>
...@@ -85,7 +85,7 @@ ...@@ -85,7 +85,7 @@
<string name="msg_content_description_log_in_using_meteor">Fazer login através do Meteor</string> <string name="msg_content_description_log_in_using_meteor">Fazer login através do Meteor</string>
<string name="msg_content_description_log_in_using_twitter">Fazer login através do Twitter</string> <string name="msg_content_description_log_in_using_twitter">Fazer login através do Twitter</string>
<string name="msg_content_description_log_in_using_gitlab">Fazer login através do Gitlab</string> <string name="msg_content_description_log_in_using_gitlab">Fazer login através do Gitlab</string>
<string name="msg_content_description_log_in_using_wordprees">Fazer login através do WordPress</string> <string name="msg_content_description_log_in_using_wordpress">Fazer login através do WordPress</string>
<string name="msg_content_description_send_message">Enviar mensagem</string> <string name="msg_content_description_send_message">Enviar mensagem</string>
<string name="msg_content_description_show_attachment_options">Mostrar opções de anexo</string> <string name="msg_content_description_show_attachment_options">Mostrar opções de anexo</string>
<string name="msg_you">Você</string> <string name="msg_you">Você</string>
...@@ -100,8 +100,6 @@ ...@@ -100,8 +100,6 @@
<string name="msg_preview_photo">Foto</string> <string name="msg_preview_photo">Foto</string>
<string name="msg_preview_file">Arquivo</string> <string name="msg_preview_file">Arquivo</string>
<string name="msg_no_messages_yet">Nenhuma mensagem ainda</string> <string name="msg_no_messages_yet">Nenhuma mensagem ainda</string>
<string name="msg_version">Versão %1$s</string>
<string name="msg_build">Build %1$d</string>
<string name="msg_ok">OK</string> <string name="msg_ok">OK</string>
<string name="msg_update_app_version_in_order_to_continue">Versão do servidor desatualizada. Por favor, entre em contato com o administrador do sistema para continuar.</string> <string name="msg_update_app_version_in_order_to_continue">Versão do servidor desatualizada. Por favor, entre em contato com o administrador do sistema para continuar.</string>
<string name="msg_ver_not_recommended"> <string name="msg_ver_not_recommended">
...@@ -135,18 +133,6 @@ ...@@ -135,18 +133,6 @@
// TODO: Add proper translation. // TODO: Add proper translation.
<string name="msg_delete_message">Delete Message</string> <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_description">Are you sure you want to delete this message</string>
// TODO: Verify translations
<string name="msg_welcome_to_rocket_chat">Bem-vindo ao Rocket.Chat</string>
<string name="msg_open_source_communication">Comunicação Open Source</string>
<string name="msg_login_with_email">Login com e-mail</string>
<string name="msg_create_account">Crie a sua conta aqui</string>
<string name="msg_continue_with_facebook">Continue com o Facebook</string>
<string name="msg_continue_with_github">Continue com o Github</string>
<string name="msg_continue_with_google">Continue com o Google</string>
<string name="msg_continue_with_linkedin">Continue com o Linkedin</string>
<string name="msg_continue_with_gitlab">Continue com o GitLab</string>
<string name="msg_two_factor_authentication">Autenticação de dois fatores</string>
<string name="msg__your_2fa_code">Qual é o seu código 2FA?</string>
<!-- Create channel messages --> <!-- Create channel messages -->
<string name="msg_private_channel">Privado</string> <string name="msg_private_channel">Privado</string>
...@@ -160,6 +146,12 @@ ...@@ -160,6 +146,12 @@
<string name="msg_member_not_found">Membro não encontrado</string> <string name="msg_member_not_found">Membro não encontrado</string>
<string name="msg_channel_created_successfully">Chat criado com sucesso</string> <string name="msg_channel_created_successfully">Chat criado com sucesso</string>
<!-- Preferences messages -->
<string name="msg_analytics_tracking">Rastreamento de análises</string>
<string name="msg_send_analytics_tracking">Envie estatísticas anônimas para ajudar a melhorar este app</string>
<string name="msg_do_not_send_analytics_tracking">Não envie estatísticas anônimas para ajudar a melhorar este app</string>
<string name="msg_not_applicable_since_it_is_a_foss_version">Not aplicável devido a versão do aplicativo ser FOSS</string> <!-- TODO Add translation -->
<!-- System messages --> <!-- System messages -->
<string name="message_room_name_changed">Nome da sala alterado para: %1$s por %2$s</string> <string name="message_room_name_changed">Nome da sala alterado para: %1$s por %2$s</string>
<string name="message_user_added_by">Usuário %1$s adicionado por %2$s</string> <string name="message_user_added_by">Usuário %1$s adicionado por %2$s</string>
...@@ -277,6 +269,7 @@ ...@@ -277,6 +269,7 @@
<string name="dialog_sort_by_activity">Atividade</string> <string name="dialog_sort_by_activity">Atividade</string>
<string name="dialog_group_by_type">Agrupar por tipo</string> <string name="dialog_group_by_type">Agrupar por tipo</string>
<string name="dialog_group_favourites">Grupos favoritos</string> <string name="dialog_group_favourites">Grupos favoritos</string>
<string name="dialog_button_done">Done</string><!-- TODO Add translation -->
<string name="chatroom_header">Cabeçalho</string> <string name="chatroom_header">Cabeçalho</string>
<!--ChatRooms Headers--> <!--ChatRooms Headers-->
...@@ -291,7 +284,6 @@ ...@@ -291,7 +284,6 @@
<string name="notif_action_reply_hint">RESPONDER</string> <string name="notif_action_reply_hint">RESPONDER</string>
<string name="notif_error_sending">Falha ao enviar a mensagem.</string> <string name="notif_error_sending">Falha ao enviar a mensagem.</string>
<string name="notif_success_sending">Mensagem enviada para %1$s!</string> <string name="notif_success_sending">Mensagem enviada para %1$s!</string>
<string name="action_share">Compartilhar</string>
<string name="read_by">Lida por</string> <string name="read_by">Lida por</string>
<string name="message_information_title">Informações da mensagem</string> <string name="message_information_title">Informações da mensagem</string>
<string name="msg_log_out">Deslogando…</string> <string name="msg_log_out">Deslogando…</string>
......
<resources> <resources>
<!-- Titles --> <!-- Titles -->
<string name="title_sign_in_your_server">Войдите на ваш сервер</string> <string name="title_sign_in_your_server">Ваш сервер</string>
<string name="title_log_in">Войти</string> <string name="title_log_in">Войти</string>
<string name="title_register_username">Зарегистрировать имя</string> <string name="title_register_username">Зарегистрировать имя</string>
<string name="title_reset_password">Сброс пароля</string> <string name="title_reset_password">Сброс пароля</string>
...@@ -12,13 +12,16 @@ ...@@ -12,13 +12,16 @@
<string name="title_profile">Профиль</string> <string name="title_profile">Профиль</string>
<string name="title_members">Пользователи (%d)</string> <string name="title_members">Пользователи (%d)</string>
<string name="title_settings">Настройки</string> <string name="title_settings">Настройки</string>
<string name="title_preferences">Preferences</string> <!-- TODO Add translation -->
<string name="title_change_password">Изменить пароль</string>
<string name="title_admin_panel">Admin panel</string> <!-- TODO Add translation -->
<string name="title_password">Изменить пароль</string> <string name="title_password">Изменить пароль</string>
<string name="title_update_profile">Обновить профиль</string> <string name="title_update_profile">Обновить профиль</string>
<string name="title_about">О программе</string> <string name="title_about">О программе</string>
<string name="title_create_channel">Создать новый канал</string> <string name="title_create_channel">Создать новый канал</string>
<!-- Actions --> <!-- Actions -->
<string name="action_connect">Соединение</string> <string name="action_connect">Подключиться</string>
<string name="action_use_this_username">Использовать это имя</string> <string name="action_use_this_username">Использовать это имя</string>
<string name="action_login_or_sign_up">Нажмите эту кнопку, чтобы войти в систему или создать учетную запись</string> <string name="action_login_or_sign_up">Нажмите эту кнопку, чтобы войти в систему или создать учетную запись</string>
<string name="action_terms_of_service">Условия использования</string> <string name="action_terms_of_service">Условия использования</string>
...@@ -37,17 +40,15 @@ ...@@ -37,17 +40,15 @@
<string name="action_away">Отошел</string> <string name="action_away">Отошел</string>
<string name="action_busy">Занят</string> <string name="action_busy">Занят</string>
<string name="action_invisible">Невидимый</string> <string name="action_invisible">Невидимый</string>
<string name="action_drawing">Рисование</string> <string name="action_drawing">Рисунок</string>
<string name="action_save_to_gallery">Сохранить в галерею</string> <string name="action_save_to_gallery">Сохранить в галерею</string>
// TODO: Verify translations <string name="action_select_photo_from_gallery">Выбрать из галереи</string>
<string name="action_connect_server">Подключение к серверу</string> <string name="action_take_photo">Сделать снимок</string>
<string name="action_join_community">Присоединиться к сообществу</string> <string name="action_reset_avatar">Восстановить аватар</string>
<string name="action_create_server">Создать новый сервер</string>
<string name="action_register">регистр</string>
<string name="action_confirm">подтвердить</string>
<!-- Settings List --> <!-- Settings List -->
<string-array name="settings_actions"> <string-array name="settings_actions">
<item name="item_preferences">Preferences</item> <!-- TODO Add translation -->
<item name="item_password">Изменить пароль</item> <item name="item_password">Изменить пароль</item>
<item name="item_password">О программе</item> <item name="item_password">О программе</item>
</string-array> </string-array>
...@@ -64,28 +65,27 @@ ...@@ -64,28 +65,27 @@
<string name="msg_avatar_url">URL аватара</string> <string name="msg_avatar_url">URL аватара</string>
<string name="msg_or_continue_using_social_accounts">Или продолжить, используя социальные учетные записи</string> <string name="msg_or_continue_using_social_accounts">Или продолжить, используя социальные учетные записи</string>
<string name="msg_new_user">Новый пользователь? %1$s</string> <string name="msg_new_user">Новый пользователь? %1$s</string>
// TODO: Verify transaltion <string name="msg_forgot_password">Забыли пароль? %1$s</string>
<string name="msg_forgot_password">Забыли пароль?</string>
<string name="msg_reset">Сброс</string> <string name="msg_reset">Сброс</string>
<string name="msg_check_your_email_to_reset_your_password">Письмо отправлено! Проверьте свой почтовый ящик, чтобы сбросить пароль.</string> <string name="msg_check_your_email_to_reset_your_password">Письмо отправлено! Проверьте свой почтовый ящик, чтобы сбросить пароль.</string>
<string name="msg_invalid_email">Введите действующий e-mail</string> <string name="msg_invalid_email">Введите действующий e-mail</string>
<string name="msg_new_user_agreement">Продолжая, вы соглашаетесь с нашими\n%1$s и %2$s</string> <string name="msg_new_user_agreement">Продолжая, вы принимаете\n%1$s и %2$s</string>
<string name="msg_2fa_code">Код 2FA</string> <string name="msg_2fa_code">Код 2FA</string>
<string name="msg_yesterday">Вчера</string> <string name="msg_yesterday">Вчера</string>
<string name="msg_today">Сегодня</string> <string name="msg_today">Сегодня</string>
<string name="msg_message">Сообщение</string> <string name="msg_message">Сообщение</string>
<string name="msg_this_room_is_read_only">Этот канал только для чтения</string> <string name="msg_this_room_is_read_only">Канал только для чтения</string>
<string name="msg_invalid_2fa_code">Неверный код 2FA</string> <string name="msg_invalid_2fa_code">Неверный код 2FA</string>
<string name="msg_invalid_file">Неверный файл</string> <string name="msg_invalid_file">Неверный файл</string>
<string name="msg_invalid_server_url">Неверный URL-адрес сервера.</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_facebook">Войти с помощью Facebook</string>
<string name="msg_content_description_log_in_using_github">Войти используя Github</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_google">Войти с помощью Google</string>
<string name="msg_content_description_log_in_using_linkedin">Войти используя Linkedin</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_meteor">Войти с помощью Meteor</string>
<string name="msg_content_description_log_in_using_twitter">Войти используя Twitter</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_gitlab">Войти с помощью Gitlab</string>
<string name="msg_content_description_log_in_using_wordprees">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_send_message">Отправить сообщение</string>
<string name="msg_content_description_show_attachment_options">Показать параметры вложения</string> <string name="msg_content_description_show_attachment_options">Показать параметры вложения</string>
<string name="msg_you">Вы</string> <string name="msg_you">Вы</string>
...@@ -100,17 +100,13 @@ ...@@ -100,17 +100,13 @@
<string name="msg_preview_photo">Фото</string> <string name="msg_preview_photo">Фото</string>
<string name="msg_preview_file">Файл</string> <string name="msg_preview_file">Файл</string>
<string name="msg_no_messages_yet">Нет сообщений</string> <string name="msg_no_messages_yet">Нет сообщений</string>
<string name="msg_version">Версия %1$s</string> <string name="msg_build">Сборка %1$d - %2$s - %3$s</string>
<string name="msg_build">Сборка %1$d</string>
<string name="msg_ok">OK</string> <string name="msg_ok">OK</string>
<string name="msg_update_app_version_in_order_to_continue">Версия сервера устарела. Пожалуйста, свяжитесь с администратором, чтобы обновить версию сервера.</string> <string name="msg_update_app_version_in_order_to_continue">Версия сервера устарела. Пожалуйста, свяжитесь с администратором, чтобы обновить сервер.</string>
<string name="msg_ver_not_recommended"> <string name="msg_ver_not_recommended">Похоже, версия сервера меньше рекомендуемой %1$s.\nМожно войти, но могут возникнуть те или иные проблемы при работе.\n</string>
Похоже ваша версия сервера ниже рекомендуемой %1$s.\nВы можете войти, но будьте готовы столкнуться с непредвиденными трудностями.</string> <string name="msg_ver_not_minimum">Похоже, версия сервера меньше минимально необходимой %1$s.\nДля работы потребуется обновить сервер!</string>
<string name="msg_ver_not_minimum"> <string name="msg_no_chat_title">Нет сообщений</string>
Похоже ваша версия сервера ниже минимально необходимой версии %1$s.\nОбновите свой сервер чтобы войти в систему! <string name="msg_no_chat_description">Начните общаться, чтобы увидеть\nтут сообщения.</string>
</string>
<string name="msg_no_chat_title">Нет сообщений чата</string>
<string name="msg_no_chat_description">Начните разговор, чтобы увидеть ваши\nсообщения здесь.</string>
<string name="msg_proceed">ПРОДОЛЖИТЬ</string> <string name="msg_proceed">ПРОДОЛЖИТЬ</string>
<string name="msg_cancel">ОТМЕНА</string> <string name="msg_cancel">ОТМЕНА</string>
<string name="msg_warning">ПРЕДУПРЕЖДЕНИЕ</string> <string name="msg_warning">ПРЕДУПРЕЖДЕНИЕ</string>
...@@ -128,24 +124,12 @@ ...@@ -128,24 +124,12 @@
<string name="msg_message_copied">Сообщение скопировано</string> <string name="msg_message_copied">Сообщение скопировано</string>
<string name="msg_upload_file">Загрузить файл</string> <string name="msg_upload_file">Загрузить файл</string>
<string name="msg_file_description">Описание файла</string> <string name="msg_file_description">Описание файла</string>
<string name="msg_send">послать</string> <string name="msg_send">Отправить</string>
<string name="msg_sent_attachment">Отправить вложение</string> <string name="msg_sent_attachment">Вложение отправлено</string>
<string name="msg_delete_message">Удалить сообщение</string> <string name="msg_delete_message">Удалить сообщение</string>
<string name="msg_delete_description">Вы уверены, что хотите удалить это сообщение?</string> <string name="msg_delete_description">Вы уверены, что хотите удалить это сообщение?</string>
<string name="msg_channel_name">Название канала</string> <string name="msg_channel_name">Название канала</string>
<string name="msg_search">Поиск</string> <string name="msg_search">Поиск</string>
// TODO: Verify translations
<string name="msg_welcome_to_rocket_chat">Добро пожаловать в Rocket.Chat</string>
<string name="msg_open_source_communication">Связь с открытым исходным кодом</string>
<string name="msg_login_with_email">Войти через E-mail</string>
<string name="msg_create_account">Завести аккаунт</string>
<string name="msg_continue_with_facebook">Продолжить работу с Facebook</string>
<string name="msg_continue_with_github">Продолжить работу с Github</string>
<string name="msg_continue_with_google">Продолжить работу с Google</string>
<string name="msg_continue_with_linkedin">Продолжить работу с Linkedin</string>
<string name="msg_continue_with_gitlab">Продолжить работу с GitLab</string>
<string name="msg_two_factor_authentication">Двухфакторная аутентификация</string>
<string name="msg__your_2fa_code">Какой у вас код 2FA?</string>
<!-- Create channel messages --> <!-- Create channel messages -->
<string name="msg_private_channel">Приватный</string> <string name="msg_private_channel">Приватный</string>
...@@ -154,54 +138,59 @@ ...@@ -154,54 +138,59 @@
<string name="msg_public_channel_description">Каждый может получить доступ к этому каналу</string> <string name="msg_public_channel_description">Каждый может получить доступ к этому каналу</string>
<string name="msg_ready_only_channel">Канал только для чтения</string> <string name="msg_ready_only_channel">Канал только для чтения</string>
<string name="msg_ready_only_channel_description">Только администратор может писать новые сообщения</string> <string name="msg_ready_only_channel_description">Только администратор может писать новые сообщения</string>
<string name="msg_invite_members">Пригласить пользователей на канал</string> <string name="msg_invite_members">Пригласить пользователей в канал</string>
<string name="msg_member_already_added">Вы уже выбрали этого пользователя</string> <string name="msg_member_already_added">Вы уже выбрали этого пользователя</string>
<string name="msg_member_not_found">Пользователь не найден</string> <string name="msg_member_not_found">Пользователь не найден</string>
<string name="msg_channel_created_successfully">Канал создан успешно</string> <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 -->
<!-- System messages --> <!-- System messages -->
<string name="message_room_name_changed">Название канала изменено на: %1$s by %2$s</string> <string name="message_room_name_changed">%2$s изменил название канала на %1$s</string>
<string name="message_user_added_by">Пользователь %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_removed_by">Пользователь %1$s удален пользователем %2$s</string>
<string name="message_user_left">Покинул канал.</string> <string name="message_user_left">Покинул канал.</string>
<string name="message_user_joined_channel">Присоединился к каналу.</string> <string name="message_user_joined_channel">Присоединился к каналу.</string>
<string name="message_welcome">Добро пожаловать %s</string> <string name="message_welcome">Привет, %s</string>
<string name="message_removed">Сообщение удалено</string> <string name="message_removed">Сообщение удалено</string>
<string name="message_pinned">Прикрепленное сообщение:</string> <string name="message_pinned">Прикрепленное сообщение:</string>
<string name="message_muted">Пользователь %1$s заглушен пользователем %2$s</string> <string name="message_muted">Пользователь %2$s лишил %1$s дара речи</string>
<string name="message_unmuted">Пользователь %1$s перестал быть заглушенным по решению %2$s</string> <string name="message_unmuted">Пользователю %1$s вернули дар речи по решению %2$s</string>
<string name="message_role_add">%1$s был назначен %2$s пользователем %3$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_role_removed">%1$s больше не %2$s по решению %3$s</string>
<string name="message_credentials_saved_successfully">Учетные данные успешно сохранены</string> <string name="message_credentials_saved_successfully">Учетные данные успешно сохранены</string>
<!-- Message actions --> <!-- Message actions -->
<string name="action_msg_reply">Ответить</string> <string name="action_msg_reply">Ответить</string>
<string name="action_msg_info">О сообщении</string> <string name="action_msg_info">Информация о прочтении</string>
<string name="action_msg_edit">Редактировать</string> <string name="action_msg_edit">Редактировать</string>
<string name="action_msg_copy">Копировать</string> <string name="action_msg_copy">Копировать</string>
<string name="action_msg_quote">Цитата</string> <string name="action_msg_quote">Цитировать</string>
<string name="action_msg_delete">Удалить</string> <string name="action_msg_delete">Удалить</string>
<string name="action_msg_pin">Прикрепить сообщение</string> <string name="action_msg_pin">Закрепить сообщение</string>
<string name="action_msg_unpin">Открепить сообщение</string> <string name="action_msg_unpin">Открепить сообщение</string>
<string name="action_msg_star">Пометить сообщение</string> <string name="action_msg_star">В избранное</string>
<string name="action_msg_unstar">Снять отметку</string> <string name="action_msg_unstar">Из избранного</string>
<string name="action_msg_share">Поделиться</string> <string name="action_msg_share">Поделиться</string>
<string name="action_title_editing">Редактирование сообщения</string> <string name="action_title_editing">Редактирование сообщения</string>
<string name="action_msg_add_reaction">Добавить реакцию</string> <string name="action_msg_add_reaction">Отреагировать</string>
<string name="action_share">Поделиться</string>
<!-- Permission messages --> <!-- Permission messages -->
<string name="permission_editing_not_allowed">Редактирование запрещено</string> <string name="permission_editing_not_allowed">Редактирование запрещено</string>
<string name="permission_deleting_not_allowed">Удаление запрещено</string> <string name="permission_deleting_not_allowed">Удаление запрещено</string>
<string name="permission_pinning_not_allowed">Прикрепление запрещено</string> <string name="permission_pinning_not_allowed">Нельзя закрепить</string>
<string name="permission_starring_not_allowed">Отмечивание запрещено</string> <string name="permission_starring_not_allowed">Нельзя отметить</string>
<!-- Search message --> <!-- Search message -->
<string name="title_search_message">Поиск сообщения</string> <string name="title_search_message">Поиск сообщения</string>
<!-- Favorite/Unfavorite chat room --> <!-- Favorite/Unfavorite chat room -->
<string name="title_favorite_chat">Добавить чат в избранное</string> <string name="title_favorite_chat">Добавить в избранное</string>
<string name="title_unfavorite_chat">Удалить чат из избранного</string> <string name="title_unfavorite_chat">Удалить из избранного</string>
<!-- Members List --> <!-- Members List -->
<string name="title_members_list">Пользователи</string> <string name="title_members_list">Пользователи</string>
...@@ -209,17 +198,17 @@ ...@@ -209,17 +198,17 @@
<!-- Mentions --> <!-- Mentions -->
<string name="msg_mentions">Упоминания</string> <string name="msg_mentions">Упоминания</string>
<string name="msg_no_mention">Нет упоминаний</string> <string name="msg_no_mention">Нет упоминаний</string>
<string name="msg_all_the_mentions_appear_here">Все упоминания\nотображаются здесь</string> <string name="msg_all_the_mentions_appear_here">Упоминания\nотображаются здесь</string>
<!-- Pinned Messages --> <!-- Pinned Messages -->
<string name="title_pinned_messages">Прикрепленные сообщения</string> <string name="title_pinned_messages">Прикрепленные сообщения</string>
<string name="no_pinned_messages">Нет прикрепленных сообщений</string> <string name="no_pinned_messages">Нет прикрепленных сообщений</string>
<string name="no_pinned_description">Все прикрепленные сообщения\nотображаются здесь</string> <string name="no_pinned_description">Прикрепленные сообщения\nотображаются здесь</string>
<!-- Favorite Messages --> <!-- Favorite Messages -->
<string name="title_favorite_messages">Избранные сообщения</string> <string name="title_favorite_messages">Избранные сообщения</string>
<string name="no_favorite_messages">Нет избранных сообщений</string> <string name="no_favorite_messages">Нет избранных сообщений</string>
<string name="no_favorite_description">Все избранные сообщения\nотображаются здесь</string> <string name="no_favorite_description">Избранные сообщения\nотображаются здесь</string>
<!-- Files --> <!-- Files -->
<string name="title_files">Файлы</string> <string name="title_files">Файлы</string>
...@@ -228,14 +217,14 @@ ...@@ -228,14 +217,14 @@
<string name="msg_all_files_appear_here">Все файлы отображаются здесь</string> <string name="msg_all_files_appear_here">Все файлы отображаются здесь</string>
<!-- Upload Messages --> <!-- Upload Messages -->
<string name="max_file_size_exceeded">Размер файла %1$d превысил максимально допустимый %2$d bytes</string> <string name="max_file_size_exceeded">Размер файла %1$d превысил максимально допустимый %2$d байт</string>
<!-- Socket status --> <!-- Socket status -->
<string name="status_connected">Соединено</string> <string name="status_connected">Подключено</string>
<string name="status_disconnected">Отключено</string> <string name="status_disconnected">Отключено</string>
<string name="status_connecting">Соединение</string> <string name="status_connecting">Подключаемся…</string>
<string name="status_authenticating">Аутентификация</string> <string name="status_authenticating">Аутентификация</string>
<string name="status_disconnecting">Отключение</string> <string name="status_disconnecting">Отключаемся…</string>
<string name="status_waiting">Соединение через %d секунд</string> <string name="status_waiting">Соединение через %d секунд</string>
<!--Suggestions--> <!--Suggestions-->
...@@ -263,10 +252,10 @@ ...@@ -263,10 +252,10 @@
<string name="Unarchive">Разархивировать</string> <string name="Unarchive">Разархивировать</string>
<string name="Join_the_given_channel">Присоединиться к этому каналу</string> <string name="Join_the_given_channel">Присоединиться к этому каналу</string>
<string name="Guggy_Command_Description">Создает gif на основе предоставленного текста</string> <string name="Guggy_Command_Description">Создает gif на основе предоставленного текста</string>
<string name="Slash_Topic_Description">Установить тему</string> <string name="Slash_Topic_Description">Задать тему</string>
<!-- Emoji message--> <!-- Emoji message-->
<string name="msg_no_recent_emoji">Нет недавно используемых emoji</string> <string name="msg_no_recent_emoji">Пусто</string>
<string name="alert_title_default_skin_tone">Тон кожи по умолчанию</string> <string name="alert_title_default_skin_tone">Тон кожи по умолчанию</string>
<!-- Sorting and grouping--> <!-- Sorting and grouping-->
...@@ -276,21 +265,22 @@ ...@@ -276,21 +265,22 @@
<string name="dialog_sort_by_activity">По активности</string> <string name="dialog_sort_by_activity">По активности</string>
<string name="dialog_group_by_type">Группировать по типу</string> <string name="dialog_group_by_type">Группировать по типу</string>
<string name="dialog_group_favourites">Группировать избранное</string> <string name="dialog_group_favourites">Группировать избранное</string>
<string name="dialog_button_done">Ок</string>
<string name="chatroom_header">Заголовок</string> <string name="chatroom_header">Заголовок</string>
<!--ChatRooms Headers--> <!--ChatRooms Headers-->
<string name="header_channel">Каналы</string> <string name="header_channel">Каналы</string>
<string name="header_private_groups">Приватные группы</string> <string name="header_private_groups">Приватные каналы</string>
<string name="header_direct_messages">Личная переписка</string> <string name="header_direct_messages">Личная переписка</string>
<string name="header_live_chats">Живые чаты</string> <string name="header_live_chats">Живые чаты</string>
<string name="header_unknown">Неизвестные</string> <string name="header_unknown">Неизвестные</string>
<!--Notifications--> <!--Notifications-->
<string name="share_label">Изменить общее сообщение</string> <string name="share_label">Редактировать сообщение</string>
<string name="notif_action_reply_hint">ОТВЕТИТЬ</string> <string name="notif_action_reply_hint">ОТВЕТИТЬ</string>
<string name="notif_error_sending">Ошибка ответа. Пожалуйста, попробуйте еще раз.</string> <string name="notif_error_sending">Не удалось ответить. Пожалуйста, попробуйте еще раз.</string>
<string name="notif_success_sending">Сообщение отправлено %1$s!</string> <string name="notif_success_sending">Сообщение отправлено %1$s!</string>
<string name="read_by">Прочитано</string> <string name="read_by">Прочитано</string>
<string name="message_information_title">Информация о сообщении</string> <string name="message_information_title">Информация о прочтении</string>
<string name="msg_log_out">Выход…</string> <string name="msg_log_out">Выходим</string>
</resources> </resources>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item name="menu_section_one" type="id"/>
<item name="menu_section_two" type="id"/>
<item name="menu_section_three" type="id"/>
<item name="menu_action_chats" type="id"/>
<item name="menu_action_create_channel" type="id"/>
<item name="menu_action_profile" type="id"/>
<item name="menu_action_settings" type="id"/>
<item name="menu_action_admin_panel" type="id"/>
<item name="menu_action_logout" type="id"/>
</resources>
\ No newline at end of file
<resources> <resources xmlns:tools="http://schemas.android.com/tools">
<string name="app_name" translatable="false">Rocket.Chat</string> <string name="app_name" translatable="false">Rocket.Chat</string>
<!-- Titles --> <!-- Titles -->
<string name="title_sign_in_your_server">Sign in to your server</string> <string name="title_sign_in_your_server">Sign in to your server</string>
<string name="title_log_in">Login</string> <string name="title_log_in">Log in</string>
<string name="title_register_username">Register username</string> <string name="title_register_username">Register username</string>
<string name="title_reset_password">Reset password</string> <string name="title_reset_password">Reset password</string>
<string name="title_sign_up">Sign up</string> <string name="title_sign_up">Sign up</string>
...@@ -13,6 +13,9 @@ ...@@ -13,6 +13,9 @@
<string name="title_profile">Profile</string> <string name="title_profile">Profile</string>
<string name="title_members">Members (%d)</string> <string name="title_members">Members (%d)</string>
<string name="title_settings">Settings</string> <string name="title_settings">Settings</string>
<string name="title_preferences">Preferences</string>
<string name="title_change_password">Change Password</string>
<string name="title_admin_panel">Admin panel</string>
<string name="title_password">Change Password</string> <string name="title_password">Change Password</string>
<string name="title_update_profile">Update profile</string> <string name="title_update_profile">Update profile</string>
<string name="title_about">About</string> <string name="title_about">About</string>
...@@ -40,14 +43,13 @@ ...@@ -40,14 +43,13 @@
<string name="action_invisible">Invisible</string> <string name="action_invisible">Invisible</string>
<string name="action_drawing">Drawing</string> <string name="action_drawing">Drawing</string>
<string name="action_save_to_gallery">Save to gallery</string> <string name="action_save_to_gallery">Save to gallery</string>
<string name="action_connect_server">Connect with a server</string> <string name="action_select_photo_from_gallery">Select photo from gallery</string>
<string name="action_join_community">Join in the community</string> <string name="action_take_photo">Take photo</string>
<string name="action_create_server">Create a new server</string> <string name="action_reset_avatar">Reset avatar</string>
<string name="action_register">Register</string>
<string name="action_confirm">Confirm</string>
<!-- Settings List --> <!-- Settings List -->
<string-array name="settings_actions"> <string-array name="settings_actions">
<item name="item_preferences">Preferences</item>
<item name="item_password">Change Password</item> <item name="item_password">Change Password</item>
<item name="item_password">About</item> <item name="item_password">About</item>
</string-array> </string-array>
...@@ -56,15 +58,15 @@ ...@@ -56,15 +58,15 @@
<string name="msg_generic_error">Sorry, an error has occurred, please try again</string> <string name="msg_generic_error">Sorry, an error has occurred, please try again</string>
<string name="msg_no_data_to_display">No data to display</string> <string name="msg_no_data_to_display">No data to display</string>
<string name="msg_profile_update_successfully">Profile update successfully</string> <string name="msg_profile_update_successfully">Profile update successfully</string>
<string name="msg_username">Username</string> <string name="msg_username">username</string>
<string name="msg_username_or_email">Username or email</string> <string name="msg_username_or_email">username or email</string>
<string name="msg_password">Password</string> <string name="msg_password">password</string>
<string name="msg_name">Name</string> <string name="msg_name">name</string>
<string name="msg_email">Email</string> <string name="msg_email">email</string>
<string name="msg_avatar_url">avatar URL</string> <string name="msg_avatar_url">avatar URL</string>
<string name="msg_or_continue_using_social_accounts">Or continue using social accounts</string> <string name="msg_or_continue_using_social_accounts">Or continue using social accounts</string>
<string name="msg_new_user">New user? %1$s</string> <string name="msg_new_user">New user? %1$s</string>
<string name="msg_forgot_password">Forgot your password?</string> <string name="msg_forgot_password">Forgot password? %1$s</string>
<string name="msg_reset">Reset</string> <string name="msg_reset">Reset</string>
<string name="msg_check_your_email_to_reset_your_password">Email sent! Check your inbox to reset your password.</string> <string name="msg_check_your_email_to_reset_your_password">Email sent! Check your inbox to reset your password.</string>
<string name="msg_invalid_email">Please type a valid e-mail</string> <string name="msg_invalid_email">Please type a valid e-mail</string>
...@@ -85,7 +87,7 @@ ...@@ -85,7 +87,7 @@
<string name="msg_content_description_log_in_using_meteor">Login using Meteor</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_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_gitlab">Login using Gitlab</string>
<string name="msg_content_description_log_in_using_wordprees">Login using WordPress</string> <string name="msg_content_description_log_in_using_wordpress">Login using WordPress</string>
<string name="msg_content_description_send_message">Send message</string> <string name="msg_content_description_send_message">Send message</string>
<string name="msg_content_description_show_attachment_options">Show attachment options</string> <string name="msg_content_description_show_attachment_options">Show attachment options</string>
<string name="msg_you">You</string> <string name="msg_you">You</string>
...@@ -102,8 +104,7 @@ ...@@ -102,8 +104,7 @@
<string name="msg_preview_photo">Photo</string> <string name="msg_preview_photo">Photo</string>
<string name="msg_preview_file">File</string> <string name="msg_preview_file">File</string>
<string name="msg_no_messages_yet">No messages yet</string> <string name="msg_no_messages_yet">No messages yet</string>
<string name="msg_version">Version %1$s</string> <string name="msg_build" tools:ignore="MissingTranslation">Build %1$d - %2$s - %3$s</string>
<string name="msg_build">Build %1$d</string>
<string name="msg_ok">OK</string> <string name="msg_ok">OK</string>
<string name="msg_update_app_version_in_order_to_continue">Out to date server version. Please contact the server admin to update the server version in order to continue.</string> <string name="msg_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_ver_not_recommended"> <string name="msg_ver_not_recommended">
...@@ -132,17 +133,6 @@ ...@@ -132,17 +133,6 @@
<string name="msg_file_description">File description</string> <string name="msg_file_description">File description</string>
<string name="msg_send">Send</string> <string name="msg_send">Send</string>
<string name="msg_sent_attachment">Sent an attachment</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_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>
<string name="msg_continue_with_github">Continue with Github</string>
<string name="msg_continue_with_google">Continue with Google</string>
<string name="msg_continue_with_linkedin">Continue with Linkedin</string>
<string name="msg_continue_with_gitlab">Continue with GitLab</string>
<string name="msg_two_factor_authentication">Two-factor Authentication</string>
<string name="msg__your_2fa_code">What’s your 2FA code?</string>
<!-- Create channel messages --> <!-- Create channel messages -->
<string name="msg_private_channel">Private</string> <string name="msg_private_channel">Private</string>
...@@ -159,6 +149,12 @@ ...@@ -159,6 +149,12 @@
<string name="msg_delete_message">Delete Message</string> <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_description">Are you sure you want to delete this message</string>
<!-- Preferences messages -->
<string name="msg_analytics_tracking">Analytics tracking</string>
<string name="msg_send_analytics_tracking">Send anonymous statics to help improving this app</string>
<string name="msg_do_not_send_analytics_tracking">Do not send anonymous statics to help improving this app</string>
<string name="msg_not_applicable_since_it_is_a_foss_version">Not applicable since it is a FOSS version</string>
<!-- System messages --> <!-- System messages -->
<string name="message_room_name_changed">Room name changed to: %1$s by %2$s</string> <string name="message_room_name_changed">Room name changed to: %1$s by %2$s</string>
<string name="message_user_added_by">User %1$s added by %2$s</string> <string name="message_user_added_by">User %1$s added by %2$s</string>
...@@ -188,7 +184,6 @@ ...@@ -188,7 +184,6 @@
<string name="action_msg_share">Share</string> <string name="action_msg_share">Share</string>
<string name="action_title_editing">Editing Message</string> <string name="action_title_editing">Editing Message</string>
<string name="action_msg_add_reaction">Add reaction</string> <string name="action_msg_add_reaction">Add reaction</string>
<string name="action_share">Share</string>
<!-- Permission messages --> <!-- Permission messages -->
<string name="permission_editing_not_allowed">Editing is not allowed</string> <string name="permission_editing_not_allowed">Editing is not allowed</string>
...@@ -276,6 +271,7 @@ ...@@ -276,6 +271,7 @@
<string name="dialog_sort_by_activity">Activity</string> <string name="dialog_sort_by_activity">Activity</string>
<string name="dialog_group_by_type">Group by type</string> <string name="dialog_group_by_type">Group by type</string>
<string name="dialog_group_favourites">Group favourites</string> <string name="dialog_group_favourites">Group favourites</string>
<string name="dialog_button_done">Done</string>
<string name="chatroom_header">Header</string> <string name="chatroom_header">Header</string>
<!--ChatRooms Headers--> <!--ChatRooms Headers-->
...@@ -293,6 +289,5 @@ ...@@ -293,6 +289,5 @@
<string name="read_by">Read by</string> <string name="read_by">Read by</string>
<string name="message_information_title">Message information</string> <string name="message_information_title">Message information</string>
<!-- Community Server--> <string name="foss" tools:ignore="MissingTranslation">(FOSS)</string>
<string name="community_server" translatable="false">open.rocket.chat</string>
</resources> </resources>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<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"
android:fullBackupContent="@xml/backup_config"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:networkSecurityConfig="@xml/network_security_config"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true">
<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"
android:exported="true">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
</application>
</manifest>
package chat.rocket.android.helper
import android.app.Activity
import android.content.Intent
import androidx.fragment.app.FragmentActivity
import com.google.android.gms.auth.api.credentials.Credential
import com.google.android.gms.auth.api.credentials.Credentials
fun FragmentActivity.saveCredentials(id: String, password: String) {
val credentialsClient = Credentials.getClient(this)
SmartLockHelper.save(credentialsClient, this, id, password)
}
fun Activity.requestStoredCredentials(): Pair<String, String>? {
val credentialsClient = Credentials.getClient(this)
return SmartLockHelper.requestStoredCredentials(credentialsClient, this)?.let {
null
}
}
fun getCredentials(data: Intent): Pair<String, String>? {
val credentials: Credential = data.getParcelableExtra(Credential.EXTRA_KEY)
return credentials.password?.let {
Pair(credentials.id, it)
}
}
fun hasCredentialsSupport() = true
\ No newline at end of file
package chat.rocket.android.util
import chat.rocket.android.BuildConfig
import io.fabric.sdk.android.Fabric
import com.crashlytics.android.Crashlytics
import com.crashlytics.android.core.CrashlyticsCore
import android.content.Context
import chat.rocket.android.app.RocketChatApplication
import chat.rocket.android.infrastructure.installCrashlyticsWrapper
import com.crashlytics.android.answers.Answers
fun setupFabric(context: Context) {
val core = CrashlyticsCore.Builder().disabled(BuildConfig.DEBUG).build()
Fabric.with(
context,
Crashlytics.Builder()
.core(core) // For Crashlytics
.answers(Answers()) // For Answers
.build()
)
installCrashlyticsWrapper(
context as RocketChatApplication,
context.getCurrentServerInteractor,
context.settingsInteractor,
context.accountRepository,
context.localRepository
)
}
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)
}
...@@ -10,7 +10,7 @@ buildscript { ...@@ -10,7 +10,7 @@ buildscript {
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:3.3.0-alpha04' classpath 'com.android.tools.build:gradle:3.3.0-alpha05'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin}" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin}"
classpath "org.jetbrains.dokka:dokka-gradle-plugin:${versions.dokka}" classpath "org.jetbrains.dokka:dokka-gradle-plugin:${versions.dokka}"
classpath 'com.google.gms:google-services:4.0.2' classpath 'com.google.gms:google-services:4.0.2'
......
...@@ -9,7 +9,7 @@ ext { ...@@ -9,7 +9,7 @@ ext {
dokka : '0.9.16', dokka : '0.9.16',
// For app // For app
kotlin : '1.2.51', kotlin : '1.2.61',
coroutine : '0.24.0', coroutine : '0.24.0',
appCompat : '1.0.0-beta01', appCompat : '1.0.0-beta01',
...@@ -20,7 +20,8 @@ ext { ...@@ -20,7 +20,8 @@ ext {
androidKtx : '1.0.0-beta01', androidKtx : '1.0.0-beta01',
dagger : '2.16', dagger : '2.16',
firebase : '15.0.0', firebaseCloudMessage : '17.1.0',
firebaseAnalytics : '16.0.1',
playServices : '15.0.1', playServices : '15.0.1',
exoPlayer : '2.8.2', exoPlayer : '2.8.2',
flexbox : '1.0.0', flexbox : '1.0.0',
...@@ -74,8 +75,6 @@ ext { ...@@ -74,8 +75,6 @@ ext {
daggerSupport : "com.google.dagger:dagger-android-support:${versions.dagger}", daggerSupport : "com.google.dagger:dagger-android-support:${versions.dagger}",
daggerProcessor : "com.google.dagger:dagger-compiler:${versions.dagger}", daggerProcessor : "com.google.dagger:dagger-compiler:${versions.dagger}",
daggerAndroidApt : "com.google.dagger:dagger-android-processor:${versions.dagger}", daggerAndroidApt : "com.google.dagger:dagger-android-processor:${versions.dagger}",
fcm : "com.google.firebase:firebase-messaging:${versions.firebase}",
playServicesAuth : "com.google.android.gms:play-services-auth:${versions.playServices}",
exoPlayer : "com.google.android.exoplayer:exoplayer:${versions.exoPlayer}", exoPlayer : "com.google.android.exoplayer:exoplayer:${versions.exoPlayer}",
flexbox : "com.google.android:flexbox:${versions.flexbox}", flexbox : "com.google.android:flexbox:${versions.flexbox}",
material : "com.google.android.material:material:${versions.material}", material : "com.google.android.material:material:${versions.material}",
...@@ -112,6 +111,11 @@ ext { ...@@ -112,6 +111,11 @@ ext {
aVLoadingIndicatorView: "com.wang.avi:library:${versions.aVLoadingIndicatorView}", aVLoadingIndicatorView: "com.wang.avi:library:${versions.aVLoadingIndicatorView}",
// Proprietary libraries
fcm : "com.google.firebase:firebase-messaging:${versions.firebaseCloudMessage}",
firebaseAnalytics : "com.google.firebase:firebase-core:${versions.firebaseAnalytics}",
playServicesAuth : "com.google.android.gms:play-services-auth:${versions.playServices}",
// For wearable // For wearable
wearable : "com.google.android.support:wearable:${versions.wear}", wearable : "com.google.android.support:wearable:${versions.wear}",
playServicesWearable : "com.google.android.gms:play-services-wearable:${versions.playServicesWearable}", playServicesWearable : "com.google.android.gms:play-services-wearable:${versions.playServicesWearable}",
......
...@@ -19,14 +19,36 @@ android { ...@@ -19,14 +19,36 @@ android {
minifyEnabled false minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
} }
debug {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
// Since we are using the util module here, we need to declare the same flavorDimensions and
// productFlavors from there.
flavorDimensions "type"
productFlavors {
// includes proprietary libs
play {
dimension "type"
}
// only foss
foss {
dimension "type"
}
} }
} }
dependencies { dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar']) implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation project(':util')
implementation project(':core') implementation project(':core')
implementation project(':util')
implementation libraries.kotlin implementation libraries.kotlin
implementation libraries.coroutines implementation libraries.coroutines
......
...@@ -17,6 +17,25 @@ android { ...@@ -17,6 +17,25 @@ android {
minifyEnabled false minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
} }
debug {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
flavorDimensions "type"
productFlavors {
// includes proprietary libs
play {
dimension "type"
}
// only foss
foss {
dimension "type"
}
} }
} }
...@@ -36,4 +55,9 @@ dependencies { ...@@ -36,4 +55,9 @@ 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 // 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 implementation libraries.lifecycleExtensions
kapt libraries.lifecycleCompiler kapt libraries.lifecycleCompiler
// Proprietary libraries
playImplementation('com.crashlytics.sdk.android:answers:1.4.2@aar') {
transitive = true
}
} }
\ No newline at end of file
package chat.rocket.android.util.helper.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
object AnalyticsManager : 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.util.extension package chat.rocket.android.util.extension
import android.content.Intent
import android.graphics.Bitmap import android.graphics.Bitmap
import android.provider.MediaStore
import androidx.fragment.app.Fragment
import kotlinx.coroutines.experimental.DefaultDispatcher import kotlinx.coroutines.experimental.DefaultDispatcher
import kotlinx.coroutines.experimental.withContext import kotlinx.coroutines.experimental.withContext
import java.io.ByteArrayInputStream import java.io.ByteArrayInputStream
...@@ -62,3 +65,17 @@ fun String.getCompressFormat(): Bitmap.CompressFormat { ...@@ -62,3 +65,17 @@ fun String.getCompressFormat(): Bitmap.CompressFormat {
else -> Bitmap.CompressFormat.PNG else -> Bitmap.CompressFormat.PNG
} }
} }
fun Fragment.dispatchImageSelection(requestCode: Int) {
val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.type = "image/*"
intent.addCategory(Intent.CATEGORY_OPENABLE)
startActivityForResult(intent, requestCode)
}
fun Fragment.dispatchTakePicture(requestCode: Int) {
val takePictureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
if (takePictureIntent.resolveActivity(context?.packageManager) != null) {
startActivityForResult(takePictureIntent, requestCode)
}
}
\ No newline at end of file
package chat.rocket.android.util.helper.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
interface Analytics {
/**
* Logs the login event.
*
* @param event The [AuthenticationEvent] used to log in.
* @param loginSucceeded True if successful logged in, false otherwise.
*/
fun logLogin(event: AuthenticationEvent, loginSucceeded: Boolean)
/**
* Logs the sign up event.
*
* @param event The [AuthenticationEvent] used to sign up.
* @param signUpSucceeded True if successful signed up, false otherwise.
*/
fun logSignUp(event: AuthenticationEvent, signUpSucceeded: Boolean)
/**
* Logs the screen view event.
*
* @param event The [ScreenViewEvent] to log.
*/
fun logScreenView(event: ScreenViewEvent)
/**
* Logs the message sent event.
*
* @param event The [SubscriptionTypeEvent] to log.
* @param serverUrl The server URL to log.
*/
fun logMessageSent(event: SubscriptionTypeEvent, serverUrl: String)
/**
* Logs the media upload event.
*
* @param event The [SubscriptionTypeEvent] to log.
* @param mimeType The mime type of the media uploaded to log.
*/
fun logMediaUploaded(event: SubscriptionTypeEvent, mimeType: String)
/**
* Logs the reaction event.
*
* @param event The [SubscriptionTypeEvent] to log.
*/
fun logReaction(event: SubscriptionTypeEvent)
/**
* Logs the server switch event.
*
* @param serverUrl The server URL to log.
* @param serverCount The number of server(s) the use own.
*/
fun logServerSwitch(serverUrl: String, serverCount: Int)
}
package chat.rocket.android.util.helper.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
sealed class ScreenViewEvent(val screenName: String) {
object About : ScreenViewEvent("AboutFragment")
object ChatRoom : ScreenViewEvent("ChatRoomFragment")
object ChatRooms : ScreenViewEvent("ChatRoomsFragment")
object CreateChannel : ScreenViewEvent("CreateChannelFragment")
object FavoriteMessages : ScreenViewEvent("FavoriteMessagesFragment")
object Files : ScreenViewEvent("FilesFragment")
object Login : ScreenViewEvent("LoginFragment")
object MemberBottomSheet : ScreenViewEvent("MemberBottomSheetFragment")
object Members : ScreenViewEvent("MembersFragment")
object Mentions : ScreenViewEvent("MentionsFragment")
object MessageInfo : ScreenViewEvent("MessageInfoFragment")
object Password : ScreenViewEvent("PasswordFragment")
object PinnedMessages : ScreenViewEvent("PinnedMessagesFragment")
object Preferences : ScreenViewEvent("PreferencesFragment")
object Profile : ScreenViewEvent("ProfileFragment")
object RegisterUsername : ScreenViewEvent("RegisterUsernameFragment")
object ResetPassword : ScreenViewEvent("ResetPasswordFragment")
object Server : ScreenViewEvent("ServerFragment")
object Settings : ScreenViewEvent("SettingsFragment")
object SignUp : ScreenViewEvent("SignupFragment")
object TwoFa : ScreenViewEvent("TwoFAFragment")
}
\ No newline at end of file
package chat.rocket.android.util.helper.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
package chat.rocket.android.util.helper.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 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 {
override fun logLogin(event: AuthenticationEvent, loginSucceeded: Boolean) =
Answers.getInstance()
.logLogin(
LoginEvent()
.putMethod(event.methodName)
.putSuccess(loginSucceeded)
)
override fun logSignUp(event: AuthenticationEvent, signUpSucceeded: Boolean) =
Answers.getInstance()
.logSignUp(
SignUpEvent()
.putMethod(event.methodName)
.putSuccess(signUpSucceeded)
)
override fun logScreenView(event: ScreenViewEvent) =
Answers.getInstance()
.logCustom(CustomEvent("screen_view").putCustomAttribute("screen", event.screenName))
override fun logMessageSent(event: SubscriptionTypeEvent, serverUrl: String) =
Answers.getInstance()
.logCustom(
CustomEvent("message_sent")
.putCustomAttribute("subscription_type", event.subscriptionTypeName)
.putCustomAttribute("server", serverUrl)
)
override fun logMediaUploaded(event: SubscriptionTypeEvent, mimeType: String) =
Answers.getInstance()
.logCustom(
CustomEvent("media_upload")
.putCustomAttribute("subscription_type", event.subscriptionTypeName)
.putCustomAttribute("media_type", mimeType)
)
override fun logReaction(event: SubscriptionTypeEvent) =
Answers.getInstance()
.logCustom(
CustomEvent("reaction")
.putCustomAttribute("subscription_type", event.subscriptionTypeName)
)
override fun logServerSwitch(serverUrl: String, serverCount: Int) =
Answers.getInstance()
.logCustom(
CustomEvent("server_switch")
.putCustomAttribute("server_url", serverUrl)
.putCustomAttribute("server_count", serverCount)
)
}
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