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/rocket-chat.json
app/src/release/google-services.json app/src/release/google-services.json
app/src/release/res/values/api_key_strings.xml app/src/release/res/values/api_key_strings.xml
app/libs/
# Auth files
*.jks
################################# Created by https://www.gitignore.io/api/androidstudio (modified) ################################# ################################# 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 ...@@ -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 echo "$SHA" > $SDK_DIR/.last_commit_hash
exit 0 exit 0
\ No newline at end of file
...@@ -34,7 +34,7 @@ android { ...@@ -34,7 +34,7 @@ android {
} }
dependencies { dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar']) implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation project(':player') implementation project(':player')
...@@ -52,9 +52,7 @@ dependencies { ...@@ -52,9 +52,7 @@ dependencies {
implementation libraries.daggerSupport implementation libraries.daggerSupport
kapt libraries.daggerProcessor kapt libraries.daggerProcessor
kapt libraries.daggerAndroidApt kapt libraries.daggerAndroidApt
implementation libraries.playServicesGcm
implementation libraries.moshi
implementation libraries.kotshi
implementation libraries.room implementation libraries.room
kapt libraries.roomProcessor kapt libraries.roomProcessor
...@@ -63,6 +61,7 @@ dependencies { ...@@ -63,6 +61,7 @@ dependencies {
implementation libraries.rxjava implementation libraries.rxjava
implementation libraries.rxandroid implementation libraries.rxandroid
implementation libraries.moshi
implementation libraries.okhttp implementation libraries.okhttp
implementation libraries.okhttpLogger implementation libraries.okhttpLogger
...@@ -76,26 +75,22 @@ dependencies { ...@@ -76,26 +75,22 @@ dependencies {
implementation libraries.frescoImageViewer implementation libraries.frescoImageViewer
implementation libraries.kotshi
implementation libraries.floatingSearchView implementation libraries.floatingSearchView
implementation libraries.androidSvg implementation libraries.androidSvg
implementation libraries.playServicesGcm
implementation libraries.aVLoadingIndicatorView implementation libraries.aVLoadingIndicatorView
implementation libraries.textDrawable implementation libraries.textDrawable
testImplementation libraries.junit testImplementation libraries.junit
androidTestImplementation (libraries.expressoCore , { androidTestImplementation(libraries.expressoCore, {
exclude group: 'com.android.support', module: 'support-annotations' exclude group: 'com.android.support', module: 'support-annotations'
}) })
} }
repositories {
mavenCentral()
}
kotlin { kotlin {
experimental { experimental {
coroutines "enable" coroutines "enable"
...@@ -109,4 +104,4 @@ task compileSdk(type:Exec) { ...@@ -109,4 +104,4 @@ task compileSdk(type:Exec) {
} }
preBuild.dependsOn compileSdk 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 @@ ...@@ -36,13 +36,17 @@
</intent-filter> </intent-filter>
</activity> </activity>
<activity
android:name=".webview.WebViewActivity"
android:theme="@style/AppTheme" />
<activity <activity
android:name=".chatrooms.ui.MainActivity" android:name=".chatrooms.ui.MainActivity"
android:theme="@style/ChatListTheme" /> android:theme="@style/ChatListTheme" />
<activity <activity
android:name=".app.ChatRoomActivity" android:name=".chatroom.ui.ChatRoomActivity"
android:theme="@style/AppTheme"></activity> android:theme="@style/AppTheme" />
<receiver <receiver
android:name="com.google.android.gms.gcm.GcmReceiver" android:name="com.google.android.gms.gcm.GcmReceiver"
...@@ -71,10 +75,6 @@ ...@@ -71,10 +75,6 @@
<action android:name="com.google.android.c2dm.intent.RECEIVE" /> <action android:name="com.google.android.c2dm.intent.RECEIVE" />
</intent-filter> </intent-filter>
</service> </service>
<activity
android:name=".webview.WebViewActivity"
android:theme="@style/AppTheme" />
</application> </application>
</manifest> </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 { ...@@ -135,7 +135,7 @@ object DrawableHelper {
.beginConfig() .beginConfig()
.useFont(Typeface.SANS_SERIF) .useFont(Typeface.SANS_SERIF)
.endConfig() .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 ...@@ -6,7 +6,6 @@ import chat.rocket.android.authentication.ui.AuthenticationActivity
import chat.rocket.android.dagger.scope.PerActivity import chat.rocket.android.dagger.scope.PerActivity
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
import kotlinx.coroutines.experimental.Job
@Module @Module
class AuthenticationModule { class AuthenticationModule {
...@@ -14,9 +13,4 @@ class AuthenticationModule { ...@@ -14,9 +13,4 @@ class AuthenticationModule {
@Provides @Provides
@PerActivity @PerActivity
fun provideAuthenticationNavigator(activity: AuthenticationActivity, context: Context) = AuthenticationNavigator(activity, context) fun provideAuthenticationNavigator(activity: AuthenticationActivity, context: Context) = AuthenticationNavigator(activity, context)
}
@Provides \ No newline at end of file
fun provideJob(): Job {
return Job()
}
}
...@@ -6,6 +6,6 @@ import dagger.android.ContributesAndroidInjector ...@@ -6,6 +6,6 @@ import dagger.android.ContributesAndroidInjector
@Module abstract class LoginFragmentProvider { @Module abstract class LoginFragmentProvider {
@ContributesAndroidInjector(modules = arrayOf(LoginFragmentModule::class)) @ContributesAndroidInjector(modules = [(LoginFragmentModule::class)])
abstract fun provideLoginFragment(): LoginFragment abstract fun provideLoginFragment(): LoginFragment
} }
\ No newline at end of file
...@@ -15,7 +15,6 @@ import chat.rocket.core.internal.rest.login ...@@ -15,7 +15,6 @@ import chat.rocket.core.internal.rest.login
import chat.rocket.core.internal.rest.registerPushToken import chat.rocket.core.internal.rest.registerPushToken
import javax.inject.Inject import javax.inject.Inject
class LoginPresenter @Inject constructor(private val view: LoginView, class LoginPresenter @Inject constructor(private val view: LoginView,
private val strategy: CancelStrategy, private val strategy: CancelStrategy,
private val navigator: AuthenticationNavigator, private val navigator: AuthenticationNavigator,
...@@ -23,7 +22,6 @@ class LoginPresenter @Inject constructor(private val view: LoginView, ...@@ -23,7 +22,6 @@ class LoginPresenter @Inject constructor(private val view: LoginView,
private val settingsInteractor: GetSettingsInteractor, private val settingsInteractor: GetSettingsInteractor,
private val serverInteractor: GetCurrentServerInteractor, private val serverInteractor: GetCurrentServerInteractor,
factory: RocketChatClientFactory) { factory: RocketChatClientFactory) {
// TODO - we should validate the current server when opening the app, and have a nonnull get() // TODO - we should validate the current server when opening the app, and have a nonnull get()
private val client: RocketChatClient = factory.create(serverInteractor.get()!!) private val client: RocketChatClient = factory.create(serverInteractor.get()!!)
...@@ -40,6 +38,8 @@ class LoginPresenter @Inject constructor(private val view: LoginView, ...@@ -40,6 +38,8 @@ class LoginPresenter @Inject constructor(private val view: LoginView,
return return
} }
view.showSignUpView(settings.registrationEnabled())
var hasSocial = false var hasSocial = false
if (settings.facebookEnabled()) { if (settings.facebookEnabled()) {
view.enableLoginByFacebook() view.enableLoginByFacebook()
...@@ -69,8 +69,6 @@ class LoginPresenter @Inject constructor(private val view: LoginView, ...@@ -69,8 +69,6 @@ class LoginPresenter @Inject constructor(private val view: LoginView,
view.enableLoginByGitlab() view.enableLoginByGitlab()
hasSocial = true hasSocial = true
} }
view.showSignUpView(settings.registrationEnabled())
view.showOauthView(hasSocial) view.showOauthView(hasSocial)
} }
...@@ -108,9 +106,9 @@ class LoginPresenter @Inject constructor(private val view: LoginView, ...@@ -108,9 +106,9 @@ class LoginPresenter @Inject constructor(private val view: LoginView,
} }
} }
} }
} finally {
view.hideLoading()
} }
view.hideLoading()
} else { } else {
view.showNoInternetConnection() view.showNoInternetConnection()
} }
...@@ -119,9 +117,7 @@ class LoginPresenter @Inject constructor(private val view: LoginView, ...@@ -119,9 +117,7 @@ class LoginPresenter @Inject constructor(private val view: LoginView,
} }
} }
fun signup() { fun signup() = navigator.toSignUp()
navigator.toSignUp()
}
private suspend fun registerPushToken() { private suspend fun registerPushToken() {
localRepository.get(LocalRepository.KEY_PUSH_TOKEN)?.let { localRepository.get(LocalRepository.KEY_PUSH_TOKEN)?.let {
......
...@@ -8,7 +8,6 @@ import android.support.v4.app.Fragment ...@@ -8,7 +8,6 @@ import android.support.v4.app.Fragment
import android.text.style.ClickableSpan import android.text.style.ClickableSpan
import android.view.* import android.view.*
import android.widget.ImageButton import android.widget.ImageButton
import android.view.inputmethod.InputMethodManager
import android.widget.ScrollView import android.widget.ScrollView
import android.widget.Toast import android.widget.Toast
import chat.rocket.android.R import chat.rocket.android.R
...@@ -57,12 +56,6 @@ class LoginFragment : Fragment(), LoginView { ...@@ -57,12 +56,6 @@ class LoginFragment : Fragment(), LoginView {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) 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) { if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) {
tintEditTextDrawableStart() tintEditTextDrawableStart()
} }
...@@ -201,7 +194,7 @@ class LoginFragment : Fragment(), LoginView { ...@@ -201,7 +194,7 @@ class LoginFragment : Fragment(), LoginView {
private fun setupSignUpListener() { private fun setupSignUpListener() {
val signUp = getString(R.string.title_sign_up) 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 text_new_to_rocket_chat.text = newToRocketChat
...@@ -216,7 +209,7 @@ class LoginFragment : Fragment(), LoginView { ...@@ -216,7 +209,7 @@ class LoginFragment : Fragment(), LoginView {
button_log_in.isEnabled = value button_log_in.isEnabled = value
text_username_or_email.isEnabled = value text_username_or_email.isEnabled = value
text_password.isEnabled = value text_password.isEnabled = value
if (isEditTextEmpty()) { if (!isEditTextEmpty()) {
showSignUpView(value) showSignUpView(value)
showOauthView(value) showOauthView(value)
} }
......
...@@ -32,6 +32,7 @@ class AuthenticationNavigator(internal val activity: AuthenticationActivity, int ...@@ -32,6 +32,7 @@ class AuthenticationNavigator(internal val activity: AuthenticationActivity, int
fun toWebPage(url: String) { fun toWebPage(url: String) {
activity.startActivity(context.webViewIntent(url)) activity.startActivity(context.webViewIntent(url))
activity.overridePendingTransition(R.anim.slide_up, R.anim.hold)
} }
fun toChatList() { fun toChatList() {
......
...@@ -6,6 +6,6 @@ import dagger.android.ContributesAndroidInjector ...@@ -6,6 +6,6 @@ import dagger.android.ContributesAndroidInjector
@Module abstract class ServerFragmentProvider { @Module abstract class ServerFragmentProvider {
@ContributesAndroidInjector(modules = arrayOf(ServerFragmentModule::class)) @ContributesAndroidInjector(modules = [(ServerFragmentModule::class)])
abstract fun provideServerFragment(): ServerFragment abstract fun provideServerFragment(): ServerFragment
} }
\ No newline at end of file
...@@ -3,9 +3,11 @@ package chat.rocket.android.authentication.server.presentation ...@@ -3,9 +3,11 @@ package chat.rocket.android.authentication.server.presentation
import chat.rocket.android.authentication.presentation.AuthenticationNavigator import chat.rocket.android.authentication.presentation.AuthenticationNavigator
import chat.rocket.android.core.lifecycle.CancelStrategy import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.helper.NetworkHelper import chat.rocket.android.helper.NetworkHelper
import chat.rocket.android.helper.UrlHelper
import chat.rocket.android.server.domain.* import chat.rocket.android.server.domain.*
import chat.rocket.android.server.infraestructure.RocketChatClientFactory import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.launchUI import chat.rocket.android.util.launchUI
import chat.rocket.common.util.ifNull
import chat.rocket.core.RocketChatClient import chat.rocket.core.RocketChatClient
import chat.rocket.core.internal.rest.settings import chat.rocket.core.internal.rest.settings
import java.security.InvalidParameterException import java.security.InvalidParameterException
...@@ -17,41 +19,50 @@ class ServerPresenter @Inject constructor(private val view: ServerView, ...@@ -17,41 +19,50 @@ class ServerPresenter @Inject constructor(private val view: ServerView,
private val serverInteractor: SaveCurrentServerInteractor, private val serverInteractor: SaveCurrentServerInteractor,
private val settingsInteractor: SaveSettingsInteractor, private val settingsInteractor: SaveSettingsInteractor,
private val factory: RocketChatClientFactory) { private val factory: RocketChatClientFactory) {
private lateinit var client: RocketChatClient
private var settingsFilter = arrayOf(SITE_URL, SITE_NAME, private var settingsFilter = arrayOf(SITE_URL, SITE_NAME, FAVICON_512, USE_REALNAME, ALLOW_ROOM_NAME_SPECIAL_CHARS, FAVORITE_ROOMS,
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_LOGIN_FORM, ACCOUNT_GOOGLE, ACCOUNT_FACEBOOK, ACCOUNT_GITHUB, ACCOUNT_GITLAB, ACCOUNT_TWITTER, ACCOUNT_WORDPRESS, LDAP_ENABLE, ACCOUNT_REGISTRATION, STORAGE_TYPE, HIDE_USER_JOIN, HIDE_USER_LEAVE, HIDE_TYPE_AU,
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) HIDE_MUTE_UNMUTE, HIDE_TYPE_RU, ACCOUNT_CUSTOM_FIELDS)
fun connect(server: String) { fun connect(server: String) {
var cli: RocketChatClient? = null if (!UrlHelper.isValidUrl(server)) {
try { view.showInvalidServerUrl()
cli = factory.create(server) } else {
} catch (ex: InvalidParameterException) { try {
view.showMessage(ex.message!!) client = factory.create(server)
} } catch (exception: InvalidParameterException) {
exception.message?.let {
view.showMessage(it)
}.ifNull {
view.showGenericErrorMessage()
}
return
}
cli?.let { client -> client.let { rocketChatClient ->
launchUI(strategy) { launchUI(strategy) {
if (NetworkHelper.hasInternetAccess()) { if (NetworkHelper.hasInternetAccess()) {
view.showLoading() view.showLoading()
try { try {
val settings = client.settings(*settingsFilter) val settings = rocketChatClient.settings(*settingsFilter)
settingsInteractor.save(server, settings) settingsInteractor.save(server, settings)
serverInteractor.save(server) serverInteractor.save(server)
navigator.toLogin() navigator.toLogin()
} catch (ex: Exception) { } catch (exception: Exception) {
ex.printStackTrace() exception.message?.let {
view.showMessage(ex.message!!) view.showMessage(it)
} finally { }.ifNull {
view.hideLoading() view.showGenericErrorMessage()
}
} finally {
view.hideLoading()
}
} else {
view.showNoInternetConnection()
} }
} else {
view.showNoInternetConnection()
} }
} }
} }
......
...@@ -4,4 +4,10 @@ import chat.rocket.android.core.behaviours.InternetView ...@@ -4,4 +4,10 @@ import chat.rocket.android.core.behaviours.InternetView
import chat.rocket.android.core.behaviours.LoadingView import chat.rocket.android.core.behaviours.LoadingView
import chat.rocket.android.core.behaviours.MessageView import chat.rocket.android.core.behaviours.MessageView
interface ServerView : LoadingView, MessageView, InternetView interface ServerView : LoadingView, MessageView, InternetView {
\ No newline at end of file
/**
* 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 { ...@@ -44,6 +44,8 @@ class ServerFragment : Fragment(), ServerView {
relative_layout.viewTreeObserver.removeOnGlobalLayoutListener(layoutListener) relative_layout.viewTreeObserver.removeOnGlobalLayoutListener(layoutListener)
} }
override fun showInvalidServerUrl() = showMessage(getString(R.string.msg_invalid_server_url))
override fun showLoading() { override fun showLoading() {
enableUserInput(false) enableUserInput(false)
view_loading.setVisibility(true) view_loading.setVisibility(true)
......
...@@ -3,11 +3,13 @@ package chat.rocket.android.authentication.signup.presentation ...@@ -3,11 +3,13 @@ package chat.rocket.android.authentication.signup.presentation
import chat.rocket.android.authentication.presentation.AuthenticationNavigator import chat.rocket.android.authentication.presentation.AuthenticationNavigator
import chat.rocket.android.core.lifecycle.CancelStrategy import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.helper.NetworkHelper import chat.rocket.android.helper.NetworkHelper
import chat.rocket.android.helper.UrlHelper
import chat.rocket.android.infrastructure.LocalRepository import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.server.domain.GetCurrentServerInteractor import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.infraestructure.RocketChatClientFactory import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.launchUI import chat.rocket.android.util.launchUI
import chat.rocket.common.RocketChatException import chat.rocket.common.RocketChatException
import chat.rocket.common.util.ifNull
import chat.rocket.core.RocketChatClient import chat.rocket.core.RocketChatClient
import chat.rocket.core.internal.rest.login import chat.rocket.core.internal.rest.login
import chat.rocket.core.internal.rest.registerPushToken import chat.rocket.core.internal.rest.registerPushToken
...@@ -20,7 +22,6 @@ class SignupPresenter @Inject constructor(private val view: SignupView, ...@@ -20,7 +22,6 @@ class SignupPresenter @Inject constructor(private val view: SignupView,
private val localRepository: LocalRepository, private val localRepository: LocalRepository,
private val serverInteractor: GetCurrentServerInteractor, private val serverInteractor: GetCurrentServerInteractor,
private val factory: RocketChatClientFactory) { private val factory: RocketChatClientFactory) {
private val client: RocketChatClient = factory.create(serverInteractor.get()!!) private val client: RocketChatClient = factory.create(serverInteractor.get()!!)
fun signup(name: String, username: String, password: String, email: String) { fun signup(name: String, username: String, password: String, email: String) {
...@@ -53,15 +54,15 @@ class SignupPresenter @Inject constructor(private val view: SignupView, ...@@ -53,15 +54,15 @@ class SignupPresenter @Inject constructor(private val view: SignupView,
registerPushToken() registerPushToken()
navigator.toChatList() navigator.toChatList()
} catch (exception: RocketChatException) { } catch (exception: RocketChatException) {
val errorMessage = exception.message exception.message?.let {
if (errorMessage != null) { view.showMessage(it)
view.showMessage(errorMessage) }.ifNull {
} else {
view.showGenericErrorMessage() view.showGenericErrorMessage()
} }
} } finally {
view.hideLoading()
view.hideLoading() }
} else { } else {
view.showNoInternetConnection() view.showNoInternetConnection()
} }
...@@ -72,13 +73,13 @@ class SignupPresenter @Inject constructor(private val view: SignupView, ...@@ -72,13 +73,13 @@ class SignupPresenter @Inject constructor(private val view: SignupView,
fun termsOfService() { fun termsOfService() {
serverInteractor.get()?.let { serverInteractor.get()?.let {
navigator.toWebPage("/terms-of-service") navigator.toWebPage(UrlHelper.getTermsOfServiceUrl(it))
} }
} }
fun privacyPolicy() { fun privacyPolicy() {
serverInteractor.get()?.let { serverInteractor.get()?.let {
navigator.toWebPage("/privacy-policy") navigator.toWebPage(UrlHelper.getPrivacyPolicyUrl(it))
} }
} }
......
...@@ -9,6 +9,7 @@ import chat.rocket.android.server.infraestructure.RocketChatClientFactory ...@@ -9,6 +9,7 @@ import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.launchUI import chat.rocket.android.util.launchUI
import chat.rocket.common.RocketChatAuthException import chat.rocket.common.RocketChatAuthException
import chat.rocket.common.RocketChatException import chat.rocket.common.RocketChatException
import chat.rocket.common.util.ifNull
import chat.rocket.core.RocketChatClient import chat.rocket.core.RocketChatClient
import chat.rocket.core.internal.rest.login import chat.rocket.core.internal.rest.login
import chat.rocket.core.internal.rest.registerPushToken import chat.rocket.core.internal.rest.registerPushToken
...@@ -20,51 +21,50 @@ class TwoFAPresenter @Inject constructor(private val view: TwoFAView, ...@@ -20,51 +21,50 @@ class TwoFAPresenter @Inject constructor(private val view: TwoFAView,
private val localRepository: LocalRepository, private val localRepository: LocalRepository,
private val serverInteractor: GetCurrentServerInteractor, private val serverInteractor: GetCurrentServerInteractor,
private val factory: RocketChatClientFactory) { private val factory: RocketChatClientFactory) {
private val client: RocketChatClient = factory.create(serverInteractor.get()!!) 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) // 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) { fun authenticate(usernameOrEmail: String, password: String, twoFactorAuthenticationCode: String) {
val server = serverInteractor.get() val server = serverInteractor.get()
if (twoFactorAuthenticationCode.isBlank()) { when {
view.alertBlankTwoFactorAuthenticationCode() server == null -> {
} else if (server == null) { navigator.toServerScreen()
navigator.toServerScreen() }
} else { twoFactorAuthenticationCode.isBlank() -> {
launchUI(strategy) { view.alertBlankTwoFactorAuthenticationCode()
}
val client = factory.create(server) else -> {
if (NetworkHelper.hasInternetAccess()) { launchUI(strategy) {
view.showLoading() val client = factory.create(server)
if (NetworkHelper.hasInternetAccess()) {
try { view.showLoading()
// The token is saved via the client TokenProvider try {
client.login(usernameOrEmail, password, twoFactorAuthenticationCode) // The token is saved via the client TokenProvider
registerPushToken() client.login(usernameOrEmail, password, twoFactorAuthenticationCode)
navigator.toChatList() registerPushToken()
} catch (exception: RocketChatException) { navigator.toChatList()
if (exception is RocketChatAuthException) { } catch (exception: RocketChatException) {
view.alertInvalidTwoFactorAuthenticationCode() if (exception is RocketChatAuthException) {
} else { view.alertInvalidTwoFactorAuthenticationCode()
val message = exception.message
if (message != null) {
view.showMessage(message)
} else { } 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() { fun signup() = navigator.toSignUp()
navigator.toSignUp()
}
private suspend fun registerPushToken() { private suspend fun registerPushToken() {
localRepository.get(LocalRepository.KEY_PUSH_TOKEN)?.let { localRepository.get(LocalRepository.KEY_PUSH_TOKEN)?.let {
......
...@@ -21,7 +21,7 @@ class AuthenticationActivity : AppCompatActivity(), HasSupportFragmentInjector { ...@@ -21,7 +21,7 @@ class AuthenticationActivity : AppCompatActivity(), HasSupportFragmentInjector {
AndroidInjection.inject(this) AndroidInjection.inject(this)
addFragment("authenticationServerFragment", R.id.fragment_container) { addFragment("ServerFragment", R.id.fragment_container) {
ServerFragment.newInstance() 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 { ...@@ -27,9 +27,4 @@ class ChatRoomsFragmentModule {
fun provideCancelStrategy(owner: LifecycleOwner, jobs: Job): CancelStrategy { fun provideCancelStrategy(owner: LifecycleOwner, jobs: Job): CancelStrategy {
return CancelStrategy(owner, jobs) 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 ...@@ -18,11 +18,11 @@ import javax.inject.Inject
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: ChatRoomsNavigator,
private val serverInteractor: GetCurrentServerInteractor, private val serverInteractor: GetCurrentServerInteractor,
private val getChatRoomsInteractor: GetChatRoomsInteractor, private val getChatRoomsInteractor: GetChatRoomsInteractor,
private val saveChatRoomsInteractor: SaveChatRoomsInteractor, private val saveChatRoomsInteractor: SaveChatRoomsInteractor,
factory: RocketChatClientFactory) { factory: RocketChatClientFactory) {
private val client: RocketChatClient = factory.create(serverInteractor.get()!!) private val client: RocketChatClient = factory.create(serverInteractor.get()!!)
private val currentServer = serverInteractor.get()!! private val currentServer = serverInteractor.get()!!
private var reloadJob: Deferred<List<ChatRoom>>? = null private var reloadJob: Deferred<List<ChatRoom>>? = null
...@@ -36,6 +36,20 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView, ...@@ -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> { private suspend fun loadRooms(): List<ChatRoom> {
val chatRooms = client.chatRooms().update val chatRooms = client.chatRooms().update
val sortedRooms = sortRooms(chatRooms) val sortedRooms = sortRooms(chatRooms)
...@@ -54,24 +68,13 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView, ...@@ -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> { private fun getOpenChatRooms(chatRooms: List<ChatRoom>): List<ChatRoom> {
return chatRooms.filter(ChatRoom::open) return chatRooms.filter(ChatRoom::open)
} }
private fun sortChatRooms(chatRooms: List<ChatRoom>): List<ChatRoom> { private fun sortChatRooms(chatRooms: List<ChatRoom>): List<ChatRoom> {
return chatRooms.sortedByDescending { return chatRooms.sortedByDescending { chatRoom ->
chatRoom -> chatRoom.lastMessage?.timestamp chatRoom.lastMessage?.timestamp
} }
} }
......
...@@ -16,83 +16,96 @@ import chat.rocket.android.util.textContent ...@@ -16,83 +16,96 @@ import chat.rocket.android.util.textContent
import chat.rocket.common.model.BaseRoom.RoomType import chat.rocket.common.model.BaseRoom.RoomType
import chat.rocket.core.model.ChatRoom import chat.rocket.core.model.ChatRoom
import com.facebook.drawee.view.SimpleDraweeView import com.facebook.drawee.view.SimpleDraweeView
import kotlinx.android.synthetic.main.avatar.view.*
import kotlinx.android.synthetic.main.item_chat.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() 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>) { fun updateRooms(newRooms: List<ChatRoom>) {
dataSet.clear() dataSet.clear()
dataSet.addAll(newRooms) dataSet.addAll(newRooms)
} }
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder = ViewHolder(parent.inflate(R.layout.item_chat)) inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val chatRoom = dataSet[position]
val chatRoomName = chatRoom.name
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) { setOnClickListener { listener(chatRoom) }
// 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)
} }
val totalUnreadMessage = chatRoom.unread private fun bindAvatar(chatRoom: ChatRoom, avatarLayout: View, drawee: SimpleDraweeView, imageView: ImageView) {
when { val chatRoomName = chatRoom.name
totalUnreadMessage in 1..99 -> { if (chatRoom.type == RoomType.ONE_TO_ONE) {
holder.unreadMessage.textContent = totalUnreadMessage.toString() drawee.setImageURI(UrlHelper.getAvatarUrl(chatRoom.client.url, chatRoomName))
holder.unreadMessage.setVisibility(true) 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 private fun bindLastMessage(chatRoom: ChatRoom, textView: TextView) {
val lastMessageSender = lastMessage?.sender val lastMessage = chatRoom.lastMessage
if (lastMessage != null && lastMessageSender != null) { val lastMessageSender = lastMessage?.sender
val message = lastMessage.message if (lastMessage != null && lastMessageSender != null) {
val senderUsername = lastMessageSender.username val message = lastMessage.message
when (senderUsername) { val senderUsername = lastMessageSender.username
chatRoomName -> { when (senderUsername) {
holder.lastMessage.textContent = message chatRoom.name -> {
} textView.textContent = message
}
// TODO Change to MySelf // TODO Change to MySelf
// chatRoom.user?.username -> { // chatRoom.user?.username -> {
// holder.lastMessage.textContent = context.getString(R.string.msg_you) + ": $message" // holder.lastMessage.textContent = context.getString(R.string.msg_you) + ": $message"
// } // }
else -> { else -> {
holder.lastMessage.textContent = "@$senderUsername: $message" 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 private fun bindUnreadMessages(chatRoom: ChatRoom, textView: TextView) {
val totalUnreadMessage = chatRoom.unread
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { when {
val userAvatar: SimpleDraweeView = itemView.image_user_avatar totalUnreadMessage in 1..99 -> {
val roomAvatar: ImageView = itemView.image_room_avatar textView.textContent = totalUnreadMessage.toString()
val chatName: TextView = itemView.text_chat_name textView.setVisibility(true)
val lastMessage: TextView = itemView.text_last_message }
val lastMessageDateTime: TextView = itemView.text_last_message_date_time totalUnreadMessage > 99 -> {
val unreadMessage: TextView = itemView.text_total_unread_messages 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 ...@@ -12,6 +12,7 @@ import android.widget.Toast
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.chatrooms.presentation.ChatRoomsPresenter import chat.rocket.android.chatrooms.presentation.ChatRoomsPresenter
import chat.rocket.android.chatrooms.presentation.ChatRoomsView import chat.rocket.android.chatrooms.presentation.ChatRoomsView
import chat.rocket.android.util.setVisibility
import chat.rocket.android.widget.DividerItemDecoration import chat.rocket.android.widget.DividerItemDecoration
import chat.rocket.core.model.ChatRoom import chat.rocket.core.model.ChatRoom
import dagger.android.support.AndroidSupportInjection import dagger.android.support.AndroidSupportInjection
...@@ -46,8 +47,11 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView { ...@@ -46,8 +47,11 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
(activity as AppCompatActivity).apply { (activity as AppCompatActivity).apply {
recycler_view.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false) recycler_view.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
recycler_view.addItemDecoration(DividerItemDecoration(this, 144, 32)) recycler_view.addItemDecoration(DividerItemDecoration(this, 144, 32))
recycler_view.adapter = ChatRoomsAdapter(this)
recycler_view.itemAnimator = DefaultItemAnimator() recycler_view.itemAnimator = DefaultItemAnimator()
recycler_view.adapter = ChatRoomsAdapter(this) { chatRoom ->
presenter.loadChatRoom(chatRoom)
}
if (supportActionBar == null) { if (supportActionBar == null) {
setSupportActionBar(toolbar) setSupportActionBar(toolbar)
supportActionBar?.setDisplayHomeAsUpEnabled(false) supportActionBar?.setDisplayHomeAsUpEnabled(false)
...@@ -57,6 +61,7 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView { ...@@ -57,6 +61,7 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
supportActionBar?.title = "Rocket.Chat" supportActionBar?.title = "Rocket.Chat"
} }
} }
presenter.loadChatRooms() presenter.loadChatRooms()
} }
...@@ -97,9 +102,9 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView { ...@@ -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() override fun showMessage(message: String) = Toast.makeText(activity, message, Toast.LENGTH_SHORT).show()
...@@ -112,6 +117,7 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView { ...@@ -112,6 +117,7 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
class RoomsDiffCallback(private val oldRooms: List<ChatRoom>, class RoomsDiffCallback(private val oldRooms: List<ChatRoom>,
private val newRooms: List<ChatRoom>) : DiffUtil.Callback() { private val newRooms: List<ChatRoom>) : DiffUtil.Callback() {
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldRooms[oldItemPosition].id == newRooms[newItemPosition].id return oldRooms[oldItemPosition].id == newRooms[newItemPosition].id
} }
......
...@@ -6,7 +6,10 @@ import chat.rocket.android.authentication.server.di.ServerFragmentProvider ...@@ -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.signup.di.SignupFragmentProvider
import chat.rocket.android.authentication.twofactor.di.TwoFAFragmentProvider import chat.rocket.android.authentication.twofactor.di.TwoFAFragmentProvider
import chat.rocket.android.authentication.ui.AuthenticationActivity 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.ChatRoomsFragmentProvider
import chat.rocket.android.chatrooms.di.ChatRoomsModule
import chat.rocket.android.chatrooms.ui.MainActivity import chat.rocket.android.chatrooms.ui.MainActivity
import chat.rocket.android.dagger.scope.PerActivity import chat.rocket.android.dagger.scope.PerActivity
import dagger.Module import dagger.Module
...@@ -24,6 +27,11 @@ abstract class ActivityBuilder { ...@@ -24,6 +27,11 @@ abstract class ActivityBuilder {
]) ])
abstract fun bindAuthenticationActivity(): AuthenticationActivity abstract fun bindAuthenticationActivity(): AuthenticationActivity
@ContributesAndroidInjector(modules = [ChatRoomsFragmentProvider::class]) @PerActivity
@ContributesAndroidInjector(modules = [ChatRoomsModule::class, ChatRoomsFragmentProvider::class])
abstract fun bindMainActivity(): MainActivity 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 ...@@ -21,6 +21,7 @@ import chat.rocket.common.util.PlatformLogger
import chat.rocket.core.RocketChatClient import chat.rocket.core.RocketChatClient
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
import kotlinx.coroutines.experimental.Job
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor import okhttp3.logging.HttpLoggingInterceptor
import javax.inject.Singleton import javax.inject.Singleton
...@@ -47,6 +48,11 @@ class AppModule { ...@@ -47,6 +48,11 @@ class AppModule {
return Room.databaseBuilder(context, RocketChatDatabase::class.java, "rocketchat-db").build() return Room.databaseBuilder(context, RocketChatDatabase::class.java, "rocketchat-db").build()
} }
@Provides
fun provideJob(): Job {
return Job()
}
@Provides @Provides
@Singleton @Singleton
fun provideContext(application: Application): Context { fun provideContext(application: Application): Context {
...@@ -120,4 +126,4 @@ class AppModule { ...@@ -120,4 +126,4 @@ class AppModule {
fun provideChatRoomsRepository(): ChatRoomsRepository { fun provideChatRoomsRepository(): ChatRoomsRepository {
return MemoryChatRoomsRepository() 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 package chat.rocket.android.helper
import android.util.Patterns
object UrlHelper { object UrlHelper {
/** /**
* Returns the avatar URL. * Returns the avatar URL.
* *
* @param serverUrl The serverUrl. * @param serverUrl The server URL.
* @param chatRoomName The chat room name. * @param avatarName The avatar name.
* @return The avatar URL. * @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 ...@@ -3,6 +3,8 @@ package chat.rocket.android.server.domain
import chat.rocket.core.model.ChatRoom import chat.rocket.core.model.ChatRoom
interface ChatRoomsRepository { interface ChatRoomsRepository {
fun save(url: String, chatRooms: List<ChatRoom>) fun save(url: String, chatRooms: List<ChatRoom>)
fun get(url: String): List<ChatRoom> fun get(url: String): List<ChatRoom>
} }
\ No newline at end of file
...@@ -4,5 +4,6 @@ import chat.rocket.core.model.ChatRoom ...@@ -4,5 +4,6 @@ import chat.rocket.core.model.ChatRoom
import javax.inject.Inject import javax.inject.Inject
class SaveChatRoomsInteractor @Inject constructor(private val repository: ChatRoomsRepository) { class SaveChatRoomsInteractor @Inject constructor(private val repository: ChatRoomsRepository) {
fun save(url: String, chatRooms: List<ChatRoom>) = repository.save(url, chatRooms) 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 ...@@ -4,5 +4,6 @@ import chat.rocket.core.model.Value
import javax.inject.Inject import javax.inject.Inject
class SaveSettingsInteractor @Inject constructor(private val repository: SettingsRepository) { class SaveSettingsInteractor @Inject constructor(private val repository: SettingsRepository) {
fun save(url: String, settings: Map<String, Value<Any>>) = repository.save(url, settings) 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"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:theme="@style/AuthenticationTheme"> android:theme="@style/AuthenticationTheme"
tools:context=".authentication.ui.AuthenticationActivity">
<FrameLayout <FrameLayout
android:id="@+id/fragment_container" android:id="@+id/fragment_container"
......
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:theme="@style/AppTheme"> android:theme="@style/AppTheme"
tools:context=".chatroom.ui.ChatRoomActivity">
<include <include
android:id="@+id/layout_app_bar" android:id="@+id/layout_app_bar"
layout="@layout/app_bar" /> layout="@layout/app_bar_chat_room" />
<FrameLayout <FrameLayout
android:id="@+id/fragment_container" android:id="@+id/fragment_container"
......
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:theme="@style/ChatListTheme"> android:theme="@style/ChatListTheme"
tools:context=".chatrooms.ui.MainActivity">
<FrameLayout <FrameLayout
android:id="@+id/fragment_container" 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 @@ ...@@ -32,6 +32,7 @@
android:cursorVisible="false" android:cursorVisible="false"
android:hint="@string/default_server" android:hint="@string/default_server"
android:imeOptions="actionDone" android:imeOptions="actionDone"
android:digits="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-/"
android:inputType="textUri" android:inputType="textUri"
android:paddingEnd="0dp" android:paddingEnd="0dp"
android:paddingStart="0dp" /> android:paddingStart="0dp" />
......
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" <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_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical"> android:orientation="vertical">
...@@ -18,4 +20,14 @@ ...@@ -18,4 +20,14 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentBottom="true" /> 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> </RelativeLayout>
\ No newline at end of file
...@@ -18,13 +18,17 @@ ...@@ -18,13 +18,17 @@
app:layout_constraintRight_toLeftOf="@+id/middle_container" app:layout_constraintRight_toLeftOf="@+id/middle_container"
app:layout_constraintTop_toTopOf="parent"> app:layout_constraintTop_toTopOf="parent">
<com.facebook.drawee.view.SimpleDraweeView <include
android:id="@+id/image_user_avatar" android:id="@+id/layout_avatar"
layout="@layout/avatar"
android:layout_width="40dp" android:layout_width="40dp"
android:layout_height="40dp" android:layout_height="40dp"
android:visibility="gone" android:visibility="gone" />
app:roundAsCircle="true" />
<!-- 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 <ImageView
android:id="@+id/image_room_avatar" android:id="@+id/image_room_avatar"
android:layout_width="40dp" android:layout_width="40dp"
......
...@@ -4,18 +4,19 @@ ...@@ -4,18 +4,19 @@
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="16dp" android:layout_marginBottom="10dp"
android:layout_marginEnd="@dimen/screen_edge_left_and_right_margins" android:layout_marginEnd="@dimen/screen_edge_left_and_right_margins"
android:layout_marginStart="@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 <include
android:id="@+id/image_user_avatar" android:id="@+id/layout_avatar"
layout="@layout/avatar"
android:layout_width="40dp" android:layout_width="40dp"
android:layout_height="40dp" android:layout_height="40dp"
android:layout_marginTop="7dp"
app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent" />
app:roundAsCircle="true" />
<LinearLayout <LinearLayout
android:id="@+id/top_container" android:id="@+id/top_container"
...@@ -23,7 +24,7 @@ ...@@ -23,7 +24,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:orientation="horizontal" android:orientation="horizontal"
app:layout_constraintLeft_toRightOf="@+id/image_user_avatar"> app:layout_constraintLeft_toRightOf="@+id/layout_avatar">
<TextView <TextView
android:id="@+id/text_user_name" android:id="@+id/text_user_name"
...@@ -54,11 +55,13 @@ ...@@ -54,11 +55,13 @@
app:layout_constraintTop_toBottomOf="@+id/top_container" 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!" /> 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 <com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/image_attachment" android:id="@+id/image_attachment"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="150dp" android:layout_height="150dp"
android:layout_marginTop="5dp" android:layout_marginTop="5dp"
android:visibility="gone"
app:layout_constraintLeft_toLeftOf="@id/top_container" app:layout_constraintLeft_toLeftOf="@id/top_container"
app:layout_constraintRight_toRightOf="parent" app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_content" /> app:layout_constraintTop_toBottomOf="@+id/text_content" />
......
...@@ -10,6 +10,17 @@ ...@@ -10,6 +10,17 @@
android:layout_height="1dp" android:layout_height="1dp"
android:background="@color/colorDividerMessageComposer" /> 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 <LinearLayout
android:id="@+id/top_container" android:id="@+id/top_container"
android:layout_width="match_parent" android:layout_width="match_parent"
......
...@@ -11,9 +11,11 @@ ...@@ -11,9 +11,11 @@
<string name="action_send">Enviar</string> <string name="action_send">Enviar</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="search">Pesquisar</string>
<!-- Regular information messages --> <!-- Regular information messages -->
<string name="msg_no_internet_connection">Sem conexão à internet</string> <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_generic_error">Desculpe, ocorreu um erro, tente novamente</string>
<string name="msg_username">nome de usuário</string> <string name="msg_username">nome de usuário</string>
<string name="msg_username_or_email">nome de usuário ou email</string> <string name="msg_username_or_email">nome de usuário ou email</string>
...@@ -21,12 +23,13 @@ ...@@ -21,12 +23,13 @@
<string name="msg_name_and_surname">nome e sobrenome</string> <string name="msg_name_and_surname">nome e sobrenome</string>
<string name="msg_email">email</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_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_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_2fa_code">Código 2FA</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_yesterday">ontem</string> <string name="msg_yesterday">ontem</string>
<string name="msg_message">Messagem</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_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>
...@@ -35,6 +38,6 @@ ...@@ -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_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_content_description_log_in_using_gitlab">Fazer login através do Gitlab</string>
<string name="msg_you">Você</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 @@ ...@@ -12,9 +12,11 @@
<string name="action_send">Send</string> <string name="action_send">Send</string>
<string name="action_terms_of_service">Terms of Service</string> <string name="action_terms_of_service">Terms of Service</string>
<string name="action_privacy_policy">Privacy Policy</string> <string name="action_privacy_policy">Privacy Policy</string>
<string name="search">Search</string>
<!-- Regular information messages --> <!-- Regular information messages -->
<string name="msg_no_internet_connection">No internet connection</string> <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_generic_error">Sorry, an error has occurred, please try again</string>
<string name="msg_username">username</string> <string name="msg_username">username</string>
<string name="msg_username_or_email">username or email</string> <string name="msg_username_or_email">username or email</string>
...@@ -22,13 +24,14 @@ ...@@ -22,13 +24,14 @@
<string name="msg_name_and_surname">name and surname</string> <string name="msg_name_and_surname">name and surname</string>
<string name="msg_email">email</string> <string name="msg_email">email</string>
<string name="msg_or_continue_using_social_accounts">Or continue using social accounts</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_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_2fa_code">2FA Code</string>
<string name="msg_invalid_2fa_code">Invalid 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_more_than_ninety_nine_unread_messages" translatable="false">99+</string>
<string name="msg_yesterday">Yesterday</string> <string name="msg_yesterday">Yesterday</string>
<string name="msg_message">Message</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_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_github">Login using Github</string>
<string name="msg_content_description_log_in_using_google">Login using Google</string> <string name="msg_content_description_log_in_using_google">Login using Google</string>
...@@ -37,6 +40,6 @@ ...@@ -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_twitter">Login using Twitter</string>
<string name="msg_content_description_log_in_using_gitlab">Login using Gitlab</string> <string name="msg_content_description_log_in_using_gitlab">Login using Gitlab</string>
<string name="msg_you">You</string> <string name="msg_you">You</string>
<string name="search">Search</string> <string name="msg_unknown">Unknown</string>
</resources> </resources>
\ No newline at end of file
...@@ -5,6 +5,7 @@ buildscript { ...@@ -5,6 +5,7 @@ buildscript {
repositories { repositories {
google() google()
jcenter() jcenter()
mavenCentral()
} }
dependencies { dependencies {
......
...@@ -7,13 +7,13 @@ ext { ...@@ -7,13 +7,13 @@ ext {
kotlin : '1.2.10', kotlin : '1.2.10',
coroutine : '0.21', coroutine : '0.21',
dokka : '0.9.15', dokka : '0.9.15',
kotshi : '0.3.0',
// Main dependencies // Main dependencies
support : '27.0.2', support : '27.0.2',
constraintLayout : '1.0.2', constraintLayout : '1.0.2',
dagger : '2.13', dagger : '2.13',
exoPlayer : '2.6.0', exoPlayer : '2.6.0',
playServices : '11.8.0',
room : '1.0.0', room : '1.0.0',
rxjava : '2.1.4', rxjava : '2.1.4',
rxandroid : '2.0.1', rxandroid : '2.0.1',
...@@ -23,11 +23,11 @@ ext { ...@@ -23,11 +23,11 @@ ext {
threeTenABP : '1.0.5', threeTenABP : '1.0.5',
fresco : '1.7.1', fresco : '1.7.1',
frescoImageViewer : '0.5.0', frescoImageViewer : '0.5.0',
kotshi : '0.3.0',
floatingSearchView : '2.1.1', floatingSearchView : '2.1.1',
androidSvg : '1.2.1', androidSvg : '1.2.1',
aVLoadingIndicatorView : '2.1.3', aVLoadingIndicatorView : '2.1.3',
textDrawable : '1.0.2', textDrawable : '1.0.2',
playServices : '11.8.0',
// For testing // For testing
junit : '4.12', junit : '4.12',
...@@ -52,6 +52,7 @@ ext { ...@@ -52,6 +52,7 @@ ext {
daggerSupport : "com.google.dagger:dagger-android-support:${versions.dagger}", daggerSupport : "com.google.dagger:dagger-android-support:${versions.dagger}",
daggerProcessor : "com.google.dagger:dagger-compiler:${versions.dagger}", daggerProcessor : "com.google.dagger:dagger-compiler:${versions.dagger}",
daggerAndroidApt : "com.google.dagger:dagger-android-processor:${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}", exoPlayer : "com.google.android.exoplayer:exoplayer:${versions.exoPlayer}",
room : "android.arch.persistence.room:runtime:${versions.room}", room : "android.arch.persistence.room:runtime:${versions.room}",
...@@ -63,7 +64,6 @@ ext { ...@@ -63,7 +64,6 @@ ext {
moshi : "com.squareup.moshi:moshi:${versions.moshi}", moshi : "com.squareup.moshi:moshi:${versions.moshi}",
moshiKotlin : "com.squareup.moshi:moshi-kotlin:${versions.moshi}", moshiKotlin : "com.squareup.moshi:moshi-kotlin:${versions.moshi}",
kotshi : "se.ansman.kotshi:api:${versions.kotshi}",
okhttp : "com.squareup.okhttp3:okhttp:${versions.okhttp}", okhttp : "com.squareup.okhttp3:okhttp:${versions.okhttp}",
okhttpLogger : "com.squareup.okhttp3:logging-interceptor:${versions.okhttp}", okhttpLogger : "com.squareup.okhttp3:logging-interceptor:${versions.okhttp}",
...@@ -77,6 +77,8 @@ ext { ...@@ -77,6 +77,8 @@ ext {
frescoImageViewer : "com.github.stfalcon:frescoimageviewer:${versions.frescoImageViewer}", frescoImageViewer : "com.github.stfalcon:frescoimageviewer:${versions.frescoImageViewer}",
kotshi : "se.ansman.kotshi:api:${versions.kotshi}",
floatingSearchView : "com.github.arimorty:floatingsearchview:${versions.floatingSearchView}", floatingSearchView : "com.github.arimorty:floatingsearchview:${versions.floatingSearchView}",
androidSvg : "com.caverock:androidsvg:${versions.androidSvg}", androidSvg : "com.caverock:androidsvg:${versions.androidSvg}",
...@@ -85,8 +87,6 @@ ext { ...@@ -85,8 +87,6 @@ ext {
textDrawable : "com.github.rocketchat:textdrawable:${versions.textDrawable}", textDrawable : "com.github.rocketchat:textdrawable:${versions.textDrawable}",
playServicesGcm : "com.google.android.gms:play-services-gcm:${versions.playServices}",
// For testing // For testing
junit : "junit:junit:$versions.junit", junit : "junit:junit:$versions.junit",
expressoCore : "com.android.support.test.espresso:espresso-core:${versions.expresso}", 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