forked from GitHub-Mirror/riotX-android
Room list : show unread badge with room name. Still need to mark a room as read to update marker.
This commit is contained in:
parent
9b1277485e
commit
9a42c121e4
@ -61,11 +61,15 @@ class RoomSummaryController(private val callback: Callback? = null
|
||||
|
||||
private fun buildRoomModels(summaries: List<RoomSummary>, selectedRoomId: String?) {
|
||||
summaries.forEach { roomSummary ->
|
||||
val unreadCount = roomSummary.notificationCount
|
||||
val showHighlighted = roomSummary.highlightCount > 0
|
||||
val isSelected = roomSummary.roomId == selectedRoomId
|
||||
RoomSummaryItem(
|
||||
roomName = roomSummary.displayName,
|
||||
avatarUrl = roomSummary.avatarUrl,
|
||||
isSelected = isSelected,
|
||||
showHighlighted = showHighlighted,
|
||||
unreadCount = unreadCount,
|
||||
listener = { callback?.onRoomSelected(roomSummary) }
|
||||
)
|
||||
.id(roomSummary.roomId)
|
||||
|
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2019 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.riotredesign.features.home.room.list
|
||||
|
||||
object RoomSummaryFormatter {
|
||||
|
||||
/**
|
||||
* Format the unread messages counter.
|
||||
*
|
||||
* @param count the count
|
||||
* @return the formatted value
|
||||
*/
|
||||
fun formatUnreadMessagesCounter(count: Int): String {
|
||||
return if (count > 999) {
|
||||
"${count / 1000}.${count % 1000 / 100}K"
|
||||
} else {
|
||||
count.toString()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -28,14 +28,18 @@ data class RoomSummaryItem(
|
||||
val roomName: CharSequence,
|
||||
val avatarUrl: String?,
|
||||
val isSelected: Boolean,
|
||||
val unreadCount: Int,
|
||||
val showHighlighted: Boolean,
|
||||
val listener: (() -> Unit)? = null
|
||||
) : KotlinModel(R.layout.item_room) {
|
||||
|
||||
private val unreadCounterBadgeView by bind<UnreadCounterBadgeView>(R.id.roomUnreadCounterBadgeView)
|
||||
private val titleView by bind<TextView>(R.id.roomNameView)
|
||||
private val avatarImageView by bind<ImageView>(R.id.roomAvatarImageView)
|
||||
private val rootView by bind<CheckableFrameLayout>(R.id.itemRoomLayout)
|
||||
|
||||
override fun bind() {
|
||||
unreadCounterBadgeView.render(unreadCount, showHighlighted)
|
||||
rootView.isChecked = isSelected
|
||||
rootView.setOnClickListener { listener?.invoke() }
|
||||
titleView.text = roomName
|
||||
|
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2019 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package im.vector.riotredesign.features.home.room.list
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import androidx.appcompat.widget.AppCompatTextView
|
||||
import im.vector.riotredesign.R
|
||||
|
||||
class UnreadCounterBadgeView : AppCompatTextView {
|
||||
|
||||
constructor(context: Context) : super(context)
|
||||
|
||||
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
|
||||
|
||||
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
|
||||
|
||||
fun render(count: Int, highlighted: Boolean) {
|
||||
if (count == 0) {
|
||||
visibility = View.INVISIBLE
|
||||
} else {
|
||||
visibility = View.VISIBLE
|
||||
val bgRes = if (highlighted) {
|
||||
R.drawable.bg_unread_highlight
|
||||
} else {
|
||||
R.drawable.bg_unread_notification
|
||||
}
|
||||
setBackgroundResource(bgRes)
|
||||
text = RoomSummaryFormatter.formatUnreadMessagesCounter(count)
|
||||
}
|
||||
}
|
||||
|
||||
enum class Status {
|
||||
NOTIFICATION,
|
||||
HIGHLIGHT
|
||||
}
|
||||
|
||||
}
|
5
app/src/main/res/drawable/bg_unread_highlight.xml
Normal file
5
app/src/main/res/drawable/bg_unread_highlight.xml
Normal file
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="oval">
|
||||
<solid android:color="@color/rosy_pink" />
|
||||
</shape>
|
6
app/src/main/res/drawable/bg_unread_notification.xml
Normal file
6
app/src/main/res/drawable/bg_unread_notification.xml
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="oval">
|
||||
<solid android:color="@color/dark_grey" />
|
||||
</shape>
|
@ -1,18 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<im.vector.riotredesign.core.platform.CheckableFrameLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<im.vector.riotredesign.core.platform.CheckableFrameLayout 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:id="@+id/itemRoomLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="8dp"
|
||||
android:paddingRight="16dp"
|
||||
android:foreground="?attr/selectableItemBackground"
|
||||
android:background="@drawable/bg_room_item"
|
||||
android:clickable="true"
|
||||
android:focusable="true">
|
||||
android:focusable="true"
|
||||
android:foreground="?attr/selectableItemBackground"
|
||||
android:paddingLeft="8dp"
|
||||
android:paddingRight="16dp">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
@ -35,15 +34,32 @@
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:duplicateParentState="true"
|
||||
android:textColor="@color/color_room_title"
|
||||
android:textSize="14sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/roomUnreadCounterBadgeView"
|
||||
app:layout_constraintStart_toEndOf="@id/roomAvatarImageView"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="@tools:sample/full_names" />
|
||||
|
||||
<im.vector.riotredesign.features.home.room.list.UnreadCounterBadgeView
|
||||
android:id="@+id/roomUnreadCounterBadgeView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="0dp"
|
||||
android:padding="4dp"
|
||||
android:gravity="center"
|
||||
android:minWidth="24dp"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="10sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintDimensionRatio="1:1"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:background="@drawable/bg_unread_highlight"
|
||||
tools:text="115" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</im.vector.riotredesign.core.platform.CheckableFrameLayout>
|
@ -35,7 +35,6 @@
|
||||
android:id="@+id/roomCategoryAddButton"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
|
||||
android:scaleType="centerInside"
|
||||
android:src="@drawable/ic_add_circle_white"
|
||||
android:tint="@color/bluey_grey_two"
|
||||
|
@ -26,5 +26,7 @@ data class RoomSummary(
|
||||
val topic: String = "",
|
||||
val avatarUrl: String = "",
|
||||
val isDirect: Boolean,
|
||||
val otherMemberIds: List<String> = emptyList()
|
||||
val otherMemberIds: List<String> = emptyList(),
|
||||
var notificationCount: Int = 0,
|
||||
var highlightCount: Int = 0
|
||||
)
|
@ -24,12 +24,14 @@ internal object RoomSummaryMapper {
|
||||
|
||||
fun map(roomSummaryEntity: RoomSummaryEntity): RoomSummary {
|
||||
return RoomSummary(
|
||||
roomSummaryEntity.roomId,
|
||||
roomSummaryEntity.displayName ?: "",
|
||||
roomSummaryEntity.topic ?: "",
|
||||
roomSummaryEntity.avatarUrl ?: "",
|
||||
roomSummaryEntity.isDirect,
|
||||
roomSummaryEntity.otherMemberIds.toList()
|
||||
roomId = roomSummaryEntity.roomId,
|
||||
displayName = roomSummaryEntity.displayName ?: "",
|
||||
topic = roomSummaryEntity.topic ?: "",
|
||||
avatarUrl = roomSummaryEntity.avatarUrl ?: "",
|
||||
isDirect = roomSummaryEntity.isDirect,
|
||||
otherMemberIds = roomSummaryEntity.otherMemberIds.toList(),
|
||||
highlightCount = roomSummaryEntity.highlightCount,
|
||||
notificationCount = roomSummaryEntity.notificationCount
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,9 @@ internal open class RoomSummaryEntity(@PrimaryKey var roomId: String = "",
|
||||
var joinedMembersCount: Int? = 0,
|
||||
var invitedMembersCount: Int? = 0,
|
||||
var isDirect: Boolean = false,
|
||||
var otherMemberIds: RealmList<String> = RealmList()
|
||||
var otherMemberIds: RealmList<String> = RealmList(),
|
||||
var notificationCount: Int = 0,
|
||||
var highlightCount: Int = 0
|
||||
) : RealmObject() {
|
||||
|
||||
companion object
|
||||
|
@ -31,7 +31,12 @@ import im.vector.matrix.android.internal.database.model.RoomSummaryEntity
|
||||
import im.vector.matrix.android.internal.database.query.findLastLiveChunkFromRoom
|
||||
import im.vector.matrix.android.internal.database.query.where
|
||||
import im.vector.matrix.android.internal.session.room.timeline.PaginationDirection
|
||||
import im.vector.matrix.android.internal.session.sync.model.*
|
||||
import im.vector.matrix.android.internal.session.sync.model.InvitedRoomSync
|
||||
import im.vector.matrix.android.internal.session.sync.model.RoomSync
|
||||
import im.vector.matrix.android.internal.session.sync.model.RoomSyncEphemeral
|
||||
import im.vector.matrix.android.internal.session.sync.model.RoomSyncSummary
|
||||
import im.vector.matrix.android.internal.session.sync.model.RoomSyncUnreadNotifications
|
||||
import im.vector.matrix.android.internal.session.sync.model.RoomsSyncResponse
|
||||
import io.realm.Realm
|
||||
import io.realm.kotlin.createObject
|
||||
|
||||
@ -57,9 +62,9 @@ internal class RoomSyncHandler(private val monarchy: Monarchy,
|
||||
|
||||
private fun handleRoomSync(realm: Realm, handlingStrategy: HandlingStrategy) {
|
||||
val rooms = when (handlingStrategy) {
|
||||
is HandlingStrategy.JOINED -> handlingStrategy.data.map { handleJoinedRoom(realm, it.key, it.value) }
|
||||
is HandlingStrategy.JOINED -> handlingStrategy.data.map { handleJoinedRoom(realm, it.key, it.value) }
|
||||
is HandlingStrategy.INVITED -> handlingStrategy.data.map { handleInvitedRoom(realm, it.key, it.value) }
|
||||
is HandlingStrategy.LEFT -> handlingStrategy.data.map { handleLeftRoom(it.key, it.value) }
|
||||
is HandlingStrategy.LEFT -> handlingStrategy.data.map { handleLeftRoom(it.key, it.value) }
|
||||
}
|
||||
realm.insertOrUpdate(rooms)
|
||||
}
|
||||
@ -69,7 +74,7 @@ internal class RoomSyncHandler(private val monarchy: Monarchy,
|
||||
roomSync: RoomSync): RoomEntity {
|
||||
|
||||
val roomEntity = RoomEntity.where(realm, roomId).findFirst()
|
||||
?: realm.createObject(roomId)
|
||||
?: realm.createObject(roomId)
|
||||
|
||||
if (roomEntity.membership == MyMembership.INVITED) {
|
||||
roomEntity.chunks.deleteAllFromRealm()
|
||||
@ -105,9 +110,14 @@ internal class RoomSyncHandler(private val monarchy: Monarchy,
|
||||
handleRoomSummary(realm, roomId, roomSync.summary)
|
||||
}
|
||||
|
||||
if (roomSync.unreadNotifications != null) {
|
||||
handleUnreadNotifications(realm, roomId, roomSync.unreadNotifications)
|
||||
}
|
||||
|
||||
if (roomSync.ephemeral != null && roomSync.ephemeral.events.isNotEmpty()) {
|
||||
handleEphemeral(realm, roomId, roomSync.ephemeral)
|
||||
}
|
||||
|
||||
return roomEntity
|
||||
}
|
||||
|
||||
@ -159,7 +169,7 @@ internal class RoomSyncHandler(private val monarchy: Monarchy,
|
||||
roomSummary: RoomSyncSummary) {
|
||||
|
||||
val roomSummaryEntity = RoomSummaryEntity.where(realm, roomId).findFirst()
|
||||
?: RoomSummaryEntity(roomId)
|
||||
?: RoomSummaryEntity(roomId)
|
||||
|
||||
if (roomSummary.heroes.isNotEmpty()) {
|
||||
roomSummaryEntity.heroes.clear()
|
||||
@ -182,4 +192,19 @@ internal class RoomSyncHandler(private val monarchy: Monarchy,
|
||||
.map { it.content.toModel<ReadReceiptContent>() }
|
||||
.flatMap { readReceiptHandler.handle(realm, roomId, it) }
|
||||
}
|
||||
|
||||
private fun handleUnreadNotifications(realm: Realm, roomId: String, unreadNotifications: RoomSyncUnreadNotifications) {
|
||||
val roomSummaryEntity = RoomSummaryEntity.where(realm, roomId).findFirst()
|
||||
?: RoomSummaryEntity(roomId)
|
||||
|
||||
if (unreadNotifications.highlightCount != null) {
|
||||
roomSummaryEntity.highlightCount = unreadNotifications.highlightCount
|
||||
}
|
||||
if (unreadNotifications.notificationCount != null) {
|
||||
roomSummaryEntity.notificationCount = unreadNotifications.notificationCount
|
||||
}
|
||||
realm.insertOrUpdate(roomSummaryEntity)
|
||||
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user