Room list : show unread badge with room name. Still need to mark a room as read to update marker.

This commit is contained in:
ganfra 2019-01-25 18:04:08 +01:00
parent 9b1277485e
commit 9a42c121e4
12 changed files with 173 additions and 21 deletions

View File

@ -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)

View File

@ -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()
}
}

}

View File

@ -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

View File

@ -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
}

}

View 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>

View 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>

View File

@ -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>

View File

@ -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"

View File

@ -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
)

View File

@ -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
)
}
}

View File

@ -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

View File

@ -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)

}

}