Commit d3f58d35 authored by Wilko Manger's avatar Wilko Manger

Support viewing image messages

parent 9089267e
Pipeline #275 passed with stages
in 4 minutes and 11 seconds
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
</state>
</component>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Kotlin2JsCompilerArguments">
<option name="sourceMapEmbedSources" />
</component>
</project>
\ No newline at end of file
This diff is collapsed.
......@@ -2,7 +2,5 @@
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
<mapping directory="$PROJECT_DIR$/matrix-android-sdk" vcs="Git" />
<mapping directory="$PROJECT_DIR$/trace" vcs="Git" />
</component>
</project>
\ No newline at end of file
......@@ -118,6 +118,7 @@ dependencies {
def ccp_version = '2.2.2'
def keyboard_event_version = '2.1.0'
def glide_version = '4.8.0'
def shape_imageview_version = '0.9.+@aar'
def junit_version = '4.12'
def mockk_version = '1.8.5'
......@@ -206,5 +207,8 @@ dependencies {
// Flexbox
implementation "com.google.android:flexbox:$flexbox_version"
// Android Shape ImageView
implementation "com.github.siyamed:android-shape-imageview:$shape_imageview_version"
implementation 'androidx.recyclerview:recyclerview:1.0.0'
}
......@@ -28,7 +28,7 @@ class App : Application() {
viewModel { MainViewModel(get()) }
viewModel { StartViewModel(get()) }
viewModel { ChatOverviewViewModel(get(), get()) }
viewModel { ChatViewModel(get(), get()) }
viewModel { ChatViewModel(get(), get(), get()) }
}
private val appModule = module {
......
......@@ -86,6 +86,9 @@ class ChatEventRemoteSource(private val matrix: Matrix,
}
events.add(chatEvent)
if (chatEvent is ChatMessage)
Log.d("FORMATTED", chatEvent.text)
}
}
} else {
......
......@@ -23,6 +23,7 @@ import im.pattle.app.data.user.User
import org.matrix.androidsdk.data.Room
import org.matrix.androidsdk.data.cryptostore.db.hash
import org.matrix.androidsdk.rest.model.Event
import java.net.URL
import java.util.*
abstract class ChatEvent {
......@@ -83,6 +84,18 @@ fun Event.toChatEvent(room: Room): ChatEvent? {
text = text
)
}
"m.image" -> {
val url = json.get("url").asString
ChatImageMessage(
id = id,
sentAt = sentAt,
sender = sender,
original = this,
text = text,
mxUrl = url
)
}
// TODO: Add yet unhandled cases.
else -> {
ChatMessage(
......
/**
* Copyright (C) 2018 Wilko Manger
*
* This file is part of Pattle.
*
* Pattle is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Pattle is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Pattle. If not, see <https://www.gnu.org/licenses/>.
*/
package im.pattle.app.data.chat.event.model
import im.pattle.app.data.user.User
import org.matrix.androidsdk.rest.model.Event
import java.net.URL
import java.util.*
data class ChatImageMessage(override val id: String,
override val sentAt: Date,
override val sender: User,
override val original: Event,
override val text: String,
val mxUrl: String)
: ChatMessage(id, sentAt, sender, original, text) {
override val type = MessageType.IMAGE
}
\ No newline at end of file
......@@ -24,7 +24,8 @@ import java.util.*
enum class MessageType {
MESSAGE,
NOTICE
NOTICE,
IMAGE
}
interface Message {
......
......@@ -20,24 +20,25 @@
package im.pattle.app.ui.main.chat
import android.content.Context
import android.text.method.LinkMovementMethod
import android.text.util.Linkify
import android.util.Log
import android.view.LayoutInflater
import android.view.View.GONE
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.LinearLayout
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat
import androidx.core.content.res.ResourcesCompat
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import im.pattle.app.R
import im.pattle.app.data.chat.event.model.*
import im.pattle.app.data.chat.overview.ChatOverview
import im.pattle.app.data.user.User
import im.pattle.app.ui.util.*
import kotlinx.android.synthetic.main.list_item_chat_recv_image.view.imageView
import kotlinx.android.synthetic.main.list_item_chat_recv_message.view.*
import kotlinx.android.synthetic.main.view_date_header.view.*
import kotlinx.android.synthetic.main.view_info.view.*
......@@ -45,7 +46,7 @@ import org.jetbrains.anko.linkTextColor
import java.util.*
class ChatEventAdapter(private val isDirect: Boolean,
private val onClick: (ChatOverview) -> Unit,
private val loadImage: (String, ImageView) -> Unit,
private val thisUser: User,
private val context: Context) :
ListAdapter<Any, RecyclerView.ViewHolder>(ItemCallback()) {
......@@ -83,6 +84,8 @@ class ChatEventAdapter(private val isDirect: Boolean,
private const val DATE_HEADER = 2
private const val NOTICE = 3
private const val MEMBERSHIP_CHANGE = 4
private const val IMAGE_MESSAGE_RECEIVED = 5
private const val IMAGE_MESSAGE_SENT = 6
}
class InfoHolder(val layout: LinearLayout)
......@@ -125,7 +128,6 @@ class ChatEventAdapter(private val isDirect: Boolean,
}
// Add all DateHeaders to the list
mutableList?.addAll(dateHeaders)
mutableList?.sortBy {
when (it) {
......@@ -137,8 +139,6 @@ class ChatEventAdapter(private val isDirect: Boolean,
// Keep a reference to the items
items = mutableList ?: listOf()
Log.d("items", items.toString())
super.submitList(mutableList)
}
......@@ -170,6 +170,14 @@ class ChatEventAdapter(private val isDirect: Boolean,
InfoHolder(inflate(R.layout.view_info)
as LinearLayout)
}
IMAGE_MESSAGE_RECEIVED -> {
EventHolder(inflate(R.layout.list_item_chat_recv_image)
as ConstraintLayout)
}
IMAGE_MESSAGE_SENT -> {
EventHolder(inflate(R.layout.list_item_chat_sent_image)
as ConstraintLayout)
}
// TODO: Handle non supported items
else -> {
EventHolder(inflate(R.layout.list_item_chat_recv_message)
......@@ -179,10 +187,13 @@ class ChatEventAdapter(private val isDirect: Boolean,
// This is required for making links clickable
if (holder is EventHolder) {
holder.layout.content.linksClickable = true
holder.layout.content.autoLinkMask = Linkify.ALL
holder.layout.content.linkTextColor =
ContextCompat.getColor(context, R.color.colorSecondary)
if (holder.layout.content != null) {
holder.layout.content.linksClickable = true
holder.layout.content.autoLinkMask = Linkify.ALL
holder.layout.content.linkTextColor =
ContextCompat.getColor(context, R.color.colorSecondary)
}
}
return holder
......@@ -214,11 +225,24 @@ class ChatEventAdapter(private val isDirect: Boolean,
holder.layout.sender.text = item.sender.name
}
}
is ChatImageMessage -> {
holder as EventHolder
holder.layout.time.text = item.sentAt.formatAsTime()
loadImage(item.mxUrl, holder.layout.imageView)
if (viewType == IMAGE_MESSAGE_RECEIVED) {
if(isDirect) {
holder.layout.sender.visibility = GONE
} else {
holder.layout.sender.text = item.sender.name
}
}
}
is ChatMessage -> {
holder as EventHolder
holder.layout.content.text = item.text.fromHtml()
// Set the time in the message
holder.layout.time.text = item.sentAt.formatAsTime()
......@@ -243,6 +267,14 @@ class ChatEventAdapter(private val isDirect: Boolean,
return when (event) {
is DateHeader -> DATE_HEADER
is ChatNotice -> NOTICE
is ChatImageMessage -> {
Log.d("IMAGE", "mhhokay")
if (event.sender == thisUser) {
IMAGE_MESSAGE_SENT
} else {
IMAGE_MESSAGE_RECEIVED
}
}
is ChatMessage -> {
if (event.sender == thisUser) {
TEXT_MESSAGE_SENT
......
......@@ -27,10 +27,12 @@ import android.view.View.GONE
import android.view.View.VISIBLE
import android.view.ViewGroup
import android.view.WindowManager
import android.widget.ImageView
import androidx.appcompat.app.ActionBar
import androidx.appcompat.app.AppCompatActivity
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.LinearLayoutManager
import com.bumptech.glide.Glide
import im.pattle.app.R
import im.pattle.app.data.chat.event.model.ChatEvent
import im.pattle.app.data.chat.event.model.ChatMessage
......@@ -87,6 +89,21 @@ class ChatFragment : BaseFragment<ChatViewModel>() {
Unit
}
private val loadImage = { mxUrl: String, imageView: ImageView ->
model.getHttpsUrl(mxUrl)
.subscribeBy(
onSuccess = { url ->
runOnUiThread {
Glide.with(this).load(url).into(imageView)
}
}
)
.addTo(compDisposable)
Unit
}
override fun onAttach(context: Context?) {
super.onAttach(context)
......@@ -103,7 +120,7 @@ class ChatFragment : BaseFragment<ChatViewModel>() {
model.chatName = name
adapter = ChatEventAdapter(isDirect = isDirect,
onClick = {},
loadImage = loadImage,
thisUser = model.thisUser,
context = context!!)
}
......
......@@ -21,11 +21,13 @@ package im.pattle.app.ui.main.chat
import im.pattle.app.data.chat.event.ChatEventRepository
import im.pattle.app.data.chat.event.model.ChatEvent
import im.pattle.app.data.chat.event.model.Message
import im.pattle.app.data.media.MediaRepository
import im.pattle.app.data.user.UserRepository
import im.pattle.app.ui.base.BaseViewModel
class ChatViewModel(private val eventRepo: ChatEventRepository,
private val userRepo: UserRepository)
private val userRepo: UserRepository,
private val mediaRepo: MediaRepository)
: BaseViewModel() {
lateinit var chatId: String
......@@ -39,4 +41,6 @@ class ChatViewModel(private val eventRepo: ChatEventRepository,
= eventRepo.getHistoricChatEvents(chatId, from)
fun send(message: Message) = eventRepo.send(chatId, message)
fun getHttpsUrl(mxUrl: String) = mediaRepo.getHttpsMediaUrl(mxUrl)
}
\ No newline at end of file
......@@ -39,6 +39,7 @@ import io.reactivex.schedulers.Schedulers
import kotlinx.android.synthetic.main.fragment_start_username.*
import kotlinx.android.synthetic.main.fragment_start_username.view.*
import org.jetbrains.anko.sdk25.coroutines.onClick
import org.jetbrains.anko.sdk25.coroutines.textChangedListener
import org.matrix.androidsdk.rest.model.MatrixError
import java.net.SocketTimeoutException
......
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2018 Nathan van Beelen
Copyright (C) 2018 Wilko Manger
This file is part of Pattle.
Pattle is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Pattle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with Pattle. If not, see <https://www.gnu.org/licenses/>.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<!-- Background color -->
<solid android:color="@color/colorInfoUnderlay"/>
<!-- Corner radius -->
<corners android:topLeftRadius="@dimen/chat_message_corner_radius"
android:topRightRadius="@dimen/chat_message_corner_radius"
android:bottomLeftRadius="@dimen/chat_message_corner_radius"
android:bottomRightRadius="@dimen/chat_message_corner_radius"/>
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2018 Nathan van Beelen
Copyright (C) 2018 Wilko Manger
This file is part of Pattle.
Pattle is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Pattle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with Pattle. If not, see <https://www.gnu.org/licenses/>.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<!-- Background color -->
<solid android:color="@color/colorInfoUnderlay"/>
<!-- Corner radius -->
<corners android:topLeftRadius="0dp"
android:topRightRadius="@dimen/chat_message_corner_radius"
android:bottomLeftRadius="@dimen/chat_message_corner_radius"
android:bottomRightRadius="@dimen/chat_message_corner_radius"/>
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2018 Nathan van Beelen
Copyright (C) 2018 Wilko Manger
This file is part of Pattle.
Pattle is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Pattle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with Pattle. If not, see <https://www.gnu.org/licenses/>.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<!-- Background color -->
<solid android:color="@color/colorInfoUnderlay"/>
<!-- Corner radius -->
<corners android:topLeftRadius="@dimen/chat_message_corner_radius"
android:topRightRadius="@dimen/chat_message_corner_radius"
android:bottomLeftRadius="@dimen/chat_message_corner_radius"
android:bottomRightRadius="0dp"/>
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2018 Wilko Manger
This file is part of Pattle.
Pattle is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Pattle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with Pattle. If not, see <https://www.gnu.org/licenses/>.
-->
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="16dp"
android:paddingStart="16dp"
android:paddingRight="@dimen/chat_message_margin"
android:paddingEnd="@dimen/chat_message_margin"
android:paddingBottom="4dp"
android:paddingTop="4dp"
android:clipToPadding="false">
<com.github.siyamed.shapeimageview.mask.PorterShapeImageView
android:id="@+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:siShape="@drawable/chat_recv_message"
app:siSquare="true" />
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/sender"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginTop="8dp"
android:background="@drawable/chat_info_underlay_recv_name"
android:padding="@dimen/info_underlay_padding"
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
android:textColor="@android:color/primary_text_dark"
android:textSize="@dimen/chat_message_sender_text_size"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:layout_marginBottom="8dp"
android:padding="@dimen/info_underlay_padding"
android:text="12:00 AM"
android:textColor="@android:color/primary_text_dark"
android:textSize="@dimen/chat_message_time_text_size"
android:background="@drawable/chat_info_underlay"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2018 Nathan van Beelen
Copyright (C) 2018 Wilko Manger
This file is part of Pattle.
Pattle is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Pattle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with Pattle. If not, see <https://www.gnu.org/licenses/>.
-->
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="@dimen/chat_message_margin"
android:paddingStart="@dimen/chat_message_margin"
android:paddingRight="16dp"
android:paddingEnd="16dp"
android:paddingBottom="4dp"
android:paddingTop="4dp"
android:clipToPadding="false">
<com.github.siyamed.shapeimageview.mask.PorterShapeImageView
android:id="@+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:siShape="@drawable/chat_sent_message"
app:siSquare="true" />
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:layout_marginBottom="8dp"
android:background="@drawable/chat_info_underlay_sent_time"
android:padding="@dimen/info_underlay_padding"
android:text="12:00 AM"
android:textColor="@android:color/primary_text_dark"
android:textSize="@dimen/chat_message_time_text_size"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
......@@ -39,4 +39,6 @@ along with Pattle. If not, see <https://www.gnu.org/licenses/>.
<color name="colorDividerLight">#1f000000</color>
<color name="colorTextLight">#FFFFFF</color>
<color name="colorInfoUnderlay">#7F000000</color>
</resources>
......@@ -35,6 +35,7 @@ along with Pattle. If not, see <https://www.gnu.org/licenses/>.
</dimen>
<dimen name="date_header_top_margin">32dp</dimen>
<dimen name="date_header_bottom_margin">8dp</dimen>
<dimen name="info_underlay_padding">6dp</dimen>
<!-- ChatOverview -->
<dimen name="chat_overview_item_margin">16dp</dimen>
......
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