mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2024-11-28 22:18:46 +03:00
Floating date
Closes https://github.com/SchildiChat/SchildiChat-android/issues/41 Change-Id: I0eb9c6c3800309be40a1f5bc0c4420bd4066c098
This commit is contained in:
parent
85a26ae8be
commit
a96d27cb81
9 changed files with 326 additions and 3 deletions
|
@ -9,6 +9,7 @@ Here you can find some extra features and changes compared to Element Android (w
|
||||||
- Possibility to select themes for both light and dark system mode individually
|
- Possibility to select themes for both light and dark system mode individually
|
||||||
- [UnifiedPush](https://unifiedpush.org/) support
|
- [UnifiedPush](https://unifiedpush.org/) support
|
||||||
- "Easy mode" which disables public room functionality
|
- "Easy mode" which disables public room functionality
|
||||||
|
- Floating date
|
||||||
- Setting for room previews: show all events, hide membership changes, hide membership changes and reactions (individual settings for direct chats and groups)
|
- Setting for room previews: show all events, hide membership changes, hide membership changes and reactions (individual settings for direct chats and groups)
|
||||||
- More prominent unread counter for chats in the room overview (bigger, different placement, more noticeable color in SchildiChat designs)
|
- More prominent unread counter for chats in the room overview (bigger, different placement, more noticeable color in SchildiChat designs)
|
||||||
- Mark chats as unread ([MSC2867](https://github.com/matrix-org/matrix-spec-proposals/pull/2867), only works with compatible clients (SchildiChat, FluffyChat))
|
- Mark chats as unread ([MSC2867](https://github.com/matrix-org/matrix-spec-proposals/pull/2867), only works with compatible clients (SchildiChat, FluffyChat))
|
||||||
|
|
|
@ -0,0 +1,210 @@
|
||||||
|
package de.spiritcroc.recyclerview
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Source: https://gist.github.com/jonasbark/f1e1373705cfe8f6a7036763f7326f7c
|
||||||
|
* Modified to
|
||||||
|
* - make isHeader() abstract, so we don't need a predefined list of header ids
|
||||||
|
* - not require EpoxyRecyclerView
|
||||||
|
* - work with reverse layouts
|
||||||
|
* - hide the currently overlaid header for a smoother animation without duplicate headers
|
||||||
|
*/
|
||||||
|
|
||||||
|
import android.graphics.Canvas
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.airbnb.epoxy.EpoxyController
|
||||||
|
import org.matrix.android.sdk.api.extensions.orFalse
|
||||||
|
|
||||||
|
abstract class StickyHeaderItemDecoration(
|
||||||
|
private val epoxyController: EpoxyController,
|
||||||
|
private val reverse: Boolean = false
|
||||||
|
) : RecyclerView.ItemDecoration() {
|
||||||
|
private var mStickyHeaderHeight: Int = 0
|
||||||
|
|
||||||
|
private var lastHeaderPos: Int? = null
|
||||||
|
|
||||||
|
override fun onDrawOver(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
|
||||||
|
super.onDrawOver(c, parent, state)
|
||||||
|
|
||||||
|
if (parent.childCount == 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val topChild = if (reverse) {
|
||||||
|
parent.getChildAt(parent.childCount - 1) ?: return
|
||||||
|
} else {
|
||||||
|
parent.getChildAt(0) ?: return
|
||||||
|
}
|
||||||
|
|
||||||
|
val topChildPosition = parent.getChildAdapterPosition(topChild)
|
||||||
|
if (topChildPosition == RecyclerView.NO_POSITION) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val headerPos = getHeaderPositionForItem(topChildPosition)
|
||||||
|
if (headerPos != RecyclerView.NO_POSITION) {
|
||||||
|
val currentHeader = getHeaderViewForItem(headerPos, parent)
|
||||||
|
fixLayoutSize(parent, currentHeader)
|
||||||
|
val contactPoint = currentHeader.bottom
|
||||||
|
val childInContact = getChildInContact(parent, contactPoint, headerPos)
|
||||||
|
|
||||||
|
if (childInContact != null && isHeader(parent.getChildAdapterPosition(childInContact))) {
|
||||||
|
updateOverlaidHeaders(parent, headerPos)
|
||||||
|
moveHeader(c, currentHeader, childInContact)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Un-hide views early, so we don't get flashing headers while scrolling
|
||||||
|
val childBellow = getChildInContact(parent, currentHeader.top, headerPos)
|
||||||
|
val overlaidHeaderPos: Int? = if (childBellow != childInContact &&
|
||||||
|
childBellow != null &&
|
||||||
|
isHeader(parent.getChildAdapterPosition(childBellow)) &&
|
||||||
|
contactPoint - childBellow.bottom < (childBellow.bottom - childBellow.top)/8
|
||||||
|
) {
|
||||||
|
null
|
||||||
|
} else {
|
||||||
|
headerPos
|
||||||
|
}
|
||||||
|
|
||||||
|
updateOverlaidHeaders(parent, overlaidHeaderPos)
|
||||||
|
drawHeader(c, currentHeader)
|
||||||
|
} else {
|
||||||
|
// Show hidden header again
|
||||||
|
updateOverlaidHeaders(parent, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateOverlaidHeaders(parent: RecyclerView, headerPos: Int?) {
|
||||||
|
if (lastHeaderPos != headerPos) {
|
||||||
|
// Show hidden header again
|
||||||
|
lastHeaderPos?.let {
|
||||||
|
updateOverlaidHeader(parent, it, false)
|
||||||
|
}
|
||||||
|
// Remember new hidden header
|
||||||
|
lastHeaderPos = if (headerPos?.let { updateOverlaidHeader(parent, it, true) }.orFalse()) {
|
||||||
|
headerPos
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if successfully updated the view.
|
||||||
|
* Note: this has some issues when invisible views get recycled, better override this in subclasses.
|
||||||
|
*/
|
||||||
|
open fun updateOverlaidHeader(parent: RecyclerView, position: Int, isCurrentlyOverlaid: Boolean): Boolean {
|
||||||
|
val view = parent.findViewHolderForAdapterPosition(position)?.itemView
|
||||||
|
if (view != null) {
|
||||||
|
view.isVisible = !isCurrentlyOverlaid
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun getHeaderViewForItem(headerPosition: Int, parent: RecyclerView): View {
|
||||||
|
val viewHolder = epoxyController.adapter.onCreateViewHolder(
|
||||||
|
parent,
|
||||||
|
epoxyController.adapter.getItemViewType(headerPosition)
|
||||||
|
)
|
||||||
|
epoxyController.adapter.onBindViewHolder(viewHolder, headerPosition)
|
||||||
|
return viewHolder.itemView
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun drawHeader(c: Canvas, header: View) {
|
||||||
|
c.save()
|
||||||
|
c.translate(0f, 0f)
|
||||||
|
header.draw(c)
|
||||||
|
c.restore()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun moveHeader(c: Canvas, currentHeader: View, nextHeader: View) {
|
||||||
|
c.save()
|
||||||
|
c.translate(0f, (nextHeader.top - currentHeader.height).toFloat())
|
||||||
|
currentHeader.draw(c)
|
||||||
|
c.restore()
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract fun isHeader(itemPosition: Int): Boolean
|
||||||
|
|
||||||
|
private fun getChildInContact(parent: RecyclerView, contactPoint: Int, currentHeaderPos: Int): View? {
|
||||||
|
var childInContact: View? = null
|
||||||
|
for (i in 0 until parent.childCount) {
|
||||||
|
var heightTolerance = 0
|
||||||
|
val child = parent.getChildAt(i)
|
||||||
|
|
||||||
|
//measure height tolerance with child if child is another header
|
||||||
|
if (currentHeaderPos != i) {
|
||||||
|
val isChildHeader = isHeader(parent.getChildAdapterPosition(child))
|
||||||
|
if (isChildHeader) {
|
||||||
|
heightTolerance = mStickyHeaderHeight - child.height
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//add heightTolerance if child top be in display area
|
||||||
|
val childBottomPosition = if (child.top > 0) {
|
||||||
|
child.bottom + heightTolerance
|
||||||
|
} else {
|
||||||
|
child.bottom
|
||||||
|
}
|
||||||
|
|
||||||
|
if (childBottomPosition > contactPoint) {
|
||||||
|
if (child.top <= contactPoint) {
|
||||||
|
// This child overlaps the contactPoint
|
||||||
|
childInContact = child
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return childInContact
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method gets called by [StickyHeaderItemDecoration] to fetch the position of the header item in the adapter
|
||||||
|
* that is used for (represents) item at specified position.
|
||||||
|
* @param itemPosition int. Adapter's position of the item for which to do the search of the position of the header item.
|
||||||
|
* @return int. Position of the header item in the adapter.
|
||||||
|
*/
|
||||||
|
private fun getHeaderPositionForItem(itemPosition: Int): Int {
|
||||||
|
var tempPosition = itemPosition
|
||||||
|
var headerPosition = RecyclerView.NO_POSITION
|
||||||
|
val directionAdd = if (reverse) 1 else -1
|
||||||
|
do {
|
||||||
|
if (isHeader(tempPosition)) {
|
||||||
|
headerPosition = tempPosition
|
||||||
|
break
|
||||||
|
}
|
||||||
|
tempPosition += directionAdd
|
||||||
|
} while (tempPosition >= -1 && tempPosition < epoxyController.adapter.itemCount)
|
||||||
|
return headerPosition
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Properly measures and layouts the top sticky header.
|
||||||
|
* @param parent ViewGroup: RecyclerView in this case.
|
||||||
|
*/
|
||||||
|
private fun fixLayoutSize(parent: ViewGroup, view: View) {
|
||||||
|
|
||||||
|
// Specs for parent (RecyclerView)
|
||||||
|
val widthSpec = View.MeasureSpec.makeMeasureSpec(parent.width, View.MeasureSpec.EXACTLY)
|
||||||
|
val heightSpec = View.MeasureSpec.makeMeasureSpec(parent.height, View.MeasureSpec.UNSPECIFIED)
|
||||||
|
|
||||||
|
// Specs for children (headers)
|
||||||
|
val childWidthSpec =
|
||||||
|
ViewGroup.getChildMeasureSpec(widthSpec, parent.paddingLeft + parent.paddingRight, view.layoutParams.width)
|
||||||
|
val childHeightSpec = ViewGroup.getChildMeasureSpec(
|
||||||
|
heightSpec,
|
||||||
|
parent.paddingTop + parent.paddingBottom,
|
||||||
|
view.layoutParams.height
|
||||||
|
)
|
||||||
|
|
||||||
|
view.measure(childWidthSpec, childHeightSpec)
|
||||||
|
|
||||||
|
mStickyHeaderHeight = view.measuredHeight
|
||||||
|
|
||||||
|
view.layout(0, 0, view.measuredWidth, view.measuredHeight)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -62,6 +62,7 @@ import androidx.recyclerview.widget.ItemTouchHelper
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import androidx.transition.TransitionManager
|
import androidx.transition.TransitionManager
|
||||||
import com.airbnb.epoxy.EpoxyModel
|
import com.airbnb.epoxy.EpoxyModel
|
||||||
|
import com.airbnb.epoxy.EpoxyViewHolder
|
||||||
import com.airbnb.epoxy.OnModelBuildFinishedListener
|
import com.airbnb.epoxy.OnModelBuildFinishedListener
|
||||||
import com.airbnb.epoxy.addGlidePreloader
|
import com.airbnb.epoxy.addGlidePreloader
|
||||||
import com.airbnb.epoxy.glidePreloader
|
import com.airbnb.epoxy.glidePreloader
|
||||||
|
@ -72,10 +73,12 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import com.vanniktech.emoji.EmojiPopup
|
import com.vanniktech.emoji.EmojiPopup
|
||||||
import de.spiritcroc.matrixsdk.util.DbgUtil
|
import de.spiritcroc.matrixsdk.util.DbgUtil
|
||||||
import de.spiritcroc.matrixsdk.util.Dimber
|
import de.spiritcroc.matrixsdk.util.Dimber
|
||||||
|
import de.spiritcroc.recyclerview.StickyHeaderItemDecoration
|
||||||
import de.spiritcroc.recyclerview.widget.BetterLinearLayoutManager
|
import de.spiritcroc.recyclerview.widget.BetterLinearLayoutManager
|
||||||
import de.spiritcroc.recyclerview.widget.LinearLayoutManager
|
import de.spiritcroc.recyclerview.widget.LinearLayoutManager
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
import im.vector.app.core.animations.play
|
import im.vector.app.core.animations.play
|
||||||
|
import im.vector.app.core.date.DateFormatKind
|
||||||
import im.vector.app.core.dialogs.ConfirmationDialogBuilder
|
import im.vector.app.core.dialogs.ConfirmationDialogBuilder
|
||||||
import im.vector.app.core.dialogs.GalleryOrCameraDialogHelper
|
import im.vector.app.core.dialogs.GalleryOrCameraDialogHelper
|
||||||
import im.vector.app.core.epoxy.LayoutManagerStateRestorer
|
import im.vector.app.core.epoxy.LayoutManagerStateRestorer
|
||||||
|
@ -167,6 +170,7 @@ import im.vector.app.features.home.room.detail.timeline.helper.AudioMessagePlayb
|
||||||
import im.vector.app.features.home.room.detail.timeline.helper.MatrixItemColorProvider
|
import im.vector.app.features.home.room.detail.timeline.helper.MatrixItemColorProvider
|
||||||
import im.vector.app.features.home.room.detail.timeline.image.buildImageContentRendererData
|
import im.vector.app.features.home.room.detail.timeline.image.buildImageContentRendererData
|
||||||
import im.vector.app.features.home.room.detail.timeline.item.AbsMessageItem
|
import im.vector.app.features.home.room.detail.timeline.item.AbsMessageItem
|
||||||
|
import im.vector.app.features.home.room.detail.timeline.item.DaySeparatorItem
|
||||||
import im.vector.app.features.home.room.detail.timeline.item.ItemWithEvents
|
import im.vector.app.features.home.room.detail.timeline.item.ItemWithEvents
|
||||||
import im.vector.app.features.home.room.detail.timeline.item.MessageAudioItem
|
import im.vector.app.features.home.room.detail.timeline.item.MessageAudioItem
|
||||||
import im.vector.app.features.home.room.detail.timeline.item.MessageFileItem
|
import im.vector.app.features.home.room.detail.timeline.item.MessageFileItem
|
||||||
|
@ -220,6 +224,7 @@ import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import org.billcarsonfr.jsonviewer.JSonViewerDialog
|
import org.billcarsonfr.jsonviewer.JSonViewerDialog
|
||||||
import org.commonmark.parser.Parser
|
import org.commonmark.parser.Parser
|
||||||
|
import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||||
import org.matrix.android.sdk.api.session.Session
|
import org.matrix.android.sdk.api.session.Session
|
||||||
import org.matrix.android.sdk.api.session.content.ContentAttachmentData
|
import org.matrix.android.sdk.api.session.content.ContentAttachmentData
|
||||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||||
|
@ -1478,6 +1483,46 @@ class TimelineFragment @Inject constructor(
|
||||||
timelineEventController.callback = this
|
timelineEventController.callback = this
|
||||||
timelineEventController.timeline = timelineViewModel.timeline
|
timelineEventController.timeline = timelineViewModel.timeline
|
||||||
|
|
||||||
|
if (vectorPreferences.floatingDate()) {
|
||||||
|
views.timelineRecyclerView.addItemDecoration(
|
||||||
|
object : StickyHeaderItemDecoration(timelineEventController, reverse = true) {
|
||||||
|
override fun isHeader(itemPosition: Int): Boolean {
|
||||||
|
if (itemPosition != RecyclerView.NO_POSITION) {
|
||||||
|
val model = timelineEventController.adapter.getModelAtPosition(itemPosition)
|
||||||
|
return model is DaySeparatorItem
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getHeaderViewForItem(headerPosition: Int, parent: RecyclerView): View {
|
||||||
|
// Same as super
|
||||||
|
val viewHolder = timelineEventController.adapter.onCreateViewHolder(
|
||||||
|
parent,
|
||||||
|
timelineEventController.adapter.getItemViewType(headerPosition)
|
||||||
|
)
|
||||||
|
timelineEventController.adapter.onBindViewHolder(viewHolder, headerPosition)
|
||||||
|
// Same as super -- end
|
||||||
|
|
||||||
|
// We want to hide the separator line for floating dates
|
||||||
|
(viewHolder.holder as? DaySeparatorItem.Holder)?.let { DaySeparatorItem.asFloatingDate(it) }
|
||||||
|
|
||||||
|
return viewHolder.itemView
|
||||||
|
}
|
||||||
|
|
||||||
|
// While the header has a sticky overlay, only hide its text, not the separator lines
|
||||||
|
override fun updateOverlaidHeader(parent: RecyclerView, position: Int, isCurrentlyOverlaid: Boolean): Boolean {
|
||||||
|
val model = tryOrNull { timelineEventController.adapter.getModelAtPosition(position) as? DaySeparatorItem }
|
||||||
|
if (model != null) {
|
||||||
|
val viewHolder = ((parent.findViewHolderForAdapterPosition(position) as? EpoxyViewHolder)?.holder) as? DaySeparatorItem.Holder
|
||||||
|
model.shouldBeVisible(!isCurrentlyOverlaid, viewHolder)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
views.timelineRecyclerView.trackItemsVisibilityChange()
|
views.timelineRecyclerView.trackItemsVisibilityChange()
|
||||||
layoutManager = object : BetterLinearLayoutManager(context, RecyclerView.VERTICAL, true) {
|
layoutManager = object : BetterLinearLayoutManager(context, RecyclerView.VERTICAL, true) {
|
||||||
override fun onLayoutCompleted(state: RecyclerView.State?) {
|
override fun onLayoutCompleted(state: RecyclerView.State?) {
|
||||||
|
|
|
@ -16,7 +16,9 @@
|
||||||
|
|
||||||
package im.vector.app.features.home.room.detail.timeline.item
|
package im.vector.app.features.home.room.detail.timeline.item
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
|
import androidx.core.view.isVisible
|
||||||
import com.airbnb.epoxy.EpoxyAttribute
|
import com.airbnb.epoxy.EpoxyAttribute
|
||||||
import com.airbnb.epoxy.EpoxyModelClass
|
import com.airbnb.epoxy.EpoxyModelClass
|
||||||
import com.airbnb.epoxy.EpoxyModelWithHolder
|
import com.airbnb.epoxy.EpoxyModelWithHolder
|
||||||
|
@ -28,12 +30,32 @@ abstract class DaySeparatorItem : EpoxyModelWithHolder<DaySeparatorItem.Holder>(
|
||||||
|
|
||||||
@EpoxyAttribute lateinit var formattedDay: String
|
@EpoxyAttribute lateinit var formattedDay: String
|
||||||
|
|
||||||
|
private var shouldBeVisible: Boolean = true
|
||||||
|
|
||||||
override fun bind(holder: Holder) {
|
override fun bind(holder: Holder) {
|
||||||
super.bind(holder)
|
super.bind(holder)
|
||||||
holder.dayTextView.text = formattedDay
|
holder.dayTextView.text = formattedDay
|
||||||
|
// Just as background space reservation. Use same text for proper measures.
|
||||||
|
holder.dayTextCutoutView.text = formattedDay
|
||||||
|
// As we may hide this for floating dates, ensure we un-hide it on bind
|
||||||
|
holder.dayTextView.isVisible = shouldBeVisible
|
||||||
|
}
|
||||||
|
|
||||||
|
fun shouldBeVisible(shouldBeVisible: Boolean, holder: Holder?) {
|
||||||
|
holder?.dayTextView?.isVisible = shouldBeVisible
|
||||||
|
this.shouldBeVisible = shouldBeVisible
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun asFloatingDate(holder: Holder) {
|
||||||
|
holder.dayTextView.isVisible = true
|
||||||
|
holder.separatorView.isVisible = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Holder : VectorEpoxyHolder() {
|
class Holder : VectorEpoxyHolder() {
|
||||||
val dayTextView by bind<TextView>(R.id.itemDayTextView)
|
val dayTextView by bind<TextView>(R.id.itemDayTextView)
|
||||||
|
val dayTextCutoutView by bind<TextView>(R.id.itemDayTextCutoutView)
|
||||||
|
val separatorView by bind<View>(R.id.itemDayTextSeparatorView)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -222,6 +222,7 @@ class VectorPreferences @Inject constructor(
|
||||||
private const val SETTINGS_HIDE_CALL_BUTTONS = "SETTINGS_HIDE_CALL_BUTTONS"
|
private const val SETTINGS_HIDE_CALL_BUTTONS = "SETTINGS_HIDE_CALL_BUTTONS"
|
||||||
private const val SETTINGS_READ_RECEIPT_FOLLOWS_READ_MARKER = "SETTINGS_READ_RECEIPT_FOLLOWS_READ_MARKER"
|
private const val SETTINGS_READ_RECEIPT_FOLLOWS_READ_MARKER = "SETTINGS_READ_RECEIPT_FOLLOWS_READ_MARKER"
|
||||||
private const val SETTINGS_SHOW_OPEN_ANONYMOUS = "SETTINGS_SHOW_OPEN_ANONYMOUS"
|
private const val SETTINGS_SHOW_OPEN_ANONYMOUS = "SETTINGS_SHOW_OPEN_ANONYMOUS"
|
||||||
|
private const val SETTINGS_FLOATING_DATE = "SETTINGS_FLOATING_DATE"
|
||||||
|
|
||||||
private const val DID_ASK_TO_ENABLE_SESSION_PUSH = "DID_ASK_TO_ENABLE_SESSION_PUSH"
|
private const val DID_ASK_TO_ENABLE_SESSION_PUSH = "DID_ASK_TO_ENABLE_SESSION_PUSH"
|
||||||
|
|
||||||
|
@ -1133,6 +1134,10 @@ class VectorPreferences @Inject constructor(
|
||||||
return defaultPrefs.getBoolean(SETTINGS_SHOW_OPEN_ANONYMOUS, false)
|
return defaultPrefs.getBoolean(SETTINGS_SHOW_OPEN_ANONYMOUS, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun floatingDate(): Boolean {
|
||||||
|
return defaultPrefs.getBoolean(SETTINGS_FLOATING_DATE, true)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* I likely do more fresh installs of the app than anyone else, so a shortcut to change some of the default settings to
|
* I likely do more fresh installs of the app than anyone else, so a shortcut to change some of the default settings to
|
||||||
* my preferred values can safe me some time
|
* my preferred values can safe me some time
|
||||||
|
@ -1158,6 +1163,7 @@ class VectorPreferences @Inject constructor(
|
||||||
.putBoolean(SETTINGS_ENABLE_SPACE_PAGER, true)
|
.putBoolean(SETTINGS_ENABLE_SPACE_PAGER, true)
|
||||||
.putBoolean(SETTINGS_READ_RECEIPT_FOLLOWS_READ_MARKER, true)
|
.putBoolean(SETTINGS_READ_RECEIPT_FOLLOWS_READ_MARKER, true)
|
||||||
.putBoolean(SETTINGS_SHOW_OPEN_ANONYMOUS, true)
|
.putBoolean(SETTINGS_SHOW_OPEN_ANONYMOUS, true)
|
||||||
|
.putBoolean(SETTINGS_FLOATING_DATE, true)
|
||||||
.apply()
|
.apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
11
vector/src/main/res/drawable/date_background.xml
Normal file
11
vector/src/main/res/drawable/date_background.xml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<shape
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
|
|
||||||
|
<solid android:color="?android:colorBackground" />
|
||||||
|
|
||||||
|
<corners android:radius="3dp" />
|
||||||
|
|
||||||
|
</shape>
|
|
@ -6,6 +6,10 @@
|
||||||
android:padding="8dp"
|
android:padding="8dp"
|
||||||
tools:viewBindingIgnore="true">
|
tools:viewBindingIgnore="true">
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/itemDayTextSeparatorView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
<View
|
<View
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="1dp"
|
android:layout_height="1dp"
|
||||||
|
@ -13,6 +17,20 @@
|
||||||
android:layout_marginStart="8dp"
|
android:layout_marginStart="8dp"
|
||||||
android:layout_marginEnd="8dp"
|
android:layout_marginEnd="8dp"
|
||||||
android:background="?vctr_list_separator" />
|
android:background="?vctr_list_separator" />
|
||||||
|
<!-- Just to reserve some extra space while not floating. So use invisible text. -->
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/itemDayTextCutoutView"
|
||||||
|
style="@style/Widget.Vector.TextView.Subtitle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:background="?android:colorBackground"
|
||||||
|
android:textColor="@android:color/transparent"
|
||||||
|
android:paddingStart="24dp"
|
||||||
|
android:paddingEnd="24dp"
|
||||||
|
android:textSize="15sp"
|
||||||
|
tools:text="@tools:sample/date/day_of_week" />
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/itemDayTextView"
|
android:id="@+id/itemDayTextView"
|
||||||
|
@ -20,9 +38,9 @@
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:background="?android:colorBackground"
|
android:background="@drawable/date_background"
|
||||||
android:paddingStart="24dp"
|
android:paddingStart="8dp"
|
||||||
android:paddingEnd="24dp"
|
android:paddingEnd="8dp"
|
||||||
android:textColor="?vctr_content_tertiary"
|
android:textColor="?vctr_content_tertiary"
|
||||||
android:textSize="15sp"
|
android:textSize="15sp"
|
||||||
tools:text="@tools:sample/date/day_of_week" />
|
tools:text="@tools:sample/date/day_of_week" />
|
||||||
|
|
|
@ -191,4 +191,8 @@
|
||||||
<string name="room_list_quick_actions_open_anonymous">Open without reading</string>
|
<string name="room_list_quick_actions_open_anonymous">Open without reading</string>
|
||||||
<string name="settings_show_open_anonymous">Open without reading</string>
|
<string name="settings_show_open_anonymous">Open without reading</string>
|
||||||
<string name="settings_show_open_anonymous_summary">Show option to open a room without automatically marking it read</string>
|
<string name="settings_show_open_anonymous_summary">Show option to open a room without automatically marking it read</string>
|
||||||
|
|
||||||
|
<string name="settings_floating_date">Floating date</string>
|
||||||
|
<string name="settings_floating_date_summary">Show the date on top of messages while scrolling</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -243,6 +243,12 @@
|
||||||
android:title="@string/settings_vibrate_on_mention"
|
android:title="@string/settings_vibrate_on_mention"
|
||||||
app:isPreferenceVisible="@bool/false_not_implemented" />
|
app:isPreferenceVisible="@bool/false_not_implemented" />
|
||||||
|
|
||||||
|
<im.vector.app.core.preference.VectorSwitchPreference
|
||||||
|
android:key="SETTINGS_FLOATING_DATE"
|
||||||
|
android:title="@string/settings_floating_date"
|
||||||
|
android:summary="@string/settings_floating_date_summary"
|
||||||
|
android:defaultValue="true" />
|
||||||
|
|
||||||
</im.vector.app.core.preference.VectorPreferenceCategory>
|
</im.vector.app.core.preference.VectorPreferenceCategory>
|
||||||
|
|
||||||
<im.vector.app.core.preference.VectorPreferenceCategory
|
<im.vector.app.core.preference.VectorPreferenceCategory
|
||||||
|
|
Loading…
Reference in a new issue