Commit 4668cc99 authored by Divyanshu Bhargava's avatar Divyanshu Bhargava

Merge branch 'develop-2.x' into empty-chat-view

# Conflicts:
#	app/src/main/res/values-hi-rIN/strings.xml
parents 9ac6f583 c54037ca
......@@ -57,7 +57,7 @@ jobs:
command: ./gradlew lint
- run:
name: Run Unit test
command: echo ./gradlew test # TODO: Fix unit test errors soon...
command: ./gradlew test
- store_artifacts:
path: app/build/reports/
destination: reports
......
#.travis.yml
language: android
jdk: oraclejdk8
sudo: required
android:
components: # Cookbooks version: https://github.com/travis-ci/travis-cookbooks/tree/9c6cd11
- tools # Update preinstalled tools from revision 24.0.2 to 24.4.1
- build-tools-25.0.3 # Match build-tools version used in build.gradle
- platform-tools # Update platform-tools to revision 25.0.3+
- tools # Update tools from revision 24.4.1 to 25.2.5
env:
global:
- API=26 # Android API level 26 by default
- TAG=google_apis # Google APIs by default, alternatively use default
- ABI=armeabi-v7a # ARM ABI v7a by default
- QEMU_AUDIO_DRV=none # Disable emulator audio to avoid warning
- ANDROID_HOME=/usr/local/android-sdk # Depends on the cookbooks version used in the VM
- TOOLS=${ANDROID_HOME}/tools # PATH order matters, exists more than one emulator script
- PATH=${ANDROID_HOME}:${ANDROID_HOME}/emulator:${TOOLS}:${TOOLS}/bin:${ANDROID_HOME}/platform-tools:${PATH}
- ADB_INSTALL_TIMEOUT=20 # minutes (2 minutes by default)
install:
# List and delete unnecessary components to free space
- sdkmanager --list || true
- sdkmanager --uninstall "system-images;android-15;default;armeabi-v7a"
# Update sdk tools to latest version and install/update components
- echo yes | sdkmanager "tools"
- echo yes | sdkmanager "platforms;android-26" # Latest platform required by SDK tools
- echo yes | sdkmanager "platforms;android-${API}" # Android platform required by emulator
- echo yes | sdkmanager "extras;android;m2repository"
- echo yes | sdkmanager "extras;google;m2repository"
- echo yes | sdkmanager "extras;m2repository;com;android;support;constraint;constraint-layout;1.0.2"
- echo yes | sdkmanager "extras;m2repository;com;android;support;constraint;constraint-layout-solver;1.0.2"
# - echo yes | sdkmanager "$EMULATOR" # Install emulator system image
# Create and start emulator
# - echo no | avdmanager create avd -n acib -k "$EMULATOR" -f --abi "$ABI" --tag "$TAG"
# - emulator -avd acib -engine classic -no-window -verbose -qemu -m 512 &
before_script:
# - echo y | android update sdk --no-ui --all --filter tools,platform-tools
# - echo y | android update sdk --no-ui --all --filter android-25
# - echo y | android update sdk --no-ui --all --filter extra-android-m2repository,extra-android-support
# - echo y | android update sdk --no-ui --all --filter extra-google-m2repository,extra-google-google_play_services
# - echo y | android update sdk --no-ui --all --filter build-tools-25.0.3
# - echo yes | sdkmanager "extras;m2repository;com;android;support;constraint;constraint-layout;1.0.2"
# - echo yes | sdkmanager "extras;m2repository;com;android;support;constraint;constraint-layout-solver;1.0.2"
- ./gradlew dependencies
script:
- ./gradlew checkstyle findbugs pmd
......@@ -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 {
......@@ -82,6 +78,8 @@ dependencies {
implementation libraries.room
kapt libraries.roomProcessor
implementation libraries.roomRxjava
implementation libraries.lifecycleExtensions
kapt libraries.lifecycleCompiler
implementation libraries.rxKotlin
implementation libraries.rxAndroid
......
......@@ -49,37 +49,52 @@
android:scheme="https" />
</intent-filter>
</activity>
<activity
android:name=".server.ui.ChangeServerActivity"
android:theme="@style/AuthenticationTheme" />
<activity
android:name=".main.ui.MainActivity"
android:theme="@style/AppTheme"
android:windowSoftInputMode="adjustResize|stateAlwaysHidden" />
<activity
android:name=".webview.ui.WebViewActivity"
android:theme="@style/AppTheme"
android:windowSoftInputMode="adjustResize|stateAlwaysHidden" />
<activity
android:name=".webview.cas.ui.CasWebViewActivity"
android:theme="@style/AppTheme"
android:windowSoftInputMode="adjustResize|stateAlwaysHidden" />
<activity
android:name=".webview.oauth.ui.OauthWebViewActivity"
android:theme="@style/AppTheme"
android:windowSoftInputMode="adjustResize|stateAlwaysHidden" />
<activity
android:name=".chatroom.ui.ChatRoomActivity"
android:theme="@style/AppTheme"
android:windowSoftInputMode="adjustResize|stateAlwaysHidden" />
<!-- TODO: Change to fragment-->
<activity
android:name=".chatroom.ui.PinnedMessagesActivity"
android:theme="@style/AppTheme"
android:windowSoftInputMode="adjustResize|stateAlwaysHidden" />
<!-- TODO: Change to fragment-->
<activity
android:name=".settings.password.ui.PasswordActivity"
android:theme="@style/AppTheme" />
<!-- TODO: Change to fragment-->
<activity
android:name=".settings.about.ui.AboutActivity"
android:theme="@style/AppTheme" />
<receiver
android:name="com.google.android.gms.gcm.GcmReceiver"
android:exported="true"
......@@ -123,10 +138,6 @@
<meta-data
android:name="io.fabric.ApiKey"
android:value="12ac6e94f850aaffcdff52001af77ca415d06a43" />
<activity
android:name=".settings.about.ui.AboutActivity"
android:theme="@style/AppTheme" />
</application>
</manifest>
package chat.rocket.android.app
import android.arch.lifecycle.Lifecycle
import android.arch.lifecycle.LifecycleObserver
import android.arch.lifecycle.OnLifecycleEvent
import chat.rocket.android.server.domain.GetAccountInteractor
import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.common.RocketChatException
import chat.rocket.common.model.UserStatus
import chat.rocket.core.internal.realtime.setTemporaryStatus
import kotlinx.coroutines.experimental.launch
import timber.log.Timber
import javax.inject.Inject
class AppLifecycleObserver @Inject constructor(
private val serverInteractor: GetCurrentServerInteractor,
private val factory: RocketChatClientFactory,
private val getAccountInteractor: GetAccountInteractor
) : LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun onEnterForeground() {
changeTemporaryStatus(UserStatus.Online())
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun onEnterBackground() {
changeTemporaryStatus(UserStatus.Away())
}
private fun changeTemporaryStatus(userStatus: UserStatus) {
launch {
val currentServer = serverInteractor.get()
val account = currentServer?.let { getAccountInteractor.get(currentServer) }
val client = account?.let { factory.create(currentServer) }
try {
client?.setTemporaryStatus(userStatus)
} catch (exception: RocketChatException) {
Timber.e(exception)
}
}
}
}
\ No newline at end of file
......@@ -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
......@@ -3,6 +3,7 @@ package chat.rocket.android.app
import android.app.Activity
import android.app.Application
import android.app.Service
import android.arch.lifecycle.ProcessLifecycleOwner
import android.content.BroadcastReceiver
import android.content.Context
import android.content.SharedPreferences
......@@ -43,9 +44,11 @@ import timber.log.Timber
import java.lang.ref.WeakReference
import javax.inject.Inject
class RocketChatApplication : Application(), HasActivityInjector, HasServiceInjector,
HasBroadcastReceiverInjector {
HasBroadcastReceiverInjector {
@Inject
lateinit var appLifecycleObserver: AppLifecycleObserver
@Inject
lateinit var activityDispatchingAndroidInjector: DispatchingAndroidInjector<Activity>
......@@ -84,7 +87,14 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje
override fun onCreate() {
super.onCreate()
DaggerAppComponent.builder().application(this).build().inject(this)
DaggerAppComponent.builder()
.application(this)
.build()
.inject(this)
ProcessLifecycleOwner.get()
.lifecycle
.addObserver(appLifecycleObserver)
// TODO - remove this on the future, temporary migration stuff for pre-release versions.
migrateInternalTokens()
......
......@@ -126,7 +126,7 @@ class LoginPresenter @Inject constructor(
}
private fun setupUserRegistrationView() {
if (settings.isRegistrationEnabledForNewUsers()) {
if (settings.isRegistrationEnabledForNewUsers() && settings.isLoginFormEnabled()) {
view.showSignUpView()
view.setupSignUpView()
}
......@@ -187,7 +187,21 @@ class LoginPresenter @Inject constructor(
if (settings.isGitlabAuthenticationEnabled()) {
val clientId = getOauthClientId(services, SERVICE_NAME_GILAB)
if (clientId != null) {
view.setupGitlabButtonListener(OauthHelper.getGitlabOauthUrl(clientId, currentServer, state), state)
val gitlabOauthUrl = if (settings.gitlabUrl() != null) {
OauthHelper.getGitlabOauthUrl(
host = settings.gitlabUrl(),
clientId = clientId,
serverUrl = currentServer,
state = state
)
} else {
OauthHelper.getGitlabOauthUrl(
clientId = clientId,
serverUrl = currentServer,
state = state
)
}
view.setupGitlabButtonListener(gitlabOauthUrl, state)
view.enableLoginByGitlab()
totalSocialAccountsEnabled++
}
......@@ -280,9 +294,9 @@ class LoginPresenter @Inject constructor(
}
private fun getOauthClientId(listMap: List<Map<String, Any>>, serviceName: String): String? {
return listMap.find { map -> map.containsValue(serviceName) }
?.get("clientId")
.toString()
return listMap.find { map -> map.containsValue(serviceName) }?.let {
it["clientId"] ?: it["appId"]
}.toString()
}
private suspend fun saveAccount(username: String) {
......
......@@ -42,21 +42,23 @@ import timber.log.Timber
import java.util.*
import javax.inject.Inject
class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
private val navigator: ChatRoomNavigator,
private val strategy: CancelStrategy,
getSettingsInteractor: GetSettingsInteractor,
serverInteractor: GetCurrentServerInteractor,
private val getChatRoomsInteractor: GetChatRoomsInteractor,
private val permissions: GetPermissionsInteractor,
private val uriInteractor: UriInteractor,
private val messagesRepository: MessagesRepository,
private val usersRepository: UsersRepository,
private val roomsRepository: RoomRepository,
private val localRepository: LocalRepository,
factory: ConnectionManagerFactory,
private val mapper: ViewModelMapper,
private val jobSchedulerInteractor: JobSchedulerInteractor) {
class ChatRoomPresenter @Inject constructor(
private val view: ChatRoomView,
private val navigator: ChatRoomNavigator,
private val strategy: CancelStrategy,
getSettingsInteractor: GetSettingsInteractor,
serverInteractor: GetCurrentServerInteractor,
private val getChatRoomsInteractor: GetChatRoomsInteractor,
private val permissions: GetPermissionsInteractor,
private val uriInteractor: UriInteractor,
private val messagesRepository: MessagesRepository,
private val usersRepository: UsersRepository,
private val roomsRepository: RoomRepository,
private val localRepository: LocalRepository,
factory: ConnectionManagerFactory,
private val mapper: ViewModelMapper,
private val jobSchedulerInteractor: JobSchedulerInteractor
) {
private val currentServer = serverInteractor.get()!!
private val manager = factory.create(currentServer)
private val client = manager.client
......@@ -510,7 +512,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
}
......
......@@ -4,27 +4,24 @@ import chat.rocket.android.chatroom.viewmodel.ViewModelMapper
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.server.domain.GetChatRoomsInteractor
import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.domain.GetSettingsInteractor
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.extensions.launchUI
import chat.rocket.common.RocketChatException
import chat.rocket.common.util.ifNull
import chat.rocket.core.internal.rest.getRoomPinnedMessages
import chat.rocket.core.model.Value
import chat.rocket.core.model.isSystemMessage
import timber.log.Timber
import javax.inject.Inject
class PinnedMessagesPresenter @Inject constructor(private val view: PinnedMessagesView,
private val strategy: CancelStrategy,
private val serverInteractor: GetCurrentServerInteractor,
private val roomsInteractor: GetChatRoomsInteractor,
private val mapper: ViewModelMapper,
factory: RocketChatClientFactory,
getSettingsInteractor: GetSettingsInteractor) {
class PinnedMessagesPresenter @Inject constructor(
private val view: PinnedMessagesView,
private val strategy: CancelStrategy,
private val serverInteractor: GetCurrentServerInteractor,
private val roomsInteractor: GetChatRoomsInteractor,
private val mapper: ViewModelMapper,
factory: RocketChatClientFactory
) {
private val client = factory.create(serverInteractor.get()!!)
private var settings: Map<String, Value<Any>> = getSettingsInteractor.get(serverInteractor.get()!!)
private var pinnedMessagesListOffset: Int = 0
/**
......
......@@ -3,7 +3,6 @@ package chat.rocket.android.chatroom.ui
import DrawableHelper
import android.content.Context
import android.content.Intent
import android.graphics.drawable.Drawable
import android.os.Bundle
import android.support.v4.app.Fragment
import android.support.v7.app.AppCompatActivity
......@@ -15,22 +14,21 @@ import chat.rocket.android.util.extensions.addFragment
import chat.rocket.android.util.extensions.textContent
import chat.rocket.common.model.RoomType
import chat.rocket.common.model.roomTypeOf
import chat.rocket.common.util.ifNull
import dagger.android.AndroidInjection
import dagger.android.AndroidInjector
import dagger.android.DispatchingAndroidInjector
import dagger.android.support.HasSupportFragmentInjector
import kotlinx.android.synthetic.main.app_bar_chat_room.*
import javax.inject.Inject
import timber.log.Timber
fun Context.chatRoomIntent(chatRoomId: String,
chatRoomName: String,
chatRoomType: String,
isChatRoomReadOnly: Boolean,
chatRoomLastSeen: Long,
isChatRoomSubscribed: Boolean = true): Intent {
fun Context.chatRoomIntent(
chatRoomId: String,
chatRoomName: String,
chatRoomType: String,
isChatRoomReadOnly: Boolean,
chatRoomLastSeen: Long,
isChatRoomSubscribed: Boolean = true
): Intent {
return Intent(this, ChatRoomActivity::class.java).apply {
putExtra(INTENT_CHAT_ROOM_ID, chatRoomId)
putExtra(INTENT_CHAT_ROOM_NAME, chatRoomName)
......
......@@ -38,12 +38,14 @@ import kotlinx.android.synthetic.main.message_list.*
import java.util.concurrent.atomic.AtomicInteger
import javax.inject.Inject
fun newInstance(chatRoomId: String,
chatRoomName: String,
chatRoomType: String,
isChatRoomReadOnly: Boolean,
chatRoomLastSeen: Long,
isSubscribed: Boolean = true): Fragment {
fun newInstance(
chatRoomId: String,
chatRoomName: String,
chatRoomType: String,
isChatRoomReadOnly: Boolean,
chatRoomLastSeen: Long,
isSubscribed: Boolean = true
): Fragment {
return ChatRoomFragment().apply {
arguments = Bundle(1).apply {
putString(BUNDLE_CHAT_ROOM_ID, chatRoomId)
......@@ -110,7 +112,13 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
setHasOptionsMenu(true)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? = container?.inflate(R.layout.fragment_chat_room)
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return container?.inflate(R.layout.fragment_chat_room)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
......
......@@ -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,
private val strategy: CancelStrategy,
private val navigator: MainNavigator,
private val serverInteractor: GetCurrentServerInteractor,
private val getChatRoomsInteractor: GetChatRoomsInteractor,
private val saveChatRoomsInteractor: SaveChatRoomsInteractor,
private val refreshSettingsInteractor: RefreshSettingsInteractor,
private val viewModelMapper: ViewModelMapper,
private val jobSchedulerInteractor: JobSchedulerInteractor,
settingsRepository: SettingsRepository,
factory: ConnectionManagerFactory) {
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
) {
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,
......@@ -131,7 +163,7 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
open = false,
alert = false,
unread = 0L,
userMenstions = null,
userMentions = null,
groupMentions = 0L,
lastMessage = null,
client = client
......@@ -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,
......@@ -158,7 +196,7 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
open = false,
alert = false,
unread = 0L,
userMenstions = null,
userMentions = null,
groupMentions = 0L,
lastMessage = it.lastMessage,
client = client
......@@ -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,
userMentions = it.userMentions,
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,
......@@ -365,10 +419,11 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
open = open,
alert = alert,
unread = unread,
userMenstions = userMenstions,
userMentions = userMentions,
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,
......@@ -399,19 +458,21 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
open = subscription.open,
alert = subscription.alert,
unread = subscription.unread,
userMenstions = subscription.userMentions,
userMentions = 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,
userMentions = it.userMentions,
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,34 +82,39 @@ 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 color = when (chatRoom.alert || chatRoom.unread > 0) {
true -> R.color.colorPrimaryText
false -> R.color.colorSecondaryText
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(mutateDrawable, context, color)
}
DrawableHelper.tintDrawable(mutableDrawable, context, color)
DrawableHelper.compoundDrawable(textView, mutableDrawable)
imageView.setImageDrawable(mutateDrawable)
}
}
private fun bindName(chatRoom: ChatRoom, textView: TextView) {
textView.textContent = chatRoom.name
}
private fun bindLastMessageDateTime(chatRoom: ChatRoom, textView: TextView) {
val lastMessage = chatRoom.lastMessage
if (lastMessage != null) {
......
......@@ -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 -> {
......
package chat.rocket.android.dagger
import android.app.Application
import chat.rocket.android.app.AppLifecycleObserver
import chat.rocket.android.app.RocketChatApplication
import chat.rocket.android.chatroom.service.MessageService
import chat.rocket.android.dagger.module.ActivityBuilder
......
......@@ -22,29 +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.AccountsRepository
import chat.rocket.android.server.domain.ChatRoomsRepository
import chat.rocket.android.server.domain.CurrentServerRepository
import chat.rocket.android.server.domain.GetAccountInteractor
import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.domain.GetPermissionsInteractor
import chat.rocket.android.server.domain.GetSettingsInteractor
import chat.rocket.android.server.domain.JobSchedulerInteractor
import chat.rocket.android.server.domain.MessagesRepository
import chat.rocket.android.server.domain.MultiServerTokenRepository
import chat.rocket.android.server.domain.RoomRepository
import chat.rocket.android.server.domain.SettingsRepository
import chat.rocket.android.server.domain.TokenRepository
import chat.rocket.android.server.domain.UsersRepository
import chat.rocket.android.server.infraestructure.JobSchedulerInteractorImpl
import chat.rocket.android.server.infraestructure.MemoryChatRoomsRepository
import chat.rocket.android.server.infraestructure.MemoryRoomRepository
import chat.rocket.android.server.infraestructure.MemoryUsersRepository
import chat.rocket.android.server.infraestructure.ServerDao
import chat.rocket.android.server.infraestructure.SharedPreferencesAccountsRepository
import chat.rocket.android.server.infraestructure.SharedPreferencesMessagesRepository
import chat.rocket.android.server.infraestructure.SharedPreferencesSettingsRepository
import chat.rocket.android.server.infraestructure.SharedPrefsCurrentServerRepository
import chat.rocket.android.server.domain.*
import chat.rocket.android.server.infraestructure.*
import chat.rocket.android.util.AppJsonAdapterFactory
import chat.rocket.android.util.TimberLogger
import chat.rocket.common.internal.FallbackSealedClassJsonAdapter
......@@ -55,6 +34,7 @@ import chat.rocket.common.util.Logger
import chat.rocket.common.util.PlatformLogger
import chat.rocket.core.RocketChatClient
import chat.rocket.core.internal.AttachmentAdapterFactory
import chat.rocket.core.internal.ReactionsAdapter
import com.facebook.drawee.backends.pipeline.DraweeConfig
import com.facebook.imagepipeline.backends.okhttp3.OkHttpImagePipelineConfigFactory
import com.facebook.imagepipeline.core.ImagePipelineConfig
......@@ -164,10 +144,10 @@ class AppModule {
listeners.add(RequestLoggingListener())
return OkHttpImagePipelineConfigFactory.newBuilder(context, okHttpClient)
.setRequestListeners(listeners)
.setDownsampleEnabled(true)
//.experiment().setBitmapPrepareToDraw(true).experiment()
.experiment().setPartialImageCachingEnabled(true).build()
.setRequestListeners(listeners)
.setDownsampleEnabled(true)
//.experiment().setBitmapPrepareToDraw(true).experiment()
.experiment().setPartialImageCachingEnabled(true).build()
}
@Provides
......@@ -196,7 +176,7 @@ class AppModule {
@Provides
@ForMessages
fun provideMessagesSharedPreferences(context: Application) =
context.getSharedPreferences("messages", Context.MODE_PRIVATE)
context.getSharedPreferences("messages", Context.MODE_PRIVATE)
@Provides
@Singleton
......@@ -230,17 +210,33 @@ 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()))
.build()
.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(ReactionsAdapter())
.build()
}
@Provides
......@@ -268,15 +264,15 @@ class AppModule {
fun provideConfiguration(context: Application, client: OkHttpClient): SpannableConfiguration {
val res = context.resources
return SpannableConfiguration.builder(context)
.asyncDrawableLoader(AsyncDrawableLoader.builder()
.client(client)
.executorService(Executors.newCachedThreadPool())
.resources(res)
.build())
.theme(SpannableTheme.builder()
.linkColor(res.getColor(R.color.colorAccent))
.build())
.build()
.asyncDrawableLoader(AsyncDrawableLoader.builder()
.client(client)
.executorService(Executors.newCachedThreadPool())
.resources(res)
.build())
.theme(SpannableTheme.builder()
.linkColor(res.getColor(R.color.colorAccent))
.build())
.build()
}
@Provides
......@@ -294,11 +290,11 @@ class AppModule {
@Provides
@Singleton
fun provideAccountsRepository(preferences: SharedPreferences, moshi: Moshi): AccountsRepository =
SharedPreferencesAccountsRepository(preferences, moshi)
SharedPreferencesAccountsRepository(preferences, moshi)
@Provides
fun provideNotificationManager(context: Application) =
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
@Provides
@Singleton
......@@ -307,12 +303,12 @@ class AppModule {
@Provides
@Singleton
fun providePushManager(
context: Application,
groupedPushes: GroupedPush,
manager: NotificationManager,
moshi: Moshi,
getAccountInteractor: GetAccountInteractor,
getSettingsInteractor: GetSettingsInteractor): PushManager {
context: Application,
groupedPushes: GroupedPush,
manager: NotificationManager,
moshi: Moshi,
getAccountInteractor: GetAccountInteractor,
getSettingsInteractor: GetSettingsInteractor): PushManager {
return PushManager(groupedPushes, manager, moshi, getAccountInteractor, getSettingsInteractor, context)
}
......@@ -324,9 +320,9 @@ class AppModule {
@Provides
fun provideSendMessageJob(context: Application): JobInfo {
return JobInfo.Builder(MessageService.RETRY_SEND_MESSAGE_ID,
ComponentName(context, MessageService::class.java))
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
.build()
ComponentName(context, MessageService::class.java))
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
.build()
}
@Provides
......
......@@ -54,13 +54,20 @@ object OauthHelper {
/**
* Returns the Gitlab Oauth URL.
*
* @param host The Gitlab host.
* @param clientId The Gitlab client ID.
* @param serverUrl The server URL.
* @param state An unguessable random string used to protect against forgery attacks.
* @return The Gitlab Oauth URL.
*/
fun getGitlabOauthUrl(clientId: String, serverUrl: String, state: String): String {
return "https://gitlab.com/oauth/authorize" +
fun getGitlabOauthUrl(
host: String? = "https://gitlab.com",
clientId: String,
serverUrl: String,
state: String
): String {
return host +
"/oauth/authorize" +
"?client_id=$clientId" +
"&redirect_uri=${serverUrl.removeTrailingSlash()}/_oauth/gitlab?close" +
"&state=$state" +
......
......@@ -135,7 +135,7 @@ class MainPresenter @Inject constructor(
navigator.toServerScreen()
}
fun changeStatus(userStatus: UserStatus) {
fun changeDefaultStatus(userStatus: UserStatus) {
launchUI(strategy) {
try {
client.setDefaultStatus(userStatus)
......@@ -186,9 +186,9 @@ class MainPresenter @Inject constructor(
private suspend fun subscribeMyselfUpdates() {
manager.addUserDataChannel(userDataChannel)
for (myself in userDataChannel) {
updateMyself(myself)
}
for (myself in userDataChannel) {
updateMyself(myself)
}
}
private suspend fun updateMyself(myself: Myself) {
......
......@@ -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)
)
}
}
......@@ -141,7 +138,7 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector, HasSupp
accounts_list.layoutManager = LinearLayoutManager(this)
accounts_list.adapter = AccountsAdapter(accounts, object : Selector {
override fun onStatusSelected(userStatus: UserStatus) {
presenter.changeStatus(userStatus)
presenter.changeDefaultStatus(userStatus)
}
override fun onAccountSelected(serverUrl: String) {
......
......@@ -14,12 +14,14 @@ import chat.rocket.core.RocketChatClient
import chat.rocket.core.internal.rest.getMembers
import javax.inject.Inject
class MembersPresenter @Inject constructor(private val view: MembersView,
private val navigator: MembersNavigator,
private val strategy: CancelStrategy,
private val serverInteractor: GetCurrentServerInteractor,
factory: RocketChatClientFactory,
private val mapper: MemberViewModelMapper) {
class MembersPresenter @Inject constructor(
private val view: MembersView,
private val navigator: MembersNavigator,
private val strategy: CancelStrategy,
serverInteractor: GetCurrentServerInteractor,
factory: RocketChatClientFactory,
private val mapper: MemberViewModelMapper
) {
private val client: RocketChatClient = factory.create(serverInteractor.get()!!)
fun loadChatRoomsMembers(chatRoomId: String, chatRoomType: String, offset: Long = 0) {
......@@ -49,7 +51,7 @@ class MembersPresenter @Inject constructor(private val view: MembersView,
val realName = memberViewModel.realName.toString()
val username = "@${memberViewModel.username}"
val email = memberViewModel.email ?: ""
val utcOffset = memberViewModel.utcOffset.toString()
val utcOffset = memberViewModel.utcOffset.toString()
navigator.toMemberDetails(avatarUri, realName, username, email, utcOffset)
}
......
......@@ -25,7 +25,6 @@ import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_members.*
import javax.inject.Inject
fun newInstance(chatRoomId: String, chatRoomType: String): Fragment {
return MembersFragment().apply {
arguments = Bundle(1).apply {
......
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
......@@ -8,15 +8,17 @@ import kotlinx.coroutines.experimental.async
import kotlinx.coroutines.experimental.withContext
import javax.inject.Inject
class RefreshSettingsInteractor @Inject constructor(private val factory: RocketChatClientFactory,
private val repository: SettingsRepository) {
class RefreshSettingsInteractor @Inject constructor(
private val factory: RocketChatClientFactory,
private val repository: SettingsRepository
) {
private var settingsFilter = arrayOf(
LDAP_ENABLE, CAS_ENABLE, CAS_LOGIN_URL,
ACCOUNT_REGISTRATION, ACCOUNT_LOGIN_FORM, ACCOUNT_PASSWORD_RESET, ACCOUNT_CUSTOM_FIELDS,
ACCOUNT_GOOGLE, ACCOUNT_FACEBOOK, ACCOUNT_GITHUB, ACCOUNT_LINKEDIN, ACCOUNT_METEOR,
ACCOUNT_TWITTER, ACCOUNT_WORDPRESS, ACCOUNT_GITLAB,
ACCOUNT_TWITTER, ACCOUNT_WORDPRESS, ACCOUNT_GITLAB, ACCOUNT_GITLAB_URL,
SITE_URL, SITE_NAME, FAVICON_512, FAVICON_196, USE_REALNAME, ALLOW_ROOM_NAME_SPECIAL_CHARS,
FAVORITE_ROOMS, UPLOAD_STORAGE_TYPE, UPLOAD_MAX_FILE_SIZE, UPLOAD_WHITELIST_MIMETYPES,
......
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
......@@ -26,6 +26,7 @@ const val ACCOUNT_METEOR = "Accounts_OAuth_Meteor"
const val ACCOUNT_TWITTER = "Accounts_OAuth_Twitter"
const val ACCOUNT_WORDPRESS = "Accounts_OAuth_Wordpress"
const val ACCOUNT_GITLAB = "Accounts_OAuth_Gitlab"
const val ACCOUNT_GITLAB_URL = "API_Gitlab_URL"
const val SITE_URL = "Site_Url"
const val SITE_NAME = "Site_Name"
......@@ -68,6 +69,7 @@ fun PublicSettings.isLinkedinAuthenticationEnabled(): Boolean = this[ACCOUNT_LIN
fun PublicSettings.isMeteorAuthenticationEnabled(): Boolean = this[ACCOUNT_METEOR]?.value == true
fun PublicSettings.isTwitterAuthenticationEnabled(): Boolean = this[ACCOUNT_TWITTER]?.value == true
fun PublicSettings.isGitlabAuthenticationEnabled(): Boolean = this[ACCOUNT_GITLAB]?.value == true
fun PublicSettings.gitlabUrl(): String? = this[ACCOUNT_GITLAB_URL]?.value as String?
fun PublicSettings.isWordpressAuthenticationEnabled(): Boolean = this[ACCOUNT_WORDPRESS]?.value == true
fun PublicSettings.useRealName(): Boolean = this[USE_REALNAME]?.value == true
......
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
package chat.rocket.android.settings.about.ui
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import chat.rocket.android.BuildConfig
import chat.rocket.android.R
import chat.rocket.android.util.extensions.textContent
......@@ -19,15 +19,13 @@ class AboutActivity : AppCompatActivity() {
}
private fun setupViews() {
val versionName = resources.getString(R.string.msg_version) +" "+BuildConfig.VERSION_NAME
val versionCode = resources.getString(R.string.msg_build)+" #"+ BuildConfig.VERSION_CODE
text_version_name.text = versionName
text_build_number.text = versionCode
text_version_name.text = getString(R.string.msg_version, BuildConfig.VERSION_NAME)
text_build_number.text = getString(R.string.msg_build, BuildConfig.VERSION_CODE)
}
private fun setupToolbar() {
setSupportActionBar(toolbar)
text_change_password.textContent = resources.getString(R.string.title_about)
text_change_password.textContent = getString(R.string.title_about)
}
override fun onBackPressed() {
......
......@@ -11,10 +11,12 @@ import chat.rocket.core.internal.rest.me
import chat.rocket.core.internal.rest.updateProfile
import javax.inject.Inject
class PasswordPresenter @Inject constructor (private val view: PasswordView,
private val strategy: CancelStrategy,
serverInteractor: GetCurrentServerInteractor,
factory: RocketChatClientFactory){
class PasswordPresenter @Inject constructor(
private val view: PasswordView,
private val strategy: CancelStrategy,
serverInteractor: GetCurrentServerInteractor,
factory: RocketChatClientFactory
) {
private val serverUrl = serverInteractor.get()!!
private val client: RocketChatClient = factory.create(serverUrl)
......
<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>
</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>
</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>
</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>
</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"
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/image_avatar"
android:layout_width="24dp"
android:layout_height="24dp"
app:roundedCornerRadius="3dp"
tools:src="@tools:sample/avatars" />
<ImageView
android:id="@+id/image_status"
android:layout_width="12dp"
android:layout_height="12dp"
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">
<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" />
<ImageView
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_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_centerVertical="true"
android:layout_toEndOf="@id/image_avatar_container"
android:layout_toRightOf="@id/image_avatar_container"
android:background="@color/suggestion_background_color">
<TextView
android:id="@+id/text_username"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:maxLines="1"
android:textColor="@color/black"
android:textSize="16sp"
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:maxLines="1"
android:paddingLeft="8dp"
android:paddingStart="8dp"
android:textColor="@color/gray_material"
android:textSize="16sp"
tools:text="@tools:sample/full_names" />
</RelativeLayout>
</RelativeLayout>
\ No newline at end of file
android:layout_marginStart="5dp"
android:ellipsize="end"
android:maxLines="1"
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" />
</android.support.constraint.ConstraintLayout>
\ No newline at end of file
......@@ -3,43 +3,30 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="2dp"
android:layout_marginBottom="2dp"
android:layout_marginEnd="2dp"
android:layout_marginStart="8dp"
android:layout_marginTop="2dp"
android:background="@color/suggestion_background_color">
<RelativeLayout
android:layout_width="match_parent"
<TextView
android:id="@+id/text_name"
android:layout_width="wrap_content"
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:maxLines="1"
android:textColor="@color/black"
android:textSize="16sp"
tools:text="@tools:sample/full_names" />
<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"
tools:text="@tools:sample/full_names" />
<TextView
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:textColor="@color/gray_material"
android:textSize="16sp"
tools:text="@tools:sample/full_names" />
</RelativeLayout>
<TextView
android:id="@+id/text_fullname"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toEndOf="@+id/text_name"
android:maxLines="1"
android:layout_marginStart="8dp"
android:textColor="@color/gray_material"
android:textSize="16sp"
tools:text="@tools:sample/full_names" />
</RelativeLayout>
\ No newline at end of file
<resources>
<!-- Titles -->
<string name="title_sign_in_your_server">Inicia sesión en tu servidor</string>
<string name="title_log_in">Iniciar sesión</string>
<string name="title_register_username">Registrar nombre de usuario</string>
<string name="title_sign_up">Regístrate</string>
<string name="title_authentication">Autenticación</string>
<string name="title_legal_terms">Términos legales</string>
<string name="title_chats">Chats</string>
<string name="title_profile">Perfil</string>
<string name="title_members">Miembros (%d)</string>
<string name="title_settings">Configuraciones</string>
<string name="title_password">Cambia la contraseña</string>
<string name="title_update_profile">Actualización del perfil</string>
<string name="title_about">Acerca de</string>
<!-- Actions -->
<string name="action_connect">Conectar</string>"'
<string name="action_use_this_username">Usa este nombre de usuario</string>
<string name="action_login_or_sign_up">Toca en este botón para iniciar sesión o crear una cuenta</string>
<string name="action_terms_of_service">Términos de Servicio</string>
<string name="action_privacy_policy">Política de Privacidad</string>
<string name="action_search">Buscar</string>
<string name="action_update">Actualizar</string>
<string name="action_settings">Configuraciones</string>
<string name="action_logout">Cerrar sesión</string>
<string name="action_files">Archivos</string>
<string name="action_confirm_password">Confirmar cambio de contraseña</string>
<string name="action_join_chat">Unirse al chat</string>
<string name="action_add_account">Añadir cuenta</string>
<string name="action_online">Conectado(s)</string>
<string name="action_away">Ausente</string>
<string name="action_busy">Ocupado</string>
<string name="action_invisible">Invisible</string>
<!-- Settings List -->
<string-array name="settings_actions">
<item name="item_password">Cambia la contraseña</item>
<item name="item_password">Acerca de</item>
</string-array>
<!-- Regular information messages -->
<string name="msg_generic_error">Lo sentimos, ha ocurrido un error, por favor intente de nuevo</string>
<string name="msg_no_data_to_display">No hay información para mostrar</string>
<string name="msg_profile_update_successfully">Actualización de perfil con éxito</string>
<string name="msg_username">usuario</string>
<string name="msg_username_or_email">nombre de usuario o correo electrónico</string>
<string name="msg_password">contraseña</string>
<string name="msg_name">nombre</string>
<string name="msg_email">correo electrónico</string>
<string name="msg_avatar_url">URL del avatar</string>
<string name="msg_or_continue_using_social_accounts">O continuar usando cuentas sociales</string>
<string name="msg_new_user">Nuevo usuario? %1$s</string>
<string name="msg_new_user_agreement">Al continuar estás aceptando nuestra\n%1$s y %2$s</string>
<string name="msg_2fa_code">Código 2FA</string>
<string name="msg_yesterday">Ayer</string>
<string name="msg_message">Mensaje</string>
<string name="msg_this_room_is_read_only">Esta sala es de solo lectura</string>
<string name="msg_invalid_2fa_code">Código 2FA no válido</string>
<string name="msg_invalid_file">Archivo inválido</string>
<string name="msg_invalid_server_url">URL del servidor inválido</string>
<string name="msg_content_description_log_in_using_facebook">Inicia sesión usando Facebook</string>
<string name="msg_content_description_log_in_using_github">Inicia sesión usando Github</string>
<string name="msg_content_description_log_in_using_google">Inicia sesión usando Google</string>
<string name="msg_content_description_log_in_using_linkedin">Inicia sesión usando Linkedin</string>
<string name="msg_content_description_log_in_using_meteor">Inicia sesión usando Meteor</string>
<string name="msg_content_description_log_in_using_twitter">Inicia sesión usando Twitter</string>
<string name="msg_content_description_log_in_using_gitlab">Inicia sesión usando Gitlab</string>
<string name="msg_content_description_send_message">Enviar mensaje</string>
<string name="msg_content_description_show_attachment_options">Mostrar opciones de archivo adjunto</string>
<string name="msg_you"></string>
<string name="msg_unknown">Desconocido</string>
<string name="msg_email_address">Dirección de correo electrónico</string>
<string name="msg_utc_offset">Desplazamiento UTC</string>
<string name="msg_new_password">Introduzca nueva contraseña</string>
<string name="msg_confirm_password">Confirmar nueva contraseña</string>
<string name="msg_unread_messages">Mensajes no leídos</string>
<string name="msg_preview_video">Vídeo</string>
<string name="msg_preview_audio">Audio</string>
<string name="msg_preview_photo">Foto</string>
<string name="msg_no_messages_yet">Aún no hay mensajes</string>
<string name="msg_version">Versión %1$s</string>
<string name="msg_build">Build %1$d</string>
<string name="msg_ok">OK</string>
<string name="msg_ver_not_recommended">
Parece que la versión de tu servidor está por debajo de la versión recomendada %1$s.\nAún puede iniciar sesión, pero puede experimentar comportamientos inesperados.</string>
<string name="msg_ver_not_minimum">
Parece que la versión del servidor está por debajo de la versión mínima requerida %1$s.\nActualice su servidor para iniciar sesión!
</string>
<string name="msg_proceed">PROCEDER</string>
<string name="msg_cancel">CANCELAR</string>
<string name="msg_warning">ADVERTENCIA</string>
<string name="msg_http_insecure">Al usar HTTP, te estás conectando a un servidor inseguro. No te recomendamos que hagas eso.</string>
<string name="msg_error_checking_server_version">Se ha producido un error al verificar la versión de su servidor, intente de nuevo</string>
<string name="msg_invalid_server_protocol">El protocolo seleccionado no es aceptado por este servidor, intente usar HTTPS</string>
<!-- System messages -->
<string name="message_room_name_changed">Nombre de la sala cambiado para: %1$s por %2$s</string>
<string name="message_user_added_by">Usuario %1$s añadido por %2$s</string>
<string name="message_user_removed_by">Usuario %1$s eliminado por %2$s</string>
<string name="message_user_left">Ha salido del canal.</string>
<string name="message_user_joined_channel">Se ha unido al canal.</string>
<string name="message_welcome">Bienvenido %s</string>
<string name="message_removed">Mensaje eliminado</string>
<string name="message_pinned">Fijado una mensaje:</string>
<!-- Message actions -->
<string name="action_msg_reply">Respuesta</string>
<string name="action_msg_edit">Editar</string>
<string name="action_msg_copy">Copiar</string>
<string name="action_msg_quote">Citar</string>
<string name="action_msg_delete">Borrar</string>
<string name="action_msg_pin">Fijar mensaje</string>
<string name="action_msg_unpin">Soltar mensaje</string>
<string name="action_msg_star">Star mensaje</string>
<string name="action_msg_share">Compartir</string>
<string name="action_title_editing">Edición de mensaje</string>
<string name="action_msg_add_reaction">Añadir una reacción</string>
<!-- Permission messages -->
<string name="permission_editing_not_allowed">La edición no és permitida</string>
<string name="permission_deleting_not_allowed">Eliminar no és permitido</string>
<string name="permission_pinning_not_allowed">Fijar no és permitido</string>
<!-- Members List -->
<string name="title_members_list">Lista de miembros</string>
<!-- Pinned Messages -->
<string name="title_pinned_messages">Mensajes fijados</string>
<string name="no_pinned_messages">Sin mensajes fijadas</string>
<string name="no_pinned_description">Todas las mensajes fijadas\naparecen aquí.</string>
<!-- Upload Messages -->
<string name="max_file_size_exceeded">Tamaño del archivo (%1$d bytes) excedió el tamaño máximo de carga de %2$d bytes</string>
<!-- Socket status -->
<string name="status_connected">Conectado</string>
<string name="status_disconnected">Desconectado</string>
<string name="status_connecting">Conectando</string>
<string name="status_authenticating">Autenticando</string>
<string name="status_disconnecting">Desconectando</string>
<string name="status_waiting">Conectando en %d segundos</string>
<!--Suggestions-->
<string name="suggest_all_description">Notificar a todos en esta sala</string>
<string name="suggest_here_description">Notificar usuarios activos en esta sala</string>
<!-- Slash Commands -->
<string name="Slash_Gimme_Description">Muestra ༼ つ ◕_◕ ༽つ antes de su mensaje</string>
<string name="Slash_LennyFace_Description">Mustra ( ͡° ͜ʖ ͡°) después de tu mensaje</string>
<string name="Slash_Shrug_Description">Muestra ¯\_(ツ)_/¯ después de tu mensaje</string>
<string name="Slash_Tableflip_Description">Muestra (╯°□°)╯︵ ┻━┻</string>
<string name="Slash_TableUnflip_Description">Muestra ┬─┬ ノ( ゜-゜ノ)</string>
<string name="Create_A_New_Channel">Crea un nuevo canal</string>
<string name="Show_the_keyboard_shortcut_list">Mostrar la lista de atajos de teclado</string>
<string name="Invite_user_to_join_channel_all_from">Invita a todos los usuarios de [#canal] a unirse a este canal</string>
<string name="Invite_user_to_join_channel_all_to">Invita a todos los usuarios de este canal a unirse a [#canal]</string>
<string name="Archive">Archivo</string>
<string name="Remove_someone_from_room">Quita a alguien de la sala</string>
<string name="Leave_the_current_channel">Deja el canal actual</string>
<string name="Displays_action_text">Muestra texto de acción</string>
<string name="Direct_message_someone">Mensaje directo a alguien</string>
<string name="Mute_someone_in_room">Silenciar a alguien en la sala</string>
<string name="Unmute_someone_in_room">Dejar de silenciar a alguien en la sala</string>
<string name="Invite_user_to_join_channel">Invita a un usuario a unirse a este canal</string>
<string name="Unarchive">Desarchivar</string>
<string name="Join_the_given_channel">Únete al canal dado</string>
<string name="Guggy_Command_Description">Genera un gif basado en el texto proporcionado</string>
<string name="Slash_Topic_Description">Establecer tema</string>
<!-- Emoji message-->
<string name="msg_no_recent_emoji">Sin emojis recientes</string>
<!-- Sorting and grouping-->
<string name="menu_chatroom_sort">Ordenar</string>
<string name="dialog_sort_title">Ordenar por</string>
<string name="dialog_sort_by_alphabet">Alfabético</string>
<string name="dialog_sort_by_activity">Actividad</string>
<string name="dialog_group_by_type">Agrupar por tipo</string>
<string name="dialog_group_favourites">Agrupar favoritos</string>
<string name="chatroom_header">Cabezazo</string>
<!--ChatRooms Headers-->
<string name="header_channel">Canales</string>
<string name="header_private_groups">Grupos privados</string>
<string name="header_direct_messages">Mensajes directos</string>
<string name="header_live_chats">Chats en vivo</string>
<string name="header_unknown">Desconocido</string>
<!--Notifications-->
<string name="notif_action_reply_hint">RESPUESTA</string>
<string name="notif_error_sending">La respuesta ha fallado. Inténtalo de nuevo.</string>
<string name="notif_success_sending">Mensaje enviado a %1$s!</string>
</resources>
<resources>
<!-- Titles -->
<string name="title_sign_in_your_server">Connectez-vous sur votre serveur</string>
<string name="title_log_in">S\'identifier</string>
<string name="title_register_username">Enregistrer le nom d\'utilisateur</string>
<string name="title_sign_up">S\'inscrire</string>
<string name="title_authentication">Authentification</string>
<string name="title_legal_terms">Termes légaux</string>
<string name="title_chats">Chats</string>
<string name="title_profile">Profil</string>
<string name="title_members">Membres (%d)</string>
<string name="title_settings">Paramètres</string>
<string name="title_password">Changer le mot de passe</string>
<string name="title_update_profile">Update profile</string>
<string name="title_about">Sur</string>
<!-- Actions -->
<string name="action_connect">Se connecter</string>
<string name="action_use_this_username">Utilisez ce nom d\'utilisateur</string>
<string name="action_login_or_sign_up">Touchez ce bouton pour vous connecter ou créer un compte</string>
<string name="action_terms_of_service">Conditions d\'utilisation</string>
<string name="action_privacy_policy">Politique de confidentialité</string>
<string name="action_search">Chercher</string>
<string name="action_update">Mettre à jour</string>
<string name="action_settings">Paramètres</string>
<string name="action_logout">Se déconnecter</string>
<string name="action_files">Fichiers</string>
<string name="action_confirm_password">Confirmer le mot de passe</string>
<string name="action_join_chat">Rejoignez le chat</string>
<string name="action_add_account">Ajouter un compte</string>
<string name="action_online">En ligne</string>
<string name="action_away">Loin</string>
<string name="action_busy">Occupé</string>
<string name="action_invisible">Invisible</string>
<!-- Settings List -->
<string-array name="settings_actions">
<item name="item_password">Changer le mot de passe</item>
<item name="item_password">Sur</item>
</string-array>
<!-- Regular information messages -->
<string name="msg_generic_error">Désolé, une erreur s\'est produite. Veuillez réessayer</string>
<string name="msg_no_data_to_display">Aucune donnée à afficher</string>
<string name="msg_profile_update_successfully">Mise à jour du profil avec succès</string>
<string name="msg_username">nom d\'utilisateur</string>
<string name="msg_username_or_email">nom d\'utilisateur ou email</string>
<string name="msg_password">mot de passe</string>
<string name="msg_name">prénom</string>
<string name="msg_email">email</string>
<string name="msg_avatar_url">URL de l\'avatar</string>
<string name="msg_or_continue_using_social_accounts">Ou continuer en utilisant les comptes sociaux</string>
<string name="msg_new_user">Nouvel utilisateur? %1$s</string>
<string name="msg_new_user_agreement">En procédant, vous acceptez notre\n%1$s et %2$s</string>
<string name="msg_2fa_code">Code 2FA</string>
<string name="msg_yesterday">Hier</string>
<string name="msg_message">Message</string>
<string name="msg_this_room_is_read_only">Cette salle est seulement de lecture</string>
<string name="msg_invalid_2fa_code">Code 2FA non valide</string>
<string name="msg_invalid_file">Fichier non valide</string>
<string name="msg_invalid_server_url">URL de serveur non valide</string>
<string name="msg_content_description_log_in_using_facebook">Connectez-vous en utilisant Facebook</string>
<string name="msg_content_description_log_in_using_github">Connectez-vous en utilisant Github</string>
<string name="msg_content_description_log_in_using_google">Connectez-vous en utilisant Google</string>
<string name="msg_content_description_log_in_using_linkedin">Connectez-vous en utilisant Linkedin</string>
<string name="msg_content_description_log_in_using_meteor">Connectez-vous en utilisant Meteor</string>
<string name="msg_content_description_log_in_using_twitter">Connectez-vous en utilisant Twitter</string>
<string name="msg_content_description_log_in_using_gitlab">Connectez-vous en utilisant Gitlab</string>
<string name="msg_content_description_send_message">Envoyer message</string>
<string name="msg_content_description_show_attachment_options">Afficher les options de fichiers</string>
<string name="msg_you">Toi</string>
<string name="msg_unknown">Inconnu</string>
<string name="msg_email_address">Adresse e-mail</string>
<string name="msg_utc_offset">Décalage UTC</string>
<string name="msg_new_password">Entrez un nouveau mot de passe</string>
<string name="msg_confirm_password">Confirmer le nouveau mot de passe</string>
<string name="msg_unread_messages">Messages non lus</string>
<string name="msg_preview_video">Vidéo</string>
<string name="msg_preview_audio">Audio</string>
<string name="msg_preview_photo">Photo</string>
<string name="msg_no_messages_yet">Aucun message pour le moment</string>
<string name="msg_version">Version %1$s</string>
<string name="msg_build">Build %1$d</string>
<string name="msg_ok">OK</string>
<string name="msg_ver_not_recommended">
On dirait que la version de votre serveur est en dessous de la version recommandée %1$s.\nVous pouvez toujours vous connecter mais vous pouvez rencontrer des comportements inattendus.</string>
<string name="msg_ver_not_minimum">
On dirait que la version de votre serveur est inférieure à la version minimale requise %1$s.\nVeuillez mettre à jour votre serveur pour vous connecter!
</string>
<string name="msg_proceed">PROCÉDER</string>
<string name="msg_cancel">ANNULER</string>
<string name="msg_warning">ATTENTION</string>
<string name="msg_http_insecure">Lorsque vous utilisez HTTP, vous vous connectez à un serveur non sécurisé. Nous ne vous recommandons pas de le faire.</string>
<string name="msg_error_checking_server_version">Une erreur est survenue lors de la vérification de la version de votre serveur, veuillez réessayer</string>
<string name="msg_invalid_server_protocol">Le protocole sélectionné n\'est pas accepté par ce serveur, essayez d\'utiliser HTTPS</string>
<!-- System messages -->
<string name="message_room_name_changed">Le nom de le salle a changé à: %1$s par %2$s</string>
<string name="message_user_added_by">Utilisateur %1$s ajouté par %2$s</string>
<string name="message_user_removed_by">Utilisateur %1$s enlevé par %2$s</string>
<string name="message_user_left">A quitté de la salle.</string>
<string name="message_user_joined_channel">A rejoint la salle.</string>
<string name="message_welcome">Bienvenue %s</string>
<string name="message_removed">Message supprimé</string>
<string name="message_pinned">Épinglé un message:</string>
<!-- Message actions -->
<string name="action_msg_reply">Répondre</string>
<string name="action_msg_edit">Modifier</string>
<string name="action_msg_copy">Copier</string>
<string name="action_msg_quote">Citation</string>
<string name="action_msg_delete">Effacer</string>
<string name="action_msg_pin">Épingle message</string>
<string name="action_msg_unpin">Enlever message</string>
<string name="action_msg_star">Star message</string>
<string name="action_msg_share">Partager</string>
<string name="action_title_editing">Modification du message</string>
<string name="action_msg_add_reaction">Ajouter une réaction</string>
<!-- Permission messages -->
<string name="permission_editing_not_allowed">L\'édition n\'est pas autorisée</string>
<string name="permission_deleting_not_allowed">La suppression n\'est pas autorisée</string>
<string name="permission_pinning_not_allowed">L\'épinglage n\'est pas autorisé</string>
<!-- Members List -->
<string name="title_members_list">Liste des membres</string>
<!-- Pinned Messages -->
<string name="title_pinned_messages">Messages épinglés</string>
<string name="no_pinned_messages">Aucun message épinglé</string>
<string name="no_pinned_description">Tous les messages épinglés\napparaissent ici.</string>
<!-- Upload Messages -->
<string name="max_file_size_exceeded">Taille du fichier (%1$d bytes) dépassé la taille de téléchargement maximale de %2$d bytes</string>
<!-- Socket status -->
<string name="status_connected">Connecté</string>
<string name="status_disconnected">Détaché</string>
<string name="status_connecting">Connexion</string>
<string name="status_authenticating">Authentification</string>
<string name="status_disconnecting">Déconnexion</string>
<string name="status_waiting">Connexion en %d secondes</string>
<!--Suggestions-->
<string name="suggest_all_description">Notifier tout dans cette salle</string>
<string name="suggest_here_description">Notifier les utilisateurs actifs dans cette salle</string>
<!-- Slash Commands -->
<string name="Slash_Gimme_Description">Affiche ༼ つ ◕_◕ ༽つ avant votre message</string>
<string name="Slash_LennyFace_Description">Affiche ( ͡° ͜ʖ ͡°) après votre message</string>
<string name="Slash_Shrug_Description">Affiche ¯\_(ツ)_/¯ après votre message</string>
<string name="Slash_Tableflip_Description">Affiche (╯°□°)╯︵ ┻━┻</string>
<string name="Slash_TableUnflip_Description">Affiche ┬─┬ ノ( ゜-゜ノ)</string>
<string name="Create_A_New_Channel">Créer une nouvelle salle</string>
<string name="Show_the_keyboard_shortcut_list">Afficher la liste des raccourcis clavier</string>
<string name="Invite_user_to_join_channel_all_from">Inviter tous les utilisateurs de [#salle] à rejoindre cette salle</string>
<string name="Invite_user_to_join_channel_all_to">Inviter tous les utilisateurs de cette salle à rejoindre [#salle]</string>
<string name="Archive">Archiver</string>
<string name="Remove_someone_from_room">Retirer quelqu\'un de la salle</string>
<string name="Leave_the_current_channel">Sortir de la salle actuelle</string>
<string name="Displays_action_text">Affiche le texte d\'action</string>
<string name="Direct_message_someone">Message direct avec quelqu\'un</string>
<string name="Mute_someone_in_room">Mettre en sourdine une personne dans la salle</string>
<string name="Unmute_someone_in_room">Retirer la sourdine d\'une personne dans la salle</string>
<string name="Invite_user_to_join_channel">Inviter un utilisateur à rejoindre cette salle</string>
<string name="Unarchive">Désarchiver</string>
<string name="Join_the_given_channel">Rejoignez la salle fourni</string>
<string name="Guggy_Command_Description">Génère un gif basé sur le texte fourni</string>
<string name="Slash_Topic_Description">Définir le sujet</string>
<!-- Emoji message-->
<string name="msg_no_recent_emoji">Aucun emoji récent</string>
<!-- Sorting and grouping-->
<string name="menu_chatroom_sort">Trier</string>
<string name="dialog_sort_title">Trier par</string>
<string name="dialog_sort_by_alphabet">Alphabétique</string>
<string name="dialog_sort_by_activity">Activité</string>
<string name="dialog_group_by_type">Grouper par type</string>
<string name="dialog_group_favourites">Grouper favoris</string>
<string name="chatroom_header">Entête</string>
<!--ChatRooms Headers-->
<string name="header_channel">Salles</string>
<string name="header_private_groups">Groupes privés</string>
<string name="header_direct_messages">Messages directs</string>
<string name="header_live_chats">Chats en direct</string>
<string name="header_unknown">Inconnu</string>
<!--Notifications-->
<string name="notif_action_reply_hint">RÉPONDRE</string>
<string name="notif_error_sending">La réponse a échoué. Veuillez réessayer.</string>
<string name="notif_success_sending">Message envoyé à %1$s!</string>
</resources>
......@@ -91,8 +91,6 @@
</string>
<string name="msg_version">वर्शन</string>
<string name="msg_build">बिल्ड</string>
<string name="msg_no_chat_title">कोई चैट संदेश नहीं</string>
<string name="msg_no_chat_description">यहां अपने संदेश देखने के लिए\nबातचीत शुरू करें।</string>
<!-- System messages -->
<string name="message_room_name_changed">%2$s ने रूम का नाम बदलकर %1$s किया</string>
......
......@@ -18,7 +18,7 @@
<!-- Actions -->
<string name="action_connect">Conectar</string>
<string name="action_use_this_username">Usar este nome de usuário</string>
<string name="action_login_or_sign_up">Toque este botão para fazer login ou criar uma conta</string>
<string name="action_login_or_sign_up">Toque neste botão para fazer login ou criar uma conta</string>
<string name="action_terms_of_service">Termos de Serviço</string>
<string name="action_privacy_policy">Política de Privacidade</string>
<string name="action_search">Pesquisar</string>
......@@ -59,7 +59,7 @@
<string name="msg_this_room_is_read_only">Este chat é apenas de leitura</string>
<string name="msg_invalid_2fa_code">Código 2FA inválido</string>
<string name="msg_invalid_file">Arquivo inválido</string>
<string name="msg_invalid_server_url">URL de servidor inválida</string>
<string name="msg_invalid_server_url">URL de servidor inválido</string>
<string name="msg_content_description_log_in_using_facebook">Fazer login através do Facebook</string>
<string name="msg_content_description_log_in_using_github">Fazer login através do Github</string>
<string name="msg_content_description_log_in_using_google">Fazer login através do Google</string>
......@@ -80,8 +80,8 @@
<string name="msg_preview_audio">Audio</string>
<string name="msg_preview_photo">Foto</string>
<string name="msg_no_messages_yet">Nenhuma mensagem ainda</string>
<string name="msg_version">Versão</string>
<string name="msg_build">Build</string>
<string name="msg_version">Versão %1$s</string>
<string name="msg_build">Build %1$d</string>
<string name="msg_ok">OK</string>
<string name="msg_ver_not_recommended">
Parece que a versão do seu servidor está abaixo da recomendada %1$s.\nVocê ainda assim pode logar e continuar mas podem ocorrer alguns problemas inesperados.
......@@ -103,7 +103,7 @@
<string name="message_user_added_by">Usuário %1$s adicionado por %2$s</string>
<string name="message_user_removed_by">Usuário %1$s removido por %2$s</string>
<string name="message_user_left">Saiu da sala.</string>
<string name="message_user_joined_channel">Entrou no sala.</string>
<string name="message_user_joined_channel">Entrou na sala.</string>
<string name="message_welcome">Bem-vindo, %s</string>
<string name="message_removed">Mensagem removida</string>
<string name="message_pinned">Pinou uma mensagem:</string>
......@@ -124,7 +124,7 @@
<!-- Permission messages -->
<string name="permission_editing_not_allowed">Edição não permitida</string>
<string name="permission_deleting_not_allowed">Remoção não permitida</string>
<string name="permission_pinning_not_allowed">Fixar não permitido</string>
<string name="permission_pinning_not_allowed">Pinagem não permitida</string>
<!-- Members List -->
<string name="title_members_list">Lista de Membros</string>
......@@ -164,8 +164,8 @@
<string name="Leave_the_current_channel">Sair do canal atual</string>
<string name="Displays_action_text">Exibir texto de ação</string>
<string name="Direct_message_someone">Enviar DM para alguém</string>
<string name="Mute_someone_in_room">Mutar alguém</string>
<string name="Unmute_someone_in_room">Desmutar alguém na sala</string>
<string name="Mute_someone_in_room">Silenciar alguém</string>
<string name="Unmute_someone_in_room">De-silenciar alguém na sala</string>
<string name="Invite_user_to_join_channel">Convidar algum usuário para entrar neste canal</string>
<string name="Unarchive">Desarquivar</string>
<string name="Join_the_given_channel">Entrar no canal especificado</string>
......@@ -195,4 +195,4 @@
<string name="notif_action_reply_hint">RESPONDER</string>
<string name="notif_error_sending">Falha ao enviar a mensagem.</string>
<string name="notif_success_sending">Mensagem enviada para %1$s!</string>
</resources>
\ No newline at end of file
</resources>
......@@ -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>
......
......@@ -82,8 +82,8 @@
<string name="msg_preview_audio">Audio</string>
<string name="msg_preview_photo">Photo</string>
<string name="msg_no_messages_yet">No messages yet</string>
<string name="msg_version">Version</string>
<string name="msg_build">Build</string>
<string name="msg_version">Version %1$s</string>
<string name="msg_build">Build %1$d</string>
<string name="msg_ok">OK</string>
<string name="msg_ver_not_recommended">
Looks like your server version is below the recommended version %1$s.\nYou can still login but you may experience unexpected behaviors.</string>
......
......@@ -3,6 +3,7 @@ package chat.rocket.android
import chat.rocket.android.server.infraestructure.MemoryMessagesRepository
import chat.rocket.core.model.Message
import chat.rocket.core.model.MessageType
import kotlinx.coroutines.experimental.runBlocking
import org.hamcrest.CoreMatchers.notNullValue
import org.hamcrest.MatcherAssert.assertThat
import org.junit.Before
......@@ -30,7 +31,9 @@ class MemoryMessagesRepositoryTest {
senderAlias = null,
type = MessageType.MessageRemoved(),
updatedAt = 1511443964815,
urls = null
urls = null,
pinned = false,
reactions = null
)
val msg2 = Message(
......@@ -50,86 +53,98 @@ class MemoryMessagesRepositoryTest {
senderAlias = null,
type = MessageType.MessageRemoved(),
updatedAt = 1511443964818,
urls = null
urls = null,
pinned = false,
reactions = null
)
@Before
fun setup() {
repository.clear()
runBlocking {
repository.clear()
}
}
@Test
fun `save() should save a single message`() {
assertThat(repository.getAll().size, isEqualTo(0))
repository.save(msg)
val allMessages = repository.getAll()
assertThat(allMessages.size, isEqualTo(1))
allMessages[0].apply {
assertThat(id, isEqualTo("messageId"))
assertThat(message, isEqualTo("Beam me up, Scotty."))
assertThat(roomId, isEqualTo("GENERAL"))
runBlocking {
assertThat(repository.getAll().size, isEqualTo(0))
repository.save(msg)
val allMessages = repository.getAll()
assertThat(allMessages.size, isEqualTo(1))
allMessages[0].apply {
assertThat(id, isEqualTo("messageId"))
assertThat(message, isEqualTo("Beam me up, Scotty."))
assertThat(roomId, isEqualTo("GENERAL"))
}
}
}
@Test
fun `saveAll() should all saved messages`() {
assertThat(repository.getAll().size, isEqualTo(0))
repository.saveAll(listOf(msg, msg2))
val allMessages = repository.getAll()
assertThat(allMessages.size, isEqualTo(2))
allMessages[0].apply {
assertThat(id, isEqualTo("messageId"))
assertThat(message, isEqualTo("Beam me up, Scotty."))
assertThat(roomId, isEqualTo("GENERAL"))
}
runBlocking {
assertThat(repository.getAll().size, isEqualTo(0))
repository.saveAll(listOf(msg, msg2))
val allMessages = repository.getAll()
assertThat(allMessages.size, isEqualTo(2))
allMessages[0].apply {
assertThat(id, isEqualTo("messageId"))
assertThat(message, isEqualTo("Beam me up, Scotty."))
assertThat(roomId, isEqualTo("GENERAL"))
}
allMessages[1].apply {
assertThat(id, isEqualTo("messageId2"))
assertThat(message, isEqualTo("Highly Illogical"))
assertThat(roomId, isEqualTo("sandbox"))
allMessages[1].apply {
assertThat(id, isEqualTo("messageId2"))
assertThat(message, isEqualTo("Highly Illogical"))
assertThat(roomId, isEqualTo("sandbox"))
}
}
}
@Test
fun `getById() should return a single message`() {
repository.saveAll(listOf(msg, msg2))
var singleMsg = repository.getById("messageId")
assertThat(singleMsg, notNullValue())
singleMsg!!.apply {
assertThat(id, isEqualTo("messageId"))
assertThat(message, isEqualTo("Beam me up, Scotty."))
assertThat(roomId, isEqualTo("GENERAL"))
}
runBlocking {
repository.saveAll(listOf(msg, msg2))
var singleMsg = repository.getById("messageId")
assertThat(singleMsg, notNullValue())
singleMsg!!.apply {
assertThat(id, isEqualTo("messageId"))
assertThat(message, isEqualTo("Beam me up, Scotty."))
assertThat(roomId, isEqualTo("GENERAL"))
}
singleMsg = repository.getById("messageId2")
assertThat(singleMsg, notNullValue())
singleMsg!!.apply {
assertThat(id, isEqualTo("messageId2"))
assertThat(message, isEqualTo("Highly Illogical"))
assertThat(roomId, isEqualTo("sandbox"))
singleMsg = repository.getById("messageId2")
assertThat(singleMsg, notNullValue())
singleMsg!!.apply {
assertThat(id, isEqualTo("messageId2"))
assertThat(message, isEqualTo("Highly Illogical"))
assertThat(roomId, isEqualTo("sandbox"))
}
}
}
@Test
fun `getByRoomId() should return all messages for room id or an empty list`() {
repository.saveAll(listOf(msg, msg2))
var roomMessages = repository.getByRoomId("faAad32fkasods2")
assertThat(roomMessages.isEmpty(), isEqualTo(true))
runBlocking {
repository.saveAll(listOf(msg, msg2))
var roomMessages = repository.getByRoomId("faAad32fkasods2")
assertThat(roomMessages.isEmpty(), isEqualTo(true))
roomMessages = repository.getByRoomId("sandbox")
assertThat(roomMessages.size, isEqualTo(1))
roomMessages[0].apply {
assertThat(id, isEqualTo("messageId2"))
assertThat(message, isEqualTo("Highly Illogical"))
assertThat(roomId, isEqualTo("sandbox"))
}
roomMessages = repository.getByRoomId("sandbox")
assertThat(roomMessages.size, isEqualTo(1))
roomMessages[0].apply {
assertThat(id, isEqualTo("messageId2"))
assertThat(message, isEqualTo("Highly Illogical"))
assertThat(roomId, isEqualTo("sandbox"))
}
roomMessages = repository.getByRoomId("GENERAL")
assertThat(roomMessages.size, isEqualTo(1))
roomMessages[0].apply {
assertThat(id, isEqualTo("messageId"))
assertThat(message, isEqualTo("Beam me up, Scotty."))
assertThat(roomId, isEqualTo("GENERAL"))
roomMessages = repository.getByRoomId("GENERAL")
assertThat(roomMessages.size, isEqualTo(1))
roomMessages[0].apply {
assertThat(id, isEqualTo("messageId"))
assertThat(message, isEqualTo("Beam me up, Scotty."))
assertThat(roomId, isEqualTo("GENERAL"))
}
}
}
}
\ No newline at end of file
ext {
versions = [
java : JavaVersion.VERSION_1_8,
compileSdk : 27,
targetSdk : 27,
buildTools : '27.0.3',
kotlin : '1.2.31',
coroutine : '0.22.5',
dokka : '0.9.16',
// Main dependencies
support : '27.1.0',
constraintLayout : '1.0.2',
androidKtx : '0.3',
dagger : '2.14.1',
exoPlayer : '2.6.0',
playServices : '11.8.0',
room : '1.0.0',
rxKotlin : '2.2.0',
rxAndroid : '2.0.2',
moshi : '1.6.0-SNAPSHOT',
okhttp : '3.10.0',
timber : '4.7.0',
threeTenABP : '1.0.5',
rxBinding : '2.0.0',
fresco : '1.8.1',
kotshi : '1.0.2',
frescoImageViewer : '0.5.1',
markwon : '1.0.3',
sheetMenu : '1.3.3',
aVLoadingIndicatorView : '2.1.3',
flexbox : '0.3.2',
// For testing
junit : '4.12',
truth : '0.36',
expresso : '3.0.1',
mockito : '2.10.0'
java : JavaVersion.VERSION_1_8,
compileSdk : 27,
targetSdk : 27,
buildTools : '27.0.3',
kotlin : '1.2.31',
coroutine : '0.22.5',
dokka : '0.9.16',
// Main dependencies
support : '27.1.0',
constraintLayout : '1.0.2',
androidKtx : '0.3',
dagger : '2.14.1',
exoPlayer : '2.6.0',
playServices : '11.8.0',
room : '1.0.0',
lifecycle : '1.1.1',
rxKotlin : '2.2.0',
rxAndroid : '2.0.2',
moshi : '1.6.0-SNAPSHOT',
okhttp : '3.10.0',
timber : '4.7.0',
threeTenABP : '1.0.5',
rxBinding : '2.0.0',
fresco : '1.8.1',
kotshi : '1.0.2',
frescoImageViewer : '0.5.1',
markwon : '1.0.3',
sheetMenu : '1.3.3',
aVLoadingIndicatorView: '2.1.3',
flexbox : '0.3.2',
// For testing
junit : '4.12',
truth : '0.36',
expresso : '3.0.1',
mockito : '2.10.0'
]
libraries = [
kotlin : "org.jetbrains.kotlin:kotlin-stdlib-jre8:${versions.kotlin}",
coroutines : "org.jetbrains.kotlinx:kotlinx-coroutines-core:${versions.coroutine}",
coroutinesAndroid : "org.jetbrains.kotlinx:kotlinx-coroutines-android:${versions.coroutine}",
kotlin : "org.jetbrains.kotlin:kotlin-stdlib-jre8:${versions.kotlin}",
coroutines : "org.jetbrains.kotlinx:kotlinx-coroutines-core:${versions.coroutine}",
coroutinesAndroid : "org.jetbrains.kotlinx:kotlinx-coroutines-android:${versions.coroutine}",
appCompat : "com.android.support:appcompat-v7:${versions.support}",
annotations : "com.android.support:support-annotations:${versions.support}",
recyclerview : "com.android.support:recyclerview-v7:${versions.support}",
design : "com.android.support:design:${versions.support}",
constraintLayout : "com.android.support.constraint:constraint-layout:${versions.constraintLayout}",
cardView : "com.android.support:cardview-v7:${versions.support}",
flexbox : "com.google.android:flexbox:${versions.flexbox}",
customTabs : "com.android.support:customtabs:${versions.support}",
appCompat : "com.android.support:appcompat-v7:${versions.support}",
annotations : "com.android.support:support-annotations:${versions.support}",
recyclerview : "com.android.support:recyclerview-v7:${versions.support}",
design : "com.android.support:design:${versions.support}",
constraintLayout : "com.android.support.constraint:constraint-layout:${versions.constraintLayout}",
cardView : "com.android.support:cardview-v7:${versions.support}",
flexbox : "com.google.android:flexbox:${versions.flexbox}",
customTabs : "com.android.support:customtabs:${versions.support}",
androidKtx : "androidx.core:core-ktx:${versions.androidKtx}",
androidKtx : "androidx.core:core-ktx:${versions.androidKtx}",
dagger : "com.google.dagger:dagger:${versions.dagger}",
daggerSupport : "com.google.dagger:dagger-android-support:${versions.dagger}",
daggerProcessor : "com.google.dagger:dagger-compiler:${versions.dagger}",
daggerAndroidApt : "com.google.dagger:dagger-android-processor:${versions.dagger}",
playServicesGcm : "com.google.android.gms:play-services-gcm:${versions.playServices}",
exoPlayer : "com.google.android.exoplayer:exoplayer:${versions.exoPlayer}",
dagger : "com.google.dagger:dagger:${versions.dagger}",
daggerSupport : "com.google.dagger:dagger-android-support:${versions.dagger}",
daggerProcessor : "com.google.dagger:dagger-compiler:${versions.dagger}",
daggerAndroidApt : "com.google.dagger:dagger-android-processor:${versions.dagger}",
playServicesGcm : "com.google.android.gms:play-services-gcm:${versions.playServices}",
exoPlayer : "com.google.android.exoplayer:exoplayer:${versions.exoPlayer}",
room : "android.arch.persistence.room:runtime:${versions.room}",
roomProcessor : "android.arch.persistence.room:compiler:${versions.room}",
roomRxjava : "android.arch.persistence.room:rxjava2:${versions.room}",
room : "android.arch.persistence.room:runtime:${versions.room}",
roomProcessor : "android.arch.persistence.room:compiler:${versions.room}",
roomRxjava : "android.arch.persistence.room:rxjava2:${versions.room}",
lifecycleExtensions : "android.arch.lifecycle:extensions:${versions.lifecycle}",
lifecycleCompiler : "android.arch.lifecycle:compiler:${versions.lifecycle}",
rxKotlin : "io.reactivex.rxjava2:rxkotlin:${versions.rxKotlin}",
rxAndroid : "io.reactivex.rxjava2:rxandroid:${versions.rxAndroid}",
rxKotlin : "io.reactivex.rxjava2:rxkotlin:${versions.rxKotlin}",
rxAndroid : "io.reactivex.rxjava2:rxandroid:${versions.rxAndroid}",
moshi : "com.squareup.moshi:moshi:${versions.moshi}",
moshiKotlin : "com.squareup.moshi:moshi-kotlin:${versions.moshi}",
okhttp : "com.squareup.okhttp3:okhttp:${versions.okhttp}",
okhttpLogger : "com.squareup.okhttp3:logging-interceptor:${versions.okhttp}",
moshi : "com.squareup.moshi:moshi:${versions.moshi}",
moshiKotlin : "com.squareup.moshi:moshi-kotlin:${versions.moshi}",
okhttp : "com.squareup.okhttp3:okhttp:${versions.okhttp}",
okhttpLogger : "com.squareup.okhttp3:logging-interceptor:${versions.okhttp}",
timber : "com.jakewharton.timber:timber:${versions.timber}",
threeTenABP : "com.jakewharton.threetenabp:threetenabp:${versions.threeTenABP}",
rxBinding : "com.jakewharton.rxbinding2:rxbinding-kotlin:${versions.rxBinding}",
timber : "com.jakewharton.timber:timber:${versions.timber}",
threeTenABP : "com.jakewharton.threetenabp:threetenabp:${versions.threeTenABP}",
rxBinding : "com.jakewharton.rxbinding2:rxbinding-kotlin:${versions.rxBinding}",
fresco : "com.facebook.fresco:fresco:${versions.fresco}",
frescoOkHttp : "com.facebook.fresco:imagepipeline-okhttp3:${versions.fresco}",
frescoAnimatedGif : "com.facebook.fresco:animated-gif:${versions.fresco}",
frescoWebP : "com.facebook.fresco:webpsupport:${versions.fresco}",
frescoAnimatedWebP : "com.facebook.fresco:animated-webp:${versions.fresco}",
fresco : "com.facebook.fresco:fresco:${versions.fresco}",
frescoOkHttp : "com.facebook.fresco:imagepipeline-okhttp3:${versions.fresco}",
frescoAnimatedGif : "com.facebook.fresco:animated-gif:${versions.fresco}",
frescoWebP : "com.facebook.fresco:webpsupport:${versions.fresco}",
frescoAnimatedWebP : "com.facebook.fresco:animated-webp:${versions.fresco}",
kotshiApi : "se.ansman.kotshi:api:${versions.kotshi}",
kotshiCompiler : "se.ansman.kotshi:compiler:${versions.kotshi}",
kotshiApi : "se.ansman.kotshi:api:${versions.kotshi}",
kotshiCompiler : "se.ansman.kotshi:compiler:${versions.kotshi}",
frescoImageViewer : "com.github.luciofm:FrescoImageViewer:${versions.frescoImageViewer}",
frescoImageViewer : "com.github.luciofm:FrescoImageViewer:${versions.frescoImageViewer}",
markwon : "ru.noties:markwon:${versions.markwon}",
markwonImageLoader : "ru.noties:markwon-image-loader:${versions.markwon}",
markwon : "ru.noties:markwon:${versions.markwon}",
markwonImageLoader : "ru.noties:markwon-image-loader:${versions.markwon}",
sheetMenu : "com.github.whalemare:sheetmenu:${versions.sheetMenu}",
sheetMenu : "com.github.whalemare:sheetmenu:${versions.sheetMenu}",
aVLoadingIndicatorView : "com.wang.avi:library:${versions.aVLoadingIndicatorView}",
aVLoadingIndicatorView: "com.wang.avi:library:${versions.aVLoadingIndicatorView}",
// For testing
junit : "junit:junit:$versions.junit",
expressoCore : "com.android.support.test.espresso:espresso-core:${versions.expresso}",
roomTest : "android.arch.persistence.room:testing:${versions.room}",
truth : "com.google.truth:truth:$versions.truth",
junit : "junit:junit:$versions.junit",
expressoCore : "com.android.support.test.espresso:espresso-core:${versions.expresso}",
roomTest : "android.arch.persistence.room:testing:${versions.room}",
truth : "com.google.truth:truth:$versions.truth",
]
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment