Unverified Commit 73d09b24 authored by Filipe de Lima Brito's avatar Filipe de Lima Brito Committed by GitHub

Merge pull request #1108 from RocketChat/new/active-users

[NEW] Show user status in the chat list.
parents 3e457b9d 0b789dc0
......@@ -13,8 +13,8 @@ android {
applicationId "chat.rocket.android"
minSdkVersion 21
targetSdkVersion versions.targetSdk
versionCode 2014
versionName "2.0.4"
versionCode 2015
versionName "2.1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
multiDexEnabled true
}
......@@ -47,10 +47,6 @@ android {
packagingOptions {
exclude 'META-INF/core.kotlin_module'
}
lintOptions{
disable 'MissingTranslation'
}
}
dependencies {
......
......@@ -108,18 +108,12 @@ object DrawableHelper {
* @see [UserStatus]
* @return The user status drawable.
*/
fun getUserStatusDrawable(userStatus: UserStatus, context: Context): Drawable {
fun getUserStatusDrawable(userStatus: UserStatus?, context: Context): Drawable {
return when (userStatus) {
is UserStatus.Online -> {
getDrawableFromId(R.drawable.ic_status_online_24dp, context)
}
is UserStatus.Away -> {
getDrawableFromId(R.drawable.ic_status_away_24dp, context)
}
is UserStatus.Busy -> {
getDrawableFromId(R.drawable.ic_status_busy_24dp, context)
}
else -> getDrawableFromId(R.drawable.ic_status_invisible_24dp, context)
is UserStatus.Online -> getDrawableFromId(R.drawable.ic_status_online_12dp, context)
is UserStatus.Away -> getDrawableFromId(R.drawable.ic_status_away_12dp, context)
is UserStatus.Busy -> getDrawableFromId(R.drawable.ic_status_busy_12dp, context)
else -> getDrawableFromId(R.drawable.ic_status_invisible_12dp, context)
}
}
}
\ No newline at end of file
......@@ -510,7 +510,7 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
fun loadChatRooms() {
launchUI(strategy) {
try {
val chatRooms = getChatRoomsInteractor.get(currentServer)
val chatRooms = getChatRoomsInteractor.getAll(currentServer)
.filterNot {
it.type is RoomType.DirectMessage || it.type is RoomType.Livechat
}
......
......@@ -18,6 +18,7 @@ import chat.rocket.common.model.BaseRoom
import chat.rocket.common.model.RoomType
import chat.rocket.common.model.SimpleUser
import chat.rocket.common.model.User
import chat.rocket.common.util.ifNull
import chat.rocket.core.internal.model.Subscription
import chat.rocket.core.internal.realtime.socket.model.State
import chat.rocket.core.internal.realtime.socket.model.StreamMessage
......@@ -32,26 +33,29 @@ import timber.log.Timber
import javax.inject.Inject
import kotlin.reflect.KProperty1
class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
class ChatRoomsPresenter @Inject constructor(
private val view: ChatRoomsView,
private val strategy: CancelStrategy,
private val navigator: MainNavigator,
private val serverInteractor: GetCurrentServerInteractor,
private val getChatRoomsInteractor: GetChatRoomsInteractor,
private val saveChatRoomsInteractor: SaveChatRoomsInteractor,
private val saveActiveUsersInteractor: SaveActiveUsersInteractor,
private val getActiveUsersInteractor: GetActiveUsersInteractor,
private val refreshSettingsInteractor: RefreshSettingsInteractor,
private val viewModelMapper: ViewModelMapper,
private val jobSchedulerInteractor: JobSchedulerInteractor,
settingsRepository: SettingsRepository,
factory: ConnectionManagerFactory) {
factory: ConnectionManagerFactory
) {
private val manager: ConnectionManager = factory.create(serverInteractor.get()!!)
private val currentServer = serverInteractor.get()!!
private val client = manager.client
private var reloadJob: Deferred<List<ChatRoom>>? = null
private val settings = settingsRepository.get(currentServer)
private val subscriptionsChannel = Channel<StreamMessage<BaseRoom>>()
private val stateChannel = Channel<State>()
private val subscriptionsChannel = Channel<StreamMessage<BaseRoom>>()
private val activeUserChannel = Channel<User>()
private var lastState = manager.state
fun loadChatRooms() {
......@@ -60,13 +64,18 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
view.showLoading()
subscribeStatusChange()
try {
view.updateChatRooms(loadRooms())
} catch (e: RocketChatException) {
Timber.e(e)
view.showMessage(e.message!!)
view.updateChatRooms(getUserChatRooms())
} catch (ex: RocketChatException) {
ex.message?.let {
view.showMessage(it)
}.ifNull {
view.showGenericErrorMessage()
}
Timber.e(ex)
} finally {
view.hideLoading()
}
subscribeActiveUsers()
subscribeRoomUpdates()
}
}
......@@ -94,7 +103,7 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
val currentServer = serverInteractor.get()!!
launchUI(strategy) {
try {
val roomList = getChatRoomsInteractor.getByName(currentServer, name)
val roomList = getChatRoomsInteractor.getAllByName(currentServer, name)
if (roomList.isEmpty()) {
val (users, rooms) = retryIO("spotlight($name)") {
client.spotlight(name)
......@@ -102,9 +111,13 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
val chatRoomsCombined = mutableListOf<ChatRoom>()
chatRoomsCombined.addAll(usersToChatRooms(users))
chatRoomsCombined.addAll(roomsToChatRooms(rooms))
view.updateChatRooms(getChatRoomsWithPreviews(chatRoomsCombined.toList()))
val chatRoomsWithPreview = getChatRoomsWithPreviews(chatRoomsCombined)
val chatRoomsWithStatus = getChatRoomWithStatus(chatRoomsWithPreview)
view.updateChatRooms(chatRoomsWithStatus)
} else {
view.updateChatRooms(getChatRoomsWithPreviews(roomList))
val chatRoomsWithPreview = getChatRoomsWithPreviews(roomList)
val chatRoomsWithStatus = getChatRoomWithStatus(chatRoomsWithPreview)
view.updateChatRooms(chatRoomsWithStatus)
}
} catch (ex: RocketChatException) {
Timber.e(ex)
......@@ -112,13 +125,32 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
}
}
private suspend fun usersToChatRooms(users: List<User>): List<ChatRoom> {
// In the first time it will not come with the users status, but after called by the
// [reloadRooms] function may be with.
private suspend fun getUserChatRooms(): List<ChatRoom> {
val chatRooms = retryIO("chatRooms") { manager.chatRooms().update }
val chatRoomsWithPreview = getChatRoomsWithPreviews(chatRooms)
val chatRoomsWithUserStatus = getChatRoomWithStatus(chatRoomsWithPreview)
val sortedRooms = sortRooms(chatRoomsWithUserStatus)
Timber.d("Loaded rooms: ${sortedRooms.size}")
saveChatRoomsInteractor.save(currentServer, sortedRooms)
return sortedRooms
}
private fun usersToChatRooms(users: List<User>): List<ChatRoom> {
return users.map {
ChatRoom(id = it.id,
ChatRoom(
id = it.id,
type = RoomType.DIRECT_MESSAGE,
user = SimpleUser(username = it.username, name = it.name, id = null),
status = if (it.name != null) {
getActiveUsersInteractor.getActiveUserByUsername(currentServer, it.name!!)
?.status
} else {
null
},
name = it.name ?: "",
status = null,
fullName = it.name,
readonly = false,
updatedAt = null,
......@@ -139,13 +171,19 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
}
}
private suspend fun roomsToChatRooms(rooms: List<Room>): List<ChatRoom> {
private fun roomsToChatRooms(rooms: List<Room>): List<ChatRoom> {
return rooms.map {
ChatRoom(id = it.id,
ChatRoom(
id = it.id,
type = it.type,
user = it.user,
status = if (it.name != null) {
getActiveUsersInteractor.getActiveUserByUsername(currentServer, it.name!!)
?.status
} else {
null
},
name = it.name ?: "",
status = null,
fullName = it.fullName,
readonly = it.readonly,
updatedAt = it.updatedAt,
......@@ -166,18 +204,9 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
}
}
private suspend fun loadRooms(): List<ChatRoom> {
val chatRooms = retryIO("chatRooms") { manager.chatRooms().update }
val sortedRooms = sortRooms(chatRooms)
Timber.d("Loaded rooms: ${sortedRooms.size}")
saveChatRoomsInteractor.save(currentServer, sortedRooms)
return getChatRoomsWithPreviews(sortedRooms)
}
fun updateSortedChatRooms() {
val currentServer = serverInteractor.get()!!
launchUI(strategy) {
val roomList = getChatRoomsInteractor.get(currentServer)
val roomList = getChatRoomsInteractor.getAll(currentServer)
view.updateChatRooms(sortRooms(roomList))
}
}
......@@ -223,11 +252,39 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
}
}
private fun updateRooms() {
Timber.d("Updating Rooms")
launch(strategy.jobs) {
view.updateChatRooms(getChatRoomsWithPreviews(getChatRoomsInteractor.get(currentServer)))
private fun getChatRoomWithStatus(chatRooms: List<ChatRoom>): List<ChatRoom> {
val chatRoomsList = mutableListOf<ChatRoom>()
chatRooms.forEach {
val newRoom = ChatRoom(
id = it.id,
type = it.type,
user = it.user,
status = getActiveUsersInteractor.getActiveUserByUsername(
currentServer,
it.name
)?.status,
name = it.name,
fullName = it.fullName,
readonly = it.readonly,
updatedAt = it.updatedAt,
timestamp = it.timestamp,
lastSeen = it.lastSeen,
topic = it.topic,
description = it.description,
announcement = it.announcement,
default = it.default,
favorite = it.favorite,
open = it.open,
alert = it.alert,
unread = it.unread,
userMenstions = it.userMenstions,
groupMentions = it.groupMentions,
lastMessage = it.lastMessage,
client = client
)
chatRoomsList.add(newRoom)
}
return chatRoomsList
}
private suspend fun getChatRoomsWithPreviews(chatRooms: List<ChatRoom>): List<ChatRoom> {
......@@ -244,12 +301,6 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
return chatRooms.filter(ChatRoom::open)
}
private fun sortChatRooms(chatRooms: List<ChatRoom>): List<ChatRoom> {
return chatRooms.sortedByDescending { chatRoom ->
chatRoom.lastMessage?.timestamp
}
}
private suspend fun subscribeStatusChange() {
lastState = manager.state
launch(CommonPool + strategy.jobs) {
......@@ -259,11 +310,10 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
launch(UI) {
view.showConnectionState(state)
}
if (state is State.Connected) {
jobSchedulerInteractor.scheduleSendingMessages()
reloadRooms()
updateRooms()
updateChatRooms()
}
}
lastState = state
......@@ -303,7 +353,7 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
}
}
updateRooms()
updateChatRooms()
}
private suspend fun updateSubscription(message: StreamMessage<Subscription>) {
......@@ -322,7 +372,7 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
}
}
updateRooms()
updateChatRooms()
}
private suspend fun reloadRooms() {
......@@ -333,7 +383,7 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
reloadJob = async(CommonPool + strategy.jobs) {
delay(1000)
Timber.d("reloading rooms after wait")
loadRooms()
getUserChatRooms()
}
reloadJob?.await()
} catch (ex: Exception) {
......@@ -344,14 +394,18 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
// Update a ChatRoom with a Room information
private fun updateRoom(room: Room) {
Timber.d("Updating Room: ${room.id} - ${room.name}")
val chatRooms = getChatRoomsInteractor.get(currentServer).toMutableList()
val chatRooms = getChatRoomsInteractor.getAll(currentServer).toMutableList()
val chatRoom = chatRooms.find { chatRoom -> chatRoom.id == room.id }
chatRoom?.apply {
val newRoom = ChatRoom(id = room.id,
val newRoom = ChatRoom(
id = room.id,
type = room.type,
user = room.user ?: user,
status = getActiveUsersInteractor.getActiveUserByUsername(
currentServer,
room.name ?: name
)?.status,
name = room.name ?: name,
status = null,
fullName = room.fullName ?: fullName,
readonly = room.readonly,
updatedAt = room.updatedAt ?: updatedAt,
......@@ -368,7 +422,8 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
userMenstions = userMenstions,
groupMentions = groupMentions,
lastMessage = room.lastMessage,
client = client)
client = client
)
removeRoom(room.id, chatRooms)
chatRooms.add(newRoom)
saveChatRoomsInteractor.save(currentServer, sortRooms(chatRooms))
......@@ -378,14 +433,18 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
// Update a ChatRoom with a Subscription information
private fun updateSubscription(subscription: Subscription) {
Timber.d("Updating subscription: ${subscription.id} - ${subscription.name}")
val chatRooms = getChatRoomsInteractor.get(currentServer).toMutableList()
val chatRooms = getChatRoomsInteractor.getAll(currentServer).toMutableList()
val chatRoom = chatRooms.find { chatRoom -> chatRoom.id == subscription.roomId }
chatRoom?.apply {
val newRoom = ChatRoom(id = subscription.roomId,
val newRoom = ChatRoom(
id = subscription.roomId,
type = subscription.type,
user = subscription.user ?: user,
status = getActiveUsersInteractor.getActiveUserByUsername(
currentServer,
subscription.name
)?.status,
name = subscription.name,
status = null,
fullName = subscription.fullName ?: fullName,
readonly = subscription.readonly ?: readonly,
updatedAt = subscription.updatedAt ?: updatedAt,
......@@ -402,16 +461,18 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
userMenstions = subscription.userMentions,
groupMentions = subscription.groupMentions,
lastMessage = lastMessage,
client = client)
client = client
)
removeRoom(subscription.roomId, chatRooms)
chatRooms.add(newRoom)
saveChatRoomsInteractor.save(currentServer, sortRooms(chatRooms))
}
}
private fun removeRoom(id: String,
chatRooms: MutableList<ChatRoom> = getChatRoomsInteractor.get(currentServer).toMutableList()) {
private fun removeRoom(
id: String,
chatRooms: MutableList<ChatRoom> = getChatRoomsInteractor.getAll(currentServer).toMutableList()
) {
Timber.d("Removing ROOM: $id")
synchronized(this) {
chatRooms.removeAll { chatRoom -> chatRoom.id == id }
......@@ -419,8 +480,87 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
saveChatRoomsInteractor.save(currentServer, sortRooms(chatRooms))
}
private suspend fun subscribeActiveUsers() {
manager.addActiveUserChannel(activeUserChannel)
launch(CommonPool + strategy.jobs) {
for (user in activeUserChannel) {
processActiveUser(user)
}
}
}
private fun processActiveUser(user: User) {
// The first activeUsers stream contains all details of the users (username, UTC Offset,
// etc.), so we add each user to our [saveActiveUsersInteractor] class because the following
// streams don't contain those details.
if (!getActiveUsersInteractor.isActiveUserOnRepository(currentServer, user)) {
Timber.d("Got first active user stream for the user: $user")
saveActiveUsersInteractor.addActiveUser(currentServer, user)
} else {
// After the first stream the next is about the active users updates.
Timber.d("Got update of active user stream for the user: $user")
saveActiveUsersInteractor.updateActiveUser(currentServer, user)
}
getActiveUsersInteractor.getActiveUserById(currentServer, user.id)?.let {
updateChatRoomWithUserStatus(it)
}
}
private fun updateChatRoomWithUserStatus(user_: User) {
Timber.d("active User: $user_")
val username = user_.username
val status = user_.status
if (username != null && status != null) {
getChatRoomsInteractor.getByName(currentServer, username)?.let {
val newRoom = ChatRoom(
id = it.id,
type = it.type,
user = it.user,
status = status,
name = it.name,
fullName = it.fullName,
readonly = it.readonly,
updatedAt = it.updatedAt,
timestamp = it.timestamp,
lastSeen = it.lastSeen,
topic = it.topic,
description = it.description,
announcement = it.announcement,
default = it.default,
favorite = it.favorite,
open = it.open,
alert = it.alert,
unread = it.unread,
userMenstions = it.userMenstions,
groupMentions = it.groupMentions,
lastMessage = it.lastMessage,
client = client
)
getChatRoomsInteractor.remove(currentServer, it)
getChatRoomsInteractor.add(currentServer, newRoom)
launchUI(strategy) {
view.updateChatRooms(sortRooms(getChatRoomsInteractor.getAll(currentServer)))
}
}
}
}
private fun updateChatRooms() {
Timber.i("Updating ChatRooms")
launch(strategy.jobs) {
val chatRoomsWithPreview = getChatRoomsWithPreviews(
getChatRoomsInteractor.getAll(currentServer)
)
val chatRoomsWithStatus = getChatRoomWithStatus(chatRoomsWithPreview)
view.updateChatRooms(chatRoomsWithStatus)
}
}
fun disconnect() {
manager.removeStatusChannel(stateChannel)
manager.removeRoomsAndSubscriptionsChannel(subscriptionsChannel)
manager.removeActiveUserChannel(activeUserChannel)
}
}
\ No newline at end of file
......@@ -10,6 +10,7 @@ import android.text.SpannableStringBuilder
import android.text.style.ForegroundColorSpan
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import chat.rocket.android.R
import chat.rocket.android.infrastructure.LocalRepository
......@@ -49,6 +50,7 @@ class ChatRoomsAdapter(private val context: Context,
fun bind(chatRoom: ChatRoom) = with(itemView) {
bindAvatar(chatRoom, image_avatar)
bindName(chatRoom, text_chat_name)
bindIcon(chatRoom, image_chat_icon)
bindLastMessageDateTime(chatRoom, text_last_message_date_time)
bindLastMessage(chatRoom, text_last_message)
bindUnreadMessages(chatRoom, text_total_unread_messages)
......@@ -80,32 +82,37 @@ class ChatRoomsAdapter(private val context: Context,
}
}
private fun bindName(chatRoom: ChatRoom, textView: TextView) {
textView.textContent = chatRoom.name
private fun bindIcon(chatRoom: ChatRoom, imageView: ImageView) {
val drawable = when (chatRoom.type) {
is RoomType.Channel -> {
DrawableHelper.getDrawableFromId(R.drawable.ic_room_channel, context)
}
is RoomType.PrivateGroup -> {
DrawableHelper.getDrawableFromId(R.drawable.ic_room_lock, context)
}
is RoomType.DirectMessage -> {
DrawableHelper.getDrawableFromId(R.drawable.ic_room_dm, context)
}
is RoomType.Channel -> DrawableHelper.getDrawableFromId(
R.drawable.ic_hashtag_12dp,
context
)
is RoomType.PrivateGroup -> DrawableHelper.getDrawableFromId(
R.drawable.ic_lock_12_dp,
context
)
is RoomType.DirectMessage -> DrawableHelper.getUserStatusDrawable(
chatRoom.status,
context
)
else -> null
}
drawable?.let {
val wrappedDrawable = DrawableHelper.wrapDrawable(it)
val mutableDrawable = wrappedDrawable.mutate()
val mutateDrawable = DrawableHelper.wrapDrawable(it).mutate()
if (chatRoom.type !is RoomType.DirectMessage) {
val color = when (chatRoom.alert || chatRoom.unread > 0) {
true -> R.color.colorPrimaryText
false -> R.color.colorSecondaryText
}
DrawableHelper.tintDrawable(mutableDrawable, context, color)
DrawableHelper.compoundDrawable(textView, mutableDrawable)
DrawableHelper.tintDrawable(mutateDrawable, context, color)
}
imageView.setImageDrawable(mutateDrawable)
}
}
private fun bindName(chatRoom: ChatRoom, textView: TextView) {
textView.textContent = chatRoom.name
}
private fun bindLastMessageDateTime(chatRoom: ChatRoom, textView: TextView) {
......
......@@ -32,12 +32,9 @@ import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_chat_rooms.*
import kotlinx.coroutines.experimental.Job
import kotlinx.coroutines.experimental.NonCancellable.isActive
import kotlinx.coroutines.experimental.android.UI
import kotlinx.coroutines.experimental.launch
import timber.log.Timber
import javax.inject.Inject
class ChatRoomsFragment : Fragment(), ChatRoomsView {
@Inject lateinit var presenter: ChatRoomsPresenter
@Inject lateinit var serverInteractor: GetCurrentServerInteractor
......@@ -67,7 +64,11 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
super.onDestroy()
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? = container?.inflate(R.layout.fragment_chat_rooms)
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? = container?.inflate(R.layout.fragment_chat_rooms)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
......@@ -100,7 +101,6 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
})
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.action_sort -> {
......
......@@ -22,6 +22,8 @@ import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.infrastructure.SharedPrefsLocalRepository
import chat.rocket.android.push.GroupedPush
import chat.rocket.android.push.PushManager
import chat.rocket.android.server.domain.*
import chat.rocket.android.server.infraestructure.*
import chat.rocket.android.server.domain.AccountsRepository
import chat.rocket.android.server.domain.ChatRoomsRepository
import chat.rocket.android.server.domain.CurrentServerRepository
......@@ -230,16 +232,31 @@ class AppModule {
@Provides
@Singleton
fun provideMoshi(logger: PlatformLogger,
currentServerInteractor: GetCurrentServerInteractor):
Moshi {
fun provideActiveUsersRepository(): ActiveUsersRepository {
return MemoryActiveUsersRepository()
}
@Provides
@Singleton
fun provideMoshi(
logger: PlatformLogger,
currentServerInteractor: GetCurrentServerInteractor
): Moshi {
val url = currentServerInteractor.get() ?: ""
return Moshi.Builder()
.add(FallbackSealedClassJsonAdapter.ADAPTER_FACTORY)
.add(AppJsonAdapterFactory.INSTANCE)
.add(AttachmentAdapterFactory(Logger(logger, url)))
.add(java.lang.Long::class.java, ISO8601Date::class.java, TimestampAdapter(CalendarISO8601Converter()))
.add(Long::class.java, ISO8601Date::class.java, TimestampAdapter(CalendarISO8601Converter()))
.add(
java.lang.Long::class.java,
ISO8601Date::class.java,
TimestampAdapter(CalendarISO8601Converter())
)
.add(
Long::class.java,
ISO8601Date::class.java,
TimestampAdapter(CalendarISO8601Converter())
)
.build()
}
......
......@@ -84,10 +84,7 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector, HasSupp
override fun showUserStatus(userStatus: UserStatus) {
headerLayout.apply {
image_user_status.setImageDrawable(
DrawableHelper.getUserStatusDrawable(
userStatus,
this.context
)
DrawableHelper.getUserStatusDrawable(userStatus, this.context)
)
}
}
......
package chat.rocket.android.server.domain
import chat.rocket.common.model.User
interface ActiveUsersRepository {
fun save(url: String, activeUsers: List<User>)
fun get(url: String): List<User>
}
\ No newline at end of file
package chat.rocket.android.server.domain
import chat.rocket.common.model.User
import javax.inject.Inject
class GetActiveUsersInteractor @Inject constructor(private val repository: ActiveUsersRepository) {
fun isActiveUserOnRepository(url: String, user: User): Boolean {
return repository.get(url).any { user_ -> user_.id == user.id }
}
fun getAllActiveUsers(url: String): List<User> {
return repository.get(url)
}
fun getActiveUserById(url: String, id: String): User? {
return repository.get(url).find { user -> user.id == id }
}
fun getActiveUserByUsername(url: String, username: String): User? {
return repository.get(url).find { user -> user.username == username }
}
}
\ No newline at end of file
......@@ -8,13 +8,13 @@ import javax.inject.Inject
class GetChatRoomsInteractor @Inject constructor(private val repository: ChatRoomsRepository) {
/**
* Get all ChatRoom objects.
* Get all [ChatRoom].
*
* @param url The server url.
*
* @return All the ChatRoom objects.
* @return All the [ChatRoom] objects.
*/
fun get(url: String) = repository.get(url)
fun getAll(url: String) = repository.get(url)
/**
* Get a list of chat rooms that contains the name parameter.
......@@ -23,7 +23,7 @@ class GetChatRoomsInteractor @Inject constructor(private val repository: ChatRoo
* @param name The name of chat room to look for or a chat room that contains this name.
* @return A list of ChatRoom objects with the given name.
*/
suspend fun getByName(url: String, name: String): List<ChatRoom> = withContext(CommonPool) {
suspend fun getAllByName(url: String, name: String): List<ChatRoom> = withContext(CommonPool) {
val allChatRooms = repository.get(url)
if (name.isEmpty()) {
return@withContext allChatRooms
......@@ -34,11 +34,11 @@ class GetChatRoomsInteractor @Inject constructor(private val repository: ChatRoo
}
/**
* Get a specific room by its id.
* Get a specific [ChatRoom] by its id.
*
* @param serverUrl The server url where the room is.
* @param roomId The id of the room to get.
* @return The ChatRoom object or null if we couldn't find any.
* @return The [ChatRoom] object or null if we couldn't find any.
*/
suspend fun getById(serverUrl: String, roomId: String): ChatRoom? = withContext(CommonPool) {
val allChatRooms = repository.get(serverUrl)
......@@ -46,4 +46,43 @@ class GetChatRoomsInteractor @Inject constructor(private val repository: ChatRoo
it.id == roomId
}
}
/**
* Get a specific [ChatRoom] by its name.
*
* @param serverUrl The server url where the room is.
* @param name The name of the room to get.
* @return The [ChatRoom] object or null if we couldn't find any.
*/
fun getByName(serverUrl: String, name: String): ChatRoom? {
return getAll(serverUrl).toMutableList().find { chatRoom -> chatRoom.name == name }
}
/**
* Add a [ChatRoom].
*
* @param url The server url.
* @param chatRoom The [ChatRoom] to be added to the list.
*/
fun add(url: String, chatRoom: ChatRoom) {
val chatRooms: MutableList<ChatRoom> = getAll(url).toMutableList()
synchronized(this) {
chatRooms.add(chatRoom)
}
repository.save(url, chatRooms)
}
/**
* Removes a [ChatRoom].
*
* @param url The server url.
* @param chatRoom The [ChatRoom] to be removed from the list.
*/
fun remove(url: String, chatRoom: ChatRoom) {
val chatRooms: MutableList<ChatRoom> = getAll(url).toMutableList()
synchronized(this) {
chatRooms.removeAll { chatRoom_ -> chatRoom_.id == chatRoom.id }
}
repository.save(url, chatRooms)
}
}
\ No newline at end of file
package chat.rocket.android.server.domain
import chat.rocket.common.model.User
import javax.inject.Inject
class SaveActiveUsersInteractor @Inject constructor(
private val repository: ActiveUsersRepository,
private val getActiveUsersInteractor: GetActiveUsersInteractor
) {
fun save(url: String, activeUsers: List<User>) {
repository.save(url, activeUsers)
}
fun addActiveUser(url: String, user: User) {
val activeUserList: MutableList<User> =
getActiveUsersInteractor.getAllActiveUsers(url).toMutableList()
synchronized(this) {
activeUserList.add(user)
}
save(url, activeUserList)
}
fun updateActiveUser(url: String, user: User) {
getActiveUsersInteractor.getActiveUserById(url, user.id)?.let {
val newUser = User(
id = user.id,
name = user.name ?: it.name,
username = user.username ?: it.username,
status = user.status ?: it.status,
emails = user.emails ?: it.emails,
utcOffset = user.utcOffset ?: it.utcOffset
)
val activeUserList: MutableList<User> =
getActiveUsersInteractor.getAllActiveUsers(url).toMutableList()
synchronized(this) {
activeUserList.removeAll { user_ -> user_.id == user.id }
}
activeUserList.add(newUser)
save(url, activeUserList)
}
}
}
\ No newline at end of file
package chat.rocket.android.server.infraestructure
import chat.rocket.common.model.BaseRoom
import chat.rocket.common.model.User
import chat.rocket.core.RocketChatClient
import chat.rocket.core.internal.realtime.subscribeSubscriptions
import chat.rocket.core.internal.realtime.subscribeRooms
import chat.rocket.core.internal.realtime.subscribeUserData
import chat.rocket.core.internal.realtime.subscribeActiveUsers
import chat.rocket.core.internal.realtime.subscribeRoomMessages
import chat.rocket.core.internal.realtime.unsubscribe
import chat.rocket.core.internal.realtime.socket.connect
......@@ -28,11 +30,13 @@ class ConnectionManager(internal val client: RocketChatClient) {
private val roomAndSubscriptionChannels = ArrayList<Channel<StreamMessage<BaseRoom>>>()
private val roomMessagesChannels = LinkedHashMap<String, Channel<Message>>()
private val userDataChannels = ArrayList<Channel<Myself>>()
private val activeUsersChannels = ArrayList<Channel<User>>()
private val subscriptionIdMap = HashMap<String, String>()
private var subscriptionId: String? = null
private var roomsId: String? = null
private var userId: String? = null
private var userDataId: String? = null
private var activeUserId: String? = null
fun connect() {
if (connectJob?.isActive == true && (state !is State.Disconnected)) {
......@@ -61,8 +65,12 @@ class ConnectionManager(internal val client: RocketChatClient) {
roomsId = id
}
client.subscribeUserData { _, id ->
Timber.d("Subscribed to the user: $id")
userId = id
Timber.d("Subscribed to the userData id: $id")
userDataId = id
}
client.subscribeActiveUsers { _, id ->
Timber.d("Subscribed to the activeUser id: $id")
activeUserId = id
}
resubscribeRooms()
......@@ -115,6 +123,14 @@ class ConnectionManager(internal val client: RocketChatClient) {
}
}
launch(parent = connectJob) {
for (user in client.activeUsersChannel) {
Timber.d("Got activeUsers")
for (channel in activeUsersChannels) {
channel.send(user)
}
}
}
client.connect()
// Broadcast initial state...
......@@ -154,6 +170,10 @@ class ConnectionManager(internal val client: RocketChatClient) {
fun removeUserDataChannel(channel: Channel<Myself>) = userDataChannels.remove(channel)
fun addActiveUserChannel(channel: Channel<User>) = activeUsersChannels.add(channel)
fun removeActiveUserChannel(channel: Channel<User>) = activeUsersChannels.remove(channel)
fun subscribeRoomMessages(roomId: String, channel: Channel<Message>) {
val oldSub = roomMessagesChannels.put(roomId, channel)
if (oldSub != null) {
......
package chat.rocket.android.server.infraestructure
import chat.rocket.android.server.domain.ActiveUsersRepository
import chat.rocket.common.model.User
class MemoryActiveUsersRepository : ActiveUsersRepository {
val cache = HashMap<String, List<User>>()
override fun save(url: String, activeUsers: List<User>) {
cache[url] = activeUsers
}
override fun get(url: String): List<User> = cache[url] ?: emptyList()
}
\ No newline at end of file
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="12dp"
android:height="12dp"
android:viewportHeight="12"
android:viewportWidth="12">
<path
android:fillColor="#9EA2A8"
android:fillType="evenOdd"
android:pathData="M2.4,0h1.2v12h-1.2z"
android:strokeColor="#00000000"
android:strokeWidth="1" />
<path
android:fillColor="#9EA2A8"
android:fillType="evenOdd"
android:pathData="M0,2.4h12v1.2h-12z"
android:strokeColor="#00000000"
android:strokeWidth="1" />
<path
android:fillColor="#9EA2A8"
android:fillType="evenOdd"
android:pathData="M0,8.4h12v1.2h-12z"
android:strokeColor="#00000000"
android:strokeWidth="1" />
<path
android:fillColor="#9EA2A8"
android:fillType="evenOdd"
android:pathData="M8.4,0h1.2v12h-1.2z"
android:strokeColor="#00000000"
android:strokeWidth="1" />
</vector>
\ No newline at end of file
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="12dp"
android:height="12dp"
android:viewportWidth="12"
android:viewportHeight="12">
<path
android:pathData="M1.5,5.5h9v6h-9z"
android:strokeWidth="1"
android:fillColor="#00000000"
android:strokeColor="#9EA2A8"
android:fillType="evenOdd"/>
<path
android:pathData="M2.5,5.5L9.5,5.5L9.5,4C9.5,2.067 7.933,0.5 6,0.5C4.067,0.5 2.5,2.067 2.5,4L2.5,5.5Z"
android:strokeWidth="1"
android:fillColor="#00000000"
android:strokeColor="#9EA2A8"
android:fillType="evenOdd"/>
</vector>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24"
android:viewportWidth="24">
android:width="12dp"
android:height="12dp"
android:viewportHeight="12"
android:viewportWidth="12">
<path
android:fillColor="#FFFFD100"
android:fillType="evenOdd"
android:pathData="M12,12m-10,0a10,10 0,1 1,20 0a10,10 0,1 1,-20 0"
android:pathData="M6,6m-5,0a5,5 0,1 1,10 0a5,5 0,1 1,-10 0"
android:strokeColor="#00000000"
android:strokeWidth="1" />
<path
android:fillColor="#00000000"
android:fillType="evenOdd"
android:pathData="M12,12m-11,0a11,11 0,1 1,22 0a11,11 0,1 1,-22 0"
android:strokeColor="#FFFFFFFF"
android:strokeWidth="2" />
</vector>
\ No newline at end of file
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24"
android:viewportWidth="24">
android:width="12dp"
android:height="12dp"
android:viewportHeight="12"
android:viewportWidth="12">
<path
android:fillColor="#FFFF2A57"
android:fillType="evenOdd"
android:pathData="M12,12m-10,0a10,10 0,1 1,20 0a10,10 0,1 1,-20 0"
android:pathData="M6,6m-5,0a5,5 0,1 1,10 0a5,5 0,1 1,-10 0"
android:strokeColor="#00000000"
android:strokeWidth="1" />
<path
android:fillColor="#00000000"
android:fillType="evenOdd"
android:pathData="M12,12m-11,0a11,11 0,1 1,22 0a11,11 0,1 1,-22 0"
android:strokeColor="#FFFFFFFF"
android:strokeWidth="2" />
</vector>
\ No newline at end of file
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24"
android:viewportWidth="24">
android:width="12dp"
android:height="12dp"
android:viewportHeight="12"
android:viewportWidth="12">
<path
android:fillColor="#FFCBCED1"
android:fillType="evenOdd"
android:pathData="M12,12m-10,0a10,10 0,1 1,20 0a10,10 0,1 1,-20 0"
android:pathData="M6,6m-5,0a5,5 0,1 1,10 0a5,5 0,1 1,-10 0"
android:strokeColor="#00000000"
android:strokeWidth="1" />
<path
android:fillColor="#00000000"
android:fillType="evenOdd"
android:pathData="M12,12m-11,0a11,11 0,1 1,22 0a11,11 0,1 1,-22 0"
android:strokeColor="#FFFFFFFF"
android:strokeWidth="2" />
</vector>
\ No newline at end of file
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24"
android:viewportWidth="24">
android:width="12dp"
android:height="12dp"
android:viewportHeight="12"
android:viewportWidth="12">
<path
android:fillColor="#FF2DE0A5"
android:fillType="evenOdd"
android:pathData="M12,12m-10,0a10,10 0,1 1,20 0a10,10 0,1 1,-20 0"
android:pathData="M6,6m-5,0a5,5 0,1 1,10 0a5,5 0,1 1,-10 0"
android:strokeColor="#00000000"
android:strokeWidth="1" />
<path
android:fillColor="#00000000"
android:fillType="evenOdd"
android:pathData="M12,12m-11,0a11,11 0,1 1,22 0a11,11 0,1 1,-22 0"
android:strokeColor="#FFFFFFFF"
android:strokeWidth="2" />
</vector>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="10dp"
android:height="10dp"
android:viewportWidth="10.0"
android:viewportHeight="10.0">
<path
android:pathData="M5,5m-5,0a5,5 0,1 1,10 0a5,5 0,1 1,-10 0"
android:fillType="evenOdd"
android:fillColor="#FFFFFF"
android:strokeWidth="1"/>
</vector>
\ No newline at end of file
......@@ -16,6 +16,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:visibility="gone"
app:indicatorColor="@color/black"
app:indicatorName="BallPulseIndicator" />
......@@ -33,15 +34,15 @@
android:id="@+id/connection_status_text"
android:layout_width="match_parent"
android:layout_height="32dp"
android:alpha="0"
android:background="@color/colorPrimary"
android:elevation="4dp"
android:textColor="@color/white"
android:gravity="center"
android:textAppearance="@style/TextAppearance.AppCompat.Body2"
android:textColor="@color/white"
android:visibility="gone"
android:alpha="0"
tools:alpha="1"
tools:visibility="visible"
tools:text="connected"/>
tools:text="connected"
tools:visibility="visible" />
</RelativeLayout>
\ No newline at end of file
......@@ -15,7 +15,7 @@
android:paddingStart="16dp"
android:paddingTop="8dp"
android:drawablePadding="10dp"
android:drawableStart="@drawable/ic_status_online_24dp"
android:drawableStart="@drawable/ic_status_online_12dp"
android:text="@string/action_online"
android:background="?selectableItemBackground"/>
......@@ -28,7 +28,7 @@
android:paddingStart="16dp"
android:paddingTop="8dp"
android:drawablePadding="10dp"
android:drawableStart="@drawable/ic_status_away_24dp"
android:drawableStart="@drawable/ic_status_away_12dp"
android:text="@string/action_away"
android:background="?selectableItemBackground"/>
......@@ -41,7 +41,7 @@
android:paddingStart="16dp"
android:paddingTop="8dp"
android:drawablePadding="10dp"
android:drawableStart="@drawable/ic_status_busy_24dp"
android:drawableStart="@drawable/ic_status_busy_12dp"
android:text="@string/action_busy"
android:background="?selectableItemBackground"/>
......@@ -54,7 +54,7 @@
android:paddingStart="16dp"
android:paddingTop="8dp"
android:drawablePadding="10dp"
android:drawableStart="@drawable/ic_status_invisible_24dp"
android:drawableStart="@drawable/ic_status_invisible_12dp"
android:text="@string/action_invisible"
android:background="?selectableItemBackground"/>
......
......@@ -5,62 +5,70 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:paddingStart="@dimen/screen_edge_left_and_right_padding"
android:paddingBottom="@dimen/chat_item_top_and_bottom_padding"
android:paddingEnd="@dimen/screen_edge_left_and_right_padding"
android:paddingTop="@dimen/chat_item_top_and_bottom_padding"
android:paddingBottom="@dimen/chat_item_top_and_bottom_padding">
android:paddingStart="@dimen/screen_edge_left_and_right_padding"
android:paddingTop="@dimen/chat_item_top_and_bottom_padding">
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/image_avatar"
android:layout_width="40dp"
android:layout_height="40dp"
app:roundedCornerRadius="3dp"
android:layout_marginTop="6dp"
android:layout_marginTop="5dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
app:layout_constraintTop_toTopOf="parent"
app:roundedCornerRadius="3dp" />
<ImageView
android:id="@+id/image_chat_icon"
android:layout_width="12dp"
android:layout_height="12dp"
android:layout_marginStart="16dp"
app:layout_constraintStart_toEndOf="@+id/image_avatar"
app:layout_constraintTop_toTopOf="@+id/image_avatar"
tools:src="@drawable/ic_hashtag_12dp" />
<TextView
android:id="@+id/text_chat_name"
style="@style/ChatRoom.Name.TextView"
android:layout_width="0dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
app:layout_constraintStart_toEndOf="@id/image_avatar"
android:layout_marginStart="8dp"
android:textDirection="locale"
tools:text="General"/>
app:layout_constraintStart_toEndOf="@+id/image_chat_icon"
tools:text="general" />
<TextView
android:id="@+id/text_last_message_date_time"
style="@style/Timestamp.TextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:layout_marginEnd="5dp"
app:layout_constraintBaseline_toBaselineOf="@+id/text_chat_name"
android:layout_marginStart="8dp"
app:layout_constraintBottom_toBottomOf="@+id/text_chat_name"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/text_chat_name"
tools:text="11:45 AM" />
<TextView
android:id="@+id/text_last_message"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginTop="2dp"
android:ellipsize="end"
android:maxLines="2"
android:layout_marginTop="2dp"
app:layout_constraintStart_toStartOf="@id/text_chat_name"
app:layout_constraintTop_toBottomOf="@id/text_chat_name"
app:layout_constraintEnd_toStartOf="@id/layout_unread_messages_badge"
android:textDirection="locale"
tools:text="You: Type something that is very big and need at least to lines, or maybe even more"/>
app:layout_constraintEnd_toStartOf="@+id/layout_unread_messages_badge"
app:layout_constraintStart_toStartOf="@+id/image_chat_icon"
app:layout_constraintTop_toBottomOf="@+id/text_chat_name"
tools:text="Filipe de Lima Brito: Type something that is very big and need at least to lines, or maybe even more" />
<include
android:id="@+id/layout_unread_messages_badge"
layout="@layout/unread_messages_badge"
android:layout_width="18dp"
android:layout_height="18dp"
android:layout_marginStart="5dp"
android:layout_marginEnd="5dp"
app:layout_constraintTop_toTopOf="@id/text_last_message"
app:layout_constraintEnd_toEndOf="parent"/>
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/text_last_message" />
</android.support.constraint.ConstraintLayout>
\ No newline at end of file
......@@ -43,8 +43,8 @@
<ImageView
android:id="@+id/image_user_status"
android:layout_width="14dp"
android:layout_height="14dp"
android:layout_width="12dp"
android:layout_height="12dp"
app:layout_constraintStart_toStartOf="parent" />
<TextView
......
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="2dp"
android:layout_marginEnd="2dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="2dp"
android:layout_marginStart="8dp"
android:layout_marginTop="2dp"
android:background="@color/suggestion_background_color">
<FrameLayout
android:id="@+id/image_avatar_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true">
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/image_avatar"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginTop="4dp"
android:layout_marginBottom="4dp"
app:roundedCornerRadius="3dp"
tools:src="@tools:sample/avatars" />
......@@ -31,45 +21,36 @@
android:id="@+id/image_status"
android:layout_width="12dp"
android:layout_height="12dp"
android:layout_gravity="bottom|end"
android:background="@drawable/user_status_white"
android:padding="2dp" />
</FrameLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toEndOf="@id/image_avatar_container"
android:layout_toRightOf="@id/image_avatar_container"
android:background="@color/suggestion_background_color">
android:layout_marginStart="5dp"
app:layout_constraintBottom_toBottomOf="@+id/image_avatar"
app:layout_constraintStart_toEndOf="@+id/image_avatar"
app:layout_constraintTop_toTopOf="@+id/image_avatar" />
<TextView
android:id="@+id/text_username"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginStart="5dp"
android:maxLines="1"
android:textColor="@color/black"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@+id/image_status"
app:layout_constraintTop_toTopOf="parent"
tools:text="@tools:sample/full_names" />
<TextView
android:id="@+id/text_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_toEndOf="@+id/text_username"
android:layout_toRightOf="@+id/text_username"
android:layout_marginStart="5dp"
android:ellipsize="end"
android:maxLines="1"
android:paddingLeft="8dp"
android:paddingStart="8dp"
android:textColor="@color/gray_material"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@+id/text_username"
app:layout_constraintTop_toTopOf="parent"
tools:text="@tools:sample/full_names" />
</RelativeLayout>
</RelativeLayout>
\ No newline at end of file
</android.support.constraint.ConstraintLayout>
\ No newline at end of file
......@@ -3,22 +3,16 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="2dp"
android:background="@color/suggestion_background_color">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toEndOf="@id/image_avatar_container"
android:layout_toRightOf="@id/image_avatar_container"
android:layout_marginBottom="2dp"
android:layout_marginEnd="2dp"
android:layout_marginStart="8dp"
android:layout_marginTop="2dp"
android:background="@color/suggestion_background_color">
<TextView
android:id="@+id/text_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:maxLines="1"
android:textColor="@color/black"
android:textSize="16sp"
......@@ -28,18 +22,11 @@
android:id="@+id/text_fullname"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_toEndOf="@+id/text_name"
android:layout_toRightOf="@+id/text_name"
android:maxLines="1"
android:paddingLeft="8dp"
android:paddingStart="8dp"
android:layout_marginStart="8dp"
android:textColor="@color/gray_material"
android:textSize="16sp"
tools:text="@tools:sample/full_names" />
</RelativeLayout>
</RelativeLayout>
\ No newline at end of file
<resources
xmlns:tools="http://schemas.android.com/tools"
tools:ignore="MissingTranslation">
<resources>
<!-- Titles -->
<string name="title_sign_in_your_server">अपने सर्वर में साइन इन करें</string>
<string name="title_log_in">लॉग इन करें</string>
......@@ -82,6 +79,8 @@
<string name="msg_preview_photo">तस्वीरें</string>
<string name="msg_unread_messages">अपठित संदेश</string>
<string name="msg_no_messages_yet">अभी तक कोई पोस्ट नहीं</string>
<string name="msg_version">वर्शन</string>
<string name="msg_build">बिल्ड</string>
<string name="msg_ok">OK</string>
<string name="msg_ver_not_recommended">
ऐसा लगता है कि आपका सर्वर संस्करण अनुशंसित संस्करण %1$s के नीचे है।\nआप अभी भी लॉगिन कर सकते हैं लेकिन आप अप्रत्याशित व्यवहार का अनुभव कर सकते हैं
......@@ -89,8 +88,12 @@
<string name="msg_ver_not_minimum">
ऐसा लगता है कि आपका सर्वर संस्करण न्यूनतम आवश्यक संस्करण %1$s से कम है।\nकृपया लॉगिन करने के लिए अपने सर्वर को अपग्रेड करें!
</string>
<string name="msg_version">वर्शन</string>
<string name="msg_build">बिल्ड</string>
<string name="msg_proceed">आगे बढ़ें</string>
<string name="msg_cancel">रद्द करना</string>
<string name="msg_warning">चेतावनी</string>
<string name="msg_http_insecure">HTTP का उपयोग करते समय, आप एक असुरक्षित सर्वर से कनेक्ट हो रहे हैं। हम आपको ऐसा करने की सलाह नहीं देते हैं।</string>
<string name="msg_error_checking_server_version">आपके सर्वर संस्करण की जांच करते समय एक त्रुटि आई है, कृपया पुनः प्रयास करें</string>
<string name="msg_invalid_server_protocol">चयनित प्रोटोकॉल इस सर्वर द्वारा स्वीकार नहीं किया गया है, HTTPS का उपयोग करने का प्रयास करें</string>
<!-- System messages -->
<string name="message_room_name_changed">%2$s ने रूम का नाम बदलकर %1$s किया</string>
......
......@@ -5,7 +5,6 @@
<dimen name="screen_edge_left_and_right_margins">16dp</dimen>
<dimen name="screen_edge_left_and_right_padding">16dp</dimen>
<dimen name="chat_item_top_and_bottom_padding">12dp</dimen>
<dimen name="message_item_top_and_bottom_padding">6dp</dimen>
<dimen name="member_item_top_and_bottom_padding">6dp</dimen>
......@@ -23,6 +22,10 @@
<dimen name="nav_header_height">140dp</dimen>
<!-- ChatRoom -->
<dimen name="chat_item_top_and_bottom_padding">12dp</dimen>
<!-- Emoji -->
<dimen name="picker_padding_bottom">16dp</dimen>
<dimen name="supposed_keyboard_height">252dp</dimen>
......
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