Commit 0402669e authored by Lucio Maciel's avatar Lucio Maciel

Display attachments and other fixes

parent 6d84d280
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
apply plugin: 'io.fabric'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions' apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt' apply plugin: 'kotlin-kapt'
...@@ -63,6 +64,7 @@ dependencies { ...@@ -63,6 +64,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.playServicesGcm
implementation libraries.room implementation libraries.room
...@@ -80,6 +82,7 @@ dependencies { ...@@ -80,6 +82,7 @@ dependencies {
implementation libraries.threeTenABP implementation libraries.threeTenABP
implementation libraries.fresco implementation libraries.fresco
api libraries.frescoOkHttp
implementation libraries.frescoAnimatedGif implementation libraries.frescoAnimatedGif
implementation libraries.frescoWebP implementation libraries.frescoWebP
implementation libraries.frescoAnimatedWebP implementation libraries.frescoAnimatedWebP
...@@ -97,6 +100,10 @@ dependencies { ...@@ -97,6 +100,10 @@ dependencies {
implementation libraries.textDrawable implementation libraries.textDrawable
implementation('com.crashlytics.sdk.android:crashlytics:2.6.8@aar') {
transitive = true
}
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'
......
...@@ -75,6 +75,11 @@ ...@@ -75,6 +75,11 @@
<action android:name="com.google.android.c2dm.intent.RECEIVE" /> <action android:name="com.google.android.c2dm.intent.RECEIVE" />
</intent-filter> </intent-filter>
</service> </service>
<meta-data
android:name="io.fabric.ApiKey"
android:value="12ac6e94f850aaffcdff52001af77ca415d06a43" />
</application> </application>
</manifest> </manifest>
\ No newline at end of file
...@@ -4,13 +4,15 @@ import android.app.Activity ...@@ -4,13 +4,15 @@ import android.app.Activity
import android.app.Application import android.app.Application
import android.app.Service import android.app.Service
import chat.rocket.android.BuildConfig import chat.rocket.android.BuildConfig
import chat.rocket.android.app.utils.CustomImageFormatConfigurator
import chat.rocket.android.dagger.DaggerAppComponent import chat.rocket.android.dagger.DaggerAppComponent
import chat.rocket.android.helper.CrashlyticsTree
import chat.rocket.android.server.domain.GetCurrentServerInteractor import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.domain.MultiServerTokenRepository import chat.rocket.android.server.domain.MultiServerTokenRepository
import chat.rocket.android.server.domain.SettingsRepository import chat.rocket.android.server.domain.SettingsRepository
import chat.rocket.common.model.Token import chat.rocket.common.model.Token
import chat.rocket.core.TokenRepository import chat.rocket.core.TokenRepository
import com.crashlytics.android.Crashlytics
import com.crashlytics.android.core.CrashlyticsCore
import com.facebook.drawee.backends.pipeline.DraweeConfig import com.facebook.drawee.backends.pipeline.DraweeConfig
import com.facebook.drawee.backends.pipeline.Fresco import com.facebook.drawee.backends.pipeline.Fresco
import com.facebook.imagepipeline.core.ImagePipelineConfig import com.facebook.imagepipeline.core.ImagePipelineConfig
...@@ -19,9 +21,12 @@ import dagger.android.AndroidInjector ...@@ -19,9 +21,12 @@ import dagger.android.AndroidInjector
import dagger.android.DispatchingAndroidInjector import dagger.android.DispatchingAndroidInjector
import dagger.android.HasActivityInjector import dagger.android.HasActivityInjector
import dagger.android.HasServiceInjector import dagger.android.HasServiceInjector
import io.fabric.sdk.android.Fabric
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
class RocketChatApplication : Application(), HasActivityInjector, HasServiceInjector { class RocketChatApplication : Application(), HasActivityInjector, HasServiceInjector {
@Inject @Inject
...@@ -30,9 +35,10 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje ...@@ -30,9 +35,10 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje
@Inject @Inject
lateinit var serviceDispatchingAndroidInjector: DispatchingAndroidInjector<Service> lateinit var serviceDispatchingAndroidInjector: DispatchingAndroidInjector<Service>
companion object { @Inject
lateinit var instance: RocketChatApplication lateinit var imagePipelineConfig: ImagePipelineConfig
} @Inject
lateinit var draweeConfig: DraweeConfig
// TODO - remove this from here when we have a proper service handling the connection. // TODO - remove this from here when we have a proper service handling the connection.
@Inject @Inject
...@@ -54,9 +60,9 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje ...@@ -54,9 +60,9 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje
AndroidThreeTen.init(this) AndroidThreeTen.init(this)
setupCrashlytics()
setupFresco() setupFresco()
setupTimber() setupTimber()
instance = this
} }
// TODO - remove this when we have a proper service handling connection... // TODO - remove this when we have a proper service handling connection...
...@@ -69,21 +75,20 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje ...@@ -69,21 +75,20 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje
} }
} }
private fun setupFresco() { private fun setupCrashlytics() {
val imagePipelineConfig = ImagePipelineConfig.newBuilder(this) val core = CrashlyticsCore.Builder().disabled(BuildConfig.DEBUG).build()
.setImageDecoderConfig(CustomImageFormatConfigurator.createImageDecoderConfig()) Fabric.with(this, Crashlytics.Builder().core(core).build())
.build() }
val draweeConfigBuilder = DraweeConfig.newBuilder()
CustomImageFormatConfigurator.addCustomDrawableFactories(draweeConfigBuilder)
Fresco.initialize(this, imagePipelineConfig, draweeConfigBuilder.build()) private fun setupFresco() {
Fresco.initialize(this, imagePipelineConfig, draweeConfig)
} }
private fun setupTimber() { private fun setupTimber() {
if (BuildConfig.DEBUG) { if (BuildConfig.DEBUG) {
Timber.plant(Timber.DebugTree()) Timber.plant(Timber.DebugTree())
} else {
Timber.plant(CrashlyticsTree())
} }
} }
......
...@@ -17,7 +17,7 @@ import chat.rocket.android.helper.AnimationHelper ...@@ -17,7 +17,7 @@ import chat.rocket.android.helper.AnimationHelper
import chat.rocket.android.helper.KeyboardHelper import chat.rocket.android.helper.KeyboardHelper
import chat.rocket.android.helper.TextHelper import chat.rocket.android.helper.TextHelper
import chat.rocket.android.util.inflate import chat.rocket.android.util.inflate
import chat.rocket.android.util.setVisibility import chat.rocket.android.util.setVisible
import chat.rocket.android.util.textContent import chat.rocket.android.util.textContent
import dagger.android.support.AndroidSupportInjection import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_authentication_log_in.* import kotlinx.android.synthetic.main.fragment_authentication_log_in.*
...@@ -92,8 +92,8 @@ class LoginFragment : Fragment(), LoginView { ...@@ -92,8 +92,8 @@ class LoginFragment : Fragment(), LoginView {
override fun showOauthView(value: Boolean) { override fun showOauthView(value: Boolean) {
if (value) { if (value) {
social_accounts_container.setVisibility(true) social_accounts_container.setVisible(true)
button_fab.setVisibility(true) button_fab.setVisible(true)
// We need to setup the layout to hide and show the oauth interface when the soft keyboard is shown // We need to setup the layout to hide and show the oauth interface when the soft keyboard is shown
// (means that the user touched the text_username_or_email or text_password EditText to fill that respective fields). // (means that the user touched the text_username_or_email or text_password EditText to fill that respective fields).
...@@ -102,8 +102,8 @@ class LoginFragment : Fragment(), LoginView { ...@@ -102,8 +102,8 @@ class LoginFragment : Fragment(), LoginView {
isGlobalLayoutListenerSetUp = true isGlobalLayoutListenerSetUp = true
} }
} else { } else {
social_accounts_container.setVisibility(false) social_accounts_container.setVisible(false)
button_fab.setVisibility(false) button_fab.setVisible(false)
} }
} }
...@@ -143,7 +143,7 @@ class LoginFragment : Fragment(), LoginView { ...@@ -143,7 +143,7 @@ class LoginFragment : Fragment(), LoginView {
button_gitlab.isEnabled = true button_gitlab.isEnabled = true
} }
override fun showSignUpView(value: Boolean) = text_new_to_rocket_chat.setVisibility(value) override fun showSignUpView(value: Boolean) = text_new_to_rocket_chat.setVisible(value)
override fun alertWrongUsernameOrEmail() { override fun alertWrongUsernameOrEmail() {
AnimationHelper.vibrateSmartPhone(appContext) AnimationHelper.vibrateSmartPhone(appContext)
...@@ -159,11 +159,11 @@ class LoginFragment : Fragment(), LoginView { ...@@ -159,11 +159,11 @@ class LoginFragment : Fragment(), LoginView {
override fun showLoading() { override fun showLoading() {
enableUserInput(false) enableUserInput(false)
view_loading.setVisibility(true) view_loading.setVisible(true)
} }
override fun hideLoading() { override fun hideLoading() {
view_loading.setVisibility(false) view_loading.setVisible(false)
enableUserInput(true) enableUserInput(true)
} }
...@@ -189,7 +189,7 @@ class LoginFragment : Fragment(), LoginView { ...@@ -189,7 +189,7 @@ class LoginFragment : Fragment(), LoginView {
} }
private fun showLoginButton(value: Boolean) { private fun showLoginButton(value: Boolean) {
button_log_in.setVisibility(value) button_log_in.setVisible(value)
} }
private fun setupSignUpListener() { private fun setupSignUpListener() {
......
...@@ -48,11 +48,11 @@ class ServerFragment : Fragment(), ServerView { ...@@ -48,11 +48,11 @@ class ServerFragment : Fragment(), ServerView {
override fun showLoading() { override fun showLoading() {
enableUserInput(false) enableUserInput(false)
view_loading.setVisibility(true) view_loading.setVisible(true)
} }
override fun hideLoading() { override fun hideLoading() {
view_loading.setVisibility(false) view_loading.setVisible(false)
enableUserInput(true) enableUserInput(true)
} }
......
...@@ -14,7 +14,7 @@ import chat.rocket.android.authentication.signup.presentation.SignupView ...@@ -14,7 +14,7 @@ import chat.rocket.android.authentication.signup.presentation.SignupView
import chat.rocket.android.helper.AnimationHelper import chat.rocket.android.helper.AnimationHelper
import chat.rocket.android.helper.KeyboardHelper import chat.rocket.android.helper.KeyboardHelper
import chat.rocket.android.helper.TextHelper import chat.rocket.android.helper.TextHelper
import chat.rocket.android.util.setVisibility import chat.rocket.android.util.setVisible
import chat.rocket.android.util.textContent import chat.rocket.android.util.textContent
import dagger.android.support.AndroidSupportInjection import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_authentication_sign_up.* import kotlinx.android.synthetic.main.fragment_authentication_sign_up.*
...@@ -26,9 +26,9 @@ class SignupFragment : Fragment(), SignupView { ...@@ -26,9 +26,9 @@ class SignupFragment : Fragment(), SignupView {
private val layoutListener = ViewTreeObserver.OnGlobalLayoutListener { private val layoutListener = ViewTreeObserver.OnGlobalLayoutListener {
if (KeyboardHelper.isSoftKeyboardShown(constraint_layout.rootView)) { if (KeyboardHelper.isSoftKeyboardShown(constraint_layout.rootView)) {
text_new_user_agreement.setVisibility(false) text_new_user_agreement.setVisible(false)
} else { } else {
text_new_user_agreement.setVisibility(true) text_new_user_agreement.setVisible(true)
} }
} }
...@@ -92,11 +92,11 @@ class SignupFragment : Fragment(), SignupView { ...@@ -92,11 +92,11 @@ class SignupFragment : Fragment(), SignupView {
override fun showLoading() { override fun showLoading() {
enableUserInput(false) enableUserInput(false)
view_loading.setVisibility(true) view_loading.setVisible(true)
} }
override fun hideLoading() { override fun hideLoading() {
view_loading.setVisibility(false) view_loading.setVisible(false)
enableUserInput(true) enableUserInput(true)
} }
......
...@@ -14,7 +14,7 @@ import chat.rocket.android.R ...@@ -14,7 +14,7 @@ import chat.rocket.android.R
import chat.rocket.android.authentication.twofactor.presentation.TwoFAPresenter import chat.rocket.android.authentication.twofactor.presentation.TwoFAPresenter
import chat.rocket.android.authentication.twofactor.presentation.TwoFAView import chat.rocket.android.authentication.twofactor.presentation.TwoFAView
import chat.rocket.android.helper.AnimationHelper import chat.rocket.android.helper.AnimationHelper
import chat.rocket.android.util.setVisibility import chat.rocket.android.util.setVisible
import chat.rocket.android.util.textContent import chat.rocket.android.util.textContent
import dagger.android.support.AndroidSupportInjection import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_authentication_two_fa.* import kotlinx.android.synthetic.main.fragment_authentication_two_fa.*
...@@ -75,11 +75,11 @@ class TwoFAFragment : Fragment(), TwoFAView { ...@@ -75,11 +75,11 @@ class TwoFAFragment : Fragment(), TwoFAView {
override fun showLoading() { override fun showLoading() {
enableUserInput(false) enableUserInput(false)
view_loading.setVisibility(true) view_loading.setVisible(true)
} }
override fun hideLoading() { override fun hideLoading() {
view_loading.setVisibility(false) view_loading.setVisible(false)
enableUserInput(true) enableUserInput(true)
} }
......
...@@ -26,7 +26,8 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView, ...@@ -26,7 +26,8 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
private val strategy: CancelStrategy, private val strategy: CancelStrategy,
getSettingsInteractor: GetSettingsInteractor, getSettingsInteractor: GetSettingsInteractor,
private val serverInteractor: GetCurrentServerInteractor, private val serverInteractor: GetCurrentServerInteractor,
factory: RocketChatClientFactory) { factory: RocketChatClientFactory,
private val mapper: MessageViewModelMapper) {
private val client = factory.create(serverInteractor.get()!!) private val client = factory.create(serverInteractor.get()!!)
private val roomMessages = ArrayList<Message>() private val roomMessages = ArrayList<Message>()
private var subId: String? = null private var subId: String? = null
...@@ -47,7 +48,7 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView, ...@@ -47,7 +48,7 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
roomMessages.addAll(messages) roomMessages.addAll(messages)
} }
val messagesViewModels = MessageViewModelMapper.mapToViewModelList(messages, settings) val messagesViewModels = mapper.mapToViewModelList(messages, settings)
view.showMessages(messagesViewModels, serverInteractor.get()!!) view.showMessages(messagesViewModels, serverInteractor.get()!!)
// Subscribe after getting the first page of messages from REST // Subscribe after getting the first page of messages from REST
...@@ -148,7 +149,7 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView, ...@@ -148,7 +149,7 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
private fun updateMessage(streamedMessage: Message) { private fun updateMessage(streamedMessage: Message) {
launchUI(strategy) { launchUI(strategy) {
val viewModelStreamedMessage = MessageViewModelMapper.mapToViewModel(streamedMessage, settings) val viewModelStreamedMessage = mapper.mapToViewModel(streamedMessage, settings)
synchronized(roomMessages) { synchronized(roomMessages) {
val index = roomMessages.indexOfFirst { msg -> msg.id == streamedMessage.id } val index = roomMessages.indexOfFirst { msg -> msg.id == streamedMessage.id }
if (index != -1) { if (index != -1) {
......
...@@ -4,14 +4,18 @@ import android.support.v7.widget.RecyclerView ...@@ -4,14 +4,18 @@ import android.support.v7.widget.RecyclerView
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.ImageView import android.widget.ImageView
import android.widget.TextView
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.chatroom.viewmodel.AttachmentType
import chat.rocket.android.chatroom.viewmodel.MessageViewModel import chat.rocket.android.chatroom.viewmodel.MessageViewModel
import chat.rocket.android.player.PlayerActivity
import chat.rocket.android.util.inflate import chat.rocket.android.util.inflate
import chat.rocket.android.util.setVisibility import chat.rocket.android.util.setVisible
import chat.rocket.common.util.ifNull import chat.rocket.common.util.ifNull
import com.facebook.drawee.view.SimpleDraweeView import com.facebook.drawee.view.SimpleDraweeView
import kotlinx.android.synthetic.main.avatar.view.* import kotlinx.android.synthetic.main.avatar.view.*
import kotlinx.android.synthetic.main.item_message.view.* import kotlinx.android.synthetic.main.item_message.view.*
import kotlinx.android.synthetic.main.message_attachment.view.*
class ChatRoomAdapter(private val serverUrl: String) : RecyclerView.Adapter<ChatRoomAdapter.ViewHolder>() { class ChatRoomAdapter(private val serverUrl: String) : RecyclerView.Adapter<ChatRoomAdapter.ViewHolder>() {
...@@ -61,12 +65,54 @@ class ChatRoomAdapter(private val serverUrl: String) : RecyclerView.Adapter<Chat ...@@ -61,12 +65,54 @@ class ChatRoomAdapter(private val serverUrl: String) : RecyclerView.Adapter<Chat
text_user_name.text = message.sender text_user_name.text = message.sender
text_message_time.text = message.time text_message_time.text = message.time
text_content.text = message.content text_content.text = message.content
bindAttachment(message, attachment_container, image_attachment, audio_video_attachment,
file_name)
}
private fun bindAttachment(message: MessageViewModel,
attachment_container: View,
image_attachment: SimpleDraweeView,
audio_video_attachment: View,
file_name: TextView) {
with(message) {
if (attachmentUrl == null || attachmentType == null) {
attachment_container.setVisible(false)
}
var imageVisible = false
var videoVisible = false
attachment_container.setVisible(true)
when (message.attachmentType) {
is AttachmentType.Image -> {
imageVisible = true
image_attachment.setImageURI(message.attachmentUrl)
}
is AttachmentType.Video,
is AttachmentType.Audio -> {
videoVisible = true
audio_video_attachment.setOnClickListener { view ->
message.attachmentUrl?.let { url ->
PlayerActivity.play(view.context, url)
}
}
}
}
image_attachment.setVisible(imageVisible)
audio_video_attachment.setVisible(videoVisible)
file_name.text = message.attachmentTitle
}
} }
private fun bindUserAvatar(message: MessageViewModel, drawee: SimpleDraweeView, imageUnknownAvatar: ImageView) = message.getAvatarUrl(serverUrl).let { private fun bindUserAvatar(message: MessageViewModel, drawee: SimpleDraweeView, imageUnknownAvatar: ImageView) = message.getAvatarUrl(serverUrl).let {
drawee.setImageURI(it.toString()) drawee.setImageURI(it.toString())
drawee.setVisible(true)
imageUnknownAvatar.setVisible(false)
}.ifNull { }.ifNull {
imageUnknownAvatar.setVisibility(true) drawee.setVisible(false)
imageUnknownAvatar.setVisible(true)
} }
} }
} }
\ No newline at end of file
...@@ -15,7 +15,7 @@ import chat.rocket.android.chatroom.presentation.ChatRoomView ...@@ -15,7 +15,7 @@ import chat.rocket.android.chatroom.presentation.ChatRoomView
import chat.rocket.android.chatroom.viewmodel.MessageViewModel import chat.rocket.android.chatroom.viewmodel.MessageViewModel
import chat.rocket.android.helper.EndlessRecyclerViewScrollListener import chat.rocket.android.helper.EndlessRecyclerViewScrollListener
import chat.rocket.android.util.inflate import chat.rocket.android.util.inflate
import chat.rocket.android.util.setVisibility import chat.rocket.android.util.setVisible
import chat.rocket.android.util.textContent import chat.rocket.android.util.textContent
import dagger.android.support.AndroidSupportInjection import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_chat_room.* import kotlinx.android.synthetic.main.fragment_chat_room.*
...@@ -123,9 +123,9 @@ class ChatRoomFragment : Fragment(), ChatRoomView { ...@@ -123,9 +123,9 @@ class ChatRoomFragment : Fragment(), ChatRoomView {
adapter.updateItem(index, message) adapter.updateItem(index, message)
} }
override fun showLoading() = view_loading.setVisibility(true) override fun showLoading() = view_loading.setVisible(true)
override fun hideLoading() = view_loading.setVisibility(false) override fun hideLoading() = view_loading.setVisible(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()
...@@ -133,8 +133,8 @@ class ChatRoomFragment : Fragment(), ChatRoomView { ...@@ -133,8 +133,8 @@ class ChatRoomFragment : Fragment(), ChatRoomView {
private fun setupComposer() { private fun setupComposer() {
if (isChatRoomReadOnly) { if (isChatRoomReadOnly) {
text_room_is_read_only.setVisibility(true) text_room_is_read_only.setVisible(true)
top_container.setVisibility(false) top_container.setVisible(false)
} else { } else {
text_send.setOnClickListener { sendMessage(text_message.textContent) } text_send.setOnClickListener { sendMessage(text_message.textContent) }
} }
......
...@@ -8,24 +8,52 @@ import android.text.SpannableString ...@@ -8,24 +8,52 @@ import android.text.SpannableString
import android.text.style.ForegroundColorSpan import android.text.style.ForegroundColorSpan
import android.text.style.StyleSpan import android.text.style.StyleSpan
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.app.RocketChatApplication
import chat.rocket.android.helper.UrlHelper import chat.rocket.android.helper.UrlHelper
import chat.rocket.android.server.domain.SITE_URL
import chat.rocket.android.server.domain.USE_REALNAME import chat.rocket.android.server.domain.USE_REALNAME
import chat.rocket.common.model.Token
import chat.rocket.core.model.Message import chat.rocket.core.model.Message
import chat.rocket.core.model.MessageType.* import chat.rocket.core.model.MessageType.*
import chat.rocket.core.model.Value import chat.rocket.core.model.Value
import chat.rocket.core.model.attachment.AudioAttachment
import chat.rocket.core.model.attachment.FileAttachment
import chat.rocket.core.model.attachment.ImageAttachment
import chat.rocket.core.model.attachment.VideoAttachment
import okhttp3.HttpUrl
data class MessageViewModel(private val message: Message, data class MessageViewModel(val context: Context,
private val token: Token?,
private val message: Message,
private val settings: Map<String, Value<Any>>?) { private val settings: Map<String, Value<Any>>?) {
val id: String = message.id val id: String = message.id
val time: CharSequence val time: CharSequence
val sender: CharSequence val sender: CharSequence
val content: CharSequence val content: CharSequence
var attachmentUrl: String? = null
var attachmentTitle: CharSequence? = null
var attachmentType: AttachmentType? = null
init { init {
sender = getSenderName() sender = getSenderName()
content = getContent(RocketChatApplication.instance) content = getContent(context)
time = getTime() time = getTime()
message.attachments?.let {
if (it.isEmpty() || it[0] == null) return@let
val attachment = it[0] as FileAttachment
val baseUrl = settings?.get(SITE_URL)
baseUrl?.let {
attachmentUrl = attachmentUrl("${baseUrl.value}${attachment.url}")
attachmentTitle = attachment.title
attachmentType = when (attachment) {
is ImageAttachment -> AttachmentType.Image
is VideoAttachment -> AttachmentType.Video
is AudioAttachment -> AttachmentType.Audio
else -> null
}
}
}
} }
fun getAvatarUrl(serverUrl: String): String? { fun getAvatarUrl(serverUrl: String): String? {
...@@ -41,7 +69,7 @@ data class MessageViewModel(private val message: Message, ...@@ -41,7 +69,7 @@ data class MessageViewModel(private val message: Message,
val username = message.sender?.username val username = message.sender?.username
val realName = message.sender?.name val realName = message.sender?.name
val senderName = if (useRealName) realName else username val senderName = if (useRealName) realName else username
return if (senderName == null) username.toString() else senderName.toString() return senderName ?: username.toString()
} }
fun getContent(context: Context): CharSequence { fun getContent(context: Context): CharSequence {
...@@ -91,4 +119,23 @@ data class MessageViewModel(private val message: Message, ...@@ -91,4 +119,23 @@ data class MessageViewModel(private val message: Message,
return spannableMsg return spannableMsg
} }
private fun attachmentUrl(url: String): String {
var response = url
val httpUrl = HttpUrl.parse(url)
httpUrl?.let {
response = it.newBuilder().apply {
addQueryParameter("rc_uid", token?.userId)
addQueryParameter("rc_token", token?.authToken)
}.build().toString()
}
return response
}
}
sealed class AttachmentType {
object Image : AttachmentType()
object Video : AttachmentType()
object Audio : AttachmentType()
} }
\ No newline at end of file
package chat.rocket.android.chatroom.viewmodel package chat.rocket.android.chatroom.viewmodel
import android.content.Context
import chat.rocket.core.TokenRepository
import chat.rocket.core.model.Message import chat.rocket.core.model.Message
import chat.rocket.core.model.Value import chat.rocket.core.model.Value
import javax.inject.Inject
object MessageViewModelMapper { class MessageViewModelMapper @Inject constructor(private val context: Context, private val tokenRepository: TokenRepository) {
suspend fun mapToViewModel(message: Message, settings: Map<String, Value<Any>>?) = MessageViewModel(message, settings) suspend fun mapToViewModel(message: Message, settings: Map<String, Value<Any>>?) = MessageViewModel(context, tokenRepository.get(), message, settings)
suspend fun mapToViewModelList(messageList: List<Message>, settings: Map<String, Value<Any>>?): List<MessageViewModel> { suspend fun mapToViewModelList(messageList: List<Message>, settings: Map<String, Value<Any>>?): List<MessageViewModel> {
val vmList = mutableListOf<MessageViewModel>() return messageList.map { MessageViewModel(context, tokenRepository.get(), it, settings) }
for (msg in messageList) {
vmList.add(MessageViewModel(msg, settings))
}
return vmList
} }
} }
\ No newline at end of file
...@@ -11,7 +11,7 @@ import android.widget.TextView ...@@ -11,7 +11,7 @@ import android.widget.TextView
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.helper.UrlHelper import chat.rocket.android.helper.UrlHelper
import chat.rocket.android.util.inflate import chat.rocket.android.util.inflate
import chat.rocket.android.util.setVisibility import chat.rocket.android.util.setVisible
import chat.rocket.android.util.textContent 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
...@@ -52,12 +52,12 @@ class ChatRoomsAdapter(private val context: Context, ...@@ -52,12 +52,12 @@ class ChatRoomsAdapter(private val context: Context,
val chatRoomName = chatRoom.name val chatRoomName = chatRoom.name
if (chatRoom.type == RoomType.ONE_TO_ONE) { if (chatRoom.type == RoomType.ONE_TO_ONE) {
drawee.setImageURI(UrlHelper.getAvatarUrl(chatRoom.client.url, chatRoomName)) drawee.setImageURI(UrlHelper.getAvatarUrl(chatRoom.client.url, chatRoomName))
imageView.setVisibility(false) imageView.setVisible(false)
avatarLayout.setVisibility(true) avatarLayout.setVisible(true)
} else { } else {
imageView.setImageDrawable(DrawableHelper.getTextDrawable(chatRoomName)) imageView.setImageDrawable(DrawableHelper.getTextDrawable(chatRoomName))
avatarLayout.setVisibility(false) avatarLayout.setVisible(false)
imageView.setVisibility(true) imageView.setVisible(true)
} }
} }
...@@ -99,11 +99,11 @@ class ChatRoomsAdapter(private val context: Context, ...@@ -99,11 +99,11 @@ class ChatRoomsAdapter(private val context: Context,
when { when {
totalUnreadMessage in 1..99 -> { totalUnreadMessage in 1..99 -> {
textView.textContent = totalUnreadMessage.toString() textView.textContent = totalUnreadMessage.toString()
textView.setVisibility(true) textView.setVisible(true)
} }
totalUnreadMessage > 99 -> { totalUnreadMessage > 99 -> {
textView.textContent = context.getString(R.string.msg_more_than_ninety_nine_unread_messages) textView.textContent = context.getString(R.string.msg_more_than_ninety_nine_unread_messages)
textView.setVisibility(true) textView.setVisible(true)
} }
} }
} }
......
...@@ -12,7 +12,7 @@ import android.widget.Toast ...@@ -12,7 +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.util.setVisible
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
...@@ -104,9 +104,9 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView { ...@@ -104,9 +104,9 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
} }
} }
override fun showLoading() = view_loading.setVisibility(true) override fun showLoading() = view_loading.setVisible(true)
override fun hideLoading() = view_loading.setVisibility(false) override fun hideLoading() = view_loading.setVisible(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()
......
...@@ -6,8 +6,11 @@ import android.content.Context ...@@ -6,8 +6,11 @@ import android.content.Context
import android.content.SharedPreferences import android.content.SharedPreferences
import chat.rocket.android.BuildConfig import chat.rocket.android.BuildConfig
import chat.rocket.android.app.RocketChatDatabase import chat.rocket.android.app.RocketChatDatabase
import chat.rocket.android.app.utils.CustomImageFormatConfigurator
import chat.rocket.android.authentication.infraestructure.MemoryTokenRepository import chat.rocket.android.authentication.infraestructure.MemoryTokenRepository
import chat.rocket.android.authentication.infraestructure.SharedPreferencesMultiServerTokenRepository import chat.rocket.android.authentication.infraestructure.SharedPreferencesMultiServerTokenRepository
import chat.rocket.android.dagger.qualifier.ForFresco
import chat.rocket.android.helper.FrescoAuthInterceptor
import chat.rocket.android.infrastructure.LocalRepository import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.infrastructure.SharedPrefsLocalRepository import chat.rocket.android.infrastructure.SharedPrefsLocalRepository
import chat.rocket.android.server.domain.ChatRoomsRepository import chat.rocket.android.server.domain.ChatRoomsRepository
...@@ -23,10 +26,16 @@ import chat.rocket.android.util.TimberLogger ...@@ -23,10 +26,16 @@ import chat.rocket.android.util.TimberLogger
import chat.rocket.common.util.PlatformLogger import chat.rocket.common.util.PlatformLogger
import chat.rocket.core.RocketChatClient import chat.rocket.core.RocketChatClient
import chat.rocket.core.TokenRepository import chat.rocket.core.TokenRepository
import com.facebook.drawee.backends.pipeline.DraweeConfig
import com.facebook.imagepipeline.backends.okhttp3.OkHttpImagePipelineConfigFactory
import com.facebook.imagepipeline.core.ImagePipelineConfig
import com.facebook.imagepipeline.listener.RequestListener
import com.facebook.imagepipeline.listener.RequestLoggingListener
import com.squareup.moshi.Moshi import com.squareup.moshi.Moshi
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
import kotlinx.coroutines.experimental.Job import kotlinx.coroutines.experimental.Job
import okhttp3.Interceptor
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor import okhttp3.logging.HttpLoggingInterceptor
import javax.inject.Singleton import javax.inject.Singleton
...@@ -91,6 +100,46 @@ class AppModule { ...@@ -91,6 +100,46 @@ class AppModule {
}.build() }.build()
} }
@Provides
@ForFresco
@Singleton
fun provideFrescoAuthIntercepter(tokenRepository: TokenRepository): Interceptor {
return FrescoAuthInterceptor(tokenRepository)
}
@Provides
@ForFresco
@Singleton
fun provideFrescoOkHttpClient(okHttpClient: OkHttpClient, @ForFresco authInterceptor: Interceptor): OkHttpClient {
return okHttpClient.newBuilder().apply {
//addInterceptor(authInterceptor)
}.build()
}
@Provides
@Singleton
fun provideImagePipelineConfig(context: Context, @ForFresco okHttpClient: OkHttpClient): ImagePipelineConfig {
val listeners = HashSet<RequestListener>()
listeners.add(RequestLoggingListener())
return OkHttpImagePipelineConfigFactory.newBuilder(context, okHttpClient)
.setImageDecoderConfig(CustomImageFormatConfigurator.createImageDecoderConfig())
.setRequestListeners(listeners)
.setDownsampleEnabled(true)
//.experiment().setBitmapPrepareToDraw(true).experiment()
.experiment().setPartialImageCachingEnabled(true).build()
}
@Provides
@Singleton
fun provideDraweeConfig(): DraweeConfig {
val draweeConfigBuilder = DraweeConfig.newBuilder()
CustomImageFormatConfigurator.addCustomDrawableFactories(draweeConfigBuilder)
return draweeConfigBuilder.build()
}
@Provides @Provides
@Singleton @Singleton
fun provideTokenRepository(): TokenRepository { fun provideTokenRepository(): TokenRepository {
......
package chat.rocket.android.dagger.qualifier
import javax.inject.Qualifier
@Qualifier
@Retention(AnnotationRetention.RUNTIME)
annotation class ForFresco
\ No newline at end of file
package chat.rocket.android.helper
import com.crashlytics.android.Crashlytics
import timber.log.Timber
class CrashlyticsTree : Timber.Tree() {
override fun log(priority: Int, tag: String, message: String, throwable: Throwable?) {
Crashlytics.log(priority, tag, message)
if (throwable != null) {
Crashlytics.logException(throwable)
}
}
}
\ No newline at end of file
package chat.rocket.android.helper
import chat.rocket.core.TokenRepository
import okhttp3.Interceptor
import okhttp3.Response
class FrescoAuthInterceptor(private val tokenRepository: TokenRepository) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val token = tokenRepository.get()
var request = chain.request()
token?.let {
val url = request.url().newBuilder().apply {
addQueryParameter("rc_uid", token.userId)
addQueryParameter("rc_token", token.authToken)
}.build()
request = request.newBuilder().apply {
url(url)
}.build()
}
return chain.proceed(request)
}
}
\ No newline at end of file
...@@ -13,7 +13,7 @@ fun String.ifEmpty(value: String): String { ...@@ -13,7 +13,7 @@ fun String.ifEmpty(value: String): String {
return this return this
} }
fun View.setVisibility(value: Boolean) { fun View.setVisible(value: Boolean) {
visibility = if (value) { visibility = if (value) {
View.VISIBLE View.VISIBLE
} else { } else {
......
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="80dp"
android:height="45dp"
android:viewportWidth="80.0"
android:viewportHeight="45.0">
<path
android:pathData="M0,0h80v45h-80z"
android:strokeColor="#00000000"
android:fillColor="#C3D1DA"
android:strokeWidth="1"/>
<path
android:pathData="M43.99,16.75C44.34,16.75 44.64,16.87 44.88,17.12C45.13,17.36 45.25,17.66 45.25,18.01L45.25,26.74C45.25,27.09 45.13,27.39 44.88,27.63C44.64,27.88 44.34,28 43.99,28L35.26,28C34.91,28 34.61,27.88 34.37,27.63C34.12,27.39 34,27.09 34,26.74L34,18.01C34,17.66 34.12,17.36 34.37,17.12C34.61,16.87 34.91,16.75 35.26,16.75L43.99,16.75ZM43.99,26.74L43.99,18.01L35.26,18.01L35.26,26.74L43.99,26.74ZM40.86,22.55L43.05,25.51L36.2,25.51L37.9,23.28L39.13,24.78L40.86,22.55Z"
android:strokeColor="#00000000"
android:fillColor="#5D8298"
android:strokeWidth="1"/>
</vector>
...@@ -55,13 +55,12 @@ ...@@ -55,13 +55,12 @@
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 --> <!-- TODO - Use separate adapter items for messages and attachments. -->
<com.facebook.drawee.view.SimpleDraweeView <include
android:id="@+id/image_attachment" layout="@layout/message_attachment"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="150dp" android:layout_height="wrap_content"
android:layout_marginTop="5dp" android:layout_marginTop="4dp"
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" />
......
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:fresco="http://schemas.android.com/apk/res-auto"
android:id="@+id/attachment_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/image_attachment"
android:layout_width="match_parent"
android:layout_height="150dp"
fresco:actualImageScaleType="fitStart"
fresco:placeholderImage="@drawable/image_dummy"
tools:visibility="visible"
android:visibility="gone"/>
<FrameLayout
android:id="@+id/audio_video_attachment"
android:layout_width="match_parent"
android:layout_height="150dp"
android:background="@color/black"
android:visibility="gone"
tools:visibility="gone">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="@drawable/exo_controls_play"/>
</FrameLayout>
<TextView
android:id="@+id/file_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:text="Filename.png"/>
</LinearLayout>
\ No newline at end of file
...@@ -5,6 +5,7 @@ buildscript { ...@@ -5,6 +5,7 @@ buildscript {
repositories { repositories {
google() google()
jcenter() jcenter()
maven { url 'https://maven.fabric.io/public' }
mavenCentral() mavenCentral()
} }
...@@ -13,6 +14,7 @@ buildscript { ...@@ -13,6 +14,7 @@ buildscript {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin}" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin}"
classpath "org.jetbrains.dokka:dokka-gradle-plugin:${versions.dokka}" classpath "org.jetbrains.dokka:dokka-gradle-plugin:${versions.dokka}"
classpath 'com.google.gms:google-services:3.1.2' classpath 'com.google.gms:google-services:3.1.2'
classpath 'io.fabric.tools:gradle:1.+'
// NOTE: Do not place your application dependencies here; they belong // NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files // in the individual module build.gradle files
......
...@@ -71,6 +71,7 @@ ext { ...@@ -71,6 +71,7 @@ ext {
threeTenABP : "com.jakewharton.threetenabp:threetenabp:${versions.threeTenABP}", threeTenABP : "com.jakewharton.threetenabp:threetenabp:${versions.threeTenABP}",
fresco : "com.facebook.fresco:fresco:${versions.fresco}", fresco : "com.facebook.fresco:fresco:${versions.fresco}",
frescoOkHttp : "com.facebook.fresco:imagepipeline-okhttp3:${versions.fresco}",
frescoAnimatedGif : "com.facebook.fresco:animated-gif:${versions.fresco}", frescoAnimatedGif : "com.facebook.fresco:animated-gif:${versions.fresco}",
frescoWebP : "com.facebook.fresco:webpsupport:${versions.fresco}", frescoWebP : "com.facebook.fresco:webpsupport:${versions.fresco}",
frescoAnimatedWebP : "com.facebook.fresco:animated-webp:${versions.fresco}", frescoAnimatedWebP : "com.facebook.fresco:animated-webp:${versions.fresco}",
......
package chat.rocket.android.player package chat.rocket.android.player
import android.content.Context
import android.content.Intent
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.support.v7.app.AppCompatActivity import android.support.v7.app.AppCompatActivity
import android.util.Log
import android.view.View import android.view.View
import com.google.android.exoplayer2.DefaultLoadControl import com.google.android.exoplayer2.DefaultLoadControl
import com.google.android.exoplayer2.DefaultRenderersFactory import com.google.android.exoplayer2.DefaultRenderersFactory
...@@ -22,10 +25,12 @@ class PlayerActivity : AppCompatActivity() { ...@@ -22,10 +25,12 @@ class PlayerActivity : AppCompatActivity() {
private var playWhenReady = true private var playWhenReady = true
private var currentWindow = 0 private var currentWindow = 0
private var playbackPosition = 0L private var playbackPosition = 0L
private lateinit var videoUrl: String
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_player) setContentView(R.layout.activity_player)
videoUrl = intent.getStringExtra(URL_KEY)
} }
override fun onStart() { override fun onStart() {
...@@ -65,8 +70,9 @@ class PlayerActivity : AppCompatActivity() { ...@@ -65,8 +70,9 @@ class PlayerActivity : AppCompatActivity() {
player.seekTo(currentWindow, playbackPosition) player.seekTo(currentWindow, playbackPosition)
isPlayerInitialized = true isPlayerInitialized = true
} }
val uri = Uri.parse("http://download.blender.org/peach/bigbuckbunny_movies/BigBuckBunny_320x180.mp4") val uri = Uri.parse(videoUrl)
val mediaSource = buildMediaSource(uri) val mediaSource = buildMediaSource(uri)
Log.d("PlayerActivity", "Player with: " + videoUrl)
player.prepare(mediaSource, true, false) player.prepare(mediaSource, true, false)
} }
...@@ -86,4 +92,13 @@ class PlayerActivity : AppCompatActivity() { ...@@ -86,4 +92,13 @@ class PlayerActivity : AppCompatActivity() {
// Read the docs for detailed explanation: https://developer.android.com/training/basics/firstapp/starting-activity.html and https://developer.android.com/design/patterns/fullscreen.html // Read the docs for detailed explanation: https://developer.android.com/training/basics/firstapp/starting-activity.html and https://developer.android.com/design/patterns/fullscreen.html
player_view.systemUiVisibility = View.SYSTEM_UI_FLAG_FULLSCREEN or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION player_view.systemUiVisibility = View.SYSTEM_UI_FLAG_FULLSCREEN or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
} }
companion object {
const private val URL_KEY = "URL_KEY"
fun play(context: Context, url: String) {
context.startActivity(Intent(context, PlayerActivity::class.java).apply {
putExtra(URL_KEY, url)
})
}
}
} }
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment