Commit 6a1f37b7 authored by Leonardo Aramaki's avatar Leonardo Aramaki

Merge branch 'develop-2.x' into spotlight-on-toolbar

parents c4554625 8fef2381
# Auth files
*.jks
app/rocket-chat.json
app/src/release/google-services.json
app/src/release/res/values/api_key_strings.xml
app/libs/
# Auth files
*.jks
################################# Created by https://www.gitignore.io/api/androidstudio (modified) #################################
......
......@@ -98,4 +98,4 @@ cp -v $SDK_DIR/core/build/libs/core-0.1-SNAPSHOT.jar $CURRENT_DIR/libs/core-$SHA
echo "$SHA" > $SDK_DIR/.last_commit_hash
exit 0
exit 0
\ No newline at end of file
......@@ -34,7 +34,7 @@ android {
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation project(':player')
......@@ -52,9 +52,7 @@ dependencies {
implementation libraries.daggerSupport
kapt libraries.daggerProcessor
kapt libraries.daggerAndroidApt
implementation libraries.moshi
implementation libraries.kotshi
implementation libraries.playServicesGcm
implementation libraries.room
kapt libraries.roomProcessor
......@@ -63,6 +61,7 @@ dependencies {
implementation libraries.rxjava
implementation libraries.rxandroid
implementation libraries.moshi
implementation libraries.okhttp
implementation libraries.okhttpLogger
......@@ -76,26 +75,22 @@ dependencies {
implementation libraries.frescoImageViewer
implementation libraries.kotshi
implementation libraries.floatingSearchView
implementation libraries.androidSvg
implementation libraries.playServicesGcm
implementation libraries.aVLoadingIndicatorView
implementation libraries.textDrawable
testImplementation libraries.junit
androidTestImplementation (libraries.expressoCore , {
androidTestImplementation(libraries.expressoCore, {
exclude group: 'com.android.support', module: 'support-annotations'
})
}
repositories {
mavenCentral()
}
kotlin {
experimental {
coroutines "enable"
......@@ -109,4 +104,4 @@ task compileSdk(type:Exec) {
}
preBuild.dependsOn compileSdk
apply plugin: 'com.google.gms.google-services'
apply plugin: 'com.google.gms.google-services'
\ No newline at end of file
......@@ -36,13 +36,17 @@
</intent-filter>
</activity>
<activity
android:name=".webview.WebViewActivity"
android:theme="@style/AppTheme" />
<activity
android:name=".chatrooms.ui.MainActivity"
android:theme="@style/ChatListTheme" />
<activity
android:name=".app.ChatRoomActivity"
android:theme="@style/AppTheme"></activity>
android:name=".chatroom.ui.ChatRoomActivity"
android:theme="@style/AppTheme" />
<receiver
android:name="com.google.android.gms.gcm.GcmReceiver"
......@@ -71,10 +75,6 @@
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
</intent-filter>
</service>
<activity
android:name=".webview.WebViewActivity"
android:theme="@style/AppTheme" />
</application>
</manifest>
\ No newline at end of file
package chat.rocket.android.app
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import chat.rocket.android.R
import chat.rocket.android.app.chatroom.MessageFragment
import chat.rocket.android.util.addFragment
class ChatRoomActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_chat_room)
addFragment("MessageFragment", R.id.fragment_container) {
MessageFragment()
}
}
}
\ No newline at end of file
......@@ -135,7 +135,7 @@ object DrawableHelper {
.beginConfig()
.useFont(Typeface.SANS_SERIF)
.endConfig()
.buildRound(TextHelper.getFirstCharacter(string), getAvatarBackgroundColor(string))
.buildRoundRect(TextHelper.getFirstCharacter(string), getAvatarBackgroundColor(string), 4)
}
/**
......
package chat.rocket.android.app.chatroom
import chat.rocket.android.app.User
import org.threeten.bp.LocalDateTime
data class Message(val user: User,
val textContent: String?,
val imageAttachmentUri: String?,
val localDatetime: LocalDateTime)
\ No newline at end of file
package chat.rocket.android.app.chatroom
import android.os.Bundle
import android.support.v4.app.Fragment
import android.support.v7.widget.LinearLayoutManager
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import chat.rocket.android.R
import chat.rocket.android.app.User
import kotlinx.android.synthetic.main.fragment_chat_rooms.*
import org.threeten.bp.LocalDateTime
class MessageFragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? = inflater.inflate(R.layout.fragment_message, container, false)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
showMessageList(createDumpData())
}
// This is just a sample showing 2 messages in the chat room. We need to get it rid in a real word. REMARK: remove this comment and this method.
private fun createDumpData(): List<Message> {
val user1 = User("1",
"Filipe Brito",
"filipe.brito",
"online",
"https://open.rocket.chat/avatar/filipe.brito")
val user2 = User("2",
"Lucio Maciel",
"Lucio Maciel",
"busy",
"https://open.rocket.chat/avatar/lucio.maciel")
val message1 = Message(user1,
"This is a multiline chat message from Bertie that will take more than just one line of text. I have sure that everything is amazing!",
"https://rocket.chat/images/index/livechat.png",
LocalDateTime.now())
val message2 = Message(user2, "Great!",
"https://rocket.chat/images/index/screenshot.png",
LocalDateTime.now().plusHours(1))
return listOf(message1, message2)
}
// REMARK: The presenter should call this method.
private fun showMessageList(dataSet: List<Message>) {
activity?.apply {
recycler_view.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
recycler_view.adapter = MessageListAdapter(this, dataSet.toMutableList()) {}
}
}
}
\ No newline at end of file
package chat.rocket.android.app.chatroom
import DateTimeHelper
import android.content.Context
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import chat.rocket.android.R
import com.facebook.drawee.view.SimpleDraweeView
import com.stfalcon.frescoimageviewer.ImageViewer
import kotlinx.android.synthetic.main.item_message.view.*
class MessageListAdapter(private val context: Context, private var dataSet: MutableList<Message>, private val listener: (Message) -> Unit) : RecyclerView.Adapter<MessageListAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_message, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val message = dataSet[position]
holder.userAvatar.setImageURI(message.user.avatarUri)
holder.userName.text = message.user.name
holder.time.text = DateTimeHelper.getTime(message.localDatetime)
val textContent = message.textContent
if (textContent.isNullOrBlank()) {
holder.textContent.visibility = View.GONE
} else {
holder.textContent.text = textContent
}
val imageAttachmentUri = message.imageAttachmentUri
if (imageAttachmentUri.isNullOrBlank()) {
holder.imageAttachment.visibility = View.GONE
} else {
holder.imageAttachment.setImageURI(imageAttachmentUri)
holder.imageAttachment.setOnClickListener({
ImageViewer.Builder(context, listOf(imageAttachmentUri))
.hideStatusBar(false)
.show()
})
}
}
override fun getItemCount(): Int = dataSet.size
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val userAvatar: SimpleDraweeView = itemView.image_user_avatar
val userName: TextView = itemView.text_user_name
val time: TextView = itemView.text_message_time
val textContent: TextView = itemView.text_content
val imageAttachment: SimpleDraweeView = itemView.image_attachment
}
}
\ No newline at end of file
......@@ -6,7 +6,6 @@ import chat.rocket.android.authentication.ui.AuthenticationActivity
import chat.rocket.android.dagger.scope.PerActivity
import dagger.Module
import dagger.Provides
import kotlinx.coroutines.experimental.Job
@Module
class AuthenticationModule {
......@@ -14,9 +13,4 @@ class AuthenticationModule {
@Provides
@PerActivity
fun provideAuthenticationNavigator(activity: AuthenticationActivity, context: Context) = AuthenticationNavigator(activity, context)
@Provides
fun provideJob(): Job {
return Job()
}
}
}
\ No newline at end of file
......@@ -6,6 +6,6 @@ import dagger.android.ContributesAndroidInjector
@Module abstract class LoginFragmentProvider {
@ContributesAndroidInjector(modules = arrayOf(LoginFragmentModule::class))
@ContributesAndroidInjector(modules = [(LoginFragmentModule::class)])
abstract fun provideLoginFragment(): LoginFragment
}
}
\ No newline at end of file
......@@ -15,7 +15,6 @@ import chat.rocket.core.internal.rest.login
import chat.rocket.core.internal.rest.registerPushToken
import javax.inject.Inject
class LoginPresenter @Inject constructor(private val view: LoginView,
private val strategy: CancelStrategy,
private val navigator: AuthenticationNavigator,
......@@ -23,7 +22,6 @@ class LoginPresenter @Inject constructor(private val view: LoginView,
private val settingsInteractor: GetSettingsInteractor,
private val serverInteractor: GetCurrentServerInteractor,
factory: RocketChatClientFactory) {
// TODO - we should validate the current server when opening the app, and have a nonnull get()
private val client: RocketChatClient = factory.create(serverInteractor.get()!!)
......@@ -40,6 +38,8 @@ class LoginPresenter @Inject constructor(private val view: LoginView,
return
}
view.showSignUpView(settings.registrationEnabled())
var hasSocial = false
if (settings.facebookEnabled()) {
view.enableLoginByFacebook()
......@@ -69,8 +69,6 @@ class LoginPresenter @Inject constructor(private val view: LoginView,
view.enableLoginByGitlab()
hasSocial = true
}
view.showSignUpView(settings.registrationEnabled())
view.showOauthView(hasSocial)
}
......@@ -108,9 +106,9 @@ class LoginPresenter @Inject constructor(private val view: LoginView,
}
}
}
} finally {
view.hideLoading()
}
view.hideLoading()
} else {
view.showNoInternetConnection()
}
......@@ -119,9 +117,7 @@ class LoginPresenter @Inject constructor(private val view: LoginView,
}
}
fun signup() {
navigator.toSignUp()
}
fun signup() = navigator.toSignUp()
private suspend fun registerPushToken() {
localRepository.get(LocalRepository.KEY_PUSH_TOKEN)?.let {
......
......@@ -8,7 +8,6 @@ import android.support.v4.app.Fragment
import android.text.style.ClickableSpan
import android.view.*
import android.widget.ImageButton
import android.view.inputmethod.InputMethodManager
import android.widget.ScrollView
import android.widget.Toast
import chat.rocket.android.R
......@@ -57,12 +56,6 @@ class LoginFragment : Fragment(), LoginView {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
activity?.apply {
text_username_or_email.requestFocus()
val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.showSoftInput(text_username_or_email, InputMethodManager.SHOW_IMPLICIT)
}
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) {
tintEditTextDrawableStart()
}
......@@ -201,7 +194,7 @@ class LoginFragment : Fragment(), LoginView {
private fun setupSignUpListener() {
val signUp = getString(R.string.title_sign_up)
val newToRocketChat = String.format(getString(R.string.msg_new_to_rocket_chat), signUp)
val newToRocketChat = String.format(getString(R.string.msg_new_user), signUp)
text_new_to_rocket_chat.text = newToRocketChat
......@@ -216,7 +209,7 @@ class LoginFragment : Fragment(), LoginView {
button_log_in.isEnabled = value
text_username_or_email.isEnabled = value
text_password.isEnabled = value
if (isEditTextEmpty()) {
if (!isEditTextEmpty()) {
showSignUpView(value)
showOauthView(value)
}
......
......@@ -32,6 +32,7 @@ class AuthenticationNavigator(internal val activity: AuthenticationActivity, int
fun toWebPage(url: String) {
activity.startActivity(context.webViewIntent(url))
activity.overridePendingTransition(R.anim.slide_up, R.anim.hold)
}
fun toChatList() {
......
......@@ -6,6 +6,6 @@ import dagger.android.ContributesAndroidInjector
@Module abstract class ServerFragmentProvider {
@ContributesAndroidInjector(modules = arrayOf(ServerFragmentModule::class))
@ContributesAndroidInjector(modules = [(ServerFragmentModule::class)])
abstract fun provideServerFragment(): ServerFragment
}
\ No newline at end of file
......@@ -3,9 +3,11 @@ package chat.rocket.android.authentication.server.presentation
import chat.rocket.android.authentication.presentation.AuthenticationNavigator
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.helper.NetworkHelper
import chat.rocket.android.helper.UrlHelper
import chat.rocket.android.server.domain.*
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.launchUI
import chat.rocket.common.util.ifNull
import chat.rocket.core.RocketChatClient
import chat.rocket.core.internal.rest.settings
import java.security.InvalidParameterException
......@@ -17,41 +19,50 @@ class ServerPresenter @Inject constructor(private val view: ServerView,
private val serverInteractor: SaveCurrentServerInteractor,
private val settingsInteractor: SaveSettingsInteractor,
private val factory: RocketChatClientFactory) {
private var settingsFilter = arrayOf(SITE_URL, SITE_NAME,
FAVICON_512, USE_REALNAME, ALLOW_ROOM_NAME_SPECIAL_CHARS, FAVORITE_ROOMS,
ACCOUNT_LOGIN_FORM, ACCOUNT_GOOGLE, ACCOUNT_FACEBOOK, ACCOUNT_GITHUB, ACCOUNT_GITLAB,
ACCOUNT_LINKEDIN, ACCOUNT_METEOR, ACCOUNT_TWITTER, ACCOUNT_WORDPRESS, LDAP_ENABLE,
ACCOUNT_REGISTRATION, STORAGE_TYPE, HIDE_USER_JOIN, HIDE_USER_LEAVE, HIDE_TYPE_AU,
private lateinit var client: RocketChatClient
private var settingsFilter = arrayOf(SITE_URL, SITE_NAME, FAVICON_512, USE_REALNAME, ALLOW_ROOM_NAME_SPECIAL_CHARS, FAVORITE_ROOMS,
ACCOUNT_LOGIN_FORM, ACCOUNT_GOOGLE, ACCOUNT_FACEBOOK, ACCOUNT_GITHUB, ACCOUNT_GITLAB, ACCOUNT_LINKEDIN, ACCOUNT_METEOR,
ACCOUNT_TWITTER, ACCOUNT_WORDPRESS, LDAP_ENABLE, ACCOUNT_REGISTRATION, STORAGE_TYPE, HIDE_USER_JOIN, HIDE_USER_LEAVE, HIDE_TYPE_AU,
HIDE_MUTE_UNMUTE, HIDE_TYPE_RU, ACCOUNT_CUSTOM_FIELDS)
fun connect(server: String) {
var cli: RocketChatClient? = null
try {
cli = factory.create(server)
} catch (ex: InvalidParameterException) {
view.showMessage(ex.message!!)
}
if (!UrlHelper.isValidUrl(server)) {
view.showInvalidServerUrl()
} else {
try {
client = factory.create(server)
} catch (exception: InvalidParameterException) {
exception.message?.let {
view.showMessage(it)
}.ifNull {
view.showGenericErrorMessage()
}
return
}
cli?.let { client ->
launchUI(strategy) {
if (NetworkHelper.hasInternetAccess()) {
view.showLoading()
client.let { rocketChatClient ->
launchUI(strategy) {
if (NetworkHelper.hasInternetAccess()) {
view.showLoading()
try {
val settings = client.settings(*settingsFilter)
settingsInteractor.save(server, settings)
serverInteractor.save(server)
try {
val settings = rocketChatClient.settings(*settingsFilter)
settingsInteractor.save(server, settings)
serverInteractor.save(server)
navigator.toLogin()
} catch (ex: Exception) {
ex.printStackTrace()
view.showMessage(ex.message!!)
} finally {
view.hideLoading()
navigator.toLogin()
} catch (exception: Exception) {
exception.message?.let {
view.showMessage(it)
}.ifNull {
view.showGenericErrorMessage()
}
} finally {
view.hideLoading()
}
} else {
view.showNoInternetConnection()
}
} else {
view.showNoInternetConnection()
}
}
}
......
......@@ -4,4 +4,10 @@ import chat.rocket.android.core.behaviours.InternetView
import chat.rocket.android.core.behaviours.LoadingView
import chat.rocket.android.core.behaviours.MessageView
interface ServerView : LoadingView, MessageView, InternetView
\ No newline at end of file
interface ServerView : LoadingView, MessageView, InternetView {
/**
* Notifies the user about an invalid inputted server URL.
*/
fun showInvalidServerUrl()
}
\ No newline at end of file
......@@ -44,6 +44,8 @@ class ServerFragment : Fragment(), ServerView {
relative_layout.viewTreeObserver.removeOnGlobalLayoutListener(layoutListener)
}
override fun showInvalidServerUrl() = showMessage(getString(R.string.msg_invalid_server_url))
override fun showLoading() {
enableUserInput(false)
view_loading.setVisibility(true)
......
......@@ -3,11 +3,13 @@ package chat.rocket.android.authentication.signup.presentation
import chat.rocket.android.authentication.presentation.AuthenticationNavigator
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.helper.NetworkHelper
import chat.rocket.android.helper.UrlHelper
import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.launchUI
import chat.rocket.common.RocketChatException
import chat.rocket.common.util.ifNull
import chat.rocket.core.RocketChatClient
import chat.rocket.core.internal.rest.login
import chat.rocket.core.internal.rest.registerPushToken
......@@ -20,7 +22,6 @@ class SignupPresenter @Inject constructor(private val view: SignupView,
private val localRepository: LocalRepository,
private val serverInteractor: GetCurrentServerInteractor,
private val factory: RocketChatClientFactory) {
private val client: RocketChatClient = factory.create(serverInteractor.get()!!)
fun signup(name: String, username: String, password: String, email: String) {
......@@ -53,15 +54,15 @@ class SignupPresenter @Inject constructor(private val view: SignupView,
registerPushToken()
navigator.toChatList()
} catch (exception: RocketChatException) {
val errorMessage = exception.message
if (errorMessage != null) {
view.showMessage(errorMessage)
} else {
exception.message?.let {
view.showMessage(it)
}.ifNull {
view.showGenericErrorMessage()
}
}
} finally {
view.hideLoading()
view.hideLoading()
}
} else {
view.showNoInternetConnection()
}
......@@ -72,13 +73,13 @@ class SignupPresenter @Inject constructor(private val view: SignupView,
fun termsOfService() {
serverInteractor.get()?.let {
navigator.toWebPage("/terms-of-service")
navigator.toWebPage(UrlHelper.getTermsOfServiceUrl(it))
}
}
fun privacyPolicy() {
serverInteractor.get()?.let {
navigator.toWebPage("/privacy-policy")
navigator.toWebPage(UrlHelper.getPrivacyPolicyUrl(it))
}
}
......
......@@ -9,6 +9,7 @@ import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.launchUI
import chat.rocket.common.RocketChatAuthException
import chat.rocket.common.RocketChatException
import chat.rocket.common.util.ifNull
import chat.rocket.core.RocketChatClient
import chat.rocket.core.internal.rest.login
import chat.rocket.core.internal.rest.registerPushToken
......@@ -20,51 +21,50 @@ class TwoFAPresenter @Inject constructor(private val view: TwoFAView,
private val localRepository: LocalRepository,
private val serverInteractor: GetCurrentServerInteractor,
private val factory: RocketChatClientFactory) {
private val client: RocketChatClient = factory.create(serverInteractor.get()!!)
// TODO: If the usernameOrEmail and password was informed by the user on the previous screen, then we should pass only the pin, like this: fun authenticate(pin: EditText)
fun authenticate(usernameOrEmail: String, password: String, twoFactorAuthenticationCode: String) {
val server = serverInteractor.get()
if (twoFactorAuthenticationCode.isBlank()) {
view.alertBlankTwoFactorAuthenticationCode()
} else if (server == null) {
navigator.toServerScreen()
} else {
launchUI(strategy) {
val client = factory.create(server)
if (NetworkHelper.hasInternetAccess()) {
view.showLoading()
try {
// The token is saved via the client TokenProvider
client.login(usernameOrEmail, password, twoFactorAuthenticationCode)
registerPushToken()
navigator.toChatList()
} catch (exception: RocketChatException) {
if (exception is RocketChatAuthException) {
view.alertInvalidTwoFactorAuthenticationCode()
} else {
val message = exception.message
if (message != null) {
view.showMessage(message)
when {
server == null -> {
navigator.toServerScreen()
}
twoFactorAuthenticationCode.isBlank() -> {
view.alertBlankTwoFactorAuthenticationCode()
}
else -> {
launchUI(strategy) {
val client = factory.create(server)
if (NetworkHelper.hasInternetAccess()) {
view.showLoading()
try {
// The token is saved via the client TokenProvider
client.login(usernameOrEmail, password, twoFactorAuthenticationCode)
registerPushToken()
navigator.toChatList()
} catch (exception: RocketChatException) {
if (exception is RocketChatAuthException) {
view.alertInvalidTwoFactorAuthenticationCode()
} else {
view.showGenericErrorMessage()
exception.message?.let {
view.showMessage(it)
}.ifNull {
view.showGenericErrorMessage()
}
}
} finally {
view.hideLoading()
}
} else {
view.showNoInternetConnection()
}
view.hideLoading()
} else {
view.showNoInternetConnection()
}
}
}
}
fun signup() {
navigator.toSignUp()
}
fun signup() = navigator.toSignUp()
private suspend fun registerPushToken() {
localRepository.get(LocalRepository.KEY_PUSH_TOKEN)?.let {
......
......@@ -21,7 +21,7 @@ class AuthenticationActivity : AppCompatActivity(), HasSupportFragmentInjector {
AndroidInjection.inject(this)
addFragment("authenticationServerFragment", R.id.fragment_container) {
addFragment("ServerFragment", R.id.fragment_container) {
ServerFragment.newInstance()
}
}
......
package chat.rocket.android.chatroom.di
import android.arch.lifecycle.LifecycleOwner
import chat.rocket.android.chatroom.presentation.ChatRoomView
import chat.rocket.android.chatroom.ui.ChatRoomFragment
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.dagger.scope.PerFragment
import dagger.Module
import dagger.Provides
import kotlinx.coroutines.experimental.Job
@Module
@PerFragment
class ChatRoomFragmentModule {
@Provides
fun chatRoomView(frag: ChatRoomFragment): ChatRoomView {
return frag
}
@Provides
fun provideLifecycleOwner(frag: ChatRoomFragment): LifecycleOwner {
return frag
}
@Provides
fun provideCancelStrategy(owner: LifecycleOwner, jobs: Job): CancelStrategy {
return CancelStrategy(owner, jobs)
}
}
\ No newline at end of file
package chat.rocket.android.chatroom.di
import chat.rocket.android.chatroom.ui.ChatRoomFragment
import dagger.Module
import dagger.android.ContributesAndroidInjector
@Module
abstract class ChatRoomFragmentProvider {
@ContributesAndroidInjector(modules = [ChatRoomFragmentModule::class])
abstract fun provideChatRoomFragment(): ChatRoomFragment
}
\ No newline at end of file
package chat.rocket.android.chatroom.presentation
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.launchUI
import chat.rocket.common.model.BaseRoom
import chat.rocket.common.util.ifNull
import chat.rocket.core.internal.rest.messages
import chat.rocket.core.internal.rest.sendMessage
import javax.inject.Inject
class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
private val strategy: CancelStrategy,
private val serverInteractor: GetCurrentServerInteractor,
factory: RocketChatClientFactory) {
private val client = factory.create(serverInteractor.get()!!)
fun loadMessages(chatRoomId: String, chatRoomType: String, offset: Int = 0) {
launchUI(strategy) {
view.showLoading()
try {
val messages = client.messages(chatRoomId, BaseRoom.RoomType.valueOf(chatRoomType), offset.toLong(), 30).result
view.showMessages(messages.toMutableList(), serverInteractor.get()!!)
} catch (ex: Exception) {
ex.message?.let {
view.showMessage(it)
}.ifNull {
view.showGenericErrorMessage()
}
} finally {
view.hideLoading()
}
}
}
fun sendMessage(chatRoomId: String, text: String) {
launchUI(strategy) {
try {
val message = client.sendMessage(chatRoomId, text)
view.showSentMessage(message)
} catch (ex: Exception) {
ex.message?.let {
view.showMessage(it)
}.ifNull {
view.showGenericErrorMessage()
}
}
}
}
}
\ No newline at end of file
package chat.rocket.android.chatroom.presentation
import chat.rocket.android.core.behaviours.LoadingView
import chat.rocket.android.core.behaviours.MessageView
import chat.rocket.core.model.Message
interface ChatRoomView : LoadingView, MessageView {
/**
* Shows the chat room messages.
*
* @param dataSet The data set to show.
* @param serverUrl The server URL.
*/
fun showMessages(dataSet: MutableList<Message>, serverUrl: String)
/**
* Send a message to a chat room.
*
* @param text The text to send.
*/
fun sendMessage(text: String)
/**
* Shows a (recent) message sent to a chat room.
* @param message The (recent) message sent to a chat room.
*/
fun showSentMessage(message: Message)
}
\ No newline at end of file
package chat.rocket.android.chatroom.ui
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.support.v4.app.Fragment
import android.support.v7.app.AppCompatActivity
import chat.rocket.android.R
import chat.rocket.android.util.addFragment
import chat.rocket.android.util.textContent
import dagger.android.AndroidInjection
import dagger.android.AndroidInjector
import dagger.android.DispatchingAndroidInjector
import dagger.android.support.HasSupportFragmentInjector
import kotlinx.android.synthetic.main.app_bar_chat_room.*
import javax.inject.Inject
fun Context.chatRoomIntent(chatRoomId: String, chatRoomName: String, chatRoomType: String, isChatRoomReadOnly: Boolean): Intent {
return Intent(this, ChatRoomActivity::class.java).apply {
putExtra(INTENT_CHAT_ROOM_ID, chatRoomId)
putExtra(INTENT_CHAT_ROOM_NAME, chatRoomName)
putExtra(INTENT_CHAT_ROOM_TYPE, chatRoomType)
putExtra(INTENT_IS_CHAT_ROOM_READ_ONLY, isChatRoomReadOnly)
}
}
private const val INTENT_CHAT_ROOM_ID = "chat_room_id"
private const val INTENT_CHAT_ROOM_NAME = "chat_room_name"
private const val INTENT_CHAT_ROOM_TYPE = "chat_room_type"
private const val INTENT_IS_CHAT_ROOM_READ_ONLY = "is_chat_room_read_only"
class ChatRoomActivity : AppCompatActivity(), HasSupportFragmentInjector {
@Inject lateinit var fragmentDispatchingAndroidInjector: DispatchingAndroidInjector<Fragment>
private lateinit var chatRoomId: String
private lateinit var chatRoomName: String
private lateinit var chatRoomType: String
private var isChatRoomReadOnly: Boolean = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_chat_room)
AndroidInjection.inject(this)
chatRoomId = intent.getStringExtra(INTENT_CHAT_ROOM_ID)
requireNotNull(chatRoomId) { "no chat_room_id provided in Intent extras" }
chatRoomName = intent.getStringExtra(INTENT_CHAT_ROOM_NAME)
requireNotNull(chatRoomName) { "no chat_room_name provided in Intent extras" }
chatRoomType = intent.getStringExtra(INTENT_CHAT_ROOM_TYPE)
requireNotNull(chatRoomType) { "no chat_room_type provided in Intent extras" }
isChatRoomReadOnly = intent.getBooleanExtra(INTENT_IS_CHAT_ROOM_READ_ONLY, true)
requireNotNull(chatRoomType) { "no is_chat_room_read_only provided in Intent extras" }
setupToolbar(chatRoomName)
addFragment("ChatRoomFragment", R.id.fragment_container) {
newInstance(chatRoomId, chatRoomName, chatRoomType, isChatRoomReadOnly)
}
}
override fun onBackPressed() = finishActivity()
override fun supportFragmentInjector(): AndroidInjector<Fragment> {
return fragmentDispatchingAndroidInjector
}
private fun setupToolbar(chatRoomName: String) {
text_room_name.textContent = chatRoomName
toolbar.setNavigationOnClickListener {
finishActivity()
}
}
private fun finishActivity() {
super.onBackPressed()
overridePendingTransition(R.anim.close_enter, R.anim.close_exit)
}
}
\ No newline at end of file
package chat.rocket.android.chatroom.ui
import DateTimeHelper
import android.content.Context
import android.support.v7.widget.RecyclerView
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import chat.rocket.android.R
import chat.rocket.android.helper.UrlHelper
import chat.rocket.android.util.inflate
import chat.rocket.android.util.setVisibility
import chat.rocket.android.util.textContent
import chat.rocket.common.util.ifNull
import chat.rocket.core.model.Message
import com.facebook.drawee.view.SimpleDraweeView
import kotlinx.android.synthetic.main.avatar.view.*
import kotlinx.android.synthetic.main.item_message.view.*
class ChatRoomAdapter(private val context: Context,
private var dataSet: MutableList<Message>,
private val serverUrl: String) : RecyclerView.Adapter<ChatRoomAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder = ViewHolder(parent.inflate(R.layout.item_message))
override fun onBindViewHolder(holder: ViewHolder, position: Int) = holder.bind(dataSet[position])
override fun getItemCount(): Int = dataSet.size
override fun getItemViewType(position: Int): Int = position
fun addDataSet(dataSet: List<Message>) {
val previousDataSetSize = this.dataSet.size
this.dataSet.addAll(previousDataSetSize, dataSet)
notifyItemRangeInserted(previousDataSetSize, dataSet.size)
}
fun addItem(message: Message) {
dataSet.add(0, message)
notifyItemInserted(0)
}
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bind(message: Message) = with(itemView) {
bindUserAvatar(message, image_avatar, image_unknown_avatar)
bindUserName(message, text_user_name)
bindTime(message, text_message_time)
bindContent(message, text_content)
}
private fun bindUserAvatar(message: Message, drawee: SimpleDraweeView, imageUnknownAvatar: ImageView) = message.sender?.username.let {
drawee.setImageURI(UrlHelper.getAvatarUrl(serverUrl, it.toString()))
}.ifNull {
imageUnknownAvatar.setVisibility(true)
}
private fun bindUserName(message: Message, textView: TextView) = message.sender?.username.let {
textView.textContent = it.toString()
}.ifNull {
textView.textContent = context.getString(R.string.msg_unknown)
}
private fun bindTime(message: Message, textView: TextView) {
textView.textContent = DateTimeHelper.getTime(DateTimeHelper.getLocalDateTime(message.timestamp))
}
private fun bindContent(message: Message, textView: TextView) {
textView.textContent = message.message
}
}
}
\ No newline at end of file
package chat.rocket.android.chatroom.ui
import android.os.Bundle
import android.support.v4.app.Fragment
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import chat.rocket.android.R
import chat.rocket.android.chatroom.presentation.ChatRoomPresenter
import chat.rocket.android.chatroom.presentation.ChatRoomView
import chat.rocket.android.helper.EndlessRecyclerViewScrollListener
import chat.rocket.android.util.inflate
import chat.rocket.android.util.setVisibility
import chat.rocket.android.util.textContent
import chat.rocket.core.model.Message
import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_chat_room.*
import kotlinx.android.synthetic.main.message_composer.*
import javax.inject.Inject
fun newInstance(chatRoomId: String, chatRoomName: String, chatRoomType: String, isChatRoomReadOnly: Boolean): Fragment {
return ChatRoomFragment().apply {
arguments = Bundle(1).apply {
putString(BUNDLE_CHAT_ROOM_ID, chatRoomId)
putString(BUNDLE_CHAT_ROOM_NAME, chatRoomName)
putString(BUNDLE_CHAT_ROOM_TYPE, chatRoomType)
putBoolean(BUNDLE_IS_CHAT_ROOM_READ_ONLY, isChatRoomReadOnly)
}
}
}
private const val BUNDLE_CHAT_ROOM_ID = "chat_room_id"
private const val BUNDLE_CHAT_ROOM_NAME = "chat_room_name"
private const val BUNDLE_CHAT_ROOM_TYPE = "chat_room_type"
private const val BUNDLE_IS_CHAT_ROOM_READ_ONLY = "is_chat_room_read_only"
class ChatRoomFragment : Fragment(), ChatRoomView {
@Inject lateinit var presenter: ChatRoomPresenter
private lateinit var chatRoomId: String
private lateinit var chatRoomName: String
private lateinit var chatRoomType: String
private var isChatRoomReadOnly: Boolean = false
private lateinit var adapter: ChatRoomAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
AndroidSupportInjection.inject(this)
val bundle = arguments
if (bundle != null) {
chatRoomId = bundle.getString(BUNDLE_CHAT_ROOM_ID)
chatRoomName = bundle.getString(BUNDLE_CHAT_ROOM_NAME)
chatRoomType = bundle.getString(BUNDLE_CHAT_ROOM_TYPE)
isChatRoomReadOnly = bundle.getBoolean(BUNDLE_IS_CHAT_ROOM_READ_ONLY)
} else {
requireNotNull(bundle) { "no arguments supplied when the fragment was instantiated" }
}
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? = container?.inflate(R.layout.fragment_chat_room)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
presenter.loadMessages(chatRoomId, chatRoomType)
setupComposer()
}
override fun showMessages(dataSet: MutableList<Message>, serverUrl: String) {
activity?.apply {
if (recycler_view.adapter == null) {
adapter = ChatRoomAdapter(this, dataSet, serverUrl)
recycler_view.adapter = adapter
val linearLayoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, true)
recycler_view.layoutManager = linearLayoutManager
if (dataSet.size >= 30) {
recycler_view.addOnScrollListener(object : EndlessRecyclerViewScrollListener(linearLayoutManager) {
override fun onLoadMore(page: Int, totalItemsCount: Int, recyclerView: RecyclerView?) {
presenter.loadMessages(chatRoomId, chatRoomType, page * 30)
}
})
}
} else {
adapter.addDataSet(dataSet)
}
}
}
override fun sendMessage(text: String) {
if (!text.isBlank()) {
presenter.sendMessage(chatRoomId, text)
}
}
override fun showSentMessage(message: Message) {
text_message.textContent = ""
adapter.addItem(message)
recycler_view.smoothScrollToPosition(0)
}
override fun showLoading() = view_loading.setVisibility(true)
override fun hideLoading() = view_loading.setVisibility(false)
override fun showMessage(message: String) = Toast.makeText(activity, message, Toast.LENGTH_SHORT).show()
override fun showGenericErrorMessage() = showMessage(getString(R.string.msg_generic_error))
private fun setupComposer() {
if (isChatRoomReadOnly) {
text_room_is_read_only.setVisibility(true)
top_container.setVisibility(false)
} else {
text_send.setOnClickListener { sendMessage(text_message.textContent) }
}
}
}
\ No newline at end of file
......@@ -27,9 +27,4 @@ class ChatRoomsFragmentModule {
fun provideCancelStrategy(owner: LifecycleOwner, jobs: Job): CancelStrategy {
return CancelStrategy(owner, jobs)
}
@Provides
fun provideJob(): Job {
return Job()
}
}
\ No newline at end of file
package chat.rocket.android.chatrooms.di
import android.content.Context
import chat.rocket.android.chatrooms.presentation.ChatRoomsNavigator
import chat.rocket.android.chatrooms.ui.MainActivity
import chat.rocket.android.dagger.scope.PerActivity
import dagger.Module
import dagger.Provides
@Module
class ChatRoomsModule {
@Provides
@PerActivity
fun provideAuthenticationNavigator(activity: MainActivity, context: Context) = ChatRoomsNavigator(activity, context)
}
\ No newline at end of file
package chat.rocket.android.chatrooms.presentation
import android.content.Context
import chat.rocket.android.R
import chat.rocket.android.chatroom.ui.chatRoomIntent
import chat.rocket.android.chatrooms.ui.MainActivity
class ChatRoomsNavigator(private val activity: MainActivity, private val context: Context) {
fun toChatRoom(chatRoomId: String, chatRoomName: String, chatRoomType: String, isChatRoomReadOnly: Boolean) {
activity.startActivity(context.chatRoomIntent(chatRoomId, chatRoomName, chatRoomType, isChatRoomReadOnly))
activity.overridePendingTransition(R.anim.open_enter, R.anim.open_exit)
}
}
\ No newline at end of file
......@@ -18,11 +18,11 @@ import javax.inject.Inject
class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
private val strategy: CancelStrategy,
private val navigator: ChatRoomsNavigator,
private val serverInteractor: GetCurrentServerInteractor,
private val getChatRoomsInteractor: GetChatRoomsInteractor,
private val saveChatRoomsInteractor: SaveChatRoomsInteractor,
factory: RocketChatClientFactory) {
private val client: RocketChatClient = factory.create(serverInteractor.get()!!)
private val currentServer = serverInteractor.get()!!
private var reloadJob: Deferred<List<ChatRoom>>? = null
......@@ -36,6 +36,20 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
}
}
fun loadChatRoom(chatRoom: ChatRoom) = navigator.toChatRoom(chatRoom.id, chatRoom.name, chatRoom.type.name, chatRoom.readonly ?: false)
/**
* Gets a [ChatRoom] list from local repository.
* ChatRooms returned are filtered by name.
*/
fun chatRoomsByName(name: String) {
val currentServer = serverInteractor.get()!!
launchUI(strategy) {
val roomList = getChatRoomsInteractor.getByName(currentServer, name)
view.updateChatRooms(roomList)
}
}
private suspend fun loadRooms(): List<ChatRoom> {
val chatRooms = client.chatRooms().update
val sortedRooms = sortRooms(chatRooms)
......@@ -54,24 +68,13 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
}
}
/**
* Get a ChatRoom list from local repository. ChatRooms returned are filtered by name.
*/
fun chatRoomsByName(name: String) {
val currentServer = serverInteractor.get()!!
launchUI(strategy) {
val roomList = getChatRoomsInteractor.getByName(currentServer, name)
view.updateChatRooms(roomList)
}
}
private fun getOpenChatRooms(chatRooms: List<ChatRoom>): List<ChatRoom> {
return chatRooms.filter(ChatRoom::open)
}
private fun sortChatRooms(chatRooms: List<ChatRoom>): List<ChatRoom> {
return chatRooms.sortedByDescending {
chatRoom -> chatRoom.lastMessage?.timestamp
return chatRooms.sortedByDescending { chatRoom ->
chatRoom.lastMessage?.timestamp
}
}
......
......@@ -16,83 +16,96 @@ import chat.rocket.android.util.textContent
import chat.rocket.common.model.BaseRoom.RoomType
import chat.rocket.core.model.ChatRoom
import com.facebook.drawee.view.SimpleDraweeView
import kotlinx.android.synthetic.main.avatar.view.*
import kotlinx.android.synthetic.main.item_chat.view.*
class ChatRoomsAdapter(private val context: Context) : RecyclerView.Adapter<ChatRoomsAdapter.ViewHolder>() {
class ChatRoomsAdapter(private val context: Context,
private val listener: (ChatRoom) -> Unit) : RecyclerView.Adapter<ChatRoomsAdapter.ViewHolder>() {
var dataSet: MutableList<ChatRoom> = ArrayList()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder = ViewHolder(parent.inflate(R.layout.item_chat))
override fun onBindViewHolder(holder: ViewHolder, position: Int) = holder.bind(dataSet[position])
override fun getItemCount(): Int = dataSet.size
override fun getItemViewType(position: Int): Int = position
fun updateRooms(newRooms: List<ChatRoom>) {
dataSet.clear()
dataSet.addAll(newRooms)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder = ViewHolder(parent.inflate(R.layout.item_chat))
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val chatRoom = dataSet[position]
val chatRoomName = chatRoom.name
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
holder.chatName.textContent = chatRoomName
fun bind(chatRoom: ChatRoom) = with(itemView) {
bindAvatar(chatRoom, layout_avatar, image_avatar, image_room_avatar)
bindName(chatRoom, text_chat_name)
bindLastMessageDateTime(chatRoom, text_last_message_date_time)
bindLastMessage(chatRoom, text_last_message)
bindUnreadMessages(chatRoom, text_total_unread_messages)
if (chatRoom.type == RoomType.ONE_TO_ONE) {
// TODO Check the best way to get the current server url.
val canonicalUrl = chatRoom.client.url
holder.userAvatar.setImageURI(UrlHelper.getAvatarUrl(canonicalUrl, chatRoomName))
holder.userAvatar.setVisibility(true)
} else {
holder.roomAvatar.setImageDrawable(DrawableHelper.getTextDrawable(chatRoomName))
holder.roomAvatar.setVisibility(true)
setOnClickListener { listener(chatRoom) }
}
val totalUnreadMessage = chatRoom.unread
when {
totalUnreadMessage in 1..99 -> {
holder.unreadMessage.textContent = totalUnreadMessage.toString()
holder.unreadMessage.setVisibility(true)
private fun bindAvatar(chatRoom: ChatRoom, avatarLayout: View, drawee: SimpleDraweeView, imageView: ImageView) {
val chatRoomName = chatRoom.name
if (chatRoom.type == RoomType.ONE_TO_ONE) {
drawee.setImageURI(UrlHelper.getAvatarUrl(chatRoom.client.url, chatRoomName))
imageView.setVisibility(false)
avatarLayout.setVisibility(true)
} else {
imageView.setImageDrawable(DrawableHelper.getTextDrawable(chatRoomName))
avatarLayout.setVisibility(false)
imageView.setVisibility(true)
}
totalUnreadMessage > 99 -> {
holder.unreadMessage.textContent = context.getString(R.string.msg_more_than_ninety_nine_unread_messages)
holder.unreadMessage.setVisibility(true)
}
private fun bindName(chatRoom: ChatRoom, textView: TextView) {
textView.textContent = chatRoom.name
}
private fun bindLastMessageDateTime(chatRoom: ChatRoom, textView: TextView) {
val lastMessage = chatRoom.lastMessage
if (lastMessage != null) {
val localDateTime = DateTimeHelper.getLocalDateTime(lastMessage.timestamp)
textView.textContent = DateTimeHelper.getDate(localDateTime, context)
}
}
val lastMessage = chatRoom.lastMessage
val lastMessageSender = lastMessage?.sender
if (lastMessage != null && lastMessageSender != null) {
val message = lastMessage.message
val senderUsername = lastMessageSender.username
when (senderUsername) {
chatRoomName -> {
holder.lastMessage.textContent = message
}
private fun bindLastMessage(chatRoom: ChatRoom, textView: TextView) {
val lastMessage = chatRoom.lastMessage
val lastMessageSender = lastMessage?.sender
if (lastMessage != null && lastMessageSender != null) {
val message = lastMessage.message
val senderUsername = lastMessageSender.username
when (senderUsername) {
chatRoom.name -> {
textView.textContent = message
}
// TODO Change to MySelf
// chatRoom.user?.username -> {
// holder.lastMessage.textContent = context.getString(R.string.msg_you) + ": $message"
// }
else -> {
holder.lastMessage.textContent = "@$senderUsername: $message"
// chatRoom.user?.username -> {
// holder.lastMessage.textContent = context.getString(R.string.msg_you) + ": $message"
// }
else -> {
textView.textContent = "@$senderUsername: $message"
}
}
}
val localDateTime = DateTimeHelper.getLocalDateTime(lastMessage.timestamp)
holder.lastMessageDateTime.textContent = DateTimeHelper.getDate(localDateTime, context)
}
}
override fun onBindViewHolder(holder: ViewHolder, position: Int, payloads: MutableList<Any>?) {
onBindViewHolder(holder, position)
}
override fun getItemCount(): Int = dataSet.size
override fun getItemViewType(position: Int): Int = position
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val userAvatar: SimpleDraweeView = itemView.image_user_avatar
val roomAvatar: ImageView = itemView.image_room_avatar
val chatName: TextView = itemView.text_chat_name
val lastMessage: TextView = itemView.text_last_message
val lastMessageDateTime: TextView = itemView.text_last_message_date_time
val unreadMessage: TextView = itemView.text_total_unread_messages
private fun bindUnreadMessages(chatRoom: ChatRoom, textView: TextView) {
val totalUnreadMessage = chatRoom.unread
when {
totalUnreadMessage in 1..99 -> {
textView.textContent = totalUnreadMessage.toString()
textView.setVisibility(true)
}
totalUnreadMessage > 99 -> {
textView.textContent = context.getString(R.string.msg_more_than_ninety_nine_unread_messages)
textView.setVisibility(true)
}
}
}
}
}
\ No newline at end of file
......@@ -12,6 +12,7 @@ import android.widget.Toast
import chat.rocket.android.R
import chat.rocket.android.chatrooms.presentation.ChatRoomsPresenter
import chat.rocket.android.chatrooms.presentation.ChatRoomsView
import chat.rocket.android.util.setVisibility
import chat.rocket.android.widget.DividerItemDecoration
import chat.rocket.core.model.ChatRoom
import dagger.android.support.AndroidSupportInjection
......@@ -46,8 +47,11 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
(activity as AppCompatActivity).apply {
recycler_view.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
recycler_view.addItemDecoration(DividerItemDecoration(this, 144, 32))
recycler_view.adapter = ChatRoomsAdapter(this)
recycler_view.itemAnimator = DefaultItemAnimator()
recycler_view.adapter = ChatRoomsAdapter(this) { chatRoom ->
presenter.loadChatRoom(chatRoom)
}
if (supportActionBar == null) {
setSupportActionBar(toolbar)
supportActionBar?.setDisplayHomeAsUpEnabled(false)
......@@ -57,6 +61,7 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
supportActionBar?.title = "Rocket.Chat"
}
}
presenter.loadChatRooms()
}
......@@ -97,9 +102,9 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
}
}
override fun showLoading() = view_loading.show()
override fun showLoading() = view_loading.setVisibility(true)
override fun hideLoading() = view_loading.hide()
override fun hideLoading() = view_loading.setVisibility(false)
override fun showMessage(message: String) = Toast.makeText(activity, message, Toast.LENGTH_SHORT).show()
......@@ -112,6 +117,7 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
class RoomsDiffCallback(private val oldRooms: List<ChatRoom>,
private val newRooms: List<ChatRoom>) : DiffUtil.Callback() {
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldRooms[oldItemPosition].id == newRooms[newItemPosition].id
}
......
......@@ -6,7 +6,10 @@ import chat.rocket.android.authentication.server.di.ServerFragmentProvider
import chat.rocket.android.authentication.signup.di.SignupFragmentProvider
import chat.rocket.android.authentication.twofactor.di.TwoFAFragmentProvider
import chat.rocket.android.authentication.ui.AuthenticationActivity
import chat.rocket.android.chatroom.di.ChatRoomFragmentProvider
import chat.rocket.android.chatroom.ui.ChatRoomActivity
import chat.rocket.android.chatrooms.di.ChatRoomsFragmentProvider
import chat.rocket.android.chatrooms.di.ChatRoomsModule
import chat.rocket.android.chatrooms.ui.MainActivity
import chat.rocket.android.dagger.scope.PerActivity
import dagger.Module
......@@ -24,6 +27,11 @@ abstract class ActivityBuilder {
])
abstract fun bindAuthenticationActivity(): AuthenticationActivity
@ContributesAndroidInjector(modules = [ChatRoomsFragmentProvider::class])
@PerActivity
@ContributesAndroidInjector(modules = [ChatRoomsModule::class, ChatRoomsFragmentProvider::class])
abstract fun bindMainActivity(): MainActivity
@PerActivity
@ContributesAndroidInjector(modules = [ChatRoomFragmentProvider::class])
abstract fun bindChatRoomActivity(): ChatRoomActivity
}
\ No newline at end of file
......@@ -21,6 +21,7 @@ import chat.rocket.common.util.PlatformLogger
import chat.rocket.core.RocketChatClient
import dagger.Module
import dagger.Provides
import kotlinx.coroutines.experimental.Job
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import javax.inject.Singleton
......@@ -47,6 +48,11 @@ class AppModule {
return Room.databaseBuilder(context, RocketChatDatabase::class.java, "rocketchat-db").build()
}
@Provides
fun provideJob(): Job {
return Job()
}
@Provides
@Singleton
fun provideContext(application: Application): Context {
......@@ -120,4 +126,4 @@ class AppModule {
fun provideChatRoomsRepository(): ChatRoomsRepository {
return MemoryChatRoomsRepository()
}
}
}
\ No newline at end of file
package chat.rocket.android.helper
import android.support.v7.widget.GridLayoutManager
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView
import android.support.v7.widget.StaggeredGridLayoutManager
/**
* Info: https://github.com/codepath/android_guides/wiki/Endless-Scrolling-with-AdapterViews-and-RecyclerView
*/
abstract class EndlessRecyclerViewScrollListener : RecyclerView.OnScrollListener {
private var visibleThreshold = 5 // The minimum amount of items to have below of the current scroll position before loading more.
private var currentPage = 0 // The current offset index of data loaded
private var previousTotalItemCount = 0 // The total number of items in the dataset after the last load
private var loading = true // True if we are still waiting for the last set of data to load.
private val startingPageIndex = 0 // Sets the starting page index
private var layoutManager: RecyclerView.LayoutManager
constructor(layoutManager: LinearLayoutManager) {
this.layoutManager = layoutManager
}
constructor(layoutManager: GridLayoutManager) {
this.layoutManager = layoutManager
visibleThreshold *= layoutManager.spanCount
}
constructor(layoutManager: StaggeredGridLayoutManager) {
this.layoutManager = layoutManager
visibleThreshold *= layoutManager.spanCount
}
private fun getLastVisibleItem(lastVisibleItemPositions: IntArray): Int {
var maxSize = 0
for (i in lastVisibleItemPositions.indices) {
if (i == 0) {
maxSize = lastVisibleItemPositions[i]
} else if (lastVisibleItemPositions[i] > maxSize) {
maxSize = lastVisibleItemPositions[i]
}
}
return maxSize
}
// This happens many times a second during a scroll, so be wary of the code you place here.
// We are given a few useful parameters to help us work out if we need to load some more data,
// but first we check if we are waiting for the previous load to finish.
override fun onScrolled(view: RecyclerView?, dx: Int, dy: Int) {
var lastVisibleItemPosition = 0
val totalItemCount = layoutManager.itemCount
when (layoutManager) {
is StaggeredGridLayoutManager -> {
val lastVisibleItemPositions = (layoutManager as StaggeredGridLayoutManager).findLastVisibleItemPositions(null)
// get maximum element within the list
lastVisibleItemPosition = getLastVisibleItem(lastVisibleItemPositions)
}
is GridLayoutManager -> lastVisibleItemPosition = (layoutManager as GridLayoutManager).findLastVisibleItemPosition()
is LinearLayoutManager -> lastVisibleItemPosition = (layoutManager as LinearLayoutManager).findLastVisibleItemPosition()
}
// If the total item count is zero and the previous isn't, assume the
// list is invalidated and should be reset back to initial state
// If it’s still loading, we check to see if the dataset count has
// changed, if so we conclude it has finished loading and update the current page
// number and total item count.
// If it isn’t currently loading, we check to see if we have breached
// the visibleThreshold and need to reload more data.
// If we do need to reload some more data, we execute onLoadMore to fetch the data.
// threshold should reflect how many total columns there are too
// If the total item count is zero and the previous isn't, assume the
// list is invalidated and should be reset back to initial state
if (totalItemCount < previousTotalItemCount) {
this.currentPage = this.startingPageIndex
this.previousTotalItemCount = totalItemCount
if (totalItemCount == 0) {
this.loading = true
}
}
// If it’s still loading, we check to see if the dataset count has
// changed, if so we conclude it has finished loading and update the current page
// number and total item count.
if (loading && totalItemCount > previousTotalItemCount) {
loading = false
previousTotalItemCount = totalItemCount
}
// If it isn’t currently loading, we check to see if we have breached
// the visibleThreshold and need to reload more data.
// If we do need to reload some more data, we execute onLoadMore to fetch the data.
// threshold should reflect how many total columns there are too
if (!loading && lastVisibleItemPosition + visibleThreshold > totalItemCount) {
currentPage++
onLoadMore(currentPage, totalItemCount, view)
loading = true
}
}
// Call this method whenever performing new searches
fun resetState() {
this.currentPage = this.startingPageIndex
this.previousTotalItemCount = 0
this.loading = true
}
// Defines the process for actually loading more data based on page
abstract fun onLoadMore(page: Int, totalItemsCount: Int, recyclerView: RecyclerView?)
}
\ No newline at end of file
package chat.rocket.android.helper
import android.util.Patterns
object UrlHelper {
/**
* Returns the avatar URL.
*
* @param serverUrl The serverUrl.
* @param chatRoomName The chat room name.
* @param serverUrl The server URL.
* @param avatarName The avatar name.
* @return The avatar URL.
*/
fun getAvatarUrl(serverUrl: String, chatRoomName: String): String = serverUrl + "avatar/" + chatRoomName
fun getAvatarUrl(serverUrl: String, avatarName: String): String = removeTrailingSlash(serverUrl) + "/avatar/" + avatarName
/**
* Returns the server's Terms of Service URL.
*
* @param serverUrl The server URL.
* @return The server's Terms of Service URL.
*/
fun getTermsOfServiceUrl(serverUrl: String) = removeTrailingSlash(serverUrl) + "/terms-of-service"
/**
* Returns the server's Privacy Policy URL.
*
* @param serverUrl The server URL.
* @return The server's Privacy Policy URL.
*/
fun getPrivacyPolicyUrl(serverUrl: String) = removeTrailingSlash(serverUrl) + "/privacy-policy"
/**
* Returns an URL without trailing slash.
*
* @param serverUrl The URL to remove the trailing slash (if exists).
* @return An URL without trailing slash.
*/
fun removeTrailingSlash(serverUrl: String): String {
return if (serverUrl[serverUrl.length - 1] == '/') {
serverUrl.replace("/+$", "")
} else {
serverUrl
}
}
/**
* Checks if the given URL is valid or not.
* @param url The url to check its valid.
* @return True if url is valid, false otherwise.
*/
fun isValidUrl(url: String): Boolean = Patterns.WEB_URL.matcher(url).matches()
}
\ No newline at end of file
......@@ -3,6 +3,8 @@ package chat.rocket.android.server.domain
import chat.rocket.core.model.ChatRoom
interface ChatRoomsRepository {
fun save(url: String, chatRooms: List<ChatRoom>)
fun get(url: String): List<ChatRoom>
}
\ No newline at end of file
......@@ -4,5 +4,6 @@ import chat.rocket.core.model.ChatRoom
import javax.inject.Inject
class SaveChatRoomsInteractor @Inject constructor(private val repository: ChatRoomsRepository) {
fun save(url: String, chatRooms: List<ChatRoom>) = repository.save(url, chatRooms)
}
\ No newline at end of file
......@@ -4,5 +4,6 @@ import chat.rocket.core.model.Value
import javax.inject.Inject
class SaveSettingsInteractor @Inject constructor(private val repository: SettingsRepository) {
fun save(url: String, settings: Map<String, Value<Any>>) = repository.save(url, settings)
}
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<accelerateInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
android:factor="1.5" />
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:background="@android:color/black"
android:fillAfter="true"
android:fillBefore="true"
android:fillEnabled="true"
android:shareInterpolator="false"
android:zAdjustment="top">
<alpha
android:duration="250"
android:fromAlpha="0.2"
android:interpolator="@anim/accelerate_cubic"
android:toAlpha="1.0" />
<scale
android:duration="250"
android:fromXScale="0.9"
android:fromYScale="0.9"
android:interpolator="@anim/accelerate_cubic"
android:pivotX="50.0%p"
android:pivotY="50.0%p"
android:toXScale="1.0"
android:toYScale="1.0" />
</set>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:zAdjustment="top">
<translate
android:duration="250"
android:fromXDelta="0.0%p"
android:interpolator="@anim/accelerate_cubic"
android:toXDelta="100.0%p" />
</set>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<decelerateInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
android:factor="1.5" />
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:zAdjustment="top">
<translate
android:duration="250"
android:fromXDelta="100.0%p"
android:interpolator="@anim/decelerate_cubic"
android:toXDelta="0.0%p" />
</set>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:background="@android:color/black"
android:fillAfter="true"
android:fillBefore="false"
android:fillEnabled="true"
android:shareInterpolator="false"
android:zAdjustment="normal">
<alpha
android:duration="250"
android:fromAlpha="1.0"
android:interpolator="@anim/decelerate_cubic"
android:toAlpha="0.2" />
<scale
android:duration="250"
android:fromXScale="1.0"
android:fromYScale="1.0"
android:interpolator="@anim/decelerate_cubic"
android:pivotX="50.0%p"
android:pivotY="50.0%p"
android:toXScale="0.9"
android:toYScale="0.9" />
</set>
\ No newline at end of file
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,19h-2v-2h2v2zM15.07,11.25l-0.9,0.92C13.45,12.9 13,13.5 13,15h-2v-0.5c0,-1.1 0.45,-2.1 1.17,-2.83l1.24,-1.26c0.37,-0.36 0.59,-0.86 0.59,-1.41 0,-1.1 -0.9,-2 -2,-2s-2,0.9 -2,2L8,9c0,-2.21 1.79,-4 4,-4s4,1.79 4,4c0,0.88 -0.36,1.68 -0.93,2.25z"/>
</vector>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:theme="@style/AuthenticationTheme">
android:theme="@style/AuthenticationTheme"
tools:context=".authentication.ui.AuthenticationActivity">
<FrameLayout
android:id="@+id/fragment_container"
......
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:theme="@style/AppTheme">
android:theme="@style/AppTheme"
tools:context=".chatroom.ui.ChatRoomActivity">
<include
android:id="@+id/layout_app_bar"
layout="@layout/app_bar" />
layout="@layout/app_bar_chat_room" />
<FrameLayout
android:id="@+id/fragment_container"
......
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:theme="@style/ChatListTheme">
android:theme="@style/ChatListTheme"
tools:context=".chatrooms.ui.MainActivity">
<FrameLayout
android:id="@+id/fragment_container"
......
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.AppBarLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/colorPrimary"
android:theme="@style/Theme.AppCompat.Light.NoActionBar">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_scrollFlags="scroll|enterAlways"
app:navigationIcon="?android:attr/homeAsUpIndicator"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
<android.support.constraint.ConstraintLayout
android:id="@+id/toolbar_content_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<!-- TODO implement -->
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/image_room_avatar"
android:layout_width="30dp"
android:layout_height="30dp"
android:visibility="gone"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:roundAsCircle="true" />
<TextView
android:id="@+id/text_room_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:ellipsize="end"
android:maxLines="1"
android:textColor="@color/white"
android:textSize="18sp"
android:textStyle="bold"
tools:text="Developers" />
</android.support.constraint.ConstraintLayout>
</android.support.v7.widget.Toolbar>
</android.support.design.widget.AppBarLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/image_avatar"
android:layout_width="40dp"
android:layout_height="40dp"
app:roundedCornerRadius="2dp" />
<!-- TODO define the correct bg color for this-->
<ImageView
android:id="@+id/image_unknown_avatar"
android:layout_width="40dp"
android:layout_height="40dp"
android:src="@drawable/ic_help_black_24dp"
android:tint="@color/colorAccent"
android:visibility="gone"
tools:ignore="contentDescription" />
</LinearLayout>
\ No newline at end of file
......@@ -32,6 +32,7 @@
android:cursorVisible="false"
android:hint="@string/default_server"
android:imeOptions="actionDone"
android:digits="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-/"
android:inputType="textUri"
android:paddingEnd="0dp"
android:paddingStart="0dp" />
......
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
......@@ -18,4 +20,14 @@
android:layout_height="wrap_content"
android:layout_alignParentBottom="true" />
<com.wang.avi.AVLoadingIndicatorView
android:id="@+id/view_loading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:visibility="gone"
app:indicatorColor="@color/black"
app:indicatorName="BallPulseIndicator"
tools:visibility="visible" />
</RelativeLayout>
\ No newline at end of file
......@@ -18,13 +18,17 @@
app:layout_constraintRight_toLeftOf="@+id/middle_container"
app:layout_constraintTop_toTopOf="parent">
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/image_user_avatar"
<include
android:id="@+id/layout_avatar"
layout="@layout/avatar"
android:layout_width="40dp"
android:layout_height="40dp"
android:visibility="gone"
app:roundAsCircle="true" />
android:visibility="gone" />
<!-- We need to build the avatar with initials since the server returns a SVG file with a pre defined 50x50 pixel.
TODO: check to scale the SVG file on the app and remove the view bellow (or make the server to return a jpg file.
SVG scaling with fresco is impossible: http://frescolib.org/docs/resizing.html
-->
<ImageView
android:id="@+id/image_room_avatar"
android:layout_width="40dp"
......
......@@ -4,18 +4,19 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:layout_marginBottom="10dp"
android:layout_marginEnd="@dimen/screen_edge_left_and_right_margins"
android:layout_marginStart="@dimen/screen_edge_left_and_right_margins"
android:layout_marginTop="16dp">
android:layout_marginTop="10dp">
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/image_user_avatar"
<include
android:id="@+id/layout_avatar"
layout="@layout/avatar"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginTop="7dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:roundAsCircle="true" />
app:layout_constraintTop_toTopOf="parent" />
<LinearLayout
android:id="@+id/top_container"
......@@ -23,7 +24,7 @@
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:orientation="horizontal"
app:layout_constraintLeft_toRightOf="@+id/image_user_avatar">
app:layout_constraintLeft_toRightOf="@+id/layout_avatar">
<TextView
android:id="@+id/text_user_name"
......@@ -54,11 +55,13 @@
app:layout_constraintTop_toBottomOf="@+id/top_container"
tools:text="This is a multiline chat message from Bertie that will take more than just one line of text. I have sure that everything is amazing!" />
<!-- TODO implement -->
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/image_attachment"
android:layout_width="0dp"
android:layout_height="150dp"
android:layout_marginTop="5dp"
android:visibility="gone"
app:layout_constraintLeft_toLeftOf="@id/top_container"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_content" />
......
......@@ -10,6 +10,17 @@
android:layout_height="1dp"
android:background="@color/colorDividerMessageComposer" />
<TextView
android:id="@+id/text_room_is_read_only"
android:layout_width="match_parent"
android:layout_height="45dp"
android:background="@color/white"
android:gravity="center"
android:text="@string/msg_this_room_is_read_only"
android:textColor="@color/black"
android:visibility="gone"
app:layout_constraintTop_toBottomOf="@+id/divider" />
<LinearLayout
android:id="@+id/top_container"
android:layout_width="match_parent"
......
......@@ -11,9 +11,11 @@
<string name="action_send">Enviar</string>
<string name="action_terms_of_service">Termos de Serviço</string>
<string name="action_privacy_policy">Política de Privacidade</string>
<string name="search">Pesquisar</string>
<!-- Regular information messages -->
<string name="msg_no_internet_connection">Sem conexão à internet</string>
<string name="msg_invalid_server_url">URL de servidor inválida</string>
<string name="msg_generic_error">Desculpe, ocorreu um erro, tente novamente</string>
<string name="msg_username">nome de usuário</string>
<string name="msg_username_or_email">nome de usuário ou email</string>
......@@ -21,12 +23,13 @@
<string name="msg_name_and_surname">nome e sobrenome</string>
<string name="msg_email">email</string>
<string name="msg_or_continue_using_social_accounts">Ou continue através de contas sociais</string>
<string name="msg_new_to_rocket_chat">Novo no Rocket Chat? %1$s</string>
<string name="msg_new_user">Novo usuário? %1$s</string>
<string name="msg_new_user_agreement">Ao proceder você concorda com nossos %1$s e %2$s</string>
<string name="msg_2fa_code">Código 2FA</string>
<string name="msg_invalid_2fa_code">Código 2FA inválido</string>
<string name="msg_yesterday">ontem</string>
<string name="msg_message">Messagem</string>
<string name="msg_this_room_is_read_only">Este chat é apenas de leitura</string>
<string name="msg_content_description_log_in_using_facebook">Fazer login através do Facebook</string>
<string name="msg_content_description_log_in_using_github">Fazer login através do Github</string>
<string name="msg_content_description_log_in_using_google">Fazer login através do Google</string>
......@@ -35,6 +38,6 @@
<string name="msg_content_description_log_in_using_twitter">Fazer login através do Twitter</string>
<string name="msg_content_description_log_in_using_gitlab">Fazer login através do Gitlab</string>
<string name="msg_you">Você</string>
<string name="search">Pesquisar</string>
<string name="msg_unknown">Desconhecido</string>
</resources>
</resources>
\ No newline at end of file
......@@ -12,9 +12,11 @@
<string name="action_send">Send</string>
<string name="action_terms_of_service">Terms of Service</string>
<string name="action_privacy_policy">Privacy Policy</string>
<string name="search">Search</string>
<!-- Regular information messages -->
<string name="msg_no_internet_connection">No internet connection</string>
<string name="msg_invalid_server_url">Invalid server URL</string>
<string name="msg_generic_error">Sorry, an error has occurred, please try again</string>
<string name="msg_username">username</string>
<string name="msg_username_or_email">username or email</string>
......@@ -22,13 +24,14 @@
<string name="msg_name_and_surname">name and surname</string>
<string name="msg_email">email</string>
<string name="msg_or_continue_using_social_accounts">Or continue using social accounts</string>
<string name="msg_new_to_rocket_chat">New to Rocket Chat? %1$s</string>
<string name="msg_new_user">New user? %1$s</string>
<string name="msg_new_user_agreement">By proceeding you are agreeing to our\n%1$s and %2$s</string>
<string name="msg_2fa_code">2FA Code</string>
<string name="msg_invalid_2fa_code">Invalid 2FA Code</string>
<string name="msg_more_than_ninety_nine_unread_messages" translatable="false">99+</string>
<string name="msg_yesterday">Yesterday</string>
<string name="msg_message">Message</string>
<string name="msg_this_room_is_read_only">This room is read only</string>
<string name="msg_content_description_log_in_using_facebook">Login using Facebook</string>
<string name="msg_content_description_log_in_using_github">Login using Github</string>
<string name="msg_content_description_log_in_using_google">Login using Google</string>
......@@ -37,6 +40,6 @@
<string name="msg_content_description_log_in_using_twitter">Login using Twitter</string>
<string name="msg_content_description_log_in_using_gitlab">Login using Gitlab</string>
<string name="msg_you">You</string>
<string name="search">Search</string>
<string name="msg_unknown">Unknown</string>
</resources>
\ No newline at end of file
......@@ -5,6 +5,7 @@ buildscript {
repositories {
google()
jcenter()
mavenCentral()
}
dependencies {
......
......@@ -7,13 +7,13 @@ ext {
kotlin : '1.2.10',
coroutine : '0.21',
dokka : '0.9.15',
kotshi : '0.3.0',
// Main dependencies
support : '27.0.2',
constraintLayout : '1.0.2',
dagger : '2.13',
exoPlayer : '2.6.0',
playServices : '11.8.0',
room : '1.0.0',
rxjava : '2.1.4',
rxandroid : '2.0.1',
......@@ -23,11 +23,11 @@ ext {
threeTenABP : '1.0.5',
fresco : '1.7.1',
frescoImageViewer : '0.5.0',
kotshi : '0.3.0',
floatingSearchView : '2.1.1',
androidSvg : '1.2.1',
aVLoadingIndicatorView : '2.1.3',
textDrawable : '1.0.2',
playServices : '11.8.0',
// For testing
junit : '4.12',
......@@ -52,6 +52,7 @@ ext {
daggerSupport : "com.google.dagger:dagger-android-support:${versions.dagger}",
daggerProcessor : "com.google.dagger:dagger-compiler:${versions.dagger}",
daggerAndroidApt : "com.google.dagger:dagger-android-processor:${versions.dagger}",
playServicesGcm : "com.google.android.gms:play-services-gcm:${versions.playServices}",
exoPlayer : "com.google.android.exoplayer:exoplayer:${versions.exoPlayer}",
room : "android.arch.persistence.room:runtime:${versions.room}",
......@@ -63,7 +64,6 @@ ext {
moshi : "com.squareup.moshi:moshi:${versions.moshi}",
moshiKotlin : "com.squareup.moshi:moshi-kotlin:${versions.moshi}",
kotshi : "se.ansman.kotshi:api:${versions.kotshi}",
okhttp : "com.squareup.okhttp3:okhttp:${versions.okhttp}",
okhttpLogger : "com.squareup.okhttp3:logging-interceptor:${versions.okhttp}",
......@@ -77,6 +77,8 @@ ext {
frescoImageViewer : "com.github.stfalcon:frescoimageviewer:${versions.frescoImageViewer}",
kotshi : "se.ansman.kotshi:api:${versions.kotshi}",
floatingSearchView : "com.github.arimorty:floatingsearchview:${versions.floatingSearchView}",
androidSvg : "com.caverock:androidsvg:${versions.androidSvg}",
......@@ -85,8 +87,6 @@ ext {
textDrawable : "com.github.rocketchat:textdrawable:${versions.textDrawable}",
playServicesGcm : "com.google.android.gms:play-services-gcm:${versions.playServices}",
// For testing
junit : "junit:junit:$versions.junit",
expressoCore : "com.android.support.test.espresso:espresso-core:${versions.expresso}",
......
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