Unverified Commit 628a9200 authored by Noor Binte Amir's avatar Noor Binte Amir Committed by GitHub

Merge pull request #1 from RocketChat/develop

Pulling original
parents 26d85e2e 3da3ec81
......@@ -18,7 +18,7 @@ android {
applicationId "chat.rocket.android"
minSdkVersion versions.minSdk
targetSdkVersion versions.targetSdk
versionCode 2061
versionCode 2062
versionName "3.4.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
multiDexEnabled true
......
......@@ -2,7 +2,7 @@
"formatVersion": 1,
"database": {
"version": 11,
"identityHash": "fb9f1c815809b0217d326452aeb34e92",
"identityHash": "f0e15bc95a0bb09b052c484704dd3abd",
"entities": [
{
"tableName": "users",
......@@ -59,7 +59,7 @@
},
{
"tableName": "chatrooms",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `subscriptionId` TEXT NOT NULL, `type` TEXT NOT NULL, `name` TEXT NOT NULL, `fullname` TEXT, `userId` TEXT, `ownerId` TEXT, `readonly` INTEGER, `isDefault` INTEGER, `favorite` INTEGER, `topic` TEXT, `announcement` TEXT, `description` TEXT, `open` INTEGER NOT NULL, `alert` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `userMentions` INTEGER, `groupMentions` INTEGER, `updatedAt` INTEGER, `timestamp` INTEGER, `lastSeen` INTEGER, `lastMessageText` TEXT, `lastMessageUserId` TEXT, `lastMessageTimestamp` INTEGER, `broadcast` INTEGER, `muted` TEXT, PRIMARY KEY(`id`), FOREIGN KEY(`ownerId`) REFERENCES `users`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION , FOREIGN KEY(`userId`) REFERENCES `users`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION , FOREIGN KEY(`lastMessageUserId`) REFERENCES `users`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `subscriptionId` TEXT NOT NULL, `parentId` TEXT, `type` TEXT NOT NULL, `name` TEXT NOT NULL, `fullname` TEXT, `userId` TEXT, `ownerId` TEXT, `readonly` INTEGER, `isDefault` INTEGER, `favorite` INTEGER, `topic` TEXT, `announcement` TEXT, `description` TEXT, `open` INTEGER NOT NULL, `alert` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `userMentions` INTEGER, `groupMentions` INTEGER, `updatedAt` INTEGER, `timestamp` INTEGER, `lastSeen` INTEGER, `lastMessageText` TEXT, `lastMessageUserId` TEXT, `lastMessageTimestamp` INTEGER, `broadcast` INTEGER, `muted` TEXT, PRIMARY KEY(`id`), FOREIGN KEY(`ownerId`) REFERENCES `users`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION , FOREIGN KEY(`userId`) REFERENCES `users`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION , FOREIGN KEY(`lastMessageUserId`) REFERENCES `users`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )",
"fields": [
{
"fieldPath": "id",
......@@ -73,6 +73,12 @@
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "parentId",
"columnName": "parentId",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "type",
"columnName": "type",
......@@ -1099,7 +1105,7 @@
],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"fb9f1c815809b0217d326452aeb34e92\")"
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"f0e15bc95a0bb09b052c484704dd3abd\")"
]
}
}
\ No newline at end of file
This diff is collapsed.
......@@ -4,7 +4,7 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.OnLifecycleEvent
import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.infraestructure.ConnectionManagerFactory
import chat.rocket.android.server.infrastructure.ConnectionManagerFactory
import chat.rocket.common.model.UserStatus
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
......
......@@ -24,7 +24,7 @@ import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.domain.GetSettingsInteractor
import chat.rocket.android.server.domain.SITE_URL
import chat.rocket.android.server.domain.TokenRepository
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.server.infrastructure.RocketChatClientFactory
import chat.rocket.android.util.retryIO
import chat.rocket.android.util.setupFabric
import chat.rocket.common.RocketChatException
......
package chat.rocket.android.authentication.infraestructure
package chat.rocket.android.authentication.infrastructure
import chat.rocket.android.authentication.domain.model.TokenModel
import chat.rocket.android.dagger.scope.PerActivity
......
package chat.rocket.android.authentication.infraestructure
package chat.rocket.android.authentication.infrastructure
import android.content.SharedPreferences
import androidx.core.content.edit
......
......@@ -15,8 +15,9 @@ import chat.rocket.android.server.domain.favicon
import chat.rocket.android.server.domain.isLdapAuthenticationEnabled
import chat.rocket.android.server.domain.isPasswordResetEnabled
import chat.rocket.android.server.domain.model.Account
import chat.rocket.android.server.domain.siteName
import chat.rocket.android.server.domain.wideTile
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.server.infrastructure.RocketChatClientFactory
import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.extensions.avatarUrl
import chat.rocket.android.util.extensions.isEmail
......@@ -50,6 +51,7 @@ class LoginPresenter @Inject constructor(
) {
// TODO - we should validate the current server when opening the app, and have a nonnull get()
private var currentServer = serverInteractor.get()!!
private val token = tokenRepository.get(currentServer)
private lateinit var client: RocketChatClient
private lateinit var settings: PublicSettings
......@@ -140,8 +142,15 @@ class LoginPresenter @Inject constructor(
val logo = settings.wideTile()?.let {
currentServer.serverLogoUrl(it)
}
val thumb = currentServer.avatarUrl(username)
val account = Account(currentServer, icon, logo, username, thumb)
val thumb = currentServer.avatarUrl(username, token?.userId, token?.authToken)
val account = Account(
settings.siteName() ?: currentServer,
currentServer,
icon,
logo,
username,
thumb
)
saveAccountInteractor.save(account)
}
......
......@@ -14,8 +14,9 @@ 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.siteName
import chat.rocket.android.server.domain.wideTile
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.server.infrastructure.RocketChatClientFactory
import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.extensions.avatarUrl
import chat.rocket.android.util.extensions.serverLogoUrl
......@@ -54,6 +55,7 @@ class LoginOptionsPresenter @Inject constructor(
) {
// TODO - we should validate the current server when opening the app, and have a nonnull get()
private var currentServer = serverInteractor.get()!!
private val token = tokenRepository.get(currentServer)
private lateinit var client: RocketChatClient
private lateinit var settings: PublicSettings
private lateinit var credentialToken: String
......@@ -180,8 +182,15 @@ class LoginOptionsPresenter @Inject constructor(
val logo = settings.wideTile()?.let {
currentServer.serverLogoUrl(it)
}
val thumb = currentServer.avatarUrl(username)
val account = Account(currentServer, icon, logo, username, thumb)
val thumb = currentServer.avatarUrl(username, token?.userId, token?.authToken)
val account = Account(
settings.siteName() ?: currentServer,
currentServer,
icon,
logo,
username,
thumb
)
saveAccountInteractor.save(account)
}
......
package chat.rocket.android.authentication.loginoptions.ui
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
import android.app.Activity
import android.content.Intent
import android.graphics.PorterDuff
......@@ -7,10 +10,13 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.animation.AlphaAnimation
import android.view.animation.Animation
import android.widget.Button
import android.widget.LinearLayout
import androidx.appcompat.view.ContextThemeWrapper
import androidx.core.view.isVisible
import androidx.core.view.marginTop
import androidx.fragment.app.Fragment
import chat.rocket.android.R
import chat.rocket.android.analytics.AnalyticsManager
......@@ -28,6 +34,7 @@ import chat.rocket.android.webview.sso.ui.ssoWebViewIntent
import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.app_bar.*
import kotlinx.android.synthetic.main.fragment_authentication_login_options.*
import timber.log.Timber
import javax.inject.Inject
private const val SERVER_NAME = "server_name"
......@@ -61,6 +68,8 @@ internal const val REQUEST_CODE_FOR_OAUTH = 1
internal const val REQUEST_CODE_FOR_CAS = 2
internal const val REQUEST_CODE_FOR_SAML = 3
private const val DEFAULT_ANIMATION_DURATION = 400L
fun newInstance(
serverName: String,
state: String? = null,
......@@ -238,7 +247,6 @@ class LoginOptionsFragment : Fragment(), LoginOptionsView {
enableLoginByLinkedin()
}
if (gitlabOauthUrl != null && state != null) {
setupGitlabButtonListener(gitlabOauthUrl.toString(), state.toString())
enableLoginByGitlab()
......@@ -390,11 +398,11 @@ class LoginOptionsFragment : Fragment(), LoginOptionsView {
var isAccountsCollapsed = true
button_expand_collapse_accounts.setOnClickListener {
isAccountsCollapsed = if (isAccountsCollapsed) {
button_expand_collapse_accounts.rotateBy(180F, 400)
button_expand_collapse_accounts.rotateBy(180F, DEFAULT_ANIMATION_DURATION)
expandAccountsView()
false
} else {
button_expand_collapse_accounts.rotateBy(180F, 400)
button_expand_collapse_accounts.rotateBy(180F, DEFAULT_ANIMATION_DURATION)
collapseAccountsView()
true
}
......@@ -532,17 +540,73 @@ class LoginOptionsFragment : Fragment(), LoginOptionsView {
}
private fun expandAccountsView() {
(0..accounts_container.childCount)
val buttons = (0..accounts_container.childCount)
.mapNotNull { accounts_container.getChildAt(it) as? Button }
.filter { it.isClickable && !it.isVisible }
.forEach { it.isVisible = true }
val optionHeight = accounts_container.getChildAt(1).height +
accounts_container.getChildAt(1).marginTop
val collapsedHeight = accounts_container.height
val expandedHeight = collapsedHeight + optionHeight * buttons.size
with(ValueAnimator.ofInt(collapsedHeight, expandedHeight)) {
addUpdateListener {
val params = accounts_container.layoutParams
params.height = animatedValue as Int
accounts_container.layoutParams = params
}
addListener(object : AnimatorListenerAdapter() {
override fun onAnimationStart(animator: Animator) {
buttons.forEach {
it.isVisible = true
val anim = AlphaAnimation(0.0f, 1.0f)
anim.duration = DEFAULT_ANIMATION_DURATION
it.startAnimation(anim)
}
}
})
setDuration(DEFAULT_ANIMATION_DURATION).start()
}
}
private fun collapseAccountsView() {
(0..accounts_container.childCount)
val buttons = (0..accounts_container.childCount)
.mapNotNull { accounts_container.getChildAt(it) as? Button }
.filter { it.isClickable && it.isVisible }
.drop(3)
.forEach { it.isVisible = false }
val optionHeight = accounts_container.getChildAt(1).height +
accounts_container.getChildAt(1).marginTop
val expandedHeight = accounts_container.height
val collapsedHeight = expandedHeight - optionHeight * buttons.size
with(ValueAnimator.ofInt(expandedHeight, collapsedHeight)) {
addUpdateListener {
val params = accounts_container.layoutParams
params.height = animatedValue as Int
accounts_container.layoutParams = params
}
addListener(object : AnimatorListenerAdapter() {
override fun onAnimationStart(animator: Animator) {
buttons.forEach {
val anim = AlphaAnimation(1.0f, 0.0f)
anim.duration = DEFAULT_ANIMATION_DURATION
anim.setAnimationListener(object : Animation.AnimationListener {
override fun onAnimationStart(animation: Animation) {
Timber.d("Animation starts: $animation")
}
override fun onAnimationEnd(animation: Animation) {
it.isVisible = false
}
override fun onAnimationRepeat(animation: Animation) {
Timber.d("Animation repeats: $animation")
}
})
it.startAnimation(anim)
}
}
})
setDuration(DEFAULT_ANIMATION_DURATION).start()
}
}
}
......@@ -7,7 +7,7 @@ import chat.rocket.android.server.domain.GetAccountsInteractor
import chat.rocket.android.server.domain.GetSettingsInteractor
import chat.rocket.android.server.domain.RefreshSettingsInteractor
import chat.rocket.android.server.domain.SaveConnectingServerInteractor
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.server.infrastructure.RocketChatClientFactory
import chat.rocket.android.server.presentation.CheckServerPresenter
import chat.rocket.android.util.extension.launchUI
import kotlinx.coroutines.Dispatchers
......
......@@ -12,8 +12,9 @@ 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.siteName
import chat.rocket.android.server.domain.wideTile
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.server.infrastructure.RocketChatClientFactory
import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.extensions.avatarUrl
import chat.rocket.android.util.extensions.serverLogoUrl
......@@ -39,7 +40,8 @@ class RegisterUsernamePresenter @Inject constructor(
) {
private val currentServer = serverInteractor.get()!!
private val client: RocketChatClient = factory.get(currentServer)
private var settings: PublicSettings = settingsInteractor.get(serverInteractor.get()!!)
private var settings: PublicSettings = settingsInteractor.get(currentServer)
private val token = tokenRepository.get(currentServer)
fun registerUsername(username: String, userId: String, authToken: String) {
launchUI(strategy) {
......@@ -72,15 +74,22 @@ class RegisterUsernamePresenter @Inject constructor(
}
}
private suspend fun saveAccount(username: String) {
private fun saveAccount(username: String) {
val icon = settings.favicon()?.let {
currentServer.serverLogoUrl(it)
}
val logo = settings.wideTile()?.let {
currentServer.serverLogoUrl(it)
}
val thumb = currentServer.avatarUrl(username)
val account = Account(currentServer, icon, logo, username, thumb)
val thumb = currentServer.avatarUrl(username, token?.userId, token?.authToken)
val account = Account(
settings.siteName() ?: currentServer,
currentServer,
icon,
logo,
username,
thumb
)
saveAccountInteractor.save(account)
}
}
\ No newline at end of file
......@@ -3,7 +3,7 @@ package chat.rocket.android.authentication.resetpassword.presentation
import chat.rocket.android.authentication.presentation.AuthenticationNavigator
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.server.domain.GetConnectingServerInteractor
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.server.infrastructure.RocketChatClientFactory
import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.retryIO
import chat.rocket.common.RocketChatException
......
......@@ -8,7 +8,7 @@ import chat.rocket.android.server.domain.GetAccountsInteractor
import chat.rocket.android.server.domain.GetSettingsInteractor
import chat.rocket.android.server.domain.RefreshSettingsInteractor
import chat.rocket.android.server.domain.SaveConnectingServerInteractor
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.server.infrastructure.RocketChatClientFactory
import chat.rocket.android.server.presentation.CheckServerPresenter
import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.extensions.isValidUrl
......
......@@ -10,10 +10,12 @@ 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.siteName
import chat.rocket.android.server.domain.wideTile
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.server.infrastructure.RocketChatClientFactory
import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.extensions.avatarUrl
import chat.rocket.android.util.extensions.privacyPolicyUrl
......@@ -38,10 +40,12 @@ class SignupPresenter @Inject constructor(
private val analyticsManager: AnalyticsManager,
private val factory: RocketChatClientFactory,
private val saveAccountInteractor: SaveAccountInteractor,
tokenRepository: TokenRepository,
settingsInteractor: GetSettingsInteractor
) {
private val currentServer = serverInteractor.get()!!
private var settings: PublicSettings = settingsInteractor.get(serverInteractor.get()!!)
private var settings: PublicSettings = settingsInteractor.get(currentServer)
private val token = tokenRepository.get(currentServer)
fun signup(name: String, username: String, password: String, email: String) {
val client = factory.get(currentServer)
......@@ -98,8 +102,15 @@ class SignupPresenter @Inject constructor(
val logo = settings.wideTile()?.let {
currentServer.serverLogoUrl(it)
}
val thumb = currentServer.avatarUrl(me.username!!)
val account = Account(currentServer, icon, logo, me.username!!, thumb)
val thumb = currentServer.avatarUrl(me.username!!, token?.userId, token?.authToken)
val account = Account(
settings.siteName() ?: currentServer,
currentServer,
icon,
logo,
me.username!!,
thumb
)
saveAccountInteractor.save(account)
}
}
\ No newline at end of file
......@@ -13,8 +13,9 @@ 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.siteName
import chat.rocket.android.server.domain.wideTile
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.server.infrastructure.RocketChatClientFactory
import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.extensions.avatarUrl
import chat.rocket.android.util.extensions.isEmail
......@@ -43,7 +44,8 @@ class TwoFAPresenter @Inject constructor(
val settingsInteractor: GetSettingsInteractor
) {
private val currentServer = serverInteractor.get()!!
private var settings: PublicSettings = settingsInteractor.get(serverInteractor.get()!!)
private var settings: PublicSettings = settingsInteractor.get(currentServer)
private val token = tokenRepository.get(currentServer)
fun authenticate(
usernameOrEmail: String,
......@@ -101,8 +103,15 @@ class TwoFAPresenter @Inject constructor(
val logo = settings.wideTile()?.let {
currentServer.serverLogoUrl(it)
}
val thumb = currentServer.avatarUrl(me.username!!)
val account = Account(currentServer, icon, logo, me.username!!, thumb)
val thumb = currentServer.avatarUrl(me.username!!, token?.userId, token?.authToken)
val account = Account(
settings.siteName() ?: currentServer,
currentServer,
icon,
logo,
me.username!!,
thumb
)
saveAccountInteractor.save(account)
}
}
\ No newline at end of file
......@@ -4,7 +4,7 @@ import chat.rocket.android.chatdetails.domain.ChatDetails
import chat.rocket.android.chatroom.presentation.ChatRoomNavigator
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.infraestructure.ConnectionManagerFactory
import chat.rocket.android.server.infrastructure.ConnectionManagerFactory
import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.retryIO
import chat.rocket.common.RocketChatException
......
......@@ -3,7 +3,7 @@ package chat.rocket.android.chatinformation.presentation
import chat.rocket.android.chatroom.uimodel.UiModelMapper
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.infraestructure.ConnectionManagerFactory
import chat.rocket.android.server.infrastructure.ConnectionManagerFactory
import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.retryIO
import chat.rocket.common.RocketChatException
......
......@@ -56,6 +56,7 @@ class ChatRoomFragmentModule {
context: Application,
repository: SettingsRepository,
userInteractor: GetCurrentUserInteractor,
tokenRepository: TokenRepository,
@Named("currentServer") serverUrl: String,
permissionsInteractor: PermissionsInteractor
): RoomUiModelMapper {
......@@ -63,6 +64,7 @@ class ChatRoomFragmentModule {
context,
repository.get(serverUrl),
userInteractor,
tokenRepository,
serverUrl,
permissionsInteractor
)
......
......@@ -29,12 +29,13 @@ import chat.rocket.android.server.domain.JobSchedulerInteractor
import chat.rocket.android.server.domain.MessagesRepository
import chat.rocket.android.server.domain.PermissionsInteractor
import chat.rocket.android.server.domain.PublicSettings
import chat.rocket.android.server.domain.TokenRepository
import chat.rocket.android.server.domain.UsersRepository
import chat.rocket.android.server.domain.uploadMaxFileSize
import chat.rocket.android.server.domain.uploadMimeTypeFilter
import chat.rocket.android.server.domain.useRealName
import chat.rocket.android.server.infraestructure.ConnectionManagerFactory
import chat.rocket.android.server.infraestructure.state
import chat.rocket.android.server.infrastructure.ConnectionManagerFactory
import chat.rocket.android.server.infrastructure.state
import chat.rocket.android.util.extension.getByteArray
import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.extensions.avatarUrl
......@@ -101,6 +102,7 @@ class ChatRoomPresenter @Inject constructor(
private val jobSchedulerInteractor: JobSchedulerInteractor,
private val messageHelper: MessageHelper,
private val dbManager: DatabaseManager,
tokenRepository: TokenRepository,
getSettingsInteractor: GetSettingsInteractor,
serverInteractor: GetCurrentServerInteractor,
factory: ConnectionManagerFactory
......@@ -109,6 +111,7 @@ class ChatRoomPresenter @Inject constructor(
private val manager = factory.create(currentServer)
private val client = manager.client
private var settings: PublicSettings = getSettingsInteractor.get(serverInteractor.get()!!)
private val token = tokenRepository.get(currentServer)
private val currentLoggedUsername = userHelper.username()
private val messagesChannel = Channel<Message>()
......@@ -327,7 +330,7 @@ class ChatRoomPresenter @Inject constructor(
timestamp = Instant.now().toEpochMilli(),
sender = SimpleUser(user?.id, user?.username ?: username, user?.name),
attachments = null,
avatar = currentServer.avatarUrl(username ?: ""),
avatar = currentServer.avatarUrl(username!!, token?.userId, token?.authToken),
channels = null,
editedAt = null,
editedBy = null,
......@@ -812,7 +815,7 @@ class ChatRoomPresenter @Inject constructor(
val sender = it.sender
val username = sender?.username ?: ""
val name = sender?.name ?: ""
val avatarUrl = currentServer.avatarUrl(username)
val avatarUrl = currentServer.avatarUrl(username, token?.userId, token?.authToken)
val found = members.firstOrNull { member -> member.username == username }
val status = if (found != null) found.status else UserStatus.Offline()
val searchList = mutableListOf(username, name)
......@@ -833,7 +836,7 @@ class ChatRoomPresenter @Inject constructor(
activeUsers.addAll(others.map {
val username = it.username ?: ""
val name = it.name ?: ""
val avatarUrl = currentServer.avatarUrl(username)
val avatarUrl = currentServer.avatarUrl(username, token?.userId, token?.authToken)
val searchList = mutableListOf(username, name)
PeopleSuggestionUiModel(
avatarUrl,
......@@ -869,7 +872,7 @@ class ChatRoomPresenter @Inject constructor(
val searchList = mutableListOf(username, name)
it.emails?.forEach { email -> searchList.add(email.address) }
PeopleSuggestionUiModel(
currentServer.avatarUrl(username),
currentServer.avatarUrl(username, token?.userId, token?.authToken),
username, username, name, it.status, false, searchList
)
}.filterNot { filterSelfOut && self != null && self == it.text })
......@@ -931,6 +934,7 @@ class ChatRoomPresenter @Inject constructor(
ChatRoom(
id = id,
subscriptionId = subscriptionId,
parentId = parentId,
type = roomTypeOf(type),
unread = unread,
broadcast = broadcast ?: false,
......@@ -974,6 +978,7 @@ class ChatRoomPresenter @Inject constructor(
ChatRoom(
id = id,
subscriptionId = subscriptionId,
parentId = parentId,
type = roomTypeOf(type),
unread = unread,
broadcast = broadcast ?: false,
......
......@@ -4,9 +4,9 @@ import android.app.job.JobParameters
import android.app.job.JobService
import chat.rocket.android.db.DatabaseManagerFactory
import chat.rocket.android.server.domain.GetAccountsInteractor
import chat.rocket.android.server.infraestructure.ConnectionManagerFactory
import chat.rocket.android.server.infraestructure.DatabaseMessageMapper
import chat.rocket.android.server.infraestructure.DatabaseMessagesRepository
import chat.rocket.android.server.infrastructure.ConnectionManagerFactory
import chat.rocket.android.server.infrastructure.DatabaseMessageMapper
import chat.rocket.android.server.infrastructure.DatabaseMessagesRepository
import chat.rocket.core.internal.rest.sendMessage
import chat.rocket.core.model.Message
import dagger.android.AndroidInjection
......
......@@ -10,7 +10,7 @@ import androidx.fragment.app.Fragment
import chat.rocket.android.R
import chat.rocket.android.chatroom.presentation.ChatRoomNavigator
import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.infraestructure.ConnectionManagerFactory
import chat.rocket.android.server.infrastructure.ConnectionManagerFactory
import chat.rocket.android.util.extensions.addFragment
import chat.rocket.android.util.extensions.textContent
import dagger.android.AndroidInjection
......
......@@ -71,6 +71,7 @@ import chat.rocket.android.helper.AndroidPermissionsHelper.hasCameraPermission
import chat.rocket.android.helper.AndroidPermissionsHelper.hasWriteExternalStoragePermission
import chat.rocket.android.util.extension.asObservable
import chat.rocket.android.util.extension.createImageFile
import chat.rocket.android.util.extension.orFalse
import chat.rocket.android.util.extensions.circularRevealOrUnreveal
import chat.rocket.android.util.extensions.clearLightStatusBar
import chat.rocket.android.util.extensions.fadeIn
......@@ -463,6 +464,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
setupToolbar(roomUiModel.name.toString())
setupMessageComposer(roomUiModel)
isBroadcastChannel = roomUiModel.broadcast
isFavorite = roomUiModel.favorite.orFalse()
if (isBroadcastChannel && !roomUiModel.canModerate) {
disableMenu = true
activity?.invalidateOptionsMenu()
......
......@@ -30,7 +30,7 @@ import chat.rocket.android.server.domain.baseUrl
import chat.rocket.android.server.domain.messageReadReceiptEnabled
import chat.rocket.android.server.domain.messageReadReceiptStoreUsers
import chat.rocket.android.server.domain.useRealName
import chat.rocket.android.server.infraestructure.ConnectionManagerFactory
import chat.rocket.android.server.infrastructure.ConnectionManagerFactory
import chat.rocket.android.util.extension.isImage
import chat.rocket.android.util.extensions.avatarUrl
import chat.rocket.android.util.extensions.ifNotNullNorEmpty
......@@ -108,7 +108,7 @@ class UiModelMapper @Inject constructor(
readReceipts.forEach {
list.add(
ReadReceiptViewModel(
avatar = baseUrl.avatarUrl(it.user.username ?: ""),
avatar = baseUrl.avatarUrl(it.user.username!!, token?.userId, token?.authToken),
name = userHelper.displayName(it.user),
time = DateTimeHelper.getTime(DateTimeHelper.getLocalDateTime(it.timestamp))
)
......@@ -173,6 +173,7 @@ class UiModelMapper @Inject constructor(
ChatRoom(
id = id,
subscriptionId = subscriptionId,
parentId = parentId,
type = roomTypeOf(type),
unread = unread,
broadcast = broadcast ?: false,
......@@ -525,7 +526,7 @@ class UiModelMapper @Inject constructor(
val username = message.sender?.username ?: "?"
return baseUrl.let {
baseUrl.avatarUrl(username)
baseUrl.avatarUrl(username, token?.userId, token?.authToken)
}
}
......
......@@ -8,11 +8,13 @@ import chat.rocket.android.db.model.ChatRoom
import chat.rocket.android.server.domain.GetCurrentUserInteractor
import chat.rocket.android.server.domain.PermissionsInteractor
import chat.rocket.android.server.domain.PublicSettings
import chat.rocket.android.server.domain.TokenRepository
import chat.rocket.android.server.domain.showLastMessage
import chat.rocket.android.server.domain.useRealName
import chat.rocket.android.server.domain.useSpecialCharsOnRoom
import chat.rocket.android.util.extensions.avatarUrl
import chat.rocket.android.util.extensions.date
import chat.rocket.android.util.extensions.isNotNullNorEmpty
import chat.rocket.android.util.extensions.localDateTime
import chat.rocket.common.model.RoomType
import chat.rocket.common.model.User
......@@ -26,10 +28,12 @@ class RoomUiModelMapper(
private val context: Application,
private val settings: PublicSettings,
private val userInteractor: GetCurrentUserInteractor,
private val tokenRepository: TokenRepository,
private val serverUrl: String,
private val permissions: PermissionsInteractor
) {
private val currentUser by lazy { userInteractor.get() }
private val token by lazy { tokenRepository.get(serverUrl) }
fun map(
rooms: List<ChatRoom>,
......@@ -80,7 +84,7 @@ class RoomUiModelMapper(
private fun mapUser(user: User): RoomUiModel = with(user) {
val name = mapName(user.username!!, user.name)
val status = user.status
val avatar = serverUrl.avatarUrl(user.username!!)
val avatar = serverUrl.avatarUrl(user.username!!, token?.userId, token?.authToken)
val username = user.username!!
RoomUiModel(
......@@ -98,7 +102,7 @@ class RoomUiModelMapper(
id = id,
name = name!!,
type = type,
avatar = serverUrl.avatarUrl(name!!, isGroupOrChannel = true),
avatar = serverUrl.avatarUrl(name!!, token?.userId, token?.authToken, isGroupOrChannel = true),
lastMessage = if (showLastMessage) {
mapLastMessage(
lastMessage?.sender?.id, lastMessage?.sender?.username,
......@@ -113,17 +117,19 @@ class RoomUiModelMapper(
)
}
fun map(chatRoom: ChatRoom, showLastMessage: Boolean = true): RoomUiModel = with(chatRoom.chatRoom) {
fun map(chatRoom: ChatRoom, showLastMessage: Boolean = true): RoomUiModel =
with(chatRoom.chatRoom) {
val isUnread = alert || unread > 0
val type = roomTypeOf(type)
val status = chatRoom.status?.let { userStatusOf(it) }
val roomName = mapName(name, fullname)
val favorite = favorite
val timestamp = mapDate(lastMessageTimestamp ?: updatedAt)
val avatar = if (type is RoomType.DirectMessage) {
serverUrl.avatarUrl(name)
val avatar =
if (type is RoomType.DirectMessage) {
serverUrl.avatarUrl(name, token?.userId, token?.authToken)
} else {
serverUrl.avatarUrl(name, isGroupOrChannel = true)
serverUrl.avatarUrl(name, token?.userId, token?.authToken, isGroupOrChannel = true)
}
val unread = mapUnread(unread)
val lastMessage = if (showLastMessage) {
......@@ -139,10 +145,12 @@ class RoomUiModelMapper(
}
val hasMentions = mapMentions(userMentions, groupMentions)
val open = open
val lastMessageMarkdown = lastMessage?.let { Markwon.markdown(context, it.toString()).toString() }
val lastMessageMarkdown =
lastMessage?.let { Markwon.markdown(context, it.toString()).toString() }
RoomUiModel(
id = id,
isDiscussion = parentId.isNotNullNorEmpty(),
name = roomName,
type = type,
avatar = avatar,
......
......@@ -11,6 +11,9 @@ import chat.rocket.android.chatrooms.adapter.model.RoomUiModel
import chat.rocket.android.util.extension.setTextViewAppearance
import chat.rocket.common.model.RoomType
import chat.rocket.common.model.UserStatus
import com.bumptech.glide.Glide
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
import com.bumptech.glide.request.RequestOptions
import kotlinx.android.synthetic.main.item_chat.view.*
import kotlinx.android.synthetic.main.unread_messages_badge.view.*
......@@ -19,6 +22,7 @@ class RoomViewHolder(itemView: View, private val listener: (RoomUiModel) -> Unit
private val resources: Resources = itemView.resources
private val channelIcon: Drawable = resources.getDrawable(R.drawable.ic_hashtag_12dp, null)
private val groupIcon: Drawable = resources.getDrawable(R.drawable.ic_lock_12_dp, null)
private val discussionIcon: Drawable = resources.getDrawable(R.drawable.ic_discussion_20dp, null)
private val onlineIcon: Drawable = resources.getDrawable(R.drawable.ic_status_online_12dp, null)
private val awayIcon: Drawable = resources.getDrawable(R.drawable.ic_status_away_12dp, null)
private val busyIcon: Drawable = resources.getDrawable(R.drawable.ic_status_busy_12dp, null)
......@@ -27,10 +31,18 @@ class RoomViewHolder(itemView: View, private val listener: (RoomUiModel) -> Unit
override fun bindViews(data: RoomItemHolder) {
val room = data.data
with(itemView) {
image_avatar.setImageURI(room.avatar)
val avatar = room.avatar
Glide.with(image_avatar.context)
.load(room.avatar)
.apply(RequestOptions.bitmapTransform(RoundedCorners(10)))
.into(image_avatar)
text_chat_name.text = room.name
if (room.status != null && room.type is RoomType.DirectMessage) {
if (room.isDiscussion) {
image_chat_icon.setImageDrawable(discussionIcon)
} else if (room.status != null && room.type is RoomType.DirectMessage) {
image_chat_icon.setImageDrawable(getStatusDrawable(room.status))
} else {
image_chat_icon.setImageDrawable(getRoomDrawable(room.type))
......
......@@ -5,6 +5,7 @@ import chat.rocket.common.model.UserStatus
data class RoomUiModel(
val id: String,
val isDiscussion: Boolean = false,
val type: RoomType,
val name: CharSequence,
val avatar: String,
......
......@@ -15,9 +15,9 @@ import chat.rocket.android.server.domain.PermissionsInteractor
import chat.rocket.android.server.domain.PublicSettings
import chat.rocket.android.server.domain.SettingsRepository
import chat.rocket.android.server.domain.TokenRepository
import chat.rocket.android.server.infraestructure.ConnectionManager
import chat.rocket.android.server.infraestructure.ConnectionManagerFactory
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.server.infrastructure.ConnectionManager
import chat.rocket.android.server.infrastructure.ConnectionManagerFactory
import chat.rocket.android.server.infrastructure.RocketChatClientFactory
import chat.rocket.core.RocketChatClient
import dagger.Module
import dagger.Provides
......@@ -86,12 +86,20 @@ class ChatRoomsFragmentModule {
@PerFragment
fun provideRoomMapper(
context: Application,
repository: SettingsRepository,
settingsRepository: SettingsRepository,
userInteractor: GetCurrentUserInteractor,
tokenRepository: TokenRepository,
@Named("currentServer") serverUrl: String,
permissionsInteractor: PermissionsInteractor
): RoomUiModelMapper {
return RoomUiModelMapper(context, repository.get(serverUrl), userInteractor, serverUrl, permissionsInteractor)
return RoomUiModelMapper(
context,
settingsRepository.get(serverUrl),
userInteractor,
tokenRepository,
serverUrl,
permissionsInteractor
)
}
@Provides
......
......@@ -15,6 +15,10 @@ class ChatRoomsRepository @Inject constructor(private val dao: ChatRoomDao) {
Order.GROUPED_ACTIVITY -> dao.getAllGrouped()
Order.NAME -> dao.getAllAlphabetically()
Order.GROUPED_NAME -> dao.getAllAlphabeticallyGrouped()
Order.UNREAD_ON_TOP_ACTIVITY -> dao.getAllUnread();
Order.UNREAD_ON_TOP_NAME -> dao.getAllAlphabeticallyUnread();
Order.UNREAD_ON_TOP_GROUPED_ACTIVITY -> dao.getAllGroupedUnread();
Order.UNREAD_ON_TOP_GROUPED_NAME -> dao.getAllAlphabeticallyGroupedUnread();
}
}
......@@ -28,8 +32,16 @@ class ChatRoomsRepository @Inject constructor(private val dao: ChatRoomDao) {
GROUPED_ACTIVITY,
NAME,
GROUPED_NAME,
UNREAD_ON_TOP_ACTIVITY,
UNREAD_ON_TOP_NAME,
UNREAD_ON_TOP_GROUPED_ACTIVITY,
UNREAD_ON_TOP_GROUPED_NAME
}
}
fun ChatRoomsRepository.Order.isGrouped(): Boolean = this == ChatRoomsRepository.Order.GROUPED_ACTIVITY
|| this == ChatRoomsRepository.Order.GROUPED_NAME
fun ChatRoomsRepository.Order.isUnreadOnTop(): Boolean = this == ChatRoomsRepository.Order.UNREAD_ON_TOP_ACTIVITY
|| this == ChatRoomsRepository.Order.UNREAD_ON_TOP_NAME
......@@ -11,9 +11,10 @@ import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.main.presentation.MainNavigator
import chat.rocket.android.server.domain.SettingsRepository
import chat.rocket.android.server.domain.SortingAndGroupingInteractor
import chat.rocket.android.server.domain.siteName
import chat.rocket.android.server.domain.useRealName
import chat.rocket.android.server.domain.useSpecialCharsOnRoom
import chat.rocket.android.server.infraestructure.ConnectionManager
import chat.rocket.android.server.infrastructure.ConnectionManager
import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.retryDB
import chat.rocket.android.util.retryIO
......@@ -52,7 +53,7 @@ class ChatRoomsPresenter @Inject constructor(
fun toDirectory() = navigator.toDirectory()
fun getCurrentServerName() = view.setupToolbar(currentServer)
fun getCurrentServerName() = view.setupToolbar(settings.siteName() ?: currentServer)
fun getSortingAndGroupingPreferences() {
with(sortingAndGroupingInteractor) {
......@@ -93,6 +94,7 @@ class ChatRoomsPresenter @Inject constructor(
val entity = ChatRoomEntity(
id = id,
subscriptionId = "",
parentId = null,
type = type.toString(),
name = username ?: name.toString(),
fullname = name.toString(),
......
......@@ -301,10 +301,10 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
this.isGroupByFavorites = isGroupByFavorites
if (isSortByName) {
viewModel.setQuery(Query.ByName(isGroupByType))
viewModel.setQuery(Query.ByName(isGroupByType, isUnreadOnTop))
changeSortByTitle(getString(R.string.msg_sort_by_name))
} else {
viewModel.setQuery(Query.ByActivity(isGroupByType))
viewModel.setQuery(Query.ByActivity(isGroupByType, isUnreadOnTop))
changeSortByTitle(getString(R.string.msg_sort_by_activity))
}
}
......@@ -324,9 +324,9 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
private fun showAllChats() {
if (isSortByName) {
viewModel.setQuery(Query.ByName(isGroupByType))
viewModel.setQuery(Query.ByName(isGroupByType, isUnreadOnTop))
} else {
viewModel.setQuery(Query.ByActivity(isGroupByType))
viewModel.setQuery(Query.ByActivity(isGroupByType, isUnreadOnTop))
}
}
......
......@@ -9,7 +9,7 @@ import chat.rocket.android.chatrooms.adapter.LoadingItemHolder
import chat.rocket.android.chatrooms.adapter.RoomUiModelMapper
import chat.rocket.android.chatrooms.domain.FetchChatRoomsInteractor
import chat.rocket.android.chatrooms.infrastructure.ChatRoomsRepository
import chat.rocket.android.server.infraestructure.ConnectionManager
import chat.rocket.android.server.infrastructure.ConnectionManager
import chat.rocket.android.util.livedata.transform
import chat.rocket.android.util.livedata.wrap
import chat.rocket.android.util.retryIO
......@@ -140,8 +140,10 @@ sealed class LoadingState {
}
sealed class Query {
data class ByActivity(val grouped: Boolean = false) : Query()
data class ByName(val grouped: Boolean = false) : Query()
data class ByActivity(val grouped: Boolean = false, val unreadOnTop: Boolean = false) : Query()
data class ByName(val grouped: Boolean = false, val unreadOnTop: Boolean = false ) : Query()
data class Search(val query: String) : Query()
}
......@@ -155,19 +157,41 @@ fun Query.isGrouped(): Boolean {
}
}
fun Query.isUnreadOnTop(): Boolean {
return when(this) {
is Query.Search -> false
is Query.ByName -> unreadOnTop
is Query.ByActivity -> unreadOnTop
}
}
fun Query.asSortingOrder(): ChatRoomsRepository.Order {
return when(this) {
is Query.ByName -> {
if (grouped) {
if (grouped && !unreadOnTop) {
ChatRoomsRepository.Order.GROUPED_NAME
} else {
}
else if(unreadOnTop && !grouped){
ChatRoomsRepository.Order.UNREAD_ON_TOP_NAME
}
else if(unreadOnTop && grouped){
ChatRoomsRepository.Order.UNREAD_ON_TOP_GROUPED_NAME
}
else {
ChatRoomsRepository.Order.NAME
}
}
is Query.ByActivity -> {
if (grouped) {
if (grouped && !unreadOnTop) {
ChatRoomsRepository.Order.GROUPED_ACTIVITY
} else {
}
else if(unreadOnTop && !grouped){
ChatRoomsRepository.Order.UNREAD_ON_TOP_ACTIVITY
}
else if(unreadOnTop && grouped){
ChatRoomsRepository.Order.UNREAD_ON_TOP_GROUPED_ACTIVITY
}
else {
ChatRoomsRepository.Order.ACTIVITY
}
}
......
......@@ -5,7 +5,7 @@ import androidx.lifecycle.ViewModelProvider
import chat.rocket.android.chatrooms.adapter.RoomUiModelMapper
import chat.rocket.android.chatrooms.domain.FetchChatRoomsInteractor
import chat.rocket.android.chatrooms.infrastructure.ChatRoomsRepository
import chat.rocket.android.server.infraestructure.ConnectionManager
import chat.rocket.android.server.infrastructure.ConnectionManager
import javax.inject.Inject
class ChatRoomsViewModelFactory @Inject constructor(
......
package chat.rocket.android.core.behaviours
interface AppLanguageView {
/**
* Updates the app language
*
* @param language The app language to be updated.
* @param country Opcional. The country code to be updated.
*/
fun updateLanguage(language: String, country: String? = null)
}
\ No newline at end of file
......@@ -4,7 +4,7 @@ import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.main.presentation.MainNavigator
import chat.rocket.android.members.uimodel.MemberUiModelMapper
import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.server.infrastructure.RocketChatClientFactory
import chat.rocket.android.util.extension.launchUI
import chat.rocket.common.RocketChatException
import chat.rocket.common.model.RoomType
......
......@@ -14,9 +14,15 @@ import dagger.android.support.AndroidSupportInjectionModule
import javax.inject.Singleton
@Singleton
@Component(modules = [AndroidSupportInjectionModule::class,
AppModule::class, ActivityBuilder::class, ServiceBuilder::class, ReceiverBuilder::class,
AndroidWorkerInjectionModule::class])
@Component(
modules = [
AndroidSupportInjectionModule::class,
AppModule::class,
ActivityBuilder::class,
ServiceBuilder::class,
ReceiverBuilder::class,
AndroidWorkerInjectionModule::class]
)
interface AppComponent {
@Component.Builder
......
......@@ -12,8 +12,8 @@ import chat.rocket.android.R
import chat.rocket.android.analytics.AnalyticsManager
import chat.rocket.android.analytics.AnswersAnalytics
import chat.rocket.android.analytics.GoogleAnalyticsForFirebase
import chat.rocket.android.authentication.infraestructure.SharedPreferencesMultiServerTokenRepository
import chat.rocket.android.authentication.infraestructure.SharedPreferencesTokenRepository
import chat.rocket.android.authentication.infrastructure.SharedPreferencesMultiServerTokenRepository
import chat.rocket.android.authentication.infrastructure.SharedPreferencesTokenRepository
import chat.rocket.android.chatroom.service.MessageService
import chat.rocket.android.dagger.qualifier.ForAuthentication
import chat.rocket.android.dagger.qualifier.ForMessages
......@@ -44,19 +44,21 @@ import chat.rocket.android.server.domain.SettingsRepository
import chat.rocket.android.server.domain.SortingAndGroupingRepository
import chat.rocket.android.server.domain.TokenRepository
import chat.rocket.android.server.domain.UsersRepository
import chat.rocket.android.server.infraestructure.DatabaseMessageMapper
import chat.rocket.android.server.infraestructure.DatabaseMessagesRepository
import chat.rocket.android.server.infraestructure.JobSchedulerInteractorImpl
import chat.rocket.android.server.infraestructure.MemoryChatRoomsRepository
import chat.rocket.android.server.infraestructure.MemoryUsersRepository
import chat.rocket.android.server.infraestructure.SharedPreferencesAccountsRepository
import chat.rocket.android.server.infraestructure.SharedPreferencesPermissionsRepository
import chat.rocket.android.server.infraestructure.SharedPreferencesSettingsRepository
import chat.rocket.android.server.infraestructure.SharedPrefsAnalyticsTrackingRepository
import chat.rocket.android.server.infraestructure.SharedPrefsBasicAuthRepository
import chat.rocket.android.server.infraestructure.SharedPrefsConnectingServerRepository
import chat.rocket.android.server.infraestructure.SharedPrefsCurrentServerRepository
import chat.rocket.android.server.infraestructure.SharedPrefsSortingAndGroupingRepository
import chat.rocket.android.server.infrastructure.CurrentLanguageRepository
import chat.rocket.android.server.infrastructure.SharedPrefsCurrentLanguageRepository
import chat.rocket.android.server.infrastructure.DatabaseMessageMapper
import chat.rocket.android.server.infrastructure.DatabaseMessagesRepository
import chat.rocket.android.server.infrastructure.JobSchedulerInteractorImpl
import chat.rocket.android.server.infrastructure.MemoryChatRoomsRepository
import chat.rocket.android.server.infrastructure.MemoryUsersRepository
import chat.rocket.android.server.infrastructure.SharedPreferencesAccountsRepository
import chat.rocket.android.server.infrastructure.SharedPreferencesPermissionsRepository
import chat.rocket.android.server.infrastructure.SharedPreferencesSettingsRepository
import chat.rocket.android.server.infrastructure.SharedPrefsAnalyticsTrackingRepository
import chat.rocket.android.server.infrastructure.SharedPrefsBasicAuthRepository
import chat.rocket.android.server.infrastructure.SharedPrefsConnectingServerRepository
import chat.rocket.android.server.infrastructure.SharedPrefsCurrentServerRepository
import chat.rocket.android.server.infrastructure.SharedPrefsSortingAndGroupingRepository
import chat.rocket.android.util.AppJsonAdapterFactory
import chat.rocket.android.util.BasicAuthenticatorInterceptor
import chat.rocket.android.util.HttpLoggingInterceptor
......@@ -175,7 +177,6 @@ class AppModule {
fun provideSharedPreferences(context: Application) =
context.getSharedPreferences("rocket.chat", Context.MODE_PRIVATE)
@Provides
@ForMessages
fun provideMessagesSharedPreferences(context: Application) =
......@@ -211,6 +212,12 @@ class AppModule {
return SharedPrefsConnectingServerRepository(prefs)
}
@Provides
@Singleton
fun provideCurrentLanguageRepository(prefs: SharedPreferences): CurrentLanguageRepository {
return SharedPrefsCurrentLanguageRepository(prefs)
}
@Provides
@Singleton
fun provideSettingsRepository(localRepository: LocalRepository): SettingsRepository {
......
......@@ -6,7 +6,7 @@ import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.infrastructure.SharedPreferencesLocalRepository
import chat.rocket.android.server.domain.CurrentServerRepository
import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.infraestructure.SharedPrefsCurrentServerRepository
import chat.rocket.android.server.infrastructure.SharedPrefsCurrentServerRepository
import chat.rocket.android.util.AppJsonAdapterFactory
import chat.rocket.android.util.TimberLogger
import chat.rocket.common.internal.FallbackSealedClassJsonAdapter
......
......@@ -69,7 +69,55 @@ abstract class ChatRoomDao : BaseDao<ChatRoomEntity> {
@Query("""
$BASE_QUERY
$FILTER_NOT_OPENED
ORDER BY name
ORDER BY
$UNREAD,
CASE
WHEN lastMessageTimeStamp IS NOT NULL THEN lastMessageTimeStamp
ELSE updatedAt
END DESC
""")
abstract fun getAllUnread(): LiveData<List<ChatRoom>>
@Transaction
@Query("""
$BASE_QUERY
$FILTER_NOT_OPENED
ORDER BY
$UNREAD,
name COLLATE NOCASE
""")
abstract fun getAllAlphabeticallyUnread(): LiveData<List<ChatRoom>>
@Transaction
@Query("""
$BASE_QUERY
$FILTER_NOT_OPENED
ORDER BY
$TYPE_ORDER,
$UNREAD,
name COLLATE NOCASE
""")
abstract fun getAllAlphabeticallyGroupedUnread(): LiveData<List<ChatRoom>>
@Transaction
@Query("""
$BASE_QUERY
$FILTER_NOT_OPENED
ORDER BY
$TYPE_ORDER,
$UNREAD,
CASE
WHEN lastMessageTimeStamp IS NOT NULL THEN lastMessageTimeStamp
ELSE updatedAt
END DESC
""")
abstract fun getAllGroupedUnread(): LiveData<List<ChatRoom>>
@Transaction
@Query("""
$BASE_QUERY
$FILTER_NOT_OPENED
ORDER BY name COLLATE NOCASE
""")
abstract fun getAllAlphabetically(): LiveData<List<ChatRoom>>
......@@ -79,7 +127,7 @@ abstract class ChatRoomDao : BaseDao<ChatRoomEntity> {
$FILTER_NOT_OPENED
ORDER BY
$TYPE_ORDER,
name
name COLLATE NOCASE
""")
abstract fun getAllAlphabeticallyGrouped(): LiveData<List<ChatRoom>>
......@@ -139,5 +187,11 @@ abstract class ChatRoomDao : BaseDao<ChatRoomEntity> {
ELSE 5
END
"""
const val UNREAD = """
CASE
WHEN alert OR unread > 0 THEN 1
ELSE 2
END
"""
}
}
\ No newline at end of file
......@@ -475,6 +475,7 @@ class DatabaseManager(val context: Application, val serverUrl: String) {
return ChatRoomEntity(
id = room.id,
subscriptionId = subscription.id,
parentId = subscription.parentId,
type = room.type.toString(),
name = room.name ?: subscription.name
?: throw NullPointerException(), // this should be filtered on the SDK
......@@ -516,6 +517,7 @@ class DatabaseManager(val context: Application, val serverUrl: String) {
return ChatRoomEntity(
id = id,
subscriptionId = subscriptionId,
parentId = parentId,
type = type.toString(),
name = name,
fullname = fullName,
......
......@@ -25,7 +25,7 @@ import chat.rocket.android.emoji.internal.db.StringListConverter
AttachmentFieldEntity::class, AttachmentActionEntity::class, UrlEntity::class,
ReactionEntity::class, MessagesSync::class
],
version = 11,
version = 12,
exportSchema = true
)
@TypeConverters(StringListConverter::class)
......
......@@ -26,6 +26,7 @@ import chat.rocket.android.emoji.internal.db.StringListConverter
data class ChatRoomEntity(
@PrimaryKey var id: String,
var subscriptionId: String,
var parentId: String?,
var type: String,
var name: String,
var fullname: String? = null,
......
......@@ -7,7 +7,7 @@ import chat.rocket.android.db.model.ChatRoomEntity
import chat.rocket.android.directory.uimodel.DirectoryUiModelMapper
import chat.rocket.android.helper.UserHelper
import chat.rocket.android.main.presentation.MainNavigator
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.server.infrastructure.RocketChatClientFactory
import chat.rocket.android.util.extension.launchUI
import chat.rocket.common.model.RoomType
import chat.rocket.common.model.roomTypeOf
......@@ -146,6 +146,7 @@ class DirectoryPresenter @Inject constructor(
val chatRoomEntity = ChatRoomEntity(
id = directMessage.id,
parentId = null,
name = username,
description = null,
type = RoomType.DIRECT_MESSAGE,
......
package chat.rocket.android.directory.uimodel
import chat.rocket.android.util.extensions.avatarUrl
import chat.rocket.common.model.Token
import chat.rocket.core.model.DirectoryResult
class DirectoryUiModel(
private val directoryResult: DirectoryResult,
private val baseUrl: String?
private val baseUrl: String?,
private val token: Token?
) {
val id: String = directoryResult.id
val channelAvatarUri: String?
......@@ -22,12 +24,12 @@ class DirectoryUiModel(
}
private fun getChannelAvatar(): String? {
return baseUrl?.avatarUrl(name, isGroupOrChannel = true)
return baseUrl?.avatarUrl(name, token?.userId, token?.authToken, isGroupOrChannel = true)
}
private fun getUserAvatar(): String? {
return directoryResult.username?.let {
baseUrl?.avatarUrl(it)
baseUrl?.avatarUrl(it, token?.userId, token?.authToken)
}
}
}
package chat.rocket.android.directory.uimodel
import chat.rocket.android.server.domain.GetSettingsInteractor
import chat.rocket.android.server.domain.TokenRepository
import chat.rocket.android.server.domain.baseUrl
import chat.rocket.core.model.DirectoryResult
import chat.rocket.core.model.Value
......@@ -9,13 +10,14 @@ import javax.inject.Named
class DirectoryUiModelMapper @Inject constructor(
getSettingsInteractor: GetSettingsInteractor,
@Named("currentServer") private val currentServer: String
@Named("currentServer") private val currentServer: String,
tokenRepository: TokenRepository
) {
private var settings: Map<String, Value<Any>> = getSettingsInteractor.get(currentServer)
private val baseUrl = settings.baseUrl()
private val token = tokenRepository.get(currentServer)
fun mapToUiModelList(directoryList: List<DirectoryResult>): List<DirectoryUiModel> {
return directoryList.map { DirectoryUiModel(it, baseUrl) }
return directoryList.map { DirectoryUiModel(it, baseUrl, token) }
}
}
\ No newline at end of file
......@@ -3,7 +3,7 @@ package chat.rocket.android.favoritemessages.presentation
import chat.rocket.android.chatroom.uimodel.UiModelMapper
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.db.DatabaseManager
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.server.infrastructure.RocketChatClientFactory
import chat.rocket.android.util.extension.launchUI
import chat.rocket.common.RocketChatException
import chat.rocket.common.model.roomTypeOf
......
......@@ -5,7 +5,7 @@ import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.db.DatabaseManager
import chat.rocket.android.files.uimodel.FileUiModel
import chat.rocket.android.files.uimodel.FileUiModelMapper
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.server.infrastructure.RocketChatClientFactory
import chat.rocket.android.util.extension.launchUI
import chat.rocket.common.RocketChatException
import chat.rocket.common.model.roomTypeOf
......
......@@ -26,7 +26,7 @@ object AndroidPermissionsHelper {
}
fun hasCameraPermission(context: Context): Boolean {
return AndroidPermissionsHelper.checkPermission(context, Manifest.permission.CAMERA)
return checkPermission(context, Manifest.permission.CAMERA)
}
fun getCameraPermission(fragment: Fragment) {
......@@ -50,10 +50,10 @@ object AndroidPermissionsHelper {
fun checkWritingPermission(context: Context) {
if (context is ContextThemeWrapper) {
val activity = if (context.baseContext is Activity) context.baseContext as Activity else context as Activity
AndroidPermissionsHelper.requestPermission(
requestPermission(
activity,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
AndroidPermissionsHelper.WRITE_EXTERNAL_STORAGE_CODE
WRITE_EXTERNAL_STORAGE_CODE
)
}
}
......
......@@ -3,7 +3,7 @@ package chat.rocket.android.helper
object JitsiHelper {
/**
* Returns the for the Jitsi video conferencing URL.
* Returns the Jitsi video conferencing URL.
*
* @param isSecureProtocol True if using SSL, false otherwise - from the public settings.
* @param domain The Jitsi domain - from public settings.
......
......@@ -94,10 +94,16 @@ class MessageParser @Inject constructor(
}
private fun getMention(user: SimpleUser): String {
user.id?.let {
if (SYSTEM_MENTIONS.contains(it)) {
return "@$it"
}
}
return if (settings.useRealName()) {
user.name ?: "@${user.username}"
user.name ?: user.username.orEmpty()
} else {
"@${user.username}"
user.username.orEmpty()
}
}
......@@ -527,5 +533,7 @@ class MessageParser @Inject constructor(
*/
private val WEB_URL = Pattern.compile(
"($WEB_URL_WITH_PROTOCOL|$WEB_URL_WITHOUT_PROTOCOL)")
private val SYSTEM_MENTIONS = arrayOf("all", "here")
}
}
package chat.rocket.android.main.di
import androidx.lifecycle.LifecycleOwner
import chat.rocket.android.core.behaviours.AppLanguageView
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.dagger.scope.PerActivity
import chat.rocket.android.main.presentation.MainNavigator
......@@ -16,6 +17,12 @@ class MainModule {
@PerActivity
fun provideMainNavigator(activity: MainActivity) = MainNavigator(activity)
@Provides
@PerActivity
fun appLanguageView(activity: MainActivity): AppLanguageView {
return activity
}
@Provides
@PerActivity
fun provideJob() = Job()
......
package chat.rocket.android.main.presentation
import chat.rocket.android.core.behaviours.AppLanguageView
import chat.rocket.android.push.GroupedPush
import chat.rocket.android.server.domain.GetCurrentLanguageInteractor
import chat.rocket.android.server.domain.RefreshPermissionsInteractor
import chat.rocket.android.server.domain.RefreshSettingsInteractor
import chat.rocket.android.server.infraestructure.ConnectionManagerFactory
import chat.rocket.android.server.infrastructure.ConnectionManagerFactory
import javax.inject.Inject
import javax.inject.Named
class MainPresenter @Inject constructor(
@Named("currentServer") private val currentServerUrl: String,
private val mainNavigator: MainNavigator,
private val appLanguageView: AppLanguageView,
private val refreshSettingsInteractor: RefreshSettingsInteractor,
private val refreshPermissionsInteractor: RefreshPermissionsInteractor,
private val connectionManagerFactory: ConnectionManagerFactory,
private var getLanguageInteractor: GetCurrentLanguageInteractor,
private val groupedPush: GroupedPush
) {
......@@ -31,4 +35,13 @@ class MainPresenter @Inject constructor(
}
fun showChatList(chatRoomId: String? = null) = mainNavigator.toChatList(chatRoomId)
fun getAppLanguage() {
with(getLanguageInteractor) {
getLanguage()?.let { language ->
appLanguageView.updateLanguage(language, getCountry())
}
}
}
}
\ No newline at end of file
......@@ -3,10 +3,14 @@ package chat.rocket.android.main.ui
import android.app.Activity
import android.app.NotificationManager
import android.content.Context
import android.content.res.Configuration
import android.os.Build
import android.os.Bundle
import android.os.LocaleList
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import chat.rocket.android.R
import chat.rocket.android.core.behaviours.AppLanguageView
import chat.rocket.android.main.presentation.MainPresenter
import chat.rocket.android.push.refreshPushToken
import chat.rocket.android.server.ui.INTENT_CHAT_ROOM_ID
......@@ -15,10 +19,11 @@ import dagger.android.AndroidInjector
import dagger.android.DispatchingAndroidInjector
import dagger.android.HasActivityInjector
import dagger.android.support.HasSupportFragmentInjector
import java.util.*
import javax.inject.Inject
class MainActivity : AppCompatActivity(), HasActivityInjector,
HasSupportFragmentInjector {
HasSupportFragmentInjector, AppLanguageView {
@Inject
lateinit var activityDispatchingAndroidInjector: DispatchingAndroidInjector<Activity>
@Inject
......@@ -30,11 +35,11 @@ class MainActivity : AppCompatActivity(), HasActivityInjector,
AndroidInjection.inject(this)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
refreshPushToken()
with(presenter) {
connect()
getAppLanguage()
intent.getStringExtra(INTENT_CHAT_ROOM_ID).let {
clearNotificationsForChatRoom(it)
showChatList(it)
......@@ -53,6 +58,28 @@ class MainActivity : AppCompatActivity(), HasActivityInjector,
override fun supportFragmentInjector(): AndroidInjector<Fragment> =
fagmentDispatchingAndroidInjector
override fun updateLanguage(language: String, country: String?) {
val locale: Locale = if (country != null) {
Locale(language, country)
} else {
Locale(language)
}
Locale.setDefault(locale)
val config = Configuration()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
config.locales = LocaleList(locale)
} else {
config.locale = locale
}
// TODO We need to check out a better way to use createConfigurationContext
// instead of updateConfiguration here since it is deprecated.
resources.updateConfiguration(config, resources.displayMetrics)
}
private fun clearAppNotifications() =
(getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager).cancelAll()
}
......@@ -6,7 +6,7 @@ import chat.rocket.android.db.DatabaseManager
import chat.rocket.android.helper.UserHelper
import chat.rocket.android.members.uimodel.MemberUiModel
import chat.rocket.android.members.uimodel.MemberUiModelMapper
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.server.infrastructure.RocketChatClientFactory
import chat.rocket.android.util.extension.launchUI
import chat.rocket.common.RocketChatException
import chat.rocket.common.model.roomTypeOf
......
......@@ -2,6 +2,7 @@ package chat.rocket.android.members.uimodel
import chat.rocket.android.server.domain.useRealName
import chat.rocket.android.util.extensions.avatarUrl
import chat.rocket.common.model.Token
import chat.rocket.common.model.User
import chat.rocket.common.model.UserStatus
import chat.rocket.core.model.Value
......@@ -9,7 +10,8 @@ import chat.rocket.core.model.Value
class MemberUiModel(
private val member: User,
private val settings: Map<String, Value<Any>>,
private val baseUrl: String?
private val baseUrl: String?,
private val token: Token?
) {
val userId: String = member.id
val avatarUri: String?
......@@ -33,7 +35,7 @@ class MemberUiModel(
private fun getUserAvatar(): String? {
val username = member.username ?: "?"
return baseUrl?.let {
baseUrl.avatarUrl(username, format = "png")
baseUrl.avatarUrl(username, token?.userId, token?.authToken, format = "png")
}
}
......
......@@ -2,19 +2,24 @@ package chat.rocket.android.members.uimodel
import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.domain.GetSettingsInteractor
import chat.rocket.android.server.domain.TokenRepository
import chat.rocket.android.server.domain.baseUrl
import chat.rocket.common.model.User
import chat.rocket.core.model.Value
import javax.inject.Inject
import javax.inject.Named
class MemberUiModelMapper @Inject constructor(
serverInteractor: GetCurrentServerInteractor,
getSettingsInteractor: GetSettingsInteractor
getSettingsInteractor: GetSettingsInteractor,
@Named("currentServer") private val currentServer: String,
tokenRepository: TokenRepository
) {
private var settings: Map<String, Value<Any>> = getSettingsInteractor.get(serverInteractor.get()!!)
private val baseUrl = settings.baseUrl()
private val token = tokenRepository.get(currentServer)
fun mapToUiModelList(memberList: List<User>): List<MemberUiModel> {
return memberList.map { MemberUiModel(it, settings, baseUrl) }
return memberList.map { MemberUiModel(it, settings, baseUrl, token) }
}
}
......@@ -2,7 +2,7 @@ package chat.rocket.android.mentions.presentention
import chat.rocket.android.chatroom.uimodel.UiModelMapper
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.server.infrastructure.RocketChatClientFactory
import chat.rocket.android.util.extension.launchUI
import chat.rocket.common.RocketChatException
import chat.rocket.common.util.ifNull
......
......@@ -3,7 +3,7 @@ package chat.rocket.android.pinnedmessages.presentation
import chat.rocket.android.chatroom.uimodel.UiModelMapper
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.db.DatabaseManager
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.server.infrastructure.RocketChatClientFactory
import chat.rocket.android.util.extension.launchUI
import chat.rocket.common.RocketChatException
import chat.rocket.common.model.roomTypeOf
......
......@@ -11,8 +11,8 @@ import chat.rocket.android.main.presentation.MainNavigator
import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.domain.RemoveAccountInteractor
import chat.rocket.android.server.domain.TokenRepository
import chat.rocket.android.server.infraestructure.ConnectionManagerFactory
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.server.infrastructure.ConnectionManagerFactory
import chat.rocket.android.server.infrastructure.RocketChatClientFactory
import chat.rocket.android.server.presentation.CheckServerPresenter
import chat.rocket.android.util.extension.compressImageAndGetByteArray
import chat.rocket.android.util.extension.launchUI
......@@ -57,6 +57,7 @@ class ProfilePresenter @Inject constructor(
private val serverUrl = serverInteractor.get()!!
private val client: RocketChatClient = factory.get(serverUrl)
private val user = userHelper.user()
private val token = tokenRepository.get(serverUrl)
fun loadUserProfile() {
launchUI(strategy) {
......@@ -68,7 +69,7 @@ class ProfilePresenter @Inject constructor(
view.showProfile(
me.status.toString(),
serverUrl.avatarUrl(me.username ?: ""),
serverUrl.avatarUrl(me.username!!, token?.userId, token?.authToken),
me.name ?: "",
me.username ?: "",
me.emails?.getOrNull(0)?.address ?: ""
......@@ -97,7 +98,7 @@ class ProfilePresenter @Inject constructor(
view.showProfileUpdateSuccessfullyMessage()
view.showProfile(
user.status.toString(),
serverUrl.avatarUrl(user.username ?: ""),
serverUrl.avatarUrl(user.username!!, token?.userId, token?.authToken),
name,
username,
email
......@@ -127,7 +128,15 @@ class ProfilePresenter @Inject constructor(
uriInteractor.getInputStream(uri)
}
}
user?.username?.let { view.reloadUserAvatar(serverUrl.avatarUrl(it)) }
user?.username?.let {
view.reloadUserAvatar(
serverUrl.avatarUrl(
it,
token?.userId,
token?.authToken
)
)
}
} catch (exception: RocketChatException) {
exception.message?.let {
view.showMessage(it)
......@@ -155,7 +164,15 @@ class ProfilePresenter @Inject constructor(
}
}
user?.username?.let { view.reloadUserAvatar(serverUrl.avatarUrl(it)) }
user?.username?.let {
view.reloadUserAvatar(
serverUrl.avatarUrl(
it,
token?.userId,
token?.authToken
)
)
}
} catch (exception: RocketChatException) {
exception.message?.let {
view.showMessage(it)
......@@ -175,7 +192,15 @@ class ProfilePresenter @Inject constructor(
user?.id?.let { id ->
retryIO { client.resetAvatar(id) }
}
user?.username?.let { view.reloadUserAvatar(serverUrl.avatarUrl(it)) }
user?.username?.let {
view.reloadUserAvatar(
serverUrl.avatarUrl(
it,
token?.userId,
token?.authToken
)
)
}
} catch (exception: RocketChatException) {
exception.message?.let {
view.showMessage(it)
......
......@@ -3,6 +3,7 @@ package chat.rocket.android.profile.ui
import DrawableHelper
import android.app.Activity
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.os.Build
import android.os.Bundle
......@@ -21,6 +22,9 @@ import androidx.fragment.app.Fragment
import chat.rocket.android.R
import chat.rocket.android.analytics.AnalyticsManager
import chat.rocket.android.analytics.event.ScreenViewEvent
import chat.rocket.android.helper.AndroidPermissionsHelper
import chat.rocket.android.helper.AndroidPermissionsHelper.getCameraPermission
import chat.rocket.android.helper.AndroidPermissionsHelper.hasCameraPermission
import chat.rocket.android.main.ui.MainActivity
import chat.rocket.android.profile.presentation.ProfilePresenter
import chat.rocket.android.profile.presentation.ProfileView
......@@ -35,12 +39,15 @@ import chat.rocket.android.util.invalidateFirebaseToken
import chat.rocket.common.model.UserStatus
import chat.rocket.common.model.userStatusOf
import com.facebook.drawee.backends.pipeline.Fresco
import com.google.android.material.snackbar.Snackbar
import dagger.android.support.AndroidSupportInjection
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.Observables
import kotlinx.android.synthetic.main.app_bar.*
import kotlinx.android.synthetic.main.avatar_profile.*
import kotlinx.android.synthetic.main.fragment_profile.*
import kotlinx.android.synthetic.main.fragment_profile.view_dim
import kotlinx.android.synthetic.main.fragment_profile.view_loading
import kotlinx.android.synthetic.main.update_avatar_options.*
import javax.inject.Inject
......@@ -52,8 +59,10 @@ private const val REQUEST_CODE_FOR_PERFORM_CAMERA = 2
fun newInstance() = ProfileFragment()
class ProfileFragment : Fragment(), ProfileView, ActionMode.Callback {
@Inject lateinit var presenter: ProfilePresenter
@Inject lateinit var analyticsManager: AnalyticsManager
@Inject
lateinit var presenter: ProfilePresenter
@Inject
lateinit var analyticsManager: AnalyticsManager
private var currentStatus = ""
private var currentName = ""
private var currentUsername = ""
......@@ -223,7 +232,13 @@ class ProfileFragment : Fragment(), ProfileView, ActionMode.Callback {
}
button_take_a_photo.setOnClickListener {
context?.let {
if (hasCameraPermission(it)) {
dispatchTakePicture(REQUEST_CODE_FOR_PERFORM_CAMERA)
} else {
getCameraPermission(this)
}
}
hideUpdateAvatarOptions()
}
......@@ -331,4 +346,28 @@ class ProfileFragment : Fragment(), ProfileView, ActionMode.Callback {
}.show()
}
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
when (requestCode) {
AndroidPermissionsHelper.CAMERA_CODE -> {
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// permission was granted
dispatchTakePicture(REQUEST_CODE_FOR_PERFORM_CAMERA)
} else {
// permission denied
Snackbar.make(
relative_layout,
R.string.msg_camera_permission_denied,
Snackbar.LENGTH_SHORT
).show()
}
return
}
}
}
}
......@@ -7,7 +7,7 @@ import android.content.Intent
import androidx.core.app.RemoteInput
import android.widget.Toast
import chat.rocket.android.R
import chat.rocket.android.server.infraestructure.ConnectionManagerFactory
import chat.rocket.android.server.infrastructure.ConnectionManagerFactory
import chat.rocket.common.RocketChatException
import chat.rocket.core.internal.rest.sendMessage
import dagger.android.AndroidInjection
......
package chat.rocket.android.server.domain
import chat.rocket.android.server.infrastructure.CurrentLanguageRepository
import javax.inject.Inject
class GetCurrentLanguageInteractor @Inject constructor(
private val repository: CurrentLanguageRepository
) {
fun getLanguage(): String? = repository.getLanguage()
fun getCountry(): String? = repository.getCountry()
}
\ No newline at end of file
package chat.rocket.android.server.domain
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.server.infrastructure.RocketChatClientFactory
import chat.rocket.android.util.retryIO
import chat.rocket.core.internal.rest.permissions
import kotlinx.coroutines.Dispatchers
......
package chat.rocket.android.server.domain
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.server.infrastructure.RocketChatClientFactory
import chat.rocket.android.util.retryIO
import chat.rocket.core.internal.rest.settings
import kotlinx.coroutines.Dispatchers
......
package chat.rocket.android.server.domain
import chat.rocket.android.server.infrastructure.CurrentLanguageRepository
import javax.inject.Inject
class SaveCurrentLanguageInteractor @Inject constructor(
private val repository: CurrentLanguageRepository
) {
fun save(language: String, country: String?) = repository.save(language, country)
}
\ No newline at end of file
......@@ -4,6 +4,7 @@ import se.ansman.kotshi.JsonSerializable
@JsonSerializable
data class Account(
val serverName: String?,
val serverUrl: String,
val serverLogo: String?,
val serverBg: String?,
......
package chat.rocket.android.server.infraestructure
package chat.rocket.android.server.infrastructure
import androidx.lifecycle.MutableLiveData
import chat.rocket.android.db.DatabaseManager
......
package chat.rocket.android.server.infraestructure
package chat.rocket.android.server.infrastructure
import chat.rocket.android.db.DatabaseManagerFactory
import timber.log.Timber
......
package chat.rocket.android.server.infrastructure
interface CurrentLanguageRepository {
fun save(language: String, country: String? = null)
fun getLanguage(): String?
fun getCountry(): String?
}
\ No newline at end of file
package chat.rocket.android.server.infraestructure
package chat.rocket.android.server.infrastructure
import chat.rocket.android.db.DatabaseManager
import chat.rocket.android.db.model.*
......
package chat.rocket.android.server.infraestructure
package chat.rocket.android.server.infrastructure
import chat.rocket.android.db.DatabaseManager
import chat.rocket.android.db.Operation
......
package chat.rocket.android.server.infraestructure
package chat.rocket.android.server.infrastructure
import android.app.job.JobInfo
import android.app.job.JobScheduler
......
package chat.rocket.android.server.infraestructure
package chat.rocket.android.server.infrastructure
import chat.rocket.android.server.domain.ChatRoomsRepository
import chat.rocket.core.model.ChatRoom
......
package chat.rocket.android.server.infraestructure
package chat.rocket.android.server.infrastructure
import chat.rocket.android.server.domain.UsersRepository
import chat.rocket.android.server.domain.UsersRepository.Query
......
package chat.rocket.android.server.infraestructure
package chat.rocket.android.server.infrastructure
import android.os.Build
import chat.rocket.android.BuildConfig
......
package chat.rocket.android.server.infraestructure
package chat.rocket.android.server.infrastructure
import android.content.SharedPreferences
import androidx.core.content.edit
......
package chat.rocket.android.server.infraestructure
package chat.rocket.android.server.infrastructure
import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.server.domain.PermissionsRepository
......
package chat.rocket.android.server.infraestructure
package chat.rocket.android.server.infrastructure
import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.infrastructure.LocalRepository.Companion.SETTINGS_KEY
......
package chat.rocket.android.server.infraestructure
package chat.rocket.android.server.infrastructure
import android.content.SharedPreferences
import chat.rocket.android.server.domain.AnalyticsTrackingRepository
......
package chat.rocket.android.server.infraestructure
package chat.rocket.android.server.infrastructure
import android.content.SharedPreferences
import androidx.core.content.edit
......
package chat.rocket.android.server.infraestructure
package chat.rocket.android.server.infrastructure
import android.content.SharedPreferences
import chat.rocket.android.server.domain.CurrentServerRepository
......
package chat.rocket.android.server.infrastructure
import android.content.SharedPreferences
private const val CURRENT_LANGUAGE = "current_language"
private const val CURRENT_LANGUAGE_COUNTRY = "current_language_country"
class SharedPrefsCurrentLanguageRepository(private val preferences: SharedPreferences) :
CurrentLanguageRepository {
override fun save(language: String, country: String?) {
with(preferences) {
edit().putString(CURRENT_LANGUAGE, language).apply()
edit().putString(CURRENT_LANGUAGE_COUNTRY, country).apply()
}
}
override fun getLanguage(): String? {
return preferences.getString(CURRENT_LANGUAGE, "")
}
override fun getCountry(): String? {
return preferences.getString(CURRENT_LANGUAGE_COUNTRY, "")
}
}
package chat.rocket.android.server.infraestructure
package chat.rocket.android.server.infrastructure
import android.content.SharedPreferences
import chat.rocket.android.server.domain.CurrentServerRepository
......
package chat.rocket.android.server.infraestructure
package chat.rocket.android.server.infrastructure
import android.content.SharedPreferences
import chat.rocket.android.server.domain.SortingAndGroupingRepository
......
......@@ -9,7 +9,7 @@ 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.infrastructure.ConnectionManagerFactory
import chat.rocket.android.util.extension.launchUI
import chat.rocket.common.util.ifNull
import javax.inject.Inject
......
......@@ -26,9 +26,9 @@ import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.domain.RemoveAccountInteractor
import chat.rocket.android.server.domain.TokenRepository
import chat.rocket.android.server.domain.RefreshSettingsInteractor
import chat.rocket.android.server.infraestructure.ConnectionManager
import chat.rocket.android.server.infraestructure.ConnectionManagerFactory
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.server.infrastructure.ConnectionManager
import chat.rocket.android.server.infrastructure.ConnectionManagerFactory
import chat.rocket.android.server.infrastructure.RocketChatClientFactory
import chat.rocket.android.util.VersionInfo
import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.extensions.casUrl
......
......@@ -13,7 +13,7 @@ class ServerViewHolder(itemView: View, private val currentServerUrl: String) :
fun bind(account: Account) {
with(itemView) {
Glide.with(context).load(account.serverLogo).into(image_server)
text_server_name.text = account.serverUrl
text_server_name.text = account.serverName ?: account.serverUrl
text_server_url.text = account.serverUrl
image_check.isInvisible = currentServerUrl != account.serverUrl
}
......
......@@ -4,7 +4,7 @@ import chat.rocket.android.analytics.AnalyticsManager
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.infraestructure.RocketChatClientFactory
import chat.rocket.android.server.infrastructure.RocketChatClientFactory
import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.retryIO
import chat.rocket.common.RocketChatException
......
......@@ -5,12 +5,14 @@ import chat.rocket.android.db.DatabaseManagerFactory
import chat.rocket.android.helper.UserHelper
import chat.rocket.android.main.presentation.MainNavigator
import chat.rocket.android.server.domain.AnalyticsTrackingInteractor
import chat.rocket.android.server.domain.GetCurrentLanguageInteractor
import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.domain.PermissionsInteractor
import chat.rocket.android.server.domain.RemoveAccountInteractor
import chat.rocket.android.server.domain.SaveCurrentLanguageInteractor
import chat.rocket.android.server.domain.TokenRepository
import chat.rocket.android.server.infraestructure.ConnectionManagerFactory
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.server.infrastructure.ConnectionManagerFactory
import chat.rocket.android.server.infrastructure.RocketChatClientFactory
import chat.rocket.android.server.presentation.CheckServerPresenter
import chat.rocket.android.util.extension.gethash
import chat.rocket.android.util.extension.launchUI
......@@ -41,7 +43,8 @@ class SettingsPresenter @Inject constructor(
getCurrentServerInteractor: GetCurrentServerInteractor,
removeAccountInteractor: RemoveAccountInteractor,
databaseManagerFactory: DatabaseManagerFactory,
connectionManagerFactory: ConnectionManagerFactory
connectionManagerFactory: ConnectionManagerFactory,
private val saveLanguageInteractor: SaveCurrentLanguageInteractor
) : CheckServerPresenter(
strategy = strategy,
factory = rocketChatClientFactory,
......@@ -53,6 +56,7 @@ class SettingsPresenter @Inject constructor(
tokenView = view,
navigator = navigator
) {
private val token = tokenRepository.get(currentServer)
fun setupView() {
launchUI(strategy) {
......@@ -67,7 +71,7 @@ class SettingsPresenter @Inject constructor(
userHelper.user()?.let { user ->
view.setupSettingsView(
currentServer.avatarUrl(me.username ?: ""),
currentServer.avatarUrl(me.username!!, token?.userId, token?.authToken),
userHelper.displayName(user) ?: me.username ?: "",
me.status.toString(),
permissions.isAdministrationEnabled(),
......@@ -123,6 +127,10 @@ class SettingsPresenter @Inject constructor(
}
}
fun saveLocale(language: String, country: String? = null) {
saveLanguageInteractor.save(language, country)
}
fun toProfile() = navigator.toProfile()
fun toAdmin() = tokenRepository.get(currentServer)?.let {
......
......@@ -17,6 +17,7 @@ import chat.rocket.android.BuildConfig
import chat.rocket.android.R
import chat.rocket.android.analytics.AnalyticsManager
import chat.rocket.android.analytics.event.ScreenViewEvent
import chat.rocket.android.core.behaviours.AppLanguageView
import chat.rocket.android.helper.TextHelper.getDeviceAndAppInformation
import chat.rocket.android.settings.presentation.SettingsPresenter
import chat.rocket.android.settings.presentation.SettingsView
......@@ -33,7 +34,7 @@ internal const val TAG_SETTINGS_FRAGMENT = "SettingsFragment"
fun newInstance(): Fragment = SettingsFragment()
class SettingsFragment : Fragment(), SettingsView {
class SettingsFragment : Fragment(), SettingsView, AppLanguageView {
@Inject lateinit var analyticsManager: AnalyticsManager
@Inject lateinit var presenter: SettingsPresenter
......@@ -74,7 +75,7 @@ class SettingsFragment : Fragment(), SettingsView {
text_contact_us.setOnClickListener { contactSupport() }
text_language.setOnClickListener {}
text_language.setOnClickListener { changeLanguage() }
text_review_this_app.setOnClickListener { showAppOnStore() }
......@@ -109,6 +110,11 @@ class SettingsFragment : Fragment(), SettingsView {
}
}
override fun updateLanguage(language: String, country: String?) {
presenter.saveLocale(language, country)
activity?.recreate()
}
override fun invalidateToken(token: String) = invalidateFirebaseToken(token)
override fun showLoading() {
......@@ -155,6 +161,38 @@ class SettingsFragment : Fragment(), SettingsView {
}
}
private fun changeLanguage() {
context?.let {
AlertDialog.Builder(it)
.setTitle(R.string.title_choose_language)
.setSingleChoiceItems(
resources.getStringArray(R.array.languages), -1
) { dialog, option ->
when (option) {
0 -> updateLanguage("en")
1 -> updateLanguage("ar")
2 -> updateLanguage("de")
3 -> updateLanguage("es")
4 -> updateLanguage("fa")
5 -> updateLanguage("fr")
6 -> updateLanguage("hi", "IN")
7 -> updateLanguage("it")
8 -> updateLanguage("ja")
9 -> updateLanguage("pt", "BR")
10 -> updateLanguage("pt", "PT")
11 -> updateLanguage("ru", "RU")
12 -> updateLanguage("tr")
13 -> updateLanguage("uk")
14 -> updateLanguage("zh", "CN")
15 -> updateLanguage("zh", "TW")
}
dialog.dismiss()
}
.create()
.show()
}
}
private fun showAppOnStore() {
try {
startActivity(Intent(Intent.ACTION_VIEW, getString(R.string.market_link).toUri()))
......
......@@ -8,12 +8,14 @@ import chat.rocket.android.db.model.ChatRoomEntity
import chat.rocket.android.db.model.UserEntity
import chat.rocket.android.server.domain.CurrentServerRepository
import chat.rocket.android.server.domain.GetSettingsInteractor
import chat.rocket.android.server.domain.TokenRepository
import chat.rocket.android.server.domain.isJitsiEnabled
import chat.rocket.android.server.infraestructure.ConnectionManagerFactory
import chat.rocket.android.server.infrastructure.ConnectionManagerFactory
import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.extensions.avatarUrl
import chat.rocket.android.util.retryIO
import chat.rocket.common.model.RoomType
import chat.rocket.common.model.Token
import chat.rocket.common.util.ifNull
import chat.rocket.core.internal.rest.createDirectMessage
import kotlinx.coroutines.Dispatchers
......@@ -26,6 +28,7 @@ class UserDetailsPresenter @Inject constructor(
private val dbManager: DatabaseManager,
private val strategy: CancelStrategy,
private val navigator: ChatRoomNavigator,
tokenRepository: TokenRepository,
settingsInteractor: GetSettingsInteractor,
serverInteractor: CurrentServerRepository,
factory: ConnectionManagerFactory
......@@ -35,6 +38,7 @@ class UserDetailsPresenter @Inject constructor(
private val client = manager.client
private val interactor = FetchChatRoomsInteractor(client, dbManager)
private val settings = settingsInteractor.get(currentServer)
private val token = tokenRepository.get(currentServer)
private lateinit var userEntity: UserEntity
fun loadUserDetails(userId: String) {
......@@ -44,7 +48,13 @@ class UserDetailsPresenter @Inject constructor(
dbManager.getUser(userId)?.let {
userEntity = it
val avatarUrl =
userEntity.username?.let { username -> currentServer.avatarUrl(avatar = username) }
userEntity.username?.let { username ->
currentServer.avatarUrl(
username,
token?.userId,
token?.authToken
)
}
val username = userEntity.username
val name = userEntity.name
val utcOffset =
......@@ -89,6 +99,7 @@ class UserDetailsPresenter @Inject constructor(
val chatRoomEntity = ChatRoomEntity(
id = directMessage.id,
name = userEntity.username ?: userEntity.name.orEmpty(),
parentId = null,
description = null,
type = RoomType.DIRECT_MESSAGE,
fullname = userEntity.name,
......
......@@ -2,7 +2,7 @@ package chat.rocket.android.util.extensions
import chat.rocket.android.db.model.MessageEntity
import chat.rocket.android.server.domain.model.Account
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.server.infrastructure.RocketChatClientFactory
import chat.rocket.android.util.retryIO
import chat.rocket.core.internal.rest.registerPushToken
import chat.rocket.core.model.Message
......
......@@ -21,13 +21,15 @@ fun String.sanitize(): String {
fun String.avatarUrl(
avatar: String,
userId: String?,
token: String?,
isGroupOrChannel: Boolean = false,
format: String = "jpeg"
): String {
return if (isGroupOrChannel) {
"${removeTrailingSlash()}/avatar/%23${avatar.removeTrailingSlash()}?format=$format"
"${removeTrailingSlash()}/avatar/%23${avatar.removeTrailingSlash()}?format=$format&rc_uid=$userId&rc_token=$token"
} else {
"${removeTrailingSlash()}/avatar/${avatar.removeTrailingSlash()}?format=$format"
"${removeTrailingSlash()}/avatar/${avatar.removeTrailingSlash()}?format=$format&rc_uid=$userId&rc_token=$token"
}
}
......
......@@ -6,7 +6,7 @@ import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.helper.JitsiHelper
import chat.rocket.android.helper.UserHelper
import chat.rocket.android.server.domain.*
import chat.rocket.android.server.infraestructure.ConnectionManagerFactory
import chat.rocket.android.server.infrastructure.ConnectionManagerFactory
import chat.rocket.android.util.extension.launchUI
import chat.rocket.common.model.RoomType
import chat.rocket.common.model.roomTypeOf
......
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="20dp"
android:height="20dp"
android:viewportWidth="20"
android:viewportHeight="20">
<path
android:fillColor="#9EA2A8"
android:fillType="nonZero"
android:pathData="M17.0178,9.0088C17.4002,8.2308 17.479,7.9789 17.479,7.6459C17.479,4.8323 15.0667,2.5397 12.1027,2.5397C9.1391,2.5397 6.7273,4.832 6.7273,7.6459C6.7273,10.6045 9.2399,12.7514 12.8986,12.7514L13.0419,12.7522L13.2199,12.7522C15.7971,12.7522 17.3253,12.49 17.9955,12.099C17.4393,11.8181 17.312,11.7402 17.0644,11.4817C16.7269,11.1295 16.5447,10.6642 16.5903,10.1635C16.6148,9.8935 16.6686,9.7447 16.7751,9.5219C16.8362,9.3843 16.8928,9.2651 17.0178,9.0088ZM18.3011,10.1607C18.1916,10.3871 18.2091,10.4113 18.8615,10.7397C19.8228,11.2213 20.24,11.6696 19.8612,12.4352C19.1566,13.8599 16.8659,14.3395 13.2199,14.3395L13.0419,14.3395L12.8892,14.3387C8.3595,14.3387 5.0607,11.5237 5.0607,7.6459C5.0607,3.9545 8.2195,0.9524 12.1027,0.9524C15.9862,0.9524 19.1457,3.9547 19.1457,7.6459C19.1457,8.2881 18.979,8.7628 18.5273,9.6817C18.4105,9.9212 18.3589,10.0296 18.3011,10.1607ZM13.5847,16.3438C13.4018,16.575 13.2033,16.7931 12.9903,16.9977C12.7079,16.9977 12.4278,16.9894 12.1501,16.973L12.251,15.4273C12.2578,15.4187 12.2646,15.4102 12.2714,15.4016L13.5847,16.3438ZM12.2535,15.3888L12.2714,15.4016C12.2744,15.3979 12.2774,15.3941 12.2804,15.3903C12.5148,15.4037 12.7523,15.4104 12.9928,15.4104L13.1738,15.4114L13.3848,15.4114C13.6721,15.4114 13.9523,15.4089 14.2253,15.4036C13.9139,15.9928 13.5075,16.528 13.0195,16.9979L12.9928,16.9977C12.9919,16.9977 12.9911,16.9977 12.9903,16.9977C11.5566,18.3752 9.469,19.1391 7.111,19.1391L6.9581,19.1399L6.7801,19.1399C4.1004,19.1399 2.2612,18.8789 1.204,18.2903L0.9763,18.1635C0.5978,17.9163 0.3174,17.6159 0.1388,17.2549C-0.1741,16.6227 0.056,16.2068 0.6871,15.8098L1.263,15.5183C1.6568,15.319 1.6328,15.3349 1.7194,15.2446C1.7599,15.2023 1.7736,15.1649 1.7694,15.1185C1.7642,15.0606 1.772,15.0866 1.7081,14.9517C1.6576,14.8377 1.6095,14.736 1.491,14.4933C1.0219,13.5389 0.8746,13.0988 0.8746,12.4656C0.8746,11.0845 1.3214,9.7648 2.1303,8.6606C2.129,8.6512 2.1278,8.6417 2.1265,8.6322C2.5652,8.0375 3.1029,7.5134 3.7168,7.0817C3.713,7.1803 3.7112,7.2792 3.7112,7.3786C3.7112,7.962 3.7726,8.5253 3.8907,9.0644C3.8873,9.068 3.8839,9.0717 3.8805,9.0753L3.896,9.0884L3.8618,9.0953C3.0102,10.0132 2.521,11.2015 2.521,12.4656C2.521,12.7985 2.5998,13.0505 2.9842,13.8326C3.1146,14.0999 3.1702,14.2175 3.2126,14.3161C3.3274,14.5534 3.3842,14.703 3.4097,14.9832C3.4553,15.4839 3.2731,15.9492 2.9356,16.3014C2.6884,16.5596 2.561,16.6376 2.0044,16.9188C2.6743,17.3097 4.2025,17.5719 6.7801,17.5719L6.9487,17.5719L7.1108,17.5711C9.3389,17.5711 11.1786,16.7606 12.251,15.4273L12.2535,15.3888ZM1.9145,16.9641C1.6344,17.1044 1.5191,17.1878 1.5466,17.1462C1.6888,16.9311 1.6902,16.6667 1.6467,16.5786C1.7065,16.6996 1.8245,16.8138 2.0044,16.9188C1.9756,16.9333 1.9457,16.9484 1.9145,16.9641ZM2.2655,9.4174C2.2101,9.1681 2.165,8.9157 2.1303,8.6606C2.2904,8.4422 2.4646,8.2321 2.6523,8.0317L3.8805,9.0753C3.8743,9.0819 3.868,9.0886 3.8618,9.0953L2.2655,9.4174ZM2.8863,15.5497L2.0452,16.92L2.8273,18.3216C3.5014,17.9804 3.7692,17.8068 4.1368,17.4308C4.8511,17.5089 5.7278,17.5526 6.7801,17.5526L6.9581,17.5526L7.1013,17.5518C9.3708,17.5518 11.1972,16.724 12.2535,15.3888C12.4966,15.4031 12.7431,15.4104 12.9928,15.4104L13.1738,15.4114L13.3848,15.4114C13.6721,15.4114 13.9523,15.4089 14.2253,15.4036C13.0193,17.6858 10.387,19.1584 7.1108,19.1584L6.9581,19.1592L6.7801,19.1592C3.1341,19.1592 0.8434,18.6796 0.1388,17.2549C-0.24,16.4893 0.1772,16.041 1.1385,15.5594C1.7909,15.231 1.8084,15.2068 1.6989,14.9803C1.6411,14.8493 1.5895,14.7409 1.4727,14.5014C1.021,13.5824 0.8543,13.1078 0.8543,12.4656C0.8543,10.2609 1.9815,8.302 3.7168,7.0817C3.713,7.1803 3.7112,7.2792 3.7112,7.3786C3.7112,7.9706 3.7745,8.542 3.896,9.0884C3.0537,9.9876 2.5412,11.1713 2.5412,12.4656C2.5412,12.7951 2.6193,13.0447 3.0005,13.8203C3.1256,14.0767 3.1822,14.1961 3.2434,14.3338C3.3508,14.5585 3.405,14.7087 3.4299,14.9815C3.4528,15.2336 3.4186,15.4768 3.3365,15.7014C3.1322,15.6538 2.9811,15.6024 2.8863,15.5497Z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>
\ No newline at end of file
......@@ -13,14 +13,15 @@
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_height="0dp"
android:layout_below="@+id/layout_app_bar"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@+id/layout_app_bar">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp">
android:paddingTop="20dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/profile_container"
......
......@@ -95,7 +95,7 @@
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/screen_edge_left_and_right_margins"
android:layout_marginTop="20dp"
android:text="@string/status"
android:text="@string/user_detail_status"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_message" />
......
......@@ -8,21 +8,22 @@
android:paddingStart="@dimen/screen_edge_left_and_right_padding"
android:paddingTop="@dimen/chat_item_top_and_bottom_padding"
android:paddingEnd="@dimen/screen_edge_left_and_right_padding"
android:paddingBottom="@dimen/chat_item_top_and_bottom_padding">
android:paddingBottom="@dimen/chat_item_top_and_bottom_padding"
tools:context=".chatrooms.adapter.RoomsAdapter">
<com.facebook.drawee.view.SimpleDraweeView
<ImageView
android:id="@+id/image_avatar"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginTop="5dp"
android:layout_marginTop="2dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:roundedCornerRadius="4dp" />
tools:src="@tools:sample/avatars" />
<ImageView
android:id="@+id/image_chat_icon"
android:layout_width="12dp"
android:layout_height="0dp"
android:layout_height="12dp"
android:layout_marginStart="16dp"
app:layout_constraintBottom_toBottomOf="@+id/text_chat_name"
app:layout_constraintStart_toEndOf="@+id/image_avatar"
......@@ -35,6 +36,7 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/text_view_drawable_padding"
android:layout_marginEnd="@dimen/text_view_drawable_padding"
android:textDirection="locale"
app:layout_constraintEnd_toStartOf="@+id/text_timestamp"
app:layout_constraintStart_toEndOf="@+id/image_chat_icon"
......
......@@ -21,6 +21,7 @@
<string name="title_password">تغيير كلمة السر</string>
<string name="title_update_profile">تحديث الملف الشخصي</string>
<string name="title_create_channel">إنشاء قناة</string>
<string name="title_choose_language">Choose Language</string> <!-- TODO Add translation -->
<string name="title_license">الترخيص</string>
<string name="title_are_you_sure">هل أنت متأكد؟</string>
<string name="title_channel_details">تفاصيل القناة</string>
......@@ -74,6 +75,25 @@
<string name="msg_delete_account">Delete account</string> <!-- TODO Translate -->
<string name="msg_change_status">Change status</string> <!-- TODO Translate -->
<string-array name="languages"> <!-- TODO Add translations -->
<item>English</item>
<item>Arabic</item>
<item>German</item>
<item>Spanish</item>
<item>Persian</item>
<item>French</item>
<item>Hindi (IN)</item>
<item>Italian</item>
<item>Japanese</item>
<item>Portuguese (BR)</item>
<item>Portuguese (PT)</item>
<item>Russian (RU)</item>
<item>Turkish</item>
<item>Ukrainian</item>
<item>Chinese (CN)</item>
<item>Chinese (TW)</item>
</string-array>
<!-- Regular information messages -->
<string name="msg_generic_error">نأسف حدث خطأ ما حاول مرة أخرى</string>
<string name="msg_no_data_to_display">لا يوجد بيانات للعرض</string>
......
......@@ -9,6 +9,7 @@
<string name="title_authentication">Login Daten prüfen</string>
<string name="title_legal_terms">Legal Terms</string>
<string name="title_chats">Chats</string>
<string name="title_choose_language">Choose Language</string> <!-- TODO Add translation -->
<string name="title_profile">Profil</string>
<string name="title_members">Benutzer</string>
<string name="title_counted_members">Benutzer (%d)</string>
......@@ -32,7 +33,7 @@
<string name="action_use_this_username">Benutze den Benutzernamen</string>
<string name="action_terms_of_service">Nutzungsbedingungen</string>
<string name="action_privacy_policy">Datenschutz</string>
<string name="action_new_channel">New channel</string> <!-- TODO Translate -->
<string name="action_new_channel">Neuer Kanal</string>
<string name="action_search">Suche</string>
<string name="action_update">Updaten</string>
<string name="action_settings">Einstellungen</string>
......@@ -60,18 +61,37 @@
<string name="action_remove_favorite">Favoriten entfernen</string>
<!-- Settings messages -->
<string name="msg_contact_us">Contact us</string> <!-- TODO Translate -->
<string name="msg_language">Language</string> <!-- TODO Translate -->
<string name="msg_review_this_app">Review this app</string> <!-- TODO Translate -->
<string name="msg_share_this_app">Share this app</string> <!-- TODO Translate -->
<string name="msg_administration">Administration</string> <!-- TODO Translate -->
<string name="msg_license">License</string> <!-- TODO Translate -->
<string name="msg_app_version">Version: %1$s (%2$d)</string> <!-- TODO Translate -->
<string name="msg_server_version">Server version: %1$s</string> <!-- TODO Translate -->
<string name="msg_send_analytics">Send analytics</string> <!-- TODO Translate -->
<string name="msg_logout_from_rocket_chat">Logout from Rocket.Chat</string> <!-- TODO Translate -->
<string name="msg_delete_account">Delete account</string> <!-- TODO Translate -->
<string name="msg_change_status">Change status</string> <!-- TODO Translate -->
<string name="msg_contact_us">Kontaktiere uns</string>
<string name="msg_language">Sprache</string>
<string name="msg_review_this_app">Bewerte diese App</string>
<string name="msg_share_this_app">Teile diese App</string>
<string name="msg_administration">Administration</string>
<string name="msg_license">Lizenz</string>
<string name="msg_app_version">Version: %1$s (%2$d)</string>
<string name="msg_server_version">Serverversion: %1$s</string>
<string name="msg_send_analytics">Analysesaten senden</string>
<string name="msg_logout_from_rocket_chat">Logout von Rocket.Chat</string>
<string name="msg_delete_account">Konto löschen</string>
<string name="msg_change_status">Status ändern</string>
<string-array name="languages"> <!-- TODO Add translations -->
<item>English</item>
<item>Arabic</item>
<item>German</item>
<item>Spanish</item>
<item>Persian</item>
<item>French</item>
<item>Hindi (IN)</item>
<item>Italian</item>
<item>Japanese</item>
<item>Portuguese (BR)</item>
<item>Portuguese (PT)</item>
<item>Russian (RU)</item>
<item>Turkish</item>
<item>Ukrainian</item>
<item>Chinese (CN)</item>
<item>Chinese (TW)</item>
</string-array>
<!-- Regular information messages -->
<string name="msg_generic_error">Entschuldigung, ein Fehler ist aufgetreten, bitte versuchen Sie es noch einmal.</string>
......@@ -172,11 +192,11 @@
<item quantity="other">%1$s reagierte mit %2$s</item>
</plurals>
<string name="msg_credentials_saved_successfully">Login-Daten erfolgreich gespeichert</string>
<string name="msg_camera_permission_denied">Camera permission is needed to open camera.</string> <!-- TODO Add translation -->
<string name="msg_storage_permission_denied">Storage permission is needed to open Drawing.</string> <!-- TODO Add translation -->
<string name="msg_server">Server</string> <!-- TODO Translate -->
<string name="msg_add_new_server">Add New Server</string> <!-- TODO Translate -->
<string name="msg_directory">Directory</string> <!-- TODO Translate -->
<string name="msg_camera_permission_denied">Zum Öffnen der Kamera ist eine Kamera-Berechtigung erforderlich.</string>
<string name="msg_storage_permission_denied">Zum Öffnen der Zeichnung ist eine Speicher-Berechtigung erforderlich.</string>
<string name="msg_server">Server</string>
<string name="msg_add_new_server">Neuen Server hinzufügen</string>
<string name="msg_directory">Verzeichnis</string>
<!-- Create channel messages -->
<string name="msg_private_channel">Privat</string>
......@@ -314,20 +334,20 @@
<string name="alert_title_default_skin_tone">Standard Hautton</string>
<!-- Sort and group -->
<string name="msg_sort_by_placeholder">Sort by %1$s</string> <!-- TODO Translate -->
<string name="msg_sort_by">Sort by</string> <!-- TODO Translate -->
<string name="msg_sort_by_activity">Activity</string> <!-- TODO Translate -->
<string name="msg_sort_by_name">Name</string> <!-- TODO Translate -->
<string name="msg_group_by_unread_on_top">Unread on top</string> <!-- TODO Translate -->
<string name="msg_group_by_type">Group by type</string> <!-- TODO Translate -->
<string name="msg_group_by_favorites">Group by favorites</string> <!-- TODO Translate -->
<string name="msg_sort_by_placeholder">Sortiere nach %1$s</string>
<string name="msg_sort_by">Sortiert nach</string>
<string name="msg_sort_by_activity">Aktivität</string>
<string name="msg_sort_by_name">Name</string>
<string name="msg_group_by_unread_on_top">Ungelesen oben</string>
<string name="msg_group_by_type">Gruppieren nach Typ</string>
<string name="msg_group_by_favorites">Gruppieren nach Favoriten</string>
<!--ChatRooms Headers-->
<string name="header_favorite">Favoriten</string>
<string name="msg_channels">Räume</string>
<string name="msg_users">Users</string> <!-- TODO Translate -->
<string name="msg_search_for_global_users">Search for global users</string> <!-- TODO Translate -->
<string name="msg_search_for_global_users_description">If you turn on, you can search for any user from others companies or servers.</string> <!-- TODO Translate -->
<string name="msg_users">Benutzer</string>
<string name="msg_search_for_global_users">Suche nach globalen Benutzern</string>
<string name="msg_search_for_global_users_description">Wenn Sie dies einschalten, können Sie nach Benutzern von anderen Unternehmen oder Servern suchen.</string>
<string name="header_private_groups">Private Räume</string>
<string name="header_direct_messages">Direkt Nachrichten</string>
<string name="header_live_chats">Live Chats</string>
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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