Show date in ChatAdapter
This commit is contained in:
parent
05a9f5881d
commit
e06044acb8
|
@ -1,6 +1,7 @@
|
||||||
package sushi.hardcore.aira
|
package sushi.hardcore.aira
|
||||||
|
|
||||||
import sushi.hardcore.aira.background_service.Protocol
|
import sushi.hardcore.aira.background_service.Protocol
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
class ChatItem(val outgoing: Boolean, val timestamp: Long, val data: ByteArray) {
|
class ChatItem(val outgoing: Boolean, val timestamp: Long, val data: ByteArray) {
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -14,4 +15,16 @@ class ChatItem(val outgoing: Boolean, val timestamp: Long, val data: ByteArray)
|
||||||
} else {
|
} else {
|
||||||
if (outgoing) OUTGOING_FILE else INCOMING_FILE
|
if (outgoing) OUTGOING_FILE else INCOMING_FILE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val calendar: Calendar by lazy {
|
||||||
|
Calendar.getInstance().apply {
|
||||||
|
time = Date(timestamp * 1000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val year by lazy {
|
||||||
|
calendar.get(Calendar.YEAR)
|
||||||
|
}
|
||||||
|
val dayOfYear by lazy {
|
||||||
|
calendar.get(Calendar.DAY_OF_YEAR)
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -19,6 +19,8 @@ import androidx.recyclerview.widget.RecyclerView
|
||||||
import sushi.hardcore.aira.ChatItem
|
import sushi.hardcore.aira.ChatItem
|
||||||
import sushi.hardcore.aira.R
|
import sushi.hardcore.aira.R
|
||||||
import sushi.hardcore.aira.utils.StringUtils
|
import sushi.hardcore.aira.utils.StringUtils
|
||||||
|
import sushi.hardcore.aira.utils.TimeUtils
|
||||||
|
import java.text.DateFormat
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class ChatAdapter(
|
class ChatAdapter(
|
||||||
|
@ -29,7 +31,7 @@ class ChatAdapter(
|
||||||
companion object {
|
companion object {
|
||||||
const val BUBBLE_MARGIN = 150
|
const val BUBBLE_MARGIN = 150
|
||||||
const val CONTAINER_PADDING = 40
|
const val CONTAINER_PADDING = 40
|
||||||
const val BUBBLE_VERTICAL_MARGIN = 40
|
const val BUBBLE_VERTICAL_MARGIN = 30
|
||||||
const val BUBBLE_CORNER_NORMAL = 50f
|
const val BUBBLE_CORNER_NORMAL = 50f
|
||||||
const val BUBBLE_CORNER_ARROW = 20f
|
const val BUBBLE_CORNER_ARROW = 20f
|
||||||
}
|
}
|
||||||
|
@ -48,6 +50,7 @@ class ChatAdapter(
|
||||||
fun newLoadedMessage(chatItem: ChatItem) {
|
fun newLoadedMessage(chatItem: ChatItem) {
|
||||||
chatItems.add(0, chatItem)
|
chatItems.add(0, chatItem)
|
||||||
notifyItemInserted(0)
|
notifyItemInserted(0)
|
||||||
|
notifyItemChanged(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun clear() {
|
fun clear() {
|
||||||
|
@ -55,10 +58,20 @@ class ChatAdapter(
|
||||||
notifyDataSetChanged()
|
notifyDataSetChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
internal open class BubbleViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) {
|
internal open class BubbleViewHolder(private val context: Context, itemView: View): RecyclerView.ViewHolder(itemView) {
|
||||||
private fun generateCorners(topLeft: Float, topRight: Float, bottomRight: Float, bottomLeft: Float): FloatArray {
|
private fun generateCorners(topLeft: Float, topRight: Float, bottomRight: Float, bottomLeft: Float): FloatArray {
|
||||||
return floatArrayOf(topLeft, topLeft, topRight, topRight, bottomRight, bottomRight, bottomLeft, bottomLeft)
|
return floatArrayOf(topLeft, topLeft, topRight, topRight, bottomRight, bottomRight, bottomLeft, bottomLeft)
|
||||||
}
|
}
|
||||||
|
protected fun setBubbleContent(layoutResource: Int) {
|
||||||
|
//if the view was recycled bubble_content will be null and we don't need to inflate a layout
|
||||||
|
itemView.findViewById<View>(R.id.bubble_content)?.let { placeHolder ->
|
||||||
|
val parent = placeHolder.parent as ViewGroup
|
||||||
|
val index = parent.indexOfChild(placeHolder)
|
||||||
|
parent.removeView(placeHolder)
|
||||||
|
val bubbleContent = LayoutInflater.from(context).inflate(layoutResource, parent, false)
|
||||||
|
parent.addView(bubbleContent, index)
|
||||||
|
}
|
||||||
|
}
|
||||||
protected fun configureContainer(outgoing: Boolean, previousOutgoing: Boolean?, isLast: Boolean) {
|
protected fun configureContainer(outgoing: Boolean, previousOutgoing: Boolean?, isLast: Boolean) {
|
||||||
val layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
|
val layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
|
||||||
if (previousOutgoing != null && previousOutgoing != outgoing) {
|
if (previousOutgoing != null && previousOutgoing != outgoing) {
|
||||||
|
@ -74,10 +87,10 @@ class ChatAdapter(
|
||||||
itemView.updatePadding(left = CONTAINER_PADDING)
|
itemView.updatePadding(left = CONTAINER_PADDING)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
protected fun configureBubble(context: Context, outgoing: Boolean, previousOutgoing: Boolean?, nextOutgoing: Boolean?) {
|
protected fun configureBubble(chatItem: ChatItem, previousChatItem: ChatItem?, nextChatItem: ChatItem?) {
|
||||||
val bubble = itemView.findViewById<LinearLayout>(R.id.bubble_content)
|
val bubble = itemView.findViewById<LinearLayout>(R.id.bubble)
|
||||||
bubble.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT).apply {
|
bubble.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT).apply {
|
||||||
gravity = if (outgoing) {
|
gravity = if (chatItem.outgoing) {
|
||||||
marginStart = BUBBLE_MARGIN
|
marginStart = BUBBLE_MARGIN
|
||||||
Gravity.END
|
Gravity.END
|
||||||
} else {
|
} else {
|
||||||
|
@ -86,7 +99,7 @@ class ChatAdapter(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val backgroundDrawable = GradientDrawable()
|
val backgroundDrawable = GradientDrawable()
|
||||||
backgroundDrawable.setColor(ContextCompat.getColor(context, if (outgoing) {
|
backgroundDrawable.setColor(ContextCompat.getColor(context, if (chatItem.outgoing) {
|
||||||
R.color.bubbleBackground
|
R.color.bubbleBackground
|
||||||
} else {
|
} else {
|
||||||
R.color.incomingBubbleBackground
|
R.color.incomingBubbleBackground
|
||||||
|
@ -95,36 +108,50 @@ class ChatAdapter(
|
||||||
var topRight = BUBBLE_CORNER_NORMAL
|
var topRight = BUBBLE_CORNER_NORMAL
|
||||||
var bottomRight = BUBBLE_CORNER_NORMAL
|
var bottomRight = BUBBLE_CORNER_NORMAL
|
||||||
var bottomLeft = BUBBLE_CORNER_NORMAL
|
var bottomLeft = BUBBLE_CORNER_NORMAL
|
||||||
if (nextOutgoing == outgoing) {
|
if (previousChatItem?.outgoing == chatItem.outgoing && TimeUtils.isInTheSameDay(chatItem, previousChatItem)) {
|
||||||
if (outgoing) {
|
if (chatItem.outgoing) {
|
||||||
bottomRight = BUBBLE_CORNER_ARROW
|
|
||||||
} else {
|
|
||||||
bottomLeft = BUBBLE_CORNER_ARROW
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (previousOutgoing == outgoing) {
|
|
||||||
if (outgoing) {
|
|
||||||
topRight = BUBBLE_CORNER_ARROW
|
topRight = BUBBLE_CORNER_ARROW
|
||||||
} else {
|
} else {
|
||||||
topLeft = BUBBLE_CORNER_ARROW
|
topLeft = BUBBLE_CORNER_ARROW
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (nextChatItem?.outgoing == chatItem.outgoing && TimeUtils.isInTheSameDay(chatItem, nextChatItem)) {
|
||||||
|
if (chatItem.outgoing) {
|
||||||
|
bottomRight = BUBBLE_CORNER_ARROW
|
||||||
|
} else {
|
||||||
|
bottomLeft = BUBBLE_CORNER_ARROW
|
||||||
|
}
|
||||||
|
}
|
||||||
backgroundDrawable.cornerRadii = generateCorners(topLeft, topRight, bottomRight, bottomLeft)
|
backgroundDrawable.cornerRadii = generateCorners(topLeft, topRight, bottomRight, bottomLeft)
|
||||||
bubble.background = backgroundDrawable
|
bubble.background = backgroundDrawable
|
||||||
}
|
}
|
||||||
protected fun setTimestamp(chatItem: ChatItem): TextView {
|
protected fun showDateAndTime(chatItem: ChatItem, previousChatItem: ChatItem?) {
|
||||||
val calendar = Calendar.getInstance().apply {
|
val showDate = if (previousChatItem == null) {
|
||||||
time = Date(chatItem.timestamp * 1000)
|
true
|
||||||
|
} else {
|
||||||
|
!TimeUtils.isInTheSameDay(chatItem, previousChatItem)
|
||||||
}
|
}
|
||||||
val textView = itemView.findViewById<TextView>(R.id.timestamp)
|
val textDate = itemView.findViewById<TextView>(R.id.text_date)
|
||||||
|
textDate.visibility = if (showDate) {
|
||||||
|
textDate.text = DateFormat.getDateInstance().format(chatItem.calendar.time)
|
||||||
|
View.VISIBLE
|
||||||
|
} else {
|
||||||
|
View.GONE
|
||||||
|
}
|
||||||
|
val textHour = itemView.findViewById<TextView>(R.id.text_hour)
|
||||||
@SuppressLint("SetTextI18n")
|
@SuppressLint("SetTextI18n")
|
||||||
textView.text = StringUtils.toTwoDigits(calendar.get(Calendar.HOUR_OF_DAY))+":"+StringUtils.toTwoDigits(calendar.get(Calendar.MINUTE))
|
textHour.text = StringUtils.toTwoDigits(chatItem.calendar.get(Calendar.HOUR_OF_DAY))+":"+StringUtils.toTwoDigits(chatItem.calendar.get(Calendar.MINUTE))
|
||||||
return textView
|
textHour.setTextColor(ContextCompat.getColor(context, if (chatItem.outgoing) {
|
||||||
|
R.color.outgoingTimestamp
|
||||||
|
} else {
|
||||||
|
R.color.incomingTimestamp
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal open class MessageViewHolder(itemView: View): BubbleViewHolder(itemView) {
|
internal open class MessageViewHolder(context: Context, itemView: View): BubbleViewHolder(context, itemView) {
|
||||||
protected fun bindMessage(chatItem: ChatItem, outgoing: Boolean): TextView {
|
protected fun bindMessage(chatItem: ChatItem, outgoing: Boolean): TextView {
|
||||||
|
setBubbleContent(R.layout.message_bubble_content)
|
||||||
itemView.findViewById<TextView>(R.id.text_message).apply {
|
itemView.findViewById<TextView>(R.id.text_message).apply {
|
||||||
text = chatItem.data.sliceArray(1 until chatItem.data.size).decodeToString()
|
text = chatItem.data.sliceArray(1 until chatItem.data.size).decodeToString()
|
||||||
if (!outgoing) {
|
if (!outgoing) {
|
||||||
|
@ -135,34 +162,31 @@ class ChatAdapter(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class OutgoingMessageViewHolder(private val context: Context, itemView: View): MessageViewHolder(itemView) {
|
internal class OutgoingMessageViewHolder(context: Context, itemView: View): MessageViewHolder(context, itemView) {
|
||||||
fun bind(chatItem: ChatItem, previousOutgoing: Boolean?, nextOutgoing: Boolean?) {
|
fun bind(chatItem: ChatItem, previousChatItem: ChatItem?, nextChatItem: ChatItem?) {
|
||||||
setTimestamp(chatItem).apply {
|
|
||||||
setTextColor(ContextCompat.getColor(context, R.color.outgoingTimestamp))
|
|
||||||
}
|
|
||||||
configureBubble(context, true, previousOutgoing, nextOutgoing)
|
|
||||||
bindMessage(chatItem, true).apply {
|
bindMessage(chatItem, true).apply {
|
||||||
setLinkTextColor(ContextCompat.getColor(context, R.color.outgoingTextLink))
|
setLinkTextColor(ContextCompat.getColor(context, R.color.outgoingTextLink))
|
||||||
}
|
}
|
||||||
configureContainer(true, previousOutgoing, nextOutgoing == null)
|
showDateAndTime(chatItem, previousChatItem)
|
||||||
|
configureBubble(chatItem, previousChatItem, nextChatItem)
|
||||||
|
configureContainer(true, previousChatItem?.outgoing, nextChatItem == null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class IncomingMessageViewHolder(private val context: Context, itemView: View): MessageViewHolder(itemView) {
|
internal class IncomingMessageViewHolder(context: Context, itemView: View): MessageViewHolder(context, itemView) {
|
||||||
fun bind(chatItem: ChatItem, previousOutgoing: Boolean?, nextOutgoing: Boolean?) {
|
fun bind(chatItem: ChatItem, previousChatItem: ChatItem?, nextChatItem: ChatItem?) {
|
||||||
setTimestamp(chatItem).apply {
|
|
||||||
setTextColor(ContextCompat.getColor(context, R.color.incomingTimestamp))
|
|
||||||
}
|
|
||||||
configureBubble(context, false, previousOutgoing, nextOutgoing)
|
|
||||||
bindMessage(chatItem, false).apply {
|
bindMessage(chatItem, false).apply {
|
||||||
setLinkTextColor(ContextCompat.getColor(context, R.color.incomingTextLink))
|
setLinkTextColor(ContextCompat.getColor(context, R.color.incomingTextLink))
|
||||||
}
|
}
|
||||||
configureContainer(false, previousOutgoing, nextOutgoing == null)
|
showDateAndTime(chatItem, previousChatItem)
|
||||||
|
configureBubble(chatItem, previousChatItem, nextChatItem)
|
||||||
|
configureContainer(false, previousChatItem?.outgoing, nextChatItem == null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal open class FileViewHolder(itemView: View, private val onSavingFile: (filename: String, rawUuid: ByteArray) -> Unit): BubbleViewHolder(itemView) {
|
internal open class FileViewHolder(context: Context, itemView: View, private val onSavingFile: (filename: String, rawUuid: ByteArray) -> Unit): BubbleViewHolder(context, itemView) {
|
||||||
protected fun bindFile(chatItem: ChatItem, outgoing: Boolean) {
|
protected fun bindFile(chatItem: ChatItem, outgoing: Boolean) {
|
||||||
|
setBubbleContent(R.layout.file_bubble_content)
|
||||||
val filename = chatItem.data.sliceArray(17 until chatItem.data.size).decodeToString()
|
val filename = chatItem.data.sliceArray(17 until chatItem.data.size).decodeToString()
|
||||||
itemView.findViewById<TextView>(R.id.text_filename).apply {
|
itemView.findViewById<TextView>(R.id.text_filename).apply {
|
||||||
text = filename
|
text = filename
|
||||||
|
@ -176,38 +200,33 @@ class ChatAdapter(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class OutgoingFileViewHolder(private val context: Context, itemView: View, onSavingFile: (filename: String, rawUuid: ByteArray) -> Unit): FileViewHolder(itemView, onSavingFile) {
|
internal class OutgoingFileViewHolder(context: Context, itemView: View, onSavingFile: (filename: String, rawUuid: ByteArray) -> Unit): FileViewHolder(context, itemView, onSavingFile) {
|
||||||
fun bind(chatItem: ChatItem, previousOutgoing: Boolean?, nextOutgoing: Boolean?) {
|
fun bind(chatItem: ChatItem, previousChatItem: ChatItem?, nextChatItem: ChatItem?) {
|
||||||
setTimestamp(chatItem).apply {
|
|
||||||
setTextColor(ContextCompat.getColor(context, R.color.outgoingTimestamp))
|
|
||||||
}
|
|
||||||
bindFile(chatItem, true)
|
bindFile(chatItem, true)
|
||||||
configureBubble(context, true, previousOutgoing, nextOutgoing)
|
showDateAndTime(chatItem, previousChatItem)
|
||||||
configureContainer(true, previousOutgoing, nextOutgoing == null)
|
configureBubble(chatItem, previousChatItem, nextChatItem)
|
||||||
|
configureContainer(true, previousChatItem?.outgoing, nextChatItem == null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class IncomingFileViewHolder(private val context: Context, itemView: View, onSavingFile: (filename: String, rawUuid: ByteArray) -> Unit): FileViewHolder(itemView, onSavingFile) {
|
internal class IncomingFileViewHolder(context: Context, itemView: View, onSavingFile: (filename: String, rawUuid: ByteArray) -> Unit): FileViewHolder(context, itemView, onSavingFile) {
|
||||||
fun bind(chatItem: ChatItem, previousOutgoing: Boolean?, nextOutgoing: Boolean?) {
|
fun bind(chatItem: ChatItem, previousChatItem: ChatItem?, nextChatItem: ChatItem?) {
|
||||||
setTimestamp(chatItem).apply {
|
|
||||||
setTextColor(ContextCompat.getColor(context, R.color.incomingTimestamp))
|
|
||||||
}
|
|
||||||
bindFile(chatItem, false)
|
bindFile(chatItem, false)
|
||||||
configureBubble(context, false, previousOutgoing, nextOutgoing)
|
showDateAndTime(chatItem, previousChatItem)
|
||||||
configureContainer(false, previousOutgoing, nextOutgoing == null)
|
configureBubble(chatItem, previousChatItem, nextChatItem)
|
||||||
|
configureContainer(false, previousChatItem?.outgoing, nextChatItem == null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||||
|
val view = inflater.inflate(R.layout.adapter_chat_item, parent, false)
|
||||||
return if (viewType == ChatItem.OUTGOING_MESSAGE || viewType == ChatItem.INCOMING_MESSAGE) {
|
return if (viewType == ChatItem.OUTGOING_MESSAGE || viewType == ChatItem.INCOMING_MESSAGE) {
|
||||||
val view = inflater.inflate(R.layout.adapter_chat_message, parent, false)
|
|
||||||
if (viewType == ChatItem.OUTGOING_MESSAGE) {
|
if (viewType == ChatItem.OUTGOING_MESSAGE) {
|
||||||
OutgoingMessageViewHolder(context, view)
|
OutgoingMessageViewHolder(context, view)
|
||||||
} else {
|
} else {
|
||||||
IncomingMessageViewHolder(context, view)
|
IncomingMessageViewHolder(context, view)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val view = inflater.inflate(R.layout.adapter_chat_file, parent, false)
|
|
||||||
if (viewType == ChatItem.OUTGOING_FILE) {
|
if (viewType == ChatItem.OUTGOING_FILE) {
|
||||||
OutgoingFileViewHolder(context, view, onSavingFile)
|
OutgoingFileViewHolder(context, view, onSavingFile)
|
||||||
} else {
|
} else {
|
||||||
|
@ -218,21 +237,21 @@ class ChatAdapter(
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||||
val chatItem = chatItems[position]
|
val chatItem = chatItems[position]
|
||||||
val previousOutgoing = if (position == 0) {
|
val previousChatItem = if (position == 0) {
|
||||||
null
|
null
|
||||||
} else {
|
} else {
|
||||||
chatItems[position-1].outgoing
|
chatItems[position-1]
|
||||||
}
|
}
|
||||||
val nextOutgoing = if (position == chatItems.size - 1) {
|
val nextChatItem = if (position == chatItems.size - 1) {
|
||||||
null
|
null
|
||||||
} else {
|
} else {
|
||||||
chatItems[position+1].outgoing
|
chatItems[position+1]
|
||||||
}
|
}
|
||||||
when (chatItem.itemType) {
|
when (chatItem.itemType) {
|
||||||
ChatItem.OUTGOING_MESSAGE -> (holder as OutgoingMessageViewHolder).bind(chatItem, previousOutgoing, nextOutgoing)
|
ChatItem.OUTGOING_MESSAGE -> (holder as OutgoingMessageViewHolder).bind(chatItem, previousChatItem, nextChatItem)
|
||||||
ChatItem.INCOMING_MESSAGE -> (holder as IncomingMessageViewHolder).bind(chatItem, previousOutgoing, nextOutgoing)
|
ChatItem.INCOMING_MESSAGE -> (holder as IncomingMessageViewHolder).bind(chatItem, previousChatItem, nextChatItem)
|
||||||
ChatItem.OUTGOING_FILE -> (holder as OutgoingFileViewHolder).bind(chatItem, previousOutgoing, nextOutgoing)
|
ChatItem.OUTGOING_FILE -> (holder as OutgoingFileViewHolder).bind(chatItem, previousChatItem, nextChatItem)
|
||||||
ChatItem.INCOMING_FILE -> (holder as IncomingFileViewHolder).bind(chatItem, previousOutgoing, nextOutgoing)
|
ChatItem.INCOMING_FILE -> (holder as IncomingFileViewHolder).bind(chatItem, previousChatItem, nextChatItem)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
package sushi.hardcore.aira.utils
|
package sushi.hardcore.aira.utils
|
||||||
|
|
||||||
|
import sushi.hardcore.aira.ChatItem
|
||||||
|
|
||||||
object TimeUtils {
|
object TimeUtils {
|
||||||
fun getTimestamp(): Long {
|
fun getTimestamp(): Long {
|
||||||
return System.currentTimeMillis()/1000
|
return System.currentTimeMillis()/1000
|
||||||
}
|
}
|
||||||
|
fun isInTheSameDay(first: ChatItem, second: ChatItem): Boolean {
|
||||||
|
return first.year == second.year && first.dayOfYear == second.dayOfYear
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,46 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:orientation="vertical" android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:paddingVertical="1dp">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/bubble_content"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical"
|
|
||||||
style="@style/Bubble">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:gravity="center_vertical">
|
|
||||||
|
|
||||||
<ImageButton
|
|
||||||
android:id="@+id/button_save"
|
|
||||||
android:layout_width="@dimen/image_button_size"
|
|
||||||
android:layout_height="@dimen/image_button_size"
|
|
||||||
android:src="@drawable/ic_save"
|
|
||||||
android:scaleType="fitXY"
|
|
||||||
android:background="@color/transparent"
|
|
||||||
android:layout_marginEnd="8dp"
|
|
||||||
android:contentDescription="@string/download" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/text_filename"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:textIsSelectable="true"
|
|
||||||
android:textColor="@color/messageTextColor"/>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/timestamp"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:textSize="12sp"/>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
|
@ -4,23 +4,27 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:paddingVertical="1dp">
|
android:paddingVertical="1dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/text_date"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_horizontal"
|
||||||
|
android:visibility="gone"/>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/bubble_content"
|
android:id="@+id/bubble"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
style="@style/Bubble">
|
style="@style/Bubble">
|
||||||
|
|
||||||
<TextView
|
<View
|
||||||
android:id="@+id/text_message"
|
android:id="@+id/bubble_content"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"/>
|
||||||
android:textIsSelectable="true"
|
|
||||||
android:autoLink="all"
|
|
||||||
android:textColor="@color/messageTextColor"/>
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/timestamp"
|
android:id="@+id/text_hour"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:textSize="12sp"/>
|
android:textSize="12sp"/>
|
|
@ -0,0 +1,24 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center_vertical">
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/button_save"
|
||||||
|
android:layout_width="@dimen/image_button_size"
|
||||||
|
android:layout_height="@dimen/image_button_size"
|
||||||
|
android:src="@drawable/ic_save"
|
||||||
|
android:scaleType="fitXY"
|
||||||
|
android:background="@color/transparent"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:contentDescription="@string/download" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/text_filename"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textIsSelectable="true"
|
||||||
|
android:textColor="@color/messageTextColor"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/text_message"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:autoLink="all"
|
||||||
|
android:textColor="@color/messageTextColor"
|
||||||
|
android:textIsSelectable="true"/>
|
Loading…
Reference in New Issue