diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/MessageAdapter.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/MessageAdapter.kt index e1b60e6b4421a188c07a3cb241a14b6675929a49..5814c3932753219eca14f31dd59ba881c1c687c0 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/MessageAdapter.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/MessageAdapter.kt @@ -104,13 +104,15 @@ class MessageAdapter( expansionListener: ((QuasselDatabase.DatabaseMessage) -> Unit)? = null ) : RecyclerView.ViewHolder(itemView) { @BindView(R.id.time) - lateinit var time: TextView + @JvmField + var time: TextView? = null @BindView(R.id.content) lateinit var content: TextView @BindView(R.id.markerline) - lateinit var markerline: View + @JvmField + var markerline: View? = null private var message: FormattedMessage? = null @@ -142,9 +144,9 @@ class MessageAdapter( fun bind(message: FormattedMessage) { this.message = message - time.text = message.time + time?.text = message.time content.text = message.content - markerline.visibleIf(message.isMarkerLine) + markerline?.visibleIf(message.isMarkerLine) this.itemView.isSelected = message.isSelected } diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/MessageListFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/MessageListFragment.kt index e6a55ceef714c35d98d5aa400963834639663570..91ae8ed45421cf582e065e17dc83a84899431f4b 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/MessageListFragment.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/MessageListFragment.kt @@ -20,6 +20,7 @@ import de.kuschku.libquassel.quassel.syncables.BufferSyncer import de.kuschku.libquassel.util.helpers.value import de.kuschku.quasseldroid.R import de.kuschku.quasseldroid.persistence.QuasselDatabase +import de.kuschku.quasseldroid.persistence.findByBufferIdPagedWithDayChange import de.kuschku.quasseldroid.settings.AppearanceSettings import de.kuschku.quasseldroid.settings.BacklogSettings import de.kuschku.quasseldroid.util.helper.* @@ -126,8 +127,7 @@ class MessageListFragment : ServiceBoundFragment() { private val boundaryCallback = object : PagedList.BoundaryCallback<DisplayMessage>() { override fun onItemAtFrontLoaded(itemAtFront: DisplayMessage) = Unit - override fun onItemAtEndLoaded(itemAtEnd: DisplayMessage) = - loadMore(lastMessageId = itemAtEnd.content.messageId) + override fun onItemAtEndLoaded(itemAtEnd: DisplayMessage) = loadMore() } override fun onCreateView( @@ -162,6 +162,9 @@ class MessageListFragment : ServiceBoundFragment() { messageList.layoutManager = linearLayoutManager messageList.itemAnimator = null messageList.setItemViewCacheSize(20) + messageList.addItemDecoration(object : RecyclerView.ItemDecoration() { + + }) var isScrolling = false messageList.addOnScrollListener( @@ -189,8 +192,9 @@ class MessageListFragment : ServiceBoundFragment() { viewModel.markerLine) .toLiveData().switchMapNotNull { (buffer, selected, expanded, markerLine) -> database.filtered().listen(accountId, buffer).switchMapNotNull { filtered -> + LivePagedListBuilder( - database.message().findByBufferIdPaged(buffer, filtered).map { + database.message().findByBufferIdPagedWithDayChange(buffer, filtered).map { DisplayMessage( content = it, isSelected = selected.contains(it.messageId), @@ -307,4 +311,4 @@ class MessageListFragment : ServiceBoundFragment() { } } -} \ No newline at end of file +} diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/QuasselMessageRenderer.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/QuasselMessageRenderer.kt index 5aff45b926bc88f70f1786acd95f323532df9e80..597b8246c1b1cb4094a206d777d005536796bb3d 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/QuasselMessageRenderer.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/QuasselMessageRenderer.kt @@ -26,6 +26,7 @@ import de.kuschku.quasseldroid.viewmodel.data.FormattedMessage import org.intellij.lang.annotations.Language import org.threeten.bp.ZoneId import org.threeten.bp.format.DateTimeFormatter +import org.threeten.bp.format.FormatStyle import javax.inject.Inject class QuasselMessageRenderer @Inject constructor( @@ -36,6 +37,8 @@ class QuasselMessageRenderer @Inject constructor( timePattern(appearanceSettings.showSeconds, appearanceSettings.use24hClock) ) + private val dateFormatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM) + val monospaceItalic = Typeface.create(Typeface.MONOSPACE, Typeface.ITALIC) private fun timePattern(showSeconds: Boolean, @@ -52,14 +55,15 @@ class QuasselMessageRenderer @Inject constructor( private val zoneId = ZoneId.systemDefault() override fun layout(type: Message_Type?, hasHighlight: Boolean) = when (type) { - Notice -> R.layout.widget_chatmessage_notice - Server -> R.layout.widget_chatmessage_server - Error -> R.layout.widget_chatmessage_error - Action -> R.layout.widget_chatmessage_action - Plain -> R.layout.widget_chatmessage_plain - Nick, Mode, Join, Part, Quit, Kick, Kill, Info, DayChange, Topic, NetsplitJoin, NetsplitQuit, - Invite -> R.layout.widget_chatmessage_info - else -> R.layout.widget_chatmessage_placeholder + Notice -> R.layout.widget_chatmessage_notice + Server -> R.layout.widget_chatmessage_server + Error -> R.layout.widget_chatmessage_error + Action -> R.layout.widget_chatmessage_action + Plain -> R.layout.widget_chatmessage_plain + Nick, Mode, Join, Part, Quit, Kick, Kill, Info, Topic, NetsplitJoin, NetsplitQuit, + Invite -> R.layout.widget_chatmessage_info + DayChange -> R.layout.widget_chatmessage_daychange + else -> R.layout.widget_chatmessage_placeholder } override fun init(viewHolder: MessageAdapter.QuasselMessageViewHolder, @@ -69,7 +73,7 @@ class QuasselMessageRenderer @Inject constructor( viewHolder.itemView.context.theme.styledAttributes( R.attr.colorForegroundHighlight, R.attr.colorBackgroundHighlight ) { - viewHolder.time.setTextColor(getColor(0, 0)) + viewHolder.time?.setTextColor(getColor(0, 0)) viewHolder.content.setTextColor(getColor(0, 0)) viewHolder.itemView.setBackgroundColor(getColor(1, 0)) } @@ -83,7 +87,7 @@ class QuasselMessageRenderer @Inject constructor( } } val textSize = appearanceSettings.textSize.toFloat() - viewHolder.time.setTextSize(TypedValue.COMPLEX_UNIT_SP, textSize) + viewHolder.time?.setTextSize(TypedValue.COMPLEX_UNIT_SP, textSize) viewHolder.content.setTextSize(TypedValue.COMPLEX_UNIT_SP, textSize) } @@ -322,7 +326,7 @@ class QuasselMessageRenderer @Inject constructor( } Message_Type.Server, Message_Type.Info, - Message_Type.Error -> FormattedMessage( + Message_Type.Error -> FormattedMessage( message.content.messageId, timeFormatter.format(message.content.time.atZone(zoneId)), formatContent(context, message.content.content, highlight), @@ -330,7 +334,7 @@ class QuasselMessageRenderer @Inject constructor( isExpanded = message.isExpanded, isSelected = message.isSelected ) - Message_Type.Topic -> FormattedMessage( + Message_Type.Topic -> FormattedMessage( message.content.messageId, timeFormatter.format(message.content.time.atZone(zoneId)), formatContent(context, message.content.content, highlight), @@ -338,7 +342,15 @@ class QuasselMessageRenderer @Inject constructor( isExpanded = message.isExpanded, isSelected = message.isSelected ) - else -> FormattedMessage( + DayChange -> FormattedMessage( + message.content.messageId, + "", + dateFormatter.format(message.content.time.atZone(zoneId)), + isMarkerLine = false, + isExpanded = false, + isSelected = false + ) + else -> FormattedMessage( message.content.messageId, timeFormatter.format(message.content.time.atZone(zoneId)), SpanFormatter.format( diff --git a/app/src/main/res/layout/widget_chatmessage_daychange.xml b/app/src/main/res/layout/widget_chatmessage_daychange.xml new file mode 100644 index 0000000000000000000000000000000000000000..9b5338654139249f7c0eeed19654e4508b9869fe --- /dev/null +++ b/app/src/main/res/layout/widget_chatmessage_daychange.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="?attr/backgroundMenuItem" + android:orientation="vertical" + android:textAppearance="?android:attr/textAppearanceListItemSmall"> + + <View + android:layout_width="match_parent" + android:layout_height="1dp" + android:background="?colorDivider" /> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:paddingBottom="@dimen/message_vertical" + android:paddingEnd="@dimen/message_horizontal" + android:paddingLeft="@dimen/message_horizontal" + android:paddingRight="@dimen/message_horizontal" + android:paddingStart="@dimen/message_horizontal" + android:paddingTop="@dimen/message_vertical"> + + <TextView + android:id="@+id/content" + android:layout_width="0dip" + android:layout_height="wrap_content" + android:layout_weight="1" + android:gravity="center" + android:textColor="?attr/colorForeground" + android:textStyle="bold" + tools:text="27.03.2018" /> + </LinearLayout> +</LinearLayout> \ No newline at end of file diff --git a/persistence/src/main/java/de/kuschku/quasseldroid/persistence/QuasselDatabase.kt b/persistence/src/main/java/de/kuschku/quasseldroid/persistence/QuasselDatabase.kt index 9f364514211dcd676b9a8fcf2aed1cbc1165bc2d..274e2465f1707152a5126b9ea01bf3ae9e60eaf6 100644 --- a/persistence/src/main/java/de/kuschku/quasseldroid/persistence/QuasselDatabase.kt +++ b/persistence/src/main/java/de/kuschku/quasseldroid/persistence/QuasselDatabase.kt @@ -2,7 +2,9 @@ package de.kuschku.quasseldroid.persistence import android.arch.lifecycle.LiveData import android.arch.paging.DataSource +import android.arch.persistence.db.SimpleSQLiteQuery import android.arch.persistence.db.SupportSQLiteDatabase +import android.arch.persistence.db.SupportSQLiteQuery import android.arch.persistence.room.* import android.arch.persistence.room.migration.Migration import android.content.Context @@ -60,6 +62,15 @@ abstract class QuasselDatabase : RoomDatabase() { ) fun findByBufferIdPaged(bufferId: Int, type: Int): DataSource.Factory<Int, DatabaseMessage> + @RawQuery(observedEntities = [DatabaseMessage::class]) + fun findMessagesRawPaged(query: SupportSQLiteQuery): DataSource.Factory<Int, DatabaseMessage> + + @RawQuery + fun findDaysRaw(query: SupportSQLiteQuery): List<Instant> + + @RawQuery + fun findMessagesRaw(query: SupportSQLiteQuery): List<DatabaseMessage> + @Query("SELECT * FROM message WHERE bufferId = :bufferId ORDER BY messageId DESC LIMIT 1") fun findLastByBufferId(bufferId: Int): DatabaseMessage? @@ -163,4 +174,72 @@ fun QuasselDatabase.MessageDao.clearMessages( idRange: kotlin.ranges.IntRange ) { this.clearMessages(bufferId, idRange.first, idRange.last) -} \ No newline at end of file +} + +fun QuasselDatabase.MessageDao.findByBufferIdPagedWithDayChange(bufferId: Int, type: Int) = + this.findMessagesRawPaged(SimpleSQLiteQuery(""" +SELECT t.* +FROM + ( + SELECT + messageId, + time, + type, + flag, + bufferId, + sender, + senderPrefixes, + content + FROM message + WHERE bufferId = ? + AND type & ~? > 0 + UNION ALL + SELECT DISTINCT + strftime('%s', date(datetime(time / 1000, 'unixepoch')), 'utc') * -1000 AS messageId, + strftime('%s', date(datetime(time / 1000, 'unixepoch')), 'utc') * 1000 AS time, + 8192 AS type, + 0 AS flag, + ? AS bufferId, + '' AS sender, + '' AS senderPrefixes, + '' AS content + FROM message + WHERE bufferId = ? + AND type & ~? > 0 + ) t +ORDER BY time DESC + """, arrayOf(bufferId, type, bufferId, bufferId, type))) + +fun QuasselDatabase.MessageDao.findByBufferIdWithDayChange(bufferId: Int, type: Int) = + this.findMessagesRaw(SimpleSQLiteQuery(""" +SELECT t.* +FROM + ( + SELECT + messageId, + time, + type, + flag, + bufferId, + sender, + senderPrefixes, + content + FROM message + WHERE bufferId = ? + AND type & ~? > 0 + UNION ALL + SELECT DISTINCT + strftime('%s', date(datetime(time / 1000, 'unixepoch'))) * -1000 AS messageId, + strftime('%s', date(datetime(time / 1000, 'unixepoch'))) * 1000 AS time, + 8192 AS type, + 0 AS flag, + ? AS bufferId, + '' AS sender, + '' AS senderPrefixes, + '' AS content + FROM message + WHERE bufferId = ? + AND type & ~? > 0 + ) t +ORDER BY time DESC + """, arrayOf(bufferId, type, bufferId, bufferId, type))) \ No newline at end of file