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: ...@@ -57,7 +57,7 @@ jobs:
command: ./gradlew lint command: ./gradlew lint
- run: - run:
name: Run Unit test name: Run Unit test
command: echo ./gradlew test # TODO: Fix unit test errors soon... command: ./gradlew test
- store_artifacts: - store_artifacts:
path: app/build/reports/ path: app/build/reports/
destination: 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 { ...@@ -13,8 +13,8 @@ android {
applicationId "chat.rocket.android" applicationId "chat.rocket.android"
minSdkVersion 21 minSdkVersion 21
targetSdkVersion versions.targetSdk targetSdkVersion versions.targetSdk
versionCode 2014 versionCode 2015
versionName "2.0.4" versionName "2.1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
multiDexEnabled true multiDexEnabled true
} }
...@@ -47,10 +47,6 @@ android { ...@@ -47,10 +47,6 @@ android {
packagingOptions { packagingOptions {
exclude 'META-INF/core.kotlin_module' exclude 'META-INF/core.kotlin_module'
} }
lintOptions{
disable 'MissingTranslation'
}
} }
dependencies { dependencies {
...@@ -82,6 +78,8 @@ dependencies { ...@@ -82,6 +78,8 @@ dependencies {
implementation libraries.room implementation libraries.room
kapt libraries.roomProcessor kapt libraries.roomProcessor
implementation libraries.roomRxjava implementation libraries.roomRxjava
implementation libraries.lifecycleExtensions
kapt libraries.lifecycleCompiler
implementation libraries.rxKotlin implementation libraries.rxKotlin
implementation libraries.rxAndroid implementation libraries.rxAndroid
......
...@@ -49,37 +49,52 @@ ...@@ -49,37 +49,52 @@
android:scheme="https" /> android:scheme="https" />
</intent-filter> </intent-filter>
</activity> </activity>
<activity <activity
android:name=".server.ui.ChangeServerActivity" android:name=".server.ui.ChangeServerActivity"
android:theme="@style/AuthenticationTheme" /> android:theme="@style/AuthenticationTheme" />
<activity <activity
android:name=".main.ui.MainActivity" android:name=".main.ui.MainActivity"
android:theme="@style/AppTheme" android:theme="@style/AppTheme"
android:windowSoftInputMode="adjustResize|stateAlwaysHidden" /> android:windowSoftInputMode="adjustResize|stateAlwaysHidden" />
<activity <activity
android:name=".webview.ui.WebViewActivity" android:name=".webview.ui.WebViewActivity"
android:theme="@style/AppTheme" android:theme="@style/AppTheme"
android:windowSoftInputMode="adjustResize|stateAlwaysHidden" /> android:windowSoftInputMode="adjustResize|stateAlwaysHidden" />
<activity <activity
android:name=".webview.cas.ui.CasWebViewActivity" android:name=".webview.cas.ui.CasWebViewActivity"
android:theme="@style/AppTheme" android:theme="@style/AppTheme"
android:windowSoftInputMode="adjustResize|stateAlwaysHidden" /> android:windowSoftInputMode="adjustResize|stateAlwaysHidden" />
<activity <activity
android:name=".webview.oauth.ui.OauthWebViewActivity" android:name=".webview.oauth.ui.OauthWebViewActivity"
android:theme="@style/AppTheme" android:theme="@style/AppTheme"
android:windowSoftInputMode="adjustResize|stateAlwaysHidden" /> android:windowSoftInputMode="adjustResize|stateAlwaysHidden" />
<activity <activity
android:name=".chatroom.ui.ChatRoomActivity" android:name=".chatroom.ui.ChatRoomActivity"
android:theme="@style/AppTheme" android:theme="@style/AppTheme"
android:windowSoftInputMode="adjustResize|stateAlwaysHidden" /> android:windowSoftInputMode="adjustResize|stateAlwaysHidden" />
<!-- TODO: Change to fragment-->
<activity <activity
android:name=".chatroom.ui.PinnedMessagesActivity" android:name=".chatroom.ui.PinnedMessagesActivity"
android:theme="@style/AppTheme" android:theme="@style/AppTheme"
android:windowSoftInputMode="adjustResize|stateAlwaysHidden" /> android:windowSoftInputMode="adjustResize|stateAlwaysHidden" />
<!-- TODO: Change to fragment-->
<activity <activity
android:name=".settings.password.ui.PasswordActivity" android:name=".settings.password.ui.PasswordActivity"
android:theme="@style/AppTheme" /> android:theme="@style/AppTheme" />
<!-- TODO: Change to fragment-->
<activity
android:name=".settings.about.ui.AboutActivity"
android:theme="@style/AppTheme" />
<receiver <receiver
android:name="com.google.android.gms.gcm.GcmReceiver" android:name="com.google.android.gms.gcm.GcmReceiver"
android:exported="true" android:exported="true"
...@@ -123,10 +138,6 @@ ...@@ -123,10 +138,6 @@
<meta-data <meta-data
android:name="io.fabric.ApiKey" android:name="io.fabric.ApiKey"
android:value="12ac6e94f850aaffcdff52001af77ca415d06a43" /> android:value="12ac6e94f850aaffcdff52001af77ca415d06a43" />
<activity
android:name=".settings.about.ui.AboutActivity"
android:theme="@style/AppTheme" />
</application> </application>
</manifest> </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 { ...@@ -108,18 +108,12 @@ object DrawableHelper {
* @see [UserStatus] * @see [UserStatus]
* @return The user status drawable. * @return The user status drawable.
*/ */
fun getUserStatusDrawable(userStatus: UserStatus, context: Context): Drawable { fun getUserStatusDrawable(userStatus: UserStatus?, context: Context): Drawable {
return when (userStatus) { return when (userStatus) {
is UserStatus.Online -> { is UserStatus.Online -> getDrawableFromId(R.drawable.ic_status_online_12dp, context)
getDrawableFromId(R.drawable.ic_status_online_24dp, context) is UserStatus.Away -> getDrawableFromId(R.drawable.ic_status_away_12dp, context)
} is UserStatus.Busy -> getDrawableFromId(R.drawable.ic_status_busy_12dp, context)
is UserStatus.Away -> { else -> getDrawableFromId(R.drawable.ic_status_invisible_12dp, context)
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)
} }
} }
} }
\ No newline at end of file
...@@ -3,6 +3,7 @@ package chat.rocket.android.app ...@@ -3,6 +3,7 @@ package chat.rocket.android.app
import android.app.Activity import android.app.Activity
import android.app.Application import android.app.Application
import android.app.Service import android.app.Service
import android.arch.lifecycle.ProcessLifecycleOwner
import android.content.BroadcastReceiver import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.SharedPreferences import android.content.SharedPreferences
...@@ -43,10 +44,12 @@ import timber.log.Timber ...@@ -43,10 +44,12 @@ import timber.log.Timber
import java.lang.ref.WeakReference import java.lang.ref.WeakReference
import javax.inject.Inject import javax.inject.Inject
class RocketChatApplication : Application(), HasActivityInjector, HasServiceInjector, class RocketChatApplication : Application(), HasActivityInjector, HasServiceInjector,
HasBroadcastReceiverInjector { HasBroadcastReceiverInjector {
@Inject
lateinit var appLifecycleObserver: AppLifecycleObserver
@Inject @Inject
lateinit var activityDispatchingAndroidInjector: DispatchingAndroidInjector<Activity> lateinit var activityDispatchingAndroidInjector: DispatchingAndroidInjector<Activity>
...@@ -84,7 +87,14 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje ...@@ -84,7 +87,14 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje
override fun onCreate() { override fun onCreate() {
super.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. // TODO - remove this on the future, temporary migration stuff for pre-release versions.
migrateInternalTokens() migrateInternalTokens()
......
...@@ -126,7 +126,7 @@ class LoginPresenter @Inject constructor( ...@@ -126,7 +126,7 @@ class LoginPresenter @Inject constructor(
} }
private fun setupUserRegistrationView() { private fun setupUserRegistrationView() {
if (settings.isRegistrationEnabledForNewUsers()) { if (settings.isRegistrationEnabledForNewUsers() && settings.isLoginFormEnabled()) {
view.showSignUpView() view.showSignUpView()
view.setupSignUpView() view.setupSignUpView()
} }
...@@ -187,7 +187,21 @@ class LoginPresenter @Inject constructor( ...@@ -187,7 +187,21 @@ class LoginPresenter @Inject constructor(
if (settings.isGitlabAuthenticationEnabled()) { if (settings.isGitlabAuthenticationEnabled()) {
val clientId = getOauthClientId(services, SERVICE_NAME_GILAB) val clientId = getOauthClientId(services, SERVICE_NAME_GILAB)
if (clientId != null) { 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() view.enableLoginByGitlab()
totalSocialAccountsEnabled++ totalSocialAccountsEnabled++
} }
...@@ -280,9 +294,9 @@ class LoginPresenter @Inject constructor( ...@@ -280,9 +294,9 @@ class LoginPresenter @Inject constructor(
} }
private fun getOauthClientId(listMap: List<Map<String, Any>>, serviceName: String): String? { private fun getOauthClientId(listMap: List<Map<String, Any>>, serviceName: String): String? {
return listMap.find { map -> map.containsValue(serviceName) } return listMap.find { map -> map.containsValue(serviceName) }?.let {
?.get("clientId") it["clientId"] ?: it["appId"]
.toString() }.toString()
} }
private suspend fun saveAccount(username: String) { private suspend fun saveAccount(username: String) {
......
...@@ -42,7 +42,8 @@ import timber.log.Timber ...@@ -42,7 +42,8 @@ import timber.log.Timber
import java.util.* import java.util.*
import javax.inject.Inject import javax.inject.Inject
class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView, class ChatRoomPresenter @Inject constructor(
private val view: ChatRoomView,
private val navigator: ChatRoomNavigator, private val navigator: ChatRoomNavigator,
private val strategy: CancelStrategy, private val strategy: CancelStrategy,
getSettingsInteractor: GetSettingsInteractor, getSettingsInteractor: GetSettingsInteractor,
...@@ -56,7 +57,8 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView, ...@@ -56,7 +57,8 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
private val localRepository: LocalRepository, private val localRepository: LocalRepository,
factory: ConnectionManagerFactory, factory: ConnectionManagerFactory,
private val mapper: ViewModelMapper, private val mapper: ViewModelMapper,
private val jobSchedulerInteractor: JobSchedulerInteractor) { private val jobSchedulerInteractor: JobSchedulerInteractor
) {
private val currentServer = serverInteractor.get()!! private val currentServer = serverInteractor.get()!!
private val manager = factory.create(currentServer) private val manager = factory.create(currentServer)
private val client = manager.client private val client = manager.client
...@@ -510,7 +512,7 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView, ...@@ -510,7 +512,7 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
fun loadChatRooms() { fun loadChatRooms() {
launchUI(strategy) { launchUI(strategy) {
try { try {
val chatRooms = getChatRoomsInteractor.get(currentServer) val chatRooms = getChatRoomsInteractor.getAll(currentServer)
.filterNot { .filterNot {
it.type is RoomType.DirectMessage || it.type is RoomType.Livechat it.type is RoomType.DirectMessage || it.type is RoomType.Livechat
} }
......
...@@ -4,27 +4,24 @@ import chat.rocket.android.chatroom.viewmodel.ViewModelMapper ...@@ -4,27 +4,24 @@ import chat.rocket.android.chatroom.viewmodel.ViewModelMapper
import chat.rocket.android.core.lifecycle.CancelStrategy import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.server.domain.GetChatRoomsInteractor import chat.rocket.android.server.domain.GetChatRoomsInteractor
import chat.rocket.android.server.domain.GetCurrentServerInteractor import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.domain.GetSettingsInteractor
import chat.rocket.android.server.infraestructure.RocketChatClientFactory import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.extensions.launchUI import chat.rocket.android.util.extensions.launchUI
import chat.rocket.common.RocketChatException import chat.rocket.common.RocketChatException
import chat.rocket.common.util.ifNull import chat.rocket.common.util.ifNull
import chat.rocket.core.internal.rest.getRoomPinnedMessages import chat.rocket.core.internal.rest.getRoomPinnedMessages
import chat.rocket.core.model.Value
import chat.rocket.core.model.isSystemMessage import chat.rocket.core.model.isSystemMessage
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
class PinnedMessagesPresenter @Inject constructor(private val view: PinnedMessagesView, class PinnedMessagesPresenter @Inject constructor(
private val view: PinnedMessagesView,
private val strategy: CancelStrategy, private val strategy: CancelStrategy,
private val serverInteractor: GetCurrentServerInteractor, private val serverInteractor: GetCurrentServerInteractor,
private val roomsInteractor: GetChatRoomsInteractor, private val roomsInteractor: GetChatRoomsInteractor,
private val mapper: ViewModelMapper, private val mapper: ViewModelMapper,
factory: RocketChatClientFactory, factory: RocketChatClientFactory
getSettingsInteractor: GetSettingsInteractor) { ) {
private val client = factory.create(serverInteractor.get()!!) private val client = factory.create(serverInteractor.get()!!)
private var settings: Map<String, Value<Any>> = getSettingsInteractor.get(serverInteractor.get()!!)
private var pinnedMessagesListOffset: Int = 0 private var pinnedMessagesListOffset: Int = 0
/** /**
......
...@@ -3,7 +3,6 @@ package chat.rocket.android.chatroom.ui ...@@ -3,7 +3,6 @@ package chat.rocket.android.chatroom.ui
import DrawableHelper import DrawableHelper
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.graphics.drawable.Drawable
import android.os.Bundle import android.os.Bundle
import android.support.v4.app.Fragment import android.support.v4.app.Fragment
import android.support.v7.app.AppCompatActivity import android.support.v7.app.AppCompatActivity
...@@ -15,22 +14,21 @@ import chat.rocket.android.util.extensions.addFragment ...@@ -15,22 +14,21 @@ import chat.rocket.android.util.extensions.addFragment
import chat.rocket.android.util.extensions.textContent import chat.rocket.android.util.extensions.textContent
import chat.rocket.common.model.RoomType import chat.rocket.common.model.RoomType
import chat.rocket.common.model.roomTypeOf import chat.rocket.common.model.roomTypeOf
import chat.rocket.common.util.ifNull
import dagger.android.AndroidInjection import dagger.android.AndroidInjection
import dagger.android.AndroidInjector import dagger.android.AndroidInjector
import dagger.android.DispatchingAndroidInjector import dagger.android.DispatchingAndroidInjector
import dagger.android.support.HasSupportFragmentInjector import dagger.android.support.HasSupportFragmentInjector
import kotlinx.android.synthetic.main.app_bar_chat_room.* import kotlinx.android.synthetic.main.app_bar_chat_room.*
import javax.inject.Inject import javax.inject.Inject
import timber.log.Timber
fun Context.chatRoomIntent(
fun Context.chatRoomIntent(chatRoomId: String, chatRoomId: String,
chatRoomName: String, chatRoomName: String,
chatRoomType: String, chatRoomType: String,
isChatRoomReadOnly: Boolean, isChatRoomReadOnly: Boolean,
chatRoomLastSeen: Long, chatRoomLastSeen: Long,
isChatRoomSubscribed: Boolean = true): Intent { isChatRoomSubscribed: Boolean = true
): Intent {
return Intent(this, ChatRoomActivity::class.java).apply { return Intent(this, ChatRoomActivity::class.java).apply {
putExtra(INTENT_CHAT_ROOM_ID, chatRoomId) putExtra(INTENT_CHAT_ROOM_ID, chatRoomId)
putExtra(INTENT_CHAT_ROOM_NAME, chatRoomName) putExtra(INTENT_CHAT_ROOM_NAME, chatRoomName)
......
...@@ -38,12 +38,14 @@ import kotlinx.android.synthetic.main.message_list.* ...@@ -38,12 +38,14 @@ import kotlinx.android.synthetic.main.message_list.*
import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.atomic.AtomicInteger
import javax.inject.Inject import javax.inject.Inject
fun newInstance(chatRoomId: String, fun newInstance(
chatRoomId: String,
chatRoomName: String, chatRoomName: String,
chatRoomType: String, chatRoomType: String,
isChatRoomReadOnly: Boolean, isChatRoomReadOnly: Boolean,
chatRoomLastSeen: Long, chatRoomLastSeen: Long,
isSubscribed: Boolean = true): Fragment { isSubscribed: Boolean = true
): Fragment {
return ChatRoomFragment().apply { return ChatRoomFragment().apply {
arguments = Bundle(1).apply { arguments = Bundle(1).apply {
putString(BUNDLE_CHAT_ROOM_ID, chatRoomId) putString(BUNDLE_CHAT_ROOM_ID, chatRoomId)
...@@ -110,7 +112,13 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -110,7 +112,13 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
setHasOptionsMenu(true) 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?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
......
...@@ -18,6 +18,7 @@ import chat.rocket.common.model.BaseRoom ...@@ -18,6 +18,7 @@ import chat.rocket.common.model.BaseRoom
import chat.rocket.common.model.RoomType import chat.rocket.common.model.RoomType
import chat.rocket.common.model.SimpleUser import chat.rocket.common.model.SimpleUser
import chat.rocket.common.model.User import chat.rocket.common.model.User
import chat.rocket.common.util.ifNull
import chat.rocket.core.internal.model.Subscription import chat.rocket.core.internal.model.Subscription
import chat.rocket.core.internal.realtime.socket.model.State import chat.rocket.core.internal.realtime.socket.model.State
import chat.rocket.core.internal.realtime.socket.model.StreamMessage import chat.rocket.core.internal.realtime.socket.model.StreamMessage
...@@ -32,26 +33,29 @@ import timber.log.Timber ...@@ -32,26 +33,29 @@ import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
import kotlin.reflect.KProperty1 import kotlin.reflect.KProperty1
class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView, class ChatRoomsPresenter @Inject constructor(
private val view: ChatRoomsView,
private val strategy: CancelStrategy, private val strategy: CancelStrategy,
private val navigator: MainNavigator, private val navigator: MainNavigator,
private val serverInteractor: GetCurrentServerInteractor, private val serverInteractor: GetCurrentServerInteractor,
private val getChatRoomsInteractor: GetChatRoomsInteractor, private val getChatRoomsInteractor: GetChatRoomsInteractor,
private val saveChatRoomsInteractor: SaveChatRoomsInteractor, private val saveChatRoomsInteractor: SaveChatRoomsInteractor,
private val saveActiveUsersInteractor: SaveActiveUsersInteractor,
private val getActiveUsersInteractor: GetActiveUsersInteractor,
private val refreshSettingsInteractor: RefreshSettingsInteractor, private val refreshSettingsInteractor: RefreshSettingsInteractor,
private val viewModelMapper: ViewModelMapper, private val viewModelMapper: ViewModelMapper,
private val jobSchedulerInteractor: JobSchedulerInteractor, private val jobSchedulerInteractor: JobSchedulerInteractor,
settingsRepository: SettingsRepository, settingsRepository: SettingsRepository,
factory: ConnectionManagerFactory) { factory: ConnectionManagerFactory
) {
private val manager: ConnectionManager = factory.create(serverInteractor.get()!!) private val manager: ConnectionManager = factory.create(serverInteractor.get()!!)
private val currentServer = serverInteractor.get()!! private val currentServer = serverInteractor.get()!!
private val client = manager.client private val client = manager.client
private var reloadJob: Deferred<List<ChatRoom>>? = null private var reloadJob: Deferred<List<ChatRoom>>? = null
private val settings = settingsRepository.get(currentServer) private val settings = settingsRepository.get(currentServer)
private val subscriptionsChannel = Channel<StreamMessage<BaseRoom>>()
private val stateChannel = Channel<State>() private val stateChannel = Channel<State>()
private val subscriptionsChannel = Channel<StreamMessage<BaseRoom>>()
private val activeUserChannel = Channel<User>()
private var lastState = manager.state private var lastState = manager.state
fun loadChatRooms() { fun loadChatRooms() {
...@@ -60,13 +64,18 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView, ...@@ -60,13 +64,18 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
view.showLoading() view.showLoading()
subscribeStatusChange() subscribeStatusChange()
try { try {
view.updateChatRooms(loadRooms()) view.updateChatRooms(getUserChatRooms())
} catch (e: RocketChatException) { } catch (ex: RocketChatException) {
Timber.e(e) ex.message?.let {
view.showMessage(e.message!!) view.showMessage(it)
}.ifNull {
view.showGenericErrorMessage()
}
Timber.e(ex)
} finally { } finally {
view.hideLoading() view.hideLoading()
} }
subscribeActiveUsers()
subscribeRoomUpdates() subscribeRoomUpdates()
} }
} }
...@@ -94,7 +103,7 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView, ...@@ -94,7 +103,7 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
val currentServer = serverInteractor.get()!! val currentServer = serverInteractor.get()!!
launchUI(strategy) { launchUI(strategy) {
try { try {
val roomList = getChatRoomsInteractor.getByName(currentServer, name) val roomList = getChatRoomsInteractor.getAllByName(currentServer, name)
if (roomList.isEmpty()) { if (roomList.isEmpty()) {
val (users, rooms) = retryIO("spotlight($name)") { val (users, rooms) = retryIO("spotlight($name)") {
client.spotlight(name) client.spotlight(name)
...@@ -102,9 +111,13 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView, ...@@ -102,9 +111,13 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
val chatRoomsCombined = mutableListOf<ChatRoom>() val chatRoomsCombined = mutableListOf<ChatRoom>()
chatRoomsCombined.addAll(usersToChatRooms(users)) chatRoomsCombined.addAll(usersToChatRooms(users))
chatRoomsCombined.addAll(roomsToChatRooms(rooms)) chatRoomsCombined.addAll(roomsToChatRooms(rooms))
view.updateChatRooms(getChatRoomsWithPreviews(chatRoomsCombined.toList())) val chatRoomsWithPreview = getChatRoomsWithPreviews(chatRoomsCombined)
val chatRoomsWithStatus = getChatRoomWithStatus(chatRoomsWithPreview)
view.updateChatRooms(chatRoomsWithStatus)
} else { } else {
view.updateChatRooms(getChatRoomsWithPreviews(roomList)) val chatRoomsWithPreview = getChatRoomsWithPreviews(roomList)
val chatRoomsWithStatus = getChatRoomWithStatus(chatRoomsWithPreview)
view.updateChatRooms(chatRoomsWithStatus)
} }
} catch (ex: RocketChatException) { } catch (ex: RocketChatException) {
Timber.e(ex) Timber.e(ex)
...@@ -112,13 +125,32 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView, ...@@ -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 { return users.map {
ChatRoom(id = it.id, ChatRoom(
id = it.id,
type = RoomType.DIRECT_MESSAGE, type = RoomType.DIRECT_MESSAGE,
user = SimpleUser(username = it.username, name = it.name, id = null), 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 ?: "", name = it.name ?: "",
status = null,
fullName = it.name, fullName = it.name,
readonly = false, readonly = false,
updatedAt = null, updatedAt = null,
...@@ -131,7 +163,7 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView, ...@@ -131,7 +163,7 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
open = false, open = false,
alert = false, alert = false,
unread = 0L, unread = 0L,
userMenstions = null, userMentions = null,
groupMentions = 0L, groupMentions = 0L,
lastMessage = null, lastMessage = null,
client = client client = client
...@@ -139,13 +171,19 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView, ...@@ -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 { return rooms.map {
ChatRoom(id = it.id, ChatRoom(
id = it.id,
type = it.type, type = it.type,
user = it.user, user = it.user,
status = if (it.name != null) {
getActiveUsersInteractor.getActiveUserByUsername(currentServer, it.name!!)
?.status
} else {
null
},
name = it.name ?: "", name = it.name ?: "",
status = null,
fullName = it.fullName, fullName = it.fullName,
readonly = it.readonly, readonly = it.readonly,
updatedAt = it.updatedAt, updatedAt = it.updatedAt,
...@@ -158,7 +196,7 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView, ...@@ -158,7 +196,7 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
open = false, open = false,
alert = false, alert = false,
unread = 0L, unread = 0L,
userMenstions = null, userMentions = null,
groupMentions = 0L, groupMentions = 0L,
lastMessage = it.lastMessage, lastMessage = it.lastMessage,
client = client client = client
...@@ -166,18 +204,9 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView, ...@@ -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() { fun updateSortedChatRooms() {
val currentServer = serverInteractor.get()!!
launchUI(strategy) { launchUI(strategy) {
val roomList = getChatRoomsInteractor.get(currentServer) val roomList = getChatRoomsInteractor.getAll(currentServer)
view.updateChatRooms(sortRooms(roomList)) view.updateChatRooms(sortRooms(roomList))
} }
} }
...@@ -223,11 +252,39 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView, ...@@ -223,11 +252,39 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
} }
} }
private fun updateRooms() { private fun getChatRoomWithStatus(chatRooms: List<ChatRoom>): List<ChatRoom> {
Timber.d("Updating Rooms") val chatRoomsList = mutableListOf<ChatRoom>()
launch(strategy.jobs) { chatRooms.forEach {
view.updateChatRooms(getChatRoomsWithPreviews(getChatRoomsInteractor.get(currentServer))) 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> { private suspend fun getChatRoomsWithPreviews(chatRooms: List<ChatRoom>): List<ChatRoom> {
...@@ -244,12 +301,6 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView, ...@@ -244,12 +301,6 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
return chatRooms.filter(ChatRoom::open) return chatRooms.filter(ChatRoom::open)
} }
private fun sortChatRooms(chatRooms: List<ChatRoom>): List<ChatRoom> {
return chatRooms.sortedByDescending { chatRoom ->
chatRoom.lastMessage?.timestamp
}
}
private suspend fun subscribeStatusChange() { private suspend fun subscribeStatusChange() {
lastState = manager.state lastState = manager.state
launch(CommonPool + strategy.jobs) { launch(CommonPool + strategy.jobs) {
...@@ -259,11 +310,10 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView, ...@@ -259,11 +310,10 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
launch(UI) { launch(UI) {
view.showConnectionState(state) view.showConnectionState(state)
} }
if (state is State.Connected) { if (state is State.Connected) {
jobSchedulerInteractor.scheduleSendingMessages() jobSchedulerInteractor.scheduleSendingMessages()
reloadRooms() reloadRooms()
updateRooms() updateChatRooms()
} }
} }
lastState = state lastState = state
...@@ -303,7 +353,7 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView, ...@@ -303,7 +353,7 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
} }
} }
updateRooms() updateChatRooms()
} }
private suspend fun updateSubscription(message: StreamMessage<Subscription>) { private suspend fun updateSubscription(message: StreamMessage<Subscription>) {
...@@ -322,7 +372,7 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView, ...@@ -322,7 +372,7 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
} }
} }
updateRooms() updateChatRooms()
} }
private suspend fun reloadRooms() { private suspend fun reloadRooms() {
...@@ -333,7 +383,7 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView, ...@@ -333,7 +383,7 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
reloadJob = async(CommonPool + strategy.jobs) { reloadJob = async(CommonPool + strategy.jobs) {
delay(1000) delay(1000)
Timber.d("reloading rooms after wait") Timber.d("reloading rooms after wait")
loadRooms() getUserChatRooms()
} }
reloadJob?.await() reloadJob?.await()
} catch (ex: Exception) { } catch (ex: Exception) {
...@@ -344,14 +394,18 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView, ...@@ -344,14 +394,18 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
// Update a ChatRoom with a Room information // Update a ChatRoom with a Room information
private fun updateRoom(room: Room) { private fun updateRoom(room: Room) {
Timber.d("Updating Room: ${room.id} - ${room.name}") 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 } val chatRoom = chatRooms.find { chatRoom -> chatRoom.id == room.id }
chatRoom?.apply { chatRoom?.apply {
val newRoom = ChatRoom(id = room.id, val newRoom = ChatRoom(
id = room.id,
type = room.type, type = room.type,
user = room.user ?: user, user = room.user ?: user,
status = getActiveUsersInteractor.getActiveUserByUsername(
currentServer,
room.name ?: name
)?.status,
name = room.name ?: name, name = room.name ?: name,
status = null,
fullName = room.fullName ?: fullName, fullName = room.fullName ?: fullName,
readonly = room.readonly, readonly = room.readonly,
updatedAt = room.updatedAt ?: updatedAt, updatedAt = room.updatedAt ?: updatedAt,
...@@ -365,10 +419,11 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView, ...@@ -365,10 +419,11 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
open = open, open = open,
alert = alert, alert = alert,
unread = unread, unread = unread,
userMenstions = userMenstions, userMentions = userMentions,
groupMentions = groupMentions, groupMentions = groupMentions,
lastMessage = room.lastMessage, lastMessage = room.lastMessage,
client = client) client = client
)
removeRoom(room.id, chatRooms) removeRoom(room.id, chatRooms)
chatRooms.add(newRoom) chatRooms.add(newRoom)
saveChatRoomsInteractor.save(currentServer, sortRooms(chatRooms)) saveChatRoomsInteractor.save(currentServer, sortRooms(chatRooms))
...@@ -378,14 +433,18 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView, ...@@ -378,14 +433,18 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
// Update a ChatRoom with a Subscription information // Update a ChatRoom with a Subscription information
private fun updateSubscription(subscription: Subscription) { private fun updateSubscription(subscription: Subscription) {
Timber.d("Updating subscription: ${subscription.id} - ${subscription.name}") 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 } val chatRoom = chatRooms.find { chatRoom -> chatRoom.id == subscription.roomId }
chatRoom?.apply { chatRoom?.apply {
val newRoom = ChatRoom(id = subscription.roomId, val newRoom = ChatRoom(
id = subscription.roomId,
type = subscription.type, type = subscription.type,
user = subscription.user ?: user, user = subscription.user ?: user,
status = getActiveUsersInteractor.getActiveUserByUsername(
currentServer,
subscription.name
)?.status,
name = subscription.name, name = subscription.name,
status = null,
fullName = subscription.fullName ?: fullName, fullName = subscription.fullName ?: fullName,
readonly = subscription.readonly ?: readonly, readonly = subscription.readonly ?: readonly,
updatedAt = subscription.updatedAt ?: updatedAt, updatedAt = subscription.updatedAt ?: updatedAt,
...@@ -399,19 +458,21 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView, ...@@ -399,19 +458,21 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
open = subscription.open, open = subscription.open,
alert = subscription.alert, alert = subscription.alert,
unread = subscription.unread, unread = subscription.unread,
userMenstions = subscription.userMentions, userMentions = subscription.userMentions,
groupMentions = subscription.groupMentions, groupMentions = subscription.groupMentions,
lastMessage = lastMessage, lastMessage = lastMessage,
client = client) client = client
)
removeRoom(subscription.roomId, chatRooms) removeRoom(subscription.roomId, chatRooms)
chatRooms.add(newRoom) chatRooms.add(newRoom)
saveChatRoomsInteractor.save(currentServer, sortRooms(chatRooms)) saveChatRoomsInteractor.save(currentServer, sortRooms(chatRooms))
} }
} }
private fun removeRoom(
private fun removeRoom(id: String, id: String,
chatRooms: MutableList<ChatRoom> = getChatRoomsInteractor.get(currentServer).toMutableList()) { chatRooms: MutableList<ChatRoom> = getChatRoomsInteractor.getAll(currentServer).toMutableList()
) {
Timber.d("Removing ROOM: $id") Timber.d("Removing ROOM: $id")
synchronized(this) { synchronized(this) {
chatRooms.removeAll { chatRoom -> chatRoom.id == id } chatRooms.removeAll { chatRoom -> chatRoom.id == id }
...@@ -419,8 +480,87 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView, ...@@ -419,8 +480,87 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
saveChatRoomsInteractor.save(currentServer, sortRooms(chatRooms)) 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() { fun disconnect() {
manager.removeStatusChannel(stateChannel) manager.removeStatusChannel(stateChannel)
manager.removeRoomsAndSubscriptionsChannel(subscriptionsChannel) manager.removeRoomsAndSubscriptionsChannel(subscriptionsChannel)
manager.removeActiveUserChannel(activeUserChannel)
} }
} }
\ No newline at end of file
...@@ -10,6 +10,7 @@ import android.text.SpannableStringBuilder ...@@ -10,6 +10,7 @@ import android.text.SpannableStringBuilder
import android.text.style.ForegroundColorSpan import android.text.style.ForegroundColorSpan
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView import android.widget.TextView
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.infrastructure.LocalRepository import chat.rocket.android.infrastructure.LocalRepository
...@@ -49,6 +50,7 @@ class ChatRoomsAdapter(private val context: Context, ...@@ -49,6 +50,7 @@ class ChatRoomsAdapter(private val context: Context,
fun bind(chatRoom: ChatRoom) = with(itemView) { fun bind(chatRoom: ChatRoom) = with(itemView) {
bindAvatar(chatRoom, image_avatar) bindAvatar(chatRoom, image_avatar)
bindName(chatRoom, text_chat_name) bindName(chatRoom, text_chat_name)
bindIcon(chatRoom, image_chat_icon)
bindLastMessageDateTime(chatRoom, text_last_message_date_time) bindLastMessageDateTime(chatRoom, text_last_message_date_time)
bindLastMessage(chatRoom, text_last_message) bindLastMessage(chatRoom, text_last_message)
bindUnreadMessages(chatRoom, text_total_unread_messages) bindUnreadMessages(chatRoom, text_total_unread_messages)
...@@ -80,32 +82,37 @@ class ChatRoomsAdapter(private val context: Context, ...@@ -80,32 +82,37 @@ class ChatRoomsAdapter(private val context: Context,
} }
} }
private fun bindName(chatRoom: ChatRoom, textView: TextView) { private fun bindIcon(chatRoom: ChatRoom, imageView: ImageView) {
textView.textContent = chatRoom.name
val drawable = when (chatRoom.type) { val drawable = when (chatRoom.type) {
is RoomType.Channel -> { is RoomType.Channel -> DrawableHelper.getDrawableFromId(
DrawableHelper.getDrawableFromId(R.drawable.ic_room_channel, context) R.drawable.ic_hashtag_12dp,
} context
is RoomType.PrivateGroup -> { )
DrawableHelper.getDrawableFromId(R.drawable.ic_room_lock, context) is RoomType.PrivateGroup -> DrawableHelper.getDrawableFromId(
} R.drawable.ic_lock_12_dp,
is RoomType.DirectMessage -> { context
DrawableHelper.getDrawableFromId(R.drawable.ic_room_dm, context) )
} is RoomType.DirectMessage -> DrawableHelper.getUserStatusDrawable(
chatRoom.status,
context
)
else -> null else -> null
} }
drawable?.let { drawable?.let {
val wrappedDrawable = DrawableHelper.wrapDrawable(it) val mutateDrawable = DrawableHelper.wrapDrawable(it).mutate()
val mutableDrawable = wrappedDrawable.mutate() if (chatRoom.type !is RoomType.DirectMessage) {
val color = when (chatRoom.alert || chatRoom.unread > 0) { val color = when (chatRoom.alert || chatRoom.unread > 0) {
true -> R.color.colorPrimaryText true -> R.color.colorPrimaryText
false -> R.color.colorSecondaryText false -> R.color.colorSecondaryText
} }
DrawableHelper.tintDrawable(mutableDrawable, context, color) DrawableHelper.tintDrawable(mutateDrawable, 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) { private fun bindLastMessageDateTime(chatRoom: ChatRoom, textView: TextView) {
......
...@@ -32,12 +32,9 @@ import dagger.android.support.AndroidSupportInjection ...@@ -32,12 +32,9 @@ import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_chat_rooms.* import kotlinx.android.synthetic.main.fragment_chat_rooms.*
import kotlinx.coroutines.experimental.Job import kotlinx.coroutines.experimental.Job
import kotlinx.coroutines.experimental.NonCancellable.isActive import kotlinx.coroutines.experimental.NonCancellable.isActive
import kotlinx.coroutines.experimental.android.UI
import kotlinx.coroutines.experimental.launch
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
class ChatRoomsFragment : Fragment(), ChatRoomsView { class ChatRoomsFragment : Fragment(), ChatRoomsView {
@Inject lateinit var presenter: ChatRoomsPresenter @Inject lateinit var presenter: ChatRoomsPresenter
@Inject lateinit var serverInteractor: GetCurrentServerInteractor @Inject lateinit var serverInteractor: GetCurrentServerInteractor
...@@ -67,7 +64,11 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView { ...@@ -67,7 +64,11 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
super.onDestroy() 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?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
...@@ -100,7 +101,6 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView { ...@@ -100,7 +101,6 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
}) })
} }
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) { when (item.itemId) {
R.id.action_sort -> { R.id.action_sort -> {
......
package chat.rocket.android.dagger package chat.rocket.android.dagger
import android.app.Application import android.app.Application
import chat.rocket.android.app.AppLifecycleObserver
import chat.rocket.android.app.RocketChatApplication import chat.rocket.android.app.RocketChatApplication
import chat.rocket.android.chatroom.service.MessageService import chat.rocket.android.chatroom.service.MessageService
import chat.rocket.android.dagger.module.ActivityBuilder import chat.rocket.android.dagger.module.ActivityBuilder
......
...@@ -22,29 +22,8 @@ import chat.rocket.android.infrastructure.LocalRepository ...@@ -22,29 +22,8 @@ import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.infrastructure.SharedPrefsLocalRepository import chat.rocket.android.infrastructure.SharedPrefsLocalRepository
import chat.rocket.android.push.GroupedPush import chat.rocket.android.push.GroupedPush
import chat.rocket.android.push.PushManager import chat.rocket.android.push.PushManager
import chat.rocket.android.server.domain.AccountsRepository import chat.rocket.android.server.domain.*
import chat.rocket.android.server.domain.ChatRoomsRepository import chat.rocket.android.server.infraestructure.*
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.util.AppJsonAdapterFactory import chat.rocket.android.util.AppJsonAdapterFactory
import chat.rocket.android.util.TimberLogger import chat.rocket.android.util.TimberLogger
import chat.rocket.common.internal.FallbackSealedClassJsonAdapter import chat.rocket.common.internal.FallbackSealedClassJsonAdapter
...@@ -55,6 +34,7 @@ import chat.rocket.common.util.Logger ...@@ -55,6 +34,7 @@ import chat.rocket.common.util.Logger
import chat.rocket.common.util.PlatformLogger import chat.rocket.common.util.PlatformLogger
import chat.rocket.core.RocketChatClient import chat.rocket.core.RocketChatClient
import chat.rocket.core.internal.AttachmentAdapterFactory import chat.rocket.core.internal.AttachmentAdapterFactory
import chat.rocket.core.internal.ReactionsAdapter
import com.facebook.drawee.backends.pipeline.DraweeConfig import com.facebook.drawee.backends.pipeline.DraweeConfig
import com.facebook.imagepipeline.backends.okhttp3.OkHttpImagePipelineConfigFactory import com.facebook.imagepipeline.backends.okhttp3.OkHttpImagePipelineConfigFactory
import com.facebook.imagepipeline.core.ImagePipelineConfig import com.facebook.imagepipeline.core.ImagePipelineConfig
...@@ -230,16 +210,32 @@ class AppModule { ...@@ -230,16 +210,32 @@ class AppModule {
@Provides @Provides
@Singleton @Singleton
fun provideMoshi(logger: PlatformLogger, fun provideActiveUsersRepository(): ActiveUsersRepository {
currentServerInteractor: GetCurrentServerInteractor): return MemoryActiveUsersRepository()
Moshi { }
@Provides
@Singleton
fun provideMoshi(
logger: PlatformLogger,
currentServerInteractor: GetCurrentServerInteractor
): Moshi {
val url = currentServerInteractor.get() ?: "" val url = currentServerInteractor.get() ?: ""
return Moshi.Builder() return Moshi.Builder()
.add(FallbackSealedClassJsonAdapter.ADAPTER_FACTORY) .add(FallbackSealedClassJsonAdapter.ADAPTER_FACTORY)
.add(AppJsonAdapterFactory.INSTANCE) .add(AppJsonAdapterFactory.INSTANCE)
.add(AttachmentAdapterFactory(Logger(logger, url))) .add(AttachmentAdapterFactory(Logger(logger, url)))
.add(java.lang.Long::class.java, ISO8601Date::class.java, TimestampAdapter(CalendarISO8601Converter())) .add(
.add(Long::class.java, ISO8601Date::class.java, TimestampAdapter(CalendarISO8601Converter())) java.lang.Long::class.java,
ISO8601Date::class.java,
TimestampAdapter(CalendarISO8601Converter())
)
.add(
Long::class.java,
ISO8601Date::class.java,
TimestampAdapter(CalendarISO8601Converter())
)
.add(ReactionsAdapter())
.build() .build()
} }
......
...@@ -54,13 +54,20 @@ object OauthHelper { ...@@ -54,13 +54,20 @@ object OauthHelper {
/** /**
* Returns the Gitlab Oauth URL. * Returns the Gitlab Oauth URL.
* *
* @param host The Gitlab host.
* @param clientId The Gitlab client ID. * @param clientId The Gitlab client ID.
* @param serverUrl The server URL. * @param serverUrl The server URL.
* @param state An unguessable random string used to protect against forgery attacks. * @param state An unguessable random string used to protect against forgery attacks.
* @return The Gitlab Oauth URL. * @return The Gitlab Oauth URL.
*/ */
fun getGitlabOauthUrl(clientId: String, serverUrl: String, state: String): String { fun getGitlabOauthUrl(
return "https://gitlab.com/oauth/authorize" + host: String? = "https://gitlab.com",
clientId: String,
serverUrl: String,
state: String
): String {
return host +
"/oauth/authorize" +
"?client_id=$clientId" + "?client_id=$clientId" +
"&redirect_uri=${serverUrl.removeTrailingSlash()}/_oauth/gitlab?close" + "&redirect_uri=${serverUrl.removeTrailingSlash()}/_oauth/gitlab?close" +
"&state=$state" + "&state=$state" +
......
...@@ -135,7 +135,7 @@ class MainPresenter @Inject constructor( ...@@ -135,7 +135,7 @@ class MainPresenter @Inject constructor(
navigator.toServerScreen() navigator.toServerScreen()
} }
fun changeStatus(userStatus: UserStatus) { fun changeDefaultStatus(userStatus: UserStatus) {
launchUI(strategy) { launchUI(strategy) {
try { try {
client.setDefaultStatus(userStatus) client.setDefaultStatus(userStatus)
......
...@@ -84,10 +84,7 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector, HasSupp ...@@ -84,10 +84,7 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector, HasSupp
override fun showUserStatus(userStatus: UserStatus) { override fun showUserStatus(userStatus: UserStatus) {
headerLayout.apply { headerLayout.apply {
image_user_status.setImageDrawable( image_user_status.setImageDrawable(
DrawableHelper.getUserStatusDrawable( DrawableHelper.getUserStatusDrawable(userStatus, this.context)
userStatus,
this.context
)
) )
} }
} }
...@@ -141,7 +138,7 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector, HasSupp ...@@ -141,7 +138,7 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector, HasSupp
accounts_list.layoutManager = LinearLayoutManager(this) accounts_list.layoutManager = LinearLayoutManager(this)
accounts_list.adapter = AccountsAdapter(accounts, object : Selector { accounts_list.adapter = AccountsAdapter(accounts, object : Selector {
override fun onStatusSelected(userStatus: UserStatus) { override fun onStatusSelected(userStatus: UserStatus) {
presenter.changeStatus(userStatus) presenter.changeDefaultStatus(userStatus)
} }
override fun onAccountSelected(serverUrl: String) { override fun onAccountSelected(serverUrl: String) {
......
...@@ -14,12 +14,14 @@ import chat.rocket.core.RocketChatClient ...@@ -14,12 +14,14 @@ import chat.rocket.core.RocketChatClient
import chat.rocket.core.internal.rest.getMembers import chat.rocket.core.internal.rest.getMembers
import javax.inject.Inject import javax.inject.Inject
class MembersPresenter @Inject constructor(private val view: MembersView, class MembersPresenter @Inject constructor(
private val view: MembersView,
private val navigator: MembersNavigator, private val navigator: MembersNavigator,
private val strategy: CancelStrategy, private val strategy: CancelStrategy,
private val serverInteractor: GetCurrentServerInteractor, serverInteractor: GetCurrentServerInteractor,
factory: RocketChatClientFactory, factory: RocketChatClientFactory,
private val mapper: MemberViewModelMapper) { private val mapper: MemberViewModelMapper
) {
private val client: RocketChatClient = factory.create(serverInteractor.get()!!) private val client: RocketChatClient = factory.create(serverInteractor.get()!!)
fun loadChatRoomsMembers(chatRoomId: String, chatRoomType: String, offset: Long = 0) { fun loadChatRoomsMembers(chatRoomId: String, chatRoomType: String, offset: Long = 0) {
......
...@@ -25,7 +25,6 @@ import dagger.android.support.AndroidSupportInjection ...@@ -25,7 +25,6 @@ import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_members.* import kotlinx.android.synthetic.main.fragment_members.*
import javax.inject.Inject import javax.inject.Inject
fun newInstance(chatRoomId: String, chatRoomType: String): Fragment { fun newInstance(chatRoomId: String, chatRoomType: String): Fragment {
return MembersFragment().apply { return MembersFragment().apply {
arguments = Bundle(1).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 ...@@ -8,13 +8,13 @@ import javax.inject.Inject
class GetChatRoomsInteractor @Inject constructor(private val repository: ChatRoomsRepository) { class GetChatRoomsInteractor @Inject constructor(private val repository: ChatRoomsRepository) {
/** /**
* Get all ChatRoom objects. * Get all [ChatRoom].
* *
* @param url The server url. * @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. * Get a list of chat rooms that contains the name parameter.
...@@ -23,7 +23,7 @@ class GetChatRoomsInteractor @Inject constructor(private val repository: ChatRoo ...@@ -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. * @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. * @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) val allChatRooms = repository.get(url)
if (name.isEmpty()) { if (name.isEmpty()) {
return@withContext allChatRooms return@withContext allChatRooms
...@@ -34,11 +34,11 @@ class GetChatRoomsInteractor @Inject constructor(private val repository: ChatRoo ...@@ -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 serverUrl The server url where the room is.
* @param roomId The id of the room to get. * @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) { suspend fun getById(serverUrl: String, roomId: String): ChatRoom? = withContext(CommonPool) {
val allChatRooms = repository.get(serverUrl) val allChatRooms = repository.get(serverUrl)
...@@ -46,4 +46,43 @@ class GetChatRoomsInteractor @Inject constructor(private val repository: ChatRoo ...@@ -46,4 +46,43 @@ class GetChatRoomsInteractor @Inject constructor(private val repository: ChatRoo
it.id == roomId 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 ...@@ -8,15 +8,17 @@ import kotlinx.coroutines.experimental.async
import kotlinx.coroutines.experimental.withContext import kotlinx.coroutines.experimental.withContext
import javax.inject.Inject import javax.inject.Inject
class RefreshSettingsInteractor @Inject constructor(private val factory: RocketChatClientFactory, class RefreshSettingsInteractor @Inject constructor(
private val repository: SettingsRepository) { private val factory: RocketChatClientFactory,
private val repository: SettingsRepository
) {
private var settingsFilter = arrayOf( private var settingsFilter = arrayOf(
LDAP_ENABLE, CAS_ENABLE, CAS_LOGIN_URL, LDAP_ENABLE, CAS_ENABLE, CAS_LOGIN_URL,
ACCOUNT_REGISTRATION, ACCOUNT_LOGIN_FORM, ACCOUNT_PASSWORD_RESET, ACCOUNT_CUSTOM_FIELDS, ACCOUNT_REGISTRATION, ACCOUNT_LOGIN_FORM, ACCOUNT_PASSWORD_RESET, ACCOUNT_CUSTOM_FIELDS,
ACCOUNT_GOOGLE, ACCOUNT_FACEBOOK, ACCOUNT_GITHUB, ACCOUNT_LINKEDIN, ACCOUNT_METEOR, 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, 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, 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" ...@@ -26,6 +26,7 @@ const val ACCOUNT_METEOR = "Accounts_OAuth_Meteor"
const val ACCOUNT_TWITTER = "Accounts_OAuth_Twitter" const val ACCOUNT_TWITTER = "Accounts_OAuth_Twitter"
const val ACCOUNT_WORDPRESS = "Accounts_OAuth_Wordpress" const val ACCOUNT_WORDPRESS = "Accounts_OAuth_Wordpress"
const val ACCOUNT_GITLAB = "Accounts_OAuth_Gitlab" const val ACCOUNT_GITLAB = "Accounts_OAuth_Gitlab"
const val ACCOUNT_GITLAB_URL = "API_Gitlab_URL"
const val SITE_URL = "Site_Url" const val SITE_URL = "Site_Url"
const val SITE_NAME = "Site_Name" const val SITE_NAME = "Site_Name"
...@@ -68,6 +69,7 @@ fun PublicSettings.isLinkedinAuthenticationEnabled(): Boolean = this[ACCOUNT_LIN ...@@ -68,6 +69,7 @@ fun PublicSettings.isLinkedinAuthenticationEnabled(): Boolean = this[ACCOUNT_LIN
fun PublicSettings.isMeteorAuthenticationEnabled(): Boolean = this[ACCOUNT_METEOR]?.value == true fun PublicSettings.isMeteorAuthenticationEnabled(): Boolean = this[ACCOUNT_METEOR]?.value == true
fun PublicSettings.isTwitterAuthenticationEnabled(): Boolean = this[ACCOUNT_TWITTER]?.value == true fun PublicSettings.isTwitterAuthenticationEnabled(): Boolean = this[ACCOUNT_TWITTER]?.value == true
fun PublicSettings.isGitlabAuthenticationEnabled(): Boolean = this[ACCOUNT_GITLAB]?.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.isWordpressAuthenticationEnabled(): Boolean = this[ACCOUNT_WORDPRESS]?.value == true
fun PublicSettings.useRealName(): Boolean = this[USE_REALNAME]?.value == true fun PublicSettings.useRealName(): Boolean = this[USE_REALNAME]?.value == true
......
package chat.rocket.android.server.infraestructure package chat.rocket.android.server.infraestructure
import chat.rocket.common.model.BaseRoom import chat.rocket.common.model.BaseRoom
import chat.rocket.common.model.User
import chat.rocket.core.RocketChatClient import chat.rocket.core.RocketChatClient
import chat.rocket.core.internal.realtime.subscribeSubscriptions import chat.rocket.core.internal.realtime.subscribeSubscriptions
import chat.rocket.core.internal.realtime.subscribeRooms import chat.rocket.core.internal.realtime.subscribeRooms
import chat.rocket.core.internal.realtime.subscribeUserData 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.subscribeRoomMessages
import chat.rocket.core.internal.realtime.unsubscribe import chat.rocket.core.internal.realtime.unsubscribe
import chat.rocket.core.internal.realtime.socket.connect import chat.rocket.core.internal.realtime.socket.connect
...@@ -28,11 +30,13 @@ class ConnectionManager(internal val client: RocketChatClient) { ...@@ -28,11 +30,13 @@ class ConnectionManager(internal val client: RocketChatClient) {
private val roomAndSubscriptionChannels = ArrayList<Channel<StreamMessage<BaseRoom>>>() private val roomAndSubscriptionChannels = ArrayList<Channel<StreamMessage<BaseRoom>>>()
private val roomMessagesChannels = LinkedHashMap<String, Channel<Message>>() private val roomMessagesChannels = LinkedHashMap<String, Channel<Message>>()
private val userDataChannels = ArrayList<Channel<Myself>>() private val userDataChannels = ArrayList<Channel<Myself>>()
private val activeUsersChannels = ArrayList<Channel<User>>()
private val subscriptionIdMap = HashMap<String, String>() private val subscriptionIdMap = HashMap<String, String>()
private var subscriptionId: String? = null private var subscriptionId: String? = null
private var roomsId: String? = null private var roomsId: String? = null
private var userId: String? = null private var userDataId: String? = null
private var activeUserId: String? = null
fun connect() { fun connect() {
if (connectJob?.isActive == true && (state !is State.Disconnected)) { if (connectJob?.isActive == true && (state !is State.Disconnected)) {
...@@ -61,8 +65,12 @@ class ConnectionManager(internal val client: RocketChatClient) { ...@@ -61,8 +65,12 @@ class ConnectionManager(internal val client: RocketChatClient) {
roomsId = id roomsId = id
} }
client.subscribeUserData { _, id -> client.subscribeUserData { _, id ->
Timber.d("Subscribed to the user: $id") Timber.d("Subscribed to the userData id: $id")
userId = id userDataId = id
}
client.subscribeActiveUsers { _, id ->
Timber.d("Subscribed to the activeUser id: $id")
activeUserId = id
} }
resubscribeRooms() resubscribeRooms()
...@@ -115,6 +123,14 @@ class ConnectionManager(internal val client: RocketChatClient) { ...@@ -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() client.connect()
// Broadcast initial state... // Broadcast initial state...
...@@ -154,6 +170,10 @@ class ConnectionManager(internal val client: RocketChatClient) { ...@@ -154,6 +170,10 @@ class ConnectionManager(internal val client: RocketChatClient) {
fun removeUserDataChannel(channel: Channel<Myself>) = userDataChannels.remove(channel) 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>) { fun subscribeRoomMessages(roomId: String, channel: Channel<Message>) {
val oldSub = roomMessagesChannels.put(roomId, channel) val oldSub = roomMessagesChannels.put(roomId, channel)
if (oldSub != null) { 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 package chat.rocket.android.settings.about.ui
import android.support.v7.app.AppCompatActivity
import android.os.Bundle import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import chat.rocket.android.BuildConfig import chat.rocket.android.BuildConfig
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.util.extensions.textContent import chat.rocket.android.util.extensions.textContent
...@@ -19,15 +19,13 @@ class AboutActivity : AppCompatActivity() { ...@@ -19,15 +19,13 @@ class AboutActivity : AppCompatActivity() {
} }
private fun setupViews() { private fun setupViews() {
val versionName = resources.getString(R.string.msg_version) +" "+BuildConfig.VERSION_NAME text_version_name.text = getString(R.string.msg_version, BuildConfig.VERSION_NAME)
val versionCode = resources.getString(R.string.msg_build)+" #"+ BuildConfig.VERSION_CODE text_build_number.text = getString(R.string.msg_build, BuildConfig.VERSION_CODE)
text_version_name.text = versionName
text_build_number.text = versionCode
} }
private fun setupToolbar() { private fun setupToolbar() {
setSupportActionBar(toolbar) setSupportActionBar(toolbar)
text_change_password.textContent = resources.getString(R.string.title_about) text_change_password.textContent = getString(R.string.title_about)
} }
override fun onBackPressed() { override fun onBackPressed() {
......
...@@ -11,10 +11,12 @@ import chat.rocket.core.internal.rest.me ...@@ -11,10 +11,12 @@ import chat.rocket.core.internal.rest.me
import chat.rocket.core.internal.rest.updateProfile import chat.rocket.core.internal.rest.updateProfile
import javax.inject.Inject import javax.inject.Inject
class PasswordPresenter @Inject constructor (private val view: PasswordView, class PasswordPresenter @Inject constructor(
private val view: PasswordView,
private val strategy: CancelStrategy, private val strategy: CancelStrategy,
serverInteractor: GetCurrentServerInteractor, serverInteractor: GetCurrentServerInteractor,
factory: RocketChatClientFactory){ factory: RocketChatClientFactory
) {
private val serverUrl = serverInteractor.get()!! private val serverUrl = serverInteractor.get()!!
private val client: RocketChatClient = factory.create(serverUrl) private val client: RocketChatClient = factory.create(serverUrl)
......
<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" <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp" android:width="12dp"
android:height="24dp" android:height="12dp"
android:viewportHeight="24" android:viewportHeight="12"
android:viewportWidth="24"> android:viewportWidth="12">
<path <path
android:fillColor="#FFFFD100" android:fillColor="#FFFFD100"
android:fillType="evenOdd" 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:strokeColor="#00000000"
android:strokeWidth="1" /> 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" <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp" android:width="12dp"
android:height="24dp" android:height="12dp"
android:viewportHeight="24" android:viewportHeight="12"
android:viewportWidth="24"> android:viewportWidth="12">
<path <path
android:fillColor="#FFFF2A57" android:fillColor="#FFFF2A57"
android:fillType="evenOdd" 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:strokeColor="#00000000"
android:strokeWidth="1" /> 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" <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp" android:width="12dp"
android:height="24dp" android:height="12dp"
android:viewportHeight="24" android:viewportHeight="12"
android:viewportWidth="24"> android:viewportWidth="12">
<path <path
android:fillColor="#FFCBCED1" android:fillColor="#FFCBCED1"
android:fillType="evenOdd" 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:strokeColor="#00000000"
android:strokeWidth="1" /> 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" <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp" android:width="12dp"
android:height="24dp" android:height="12dp"
android:viewportHeight="24" android:viewportHeight="12"
android:viewportWidth="24"> android:viewportWidth="12">
<path <path
android:fillColor="#FF2DE0A5" android:fillColor="#FF2DE0A5"
android:fillType="evenOdd" 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:strokeColor="#00000000"
android:strokeWidth="1" /> 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 @@ ...@@ -16,6 +16,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_centerInParent="true" android:layout_centerInParent="true"
android:visibility="gone"
app:indicatorColor="@color/black" app:indicatorColor="@color/black"
app:indicatorName="BallPulseIndicator" /> app:indicatorName="BallPulseIndicator" />
...@@ -33,15 +34,15 @@ ...@@ -33,15 +34,15 @@
android:id="@+id/connection_status_text" android:id="@+id/connection_status_text"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="32dp" android:layout_height="32dp"
android:alpha="0"
android:background="@color/colorPrimary" android:background="@color/colorPrimary"
android:elevation="4dp" android:elevation="4dp"
android:textColor="@color/white"
android:gravity="center" android:gravity="center"
android:textAppearance="@style/TextAppearance.AppCompat.Body2" android:textAppearance="@style/TextAppearance.AppCompat.Body2"
android:textColor="@color/white"
android:visibility="gone" android:visibility="gone"
android:alpha="0"
tools:alpha="1" tools:alpha="1"
tools:visibility="visible" tools:text="connected"
tools:text="connected"/> tools:visibility="visible" />
</RelativeLayout> </RelativeLayout>
\ No newline at end of file
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
android:paddingStart="16dp" android:paddingStart="16dp"
android:paddingTop="8dp" android:paddingTop="8dp"
android:drawablePadding="10dp" android:drawablePadding="10dp"
android:drawableStart="@drawable/ic_status_online_24dp" android:drawableStart="@drawable/ic_status_online_12dp"
android:text="@string/action_online" android:text="@string/action_online"
android:background="?selectableItemBackground"/> android:background="?selectableItemBackground"/>
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
android:paddingStart="16dp" android:paddingStart="16dp"
android:paddingTop="8dp" android:paddingTop="8dp"
android:drawablePadding="10dp" android:drawablePadding="10dp"
android:drawableStart="@drawable/ic_status_away_24dp" android:drawableStart="@drawable/ic_status_away_12dp"
android:text="@string/action_away" android:text="@string/action_away"
android:background="?selectableItemBackground"/> android:background="?selectableItemBackground"/>
...@@ -41,7 +41,7 @@ ...@@ -41,7 +41,7 @@
android:paddingStart="16dp" android:paddingStart="16dp"
android:paddingTop="8dp" android:paddingTop="8dp"
android:drawablePadding="10dp" android:drawablePadding="10dp"
android:drawableStart="@drawable/ic_status_busy_24dp" android:drawableStart="@drawable/ic_status_busy_12dp"
android:text="@string/action_busy" android:text="@string/action_busy"
android:background="?selectableItemBackground"/> android:background="?selectableItemBackground"/>
...@@ -54,7 +54,7 @@ ...@@ -54,7 +54,7 @@
android:paddingStart="16dp" android:paddingStart="16dp"
android:paddingTop="8dp" android:paddingTop="8dp"
android:drawablePadding="10dp" android:drawablePadding="10dp"
android:drawableStart="@drawable/ic_status_invisible_24dp" android:drawableStart="@drawable/ic_status_invisible_12dp"
android:text="@string/action_invisible" android:text="@string/action_invisible"
android:background="?selectableItemBackground"/> android:background="?selectableItemBackground"/>
......
...@@ -5,62 +5,70 @@ ...@@ -5,62 +5,70 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground" 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:paddingEnd="@dimen/screen_edge_left_and_right_padding"
android:paddingTop="@dimen/chat_item_top_and_bottom_padding" android:paddingStart="@dimen/screen_edge_left_and_right_padding"
android:paddingBottom="@dimen/chat_item_top_and_bottom_padding"> android:paddingTop="@dimen/chat_item_top_and_bottom_padding">
<com.facebook.drawee.view.SimpleDraweeView <com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/image_avatar" android:id="@+id/image_avatar"
android:layout_width="40dp" android:layout_width="40dp"
android:layout_height="40dp" android:layout_height="40dp"
app:roundedCornerRadius="3dp" android:layout_marginTop="5dp"
android:layout_marginTop="6dp"
app:layout_constraintStart_toStartOf="parent" 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 <TextView
android:id="@+id/text_chat_name" android:id="@+id/text_chat_name"
style="@style/ChatRoom.Name.TextView" style="@style/ChatRoom.Name.TextView"
android:layout_width="0dp" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="16dp" android:layout_marginStart="8dp"
app:layout_constraintStart_toEndOf="@id/image_avatar"
android:textDirection="locale" android:textDirection="locale"
tools:text="General"/> app:layout_constraintStart_toEndOf="@+id/image_chat_icon"
tools:text="general" />
<TextView <TextView
android:id="@+id/text_last_message_date_time" android:id="@+id/text_last_message_date_time"
style="@style/Timestamp.TextView" style="@style/Timestamp.TextView"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="5dp" android:layout_marginStart="8dp"
android:layout_marginEnd="5dp" app:layout_constraintBottom_toBottomOf="@+id/text_chat_name"
app:layout_constraintBaseline_toBaselineOf="@+id/text_chat_name"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/text_chat_name"
tools:text="11:45 AM" /> tools:text="11:45 AM" />
<TextView <TextView
android:id="@+id/text_last_message" android:id="@+id/text_last_message"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginTop="2dp"
android:ellipsize="end" android:ellipsize="end"
android:maxLines="2" 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" 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 <include
android:id="@+id/layout_unread_messages_badge" android:id="@+id/layout_unread_messages_badge"
layout="@layout/unread_messages_badge" layout="@layout/unread_messages_badge"
android:layout_width="18dp" android:layout_width="18dp"
android:layout_height="18dp" android:layout_height="18dp"
android:layout_marginStart="5dp" app:layout_constraintEnd_toEndOf="parent"
android:layout_marginEnd="5dp" app:layout_constraintTop_toTopOf="@+id/text_last_message" />
app:layout_constraintTop_toTopOf="@id/text_last_message"
app:layout_constraintEnd_toEndOf="parent"/>
</android.support.constraint.ConstraintLayout> </android.support.constraint.ConstraintLayout>
\ No newline at end of file
...@@ -43,8 +43,8 @@ ...@@ -43,8 +43,8 @@
<ImageView <ImageView
android:id="@+id/image_user_status" android:id="@+id/image_user_status"
android:layout_width="14dp" android:layout_width="12dp"
android:layout_height="14dp" android:layout_height="12dp"
app:layout_constraintStart_toStartOf="parent" /> app:layout_constraintStart_toStartOf="parent" />
<TextView <TextView
......
<?xml version="1.0" encoding="utf-8"?> <?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:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="2dp" android:layout_marginBottom="2dp"
android:layout_marginEnd="2dp" android:layout_marginEnd="2dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="2dp"
android:layout_marginStart="8dp" android:layout_marginStart="8dp"
android:layout_marginTop="2dp" android:layout_marginTop="2dp"
android:background="@color/suggestion_background_color"> android:background="@color/suggestion_background_color">
<FrameLayout
android:id="@+id/image_avatar_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true">
<com.facebook.drawee.view.SimpleDraweeView <com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/image_avatar" android:id="@+id/image_avatar"
android:layout_width="24dp" android:layout_width="24dp"
android:layout_height="24dp" android:layout_height="24dp"
android:layout_marginTop="4dp"
android:layout_marginBottom="4dp"
app:roundedCornerRadius="3dp" app:roundedCornerRadius="3dp"
tools:src="@tools:sample/avatars" /> tools:src="@tools:sample/avatars" />
...@@ -31,45 +21,36 @@ ...@@ -31,45 +21,36 @@
android:id="@+id/image_status" android:id="@+id/image_status"
android:layout_width="12dp" android:layout_width="12dp"
android:layout_height="12dp" android:layout_height="12dp"
android:layout_gravity="bottom|end" android:layout_marginStart="5dp"
android:background="@drawable/user_status_white" app:layout_constraintBottom_toBottomOf="@+id/image_avatar"
android:padding="2dp" /> app:layout_constraintStart_toEndOf="@+id/image_avatar"
</FrameLayout> app:layout_constraintTop_toTopOf="@+id/image_avatar" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toEndOf="@id/image_avatar_container"
android:layout_toRightOf="@id/image_avatar_container"
android:background="@color/suggestion_background_color">
<TextView <TextView
android:id="@+id/text_username" android:id="@+id/text_username"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_centerVertical="true" android:layout_marginStart="5dp"
android:maxLines="1" android:maxLines="1"
android:textColor="@color/black" android:textColor="@color/black"
android:textSize="16sp" 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" /> tools:text="@tools:sample/full_names" />
<TextView <TextView
android:id="@+id/text_name" android:id="@+id/text_name"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentEnd="true" android:layout_marginStart="5dp"
android:layout_alignParentRight="true" android:ellipsize="end"
android:layout_centerVertical="true"
android:layout_toEndOf="@+id/text_username"
android:layout_toRightOf="@+id/text_username"
android:maxLines="1" android:maxLines="1"
android:paddingLeft="8dp"
android:paddingStart="8dp"
android:textColor="@color/gray_material" android:textColor="@color/gray_material"
android:textSize="16sp" 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" /> tools:text="@tools:sample/full_names" />
</RelativeLayout> </android.support.constraint.ConstraintLayout>
\ No newline at end of file
</RelativeLayout>
\ No newline at end of file
...@@ -3,22 +3,16 @@ ...@@ -3,22 +3,16 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="2dp" android:layout_marginBottom="2dp"
android:background="@color/suggestion_background_color"> android:layout_marginEnd="2dp"
android:layout_marginStart="8dp"
<RelativeLayout android:layout_marginTop="2dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toEndOf="@id/image_avatar_container"
android:layout_toRightOf="@id/image_avatar_container"
android:background="@color/suggestion_background_color"> android:background="@color/suggestion_background_color">
<TextView <TextView
android:id="@+id/text_name" android:id="@+id/text_name"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:maxLines="1" android:maxLines="1"
android:textColor="@color/black" android:textColor="@color/black"
android:textSize="16sp" android:textSize="16sp"
...@@ -28,18 +22,11 @@ ...@@ -28,18 +22,11 @@
android:id="@+id/text_fullname" android:id="@+id/text_fullname"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="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_toEndOf="@+id/text_name"
android:layout_toRightOf="@+id/text_name"
android:maxLines="1" android:maxLines="1"
android:paddingLeft="8dp" android:layout_marginStart="8dp"
android:paddingStart="8dp"
android:textColor="@color/gray_material" android:textColor="@color/gray_material"
android:textSize="16sp" android:textSize="16sp"
tools:text="@tools:sample/full_names" /> tools:text="@tools:sample/full_names" />
</RelativeLayout>
</RelativeLayout> </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 @@ ...@@ -91,8 +91,6 @@
</string> </string>
<string name="msg_version">वर्शन</string> <string name="msg_version">वर्शन</string>
<string name="msg_build">बिल्ड</string> <string name="msg_build">बिल्ड</string>
<string name="msg_no_chat_title">कोई चैट संदेश नहीं</string>
<string name="msg_no_chat_description">यहां अपने संदेश देखने के लिए\nबातचीत शुरू करें।</string>
<!-- System messages --> <!-- System messages -->
<string name="message_room_name_changed">%2$s ने रूम का नाम बदलकर %1$s किया</string> <string name="message_room_name_changed">%2$s ने रूम का नाम बदलकर %1$s किया</string>
......
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
<!-- Actions --> <!-- Actions -->
<string name="action_connect">Conectar</string> <string name="action_connect">Conectar</string>
<string name="action_use_this_username">Usar este nome de usuário</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_terms_of_service">Termos de Serviço</string>
<string name="action_privacy_policy">Política de Privacidade</string> <string name="action_privacy_policy">Política de Privacidade</string>
<string name="action_search">Pesquisar</string> <string name="action_search">Pesquisar</string>
...@@ -59,7 +59,7 @@ ...@@ -59,7 +59,7 @@
<string name="msg_this_room_is_read_only">Este chat é apenas de leitura</string> <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_2fa_code">Código 2FA inválido</string>
<string name="msg_invalid_file">Arquivo 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_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_github">Fazer login através do Github</string>
<string name="msg_content_description_log_in_using_google">Fazer login através do Google</string> <string name="msg_content_description_log_in_using_google">Fazer login através do Google</string>
...@@ -80,8 +80,8 @@ ...@@ -80,8 +80,8 @@
<string name="msg_preview_audio">Audio</string> <string name="msg_preview_audio">Audio</string>
<string name="msg_preview_photo">Foto</string> <string name="msg_preview_photo">Foto</string>
<string name="msg_no_messages_yet">Nenhuma mensagem ainda</string> <string name="msg_no_messages_yet">Nenhuma mensagem ainda</string>
<string name="msg_version">Versão</string> <string name="msg_version">Versão %1$s</string>
<string name="msg_build">Build</string> <string name="msg_build">Build %1$d</string>
<string name="msg_ok">OK</string> <string name="msg_ok">OK</string>
<string name="msg_ver_not_recommended"> <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. 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 @@ ...@@ -103,7 +103,7 @@
<string name="message_user_added_by">Usuário %1$s adicionado por %2$s</string> <string name="message_user_added_by">Usuário %1$s adicionado por %2$s</string>
<string name="message_user_removed_by">Usuário %1$s removido 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_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_welcome">Bem-vindo, %s</string>
<string name="message_removed">Mensagem removida</string> <string name="message_removed">Mensagem removida</string>
<string name="message_pinned">Pinou uma mensagem:</string> <string name="message_pinned">Pinou uma mensagem:</string>
...@@ -124,7 +124,7 @@ ...@@ -124,7 +124,7 @@
<!-- Permission messages --> <!-- Permission messages -->
<string name="permission_editing_not_allowed">Edição não permitida</string> <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_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 --> <!-- Members List -->
<string name="title_members_list">Lista de Membros</string> <string name="title_members_list">Lista de Membros</string>
...@@ -164,8 +164,8 @@ ...@@ -164,8 +164,8 @@
<string name="Leave_the_current_channel">Sair do canal atual</string> <string name="Leave_the_current_channel">Sair do canal atual</string>
<string name="Displays_action_text">Exibir texto de ação</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="Direct_message_someone">Enviar DM para alguém</string>
<string name="Mute_someone_in_room">Mutar alguém</string> <string name="Mute_someone_in_room">Silenciar alguém</string>
<string name="Unmute_someone_in_room">Desmutar alguém na sala</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="Invite_user_to_join_channel">Convidar algum usuário para entrar neste canal</string>
<string name="Unarchive">Desarquivar</string> <string name="Unarchive">Desarquivar</string>
<string name="Join_the_given_channel">Entrar no canal especificado</string> <string name="Join_the_given_channel">Entrar no canal especificado</string>
......
...@@ -5,7 +5,6 @@ ...@@ -5,7 +5,6 @@
<dimen name="screen_edge_left_and_right_margins">16dp</dimen> <dimen name="screen_edge_left_and_right_margins">16dp</dimen>
<dimen name="screen_edge_left_and_right_padding">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="message_item_top_and_bottom_padding">6dp</dimen>
<dimen name="member_item_top_and_bottom_padding">6dp</dimen> <dimen name="member_item_top_and_bottom_padding">6dp</dimen>
...@@ -23,6 +22,10 @@ ...@@ -23,6 +22,10 @@
<dimen name="nav_header_height">140dp</dimen> <dimen name="nav_header_height">140dp</dimen>
<!-- ChatRoom -->
<dimen name="chat_item_top_and_bottom_padding">12dp</dimen>
<!-- Emoji --> <!-- Emoji -->
<dimen name="picker_padding_bottom">16dp</dimen> <dimen name="picker_padding_bottom">16dp</dimen>
<dimen name="supposed_keyboard_height">252dp</dimen> <dimen name="supposed_keyboard_height">252dp</dimen>
......
...@@ -82,8 +82,8 @@ ...@@ -82,8 +82,8 @@
<string name="msg_preview_audio">Audio</string> <string name="msg_preview_audio">Audio</string>
<string name="msg_preview_photo">Photo</string> <string name="msg_preview_photo">Photo</string>
<string name="msg_no_messages_yet">No messages yet</string> <string name="msg_no_messages_yet">No messages yet</string>
<string name="msg_version">Version</string> <string name="msg_version">Version %1$s</string>
<string name="msg_build">Build</string> <string name="msg_build">Build %1$d</string>
<string name="msg_ok">OK</string> <string name="msg_ok">OK</string>
<string name="msg_ver_not_recommended"> <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> 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 ...@@ -3,6 +3,7 @@ package chat.rocket.android
import chat.rocket.android.server.infraestructure.MemoryMessagesRepository import chat.rocket.android.server.infraestructure.MemoryMessagesRepository
import chat.rocket.core.model.Message import chat.rocket.core.model.Message
import chat.rocket.core.model.MessageType import chat.rocket.core.model.MessageType
import kotlinx.coroutines.experimental.runBlocking
import org.hamcrest.CoreMatchers.notNullValue import org.hamcrest.CoreMatchers.notNullValue
import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.MatcherAssert.assertThat
import org.junit.Before import org.junit.Before
...@@ -30,7 +31,9 @@ class MemoryMessagesRepositoryTest { ...@@ -30,7 +31,9 @@ class MemoryMessagesRepositoryTest {
senderAlias = null, senderAlias = null,
type = MessageType.MessageRemoved(), type = MessageType.MessageRemoved(),
updatedAt = 1511443964815, updatedAt = 1511443964815,
urls = null urls = null,
pinned = false,
reactions = null
) )
val msg2 = Message( val msg2 = Message(
...@@ -50,16 +53,21 @@ class MemoryMessagesRepositoryTest { ...@@ -50,16 +53,21 @@ class MemoryMessagesRepositoryTest {
senderAlias = null, senderAlias = null,
type = MessageType.MessageRemoved(), type = MessageType.MessageRemoved(),
updatedAt = 1511443964818, updatedAt = 1511443964818,
urls = null urls = null,
pinned = false,
reactions = null
) )
@Before @Before
fun setup() { fun setup() {
runBlocking {
repository.clear() repository.clear()
} }
}
@Test @Test
fun `save() should save a single message`() { fun `save() should save a single message`() {
runBlocking {
assertThat(repository.getAll().size, isEqualTo(0)) assertThat(repository.getAll().size, isEqualTo(0))
repository.save(msg) repository.save(msg)
val allMessages = repository.getAll() val allMessages = repository.getAll()
...@@ -70,9 +78,11 @@ class MemoryMessagesRepositoryTest { ...@@ -70,9 +78,11 @@ class MemoryMessagesRepositoryTest {
assertThat(roomId, isEqualTo("GENERAL")) assertThat(roomId, isEqualTo("GENERAL"))
} }
} }
}
@Test @Test
fun `saveAll() should all saved messages`() { fun `saveAll() should all saved messages`() {
runBlocking {
assertThat(repository.getAll().size, isEqualTo(0)) assertThat(repository.getAll().size, isEqualTo(0))
repository.saveAll(listOf(msg, msg2)) repository.saveAll(listOf(msg, msg2))
val allMessages = repository.getAll() val allMessages = repository.getAll()
...@@ -89,9 +99,11 @@ class MemoryMessagesRepositoryTest { ...@@ -89,9 +99,11 @@ class MemoryMessagesRepositoryTest {
assertThat(roomId, isEqualTo("sandbox")) assertThat(roomId, isEqualTo("sandbox"))
} }
} }
}
@Test @Test
fun `getById() should return a single message`() { fun `getById() should return a single message`() {
runBlocking {
repository.saveAll(listOf(msg, msg2)) repository.saveAll(listOf(msg, msg2))
var singleMsg = repository.getById("messageId") var singleMsg = repository.getById("messageId")
assertThat(singleMsg, notNullValue()) assertThat(singleMsg, notNullValue())
...@@ -109,9 +121,11 @@ class MemoryMessagesRepositoryTest { ...@@ -109,9 +121,11 @@ class MemoryMessagesRepositoryTest {
assertThat(roomId, isEqualTo("sandbox")) assertThat(roomId, isEqualTo("sandbox"))
} }
} }
}
@Test @Test
fun `getByRoomId() should return all messages for room id or an empty list`() { fun `getByRoomId() should return all messages for room id or an empty list`() {
runBlocking {
repository.saveAll(listOf(msg, msg2)) repository.saveAll(listOf(msg, msg2))
var roomMessages = repository.getByRoomId("faAad32fkasods2") var roomMessages = repository.getByRoomId("faAad32fkasods2")
assertThat(roomMessages.isEmpty(), isEqualTo(true)) assertThat(roomMessages.isEmpty(), isEqualTo(true))
...@@ -132,4 +146,5 @@ class MemoryMessagesRepositoryTest { ...@@ -132,4 +146,5 @@ class MemoryMessagesRepositoryTest {
assertThat(roomId, isEqualTo("GENERAL")) assertThat(roomId, isEqualTo("GENERAL"))
} }
} }
}
} }
\ No newline at end of file
...@@ -16,6 +16,7 @@ ext { ...@@ -16,6 +16,7 @@ ext {
exoPlayer : '2.6.0', exoPlayer : '2.6.0',
playServices : '11.8.0', playServices : '11.8.0',
room : '1.0.0', room : '1.0.0',
lifecycle : '1.1.1',
rxKotlin : '2.2.0', rxKotlin : '2.2.0',
rxAndroid : '2.0.2', rxAndroid : '2.0.2',
moshi : '1.6.0-SNAPSHOT', moshi : '1.6.0-SNAPSHOT',
...@@ -28,7 +29,7 @@ ext { ...@@ -28,7 +29,7 @@ ext {
frescoImageViewer : '0.5.1', frescoImageViewer : '0.5.1',
markwon : '1.0.3', markwon : '1.0.3',
sheetMenu : '1.3.3', sheetMenu : '1.3.3',
aVLoadingIndicatorView : '2.1.3', aVLoadingIndicatorView: '2.1.3',
flexbox : '0.3.2', flexbox : '0.3.2',
// For testing // For testing
...@@ -63,6 +64,8 @@ ext { ...@@ -63,6 +64,8 @@ ext {
room : "android.arch.persistence.room:runtime:${versions.room}", room : "android.arch.persistence.room:runtime:${versions.room}",
roomProcessor : "android.arch.persistence.room:compiler:${versions.room}", roomProcessor : "android.arch.persistence.room:compiler:${versions.room}",
roomRxjava : "android.arch.persistence.room:rxjava2:${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}", rxKotlin : "io.reactivex.rxjava2:rxkotlin:${versions.rxKotlin}",
rxAndroid : "io.reactivex.rxjava2:rxandroid:${versions.rxAndroid}", rxAndroid : "io.reactivex.rxjava2:rxandroid:${versions.rxAndroid}",
...@@ -92,7 +95,7 @@ ext { ...@@ -92,7 +95,7 @@ ext {
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 // For testing
junit : "junit:junit:$versions.junit", junit : "junit:junit:$versions.junit",
...@@ -100,5 +103,4 @@ ext { ...@@ -100,5 +103,4 @@ ext {
roomTest : "android.arch.persistence.room:testing:${versions.room}", roomTest : "android.arch.persistence.room:testing:${versions.room}",
truth : "com.google.truth:truth:$versions.truth", 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