Merge pull request #604 from vector-im/feature/performance
Feature/performance
This commit is contained in:
commit
4f7ec91255
|
@ -0,0 +1,24 @@
|
||||||
|
/*
|
||||||
|
* 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.matrix.rx
|
||||||
|
|
||||||
|
import im.vector.matrix.android.api.util.Optional
|
||||||
|
import io.reactivex.Observable
|
||||||
|
|
||||||
|
fun <T : Any> Observable<Optional<T>>.unwrap(): Observable<T> {
|
||||||
|
return filter { it.hasValue() }.map { it.get() }
|
||||||
|
}
|
|
@ -28,7 +28,7 @@ import io.reactivex.Single
|
||||||
|
|
||||||
class RxRoom(private val room: Room) {
|
class RxRoom(private val room: Room) {
|
||||||
|
|
||||||
fun liveRoomSummary(): Observable<RoomSummary> {
|
fun liveRoomSummary(): Observable<Optional<RoomSummary>> {
|
||||||
return room.getRoomSummaryLive().asObservable()
|
return room.getRoomSummaryLive().asObservable()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,11 +36,11 @@ class RxRoom(private val room: Room) {
|
||||||
return room.getRoomMemberIdsLive().asObservable()
|
return room.getRoomMemberIdsLive().asObservable()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun liveAnnotationSummary(eventId: String): Observable<EventAnnotationsSummary> {
|
fun liveAnnotationSummary(eventId: String): Observable<Optional<EventAnnotationsSummary>> {
|
||||||
return room.getEventSummaryLive(eventId).asObservable()
|
return room.getEventSummaryLive(eventId).asObservable()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun liveTimelineEvent(eventId: String): Observable<TimelineEvent> {
|
fun liveTimelineEvent(eventId: String): Observable<Optional<TimelineEvent>> {
|
||||||
return room.getTimeLineEventLive(eventId).asObservable()
|
return room.getTimeLineEventLive(eventId).asObservable()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ import im.vector.matrix.android.api.session.room.send.DraftService
|
||||||
import im.vector.matrix.android.api.session.room.send.SendService
|
import im.vector.matrix.android.api.session.room.send.SendService
|
||||||
import im.vector.matrix.android.api.session.room.state.StateService
|
import im.vector.matrix.android.api.session.room.state.StateService
|
||||||
import im.vector.matrix.android.api.session.room.timeline.TimelineService
|
import im.vector.matrix.android.api.session.room.timeline.TimelineService
|
||||||
|
import im.vector.matrix.android.api.util.Optional
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This interface defines methods to interact within a room.
|
* This interface defines methods to interact within a room.
|
||||||
|
@ -49,7 +50,7 @@ interface Room :
|
||||||
* A live [RoomSummary] associated with the room
|
* A live [RoomSummary] associated with the room
|
||||||
* You can observe this summary to get dynamic data from this room.
|
* You can observe this summary to get dynamic data from this room.
|
||||||
*/
|
*/
|
||||||
fun getRoomSummaryLive(): LiveData<RoomSummary>
|
fun getRoomSummaryLive(): LiveData<Optional<RoomSummary>>
|
||||||
|
|
||||||
fun roomSummary(): RoomSummary?
|
fun roomSummary(): RoomSummary?
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ import im.vector.matrix.android.api.session.events.model.Event
|
||||||
import im.vector.matrix.android.api.session.room.model.EventAnnotationsSummary
|
import im.vector.matrix.android.api.session.room.model.EventAnnotationsSummary
|
||||||
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||||
import im.vector.matrix.android.api.util.Cancelable
|
import im.vector.matrix.android.api.util.Cancelable
|
||||||
|
import im.vector.matrix.android.api.util.Optional
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* In some cases, events may wish to reference other events.
|
* In some cases, events may wish to reference other events.
|
||||||
|
@ -111,7 +112,7 @@ interface RelationService {
|
||||||
replyText: String,
|
replyText: String,
|
||||||
autoMarkdown: Boolean = false): Cancelable?
|
autoMarkdown: Boolean = false): Cancelable?
|
||||||
|
|
||||||
fun getEventSummaryLive(eventId: String): LiveData<EventAnnotationsSummary>
|
fun getEventSummaryLive(eventId: String): LiveData<Optional<EventAnnotationsSummary>>
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -17,6 +17,7 @@
|
||||||
package im.vector.matrix.android.api.session.room.timeline
|
package im.vector.matrix.android.api.session.room.timeline
|
||||||
|
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
|
import im.vector.matrix.android.api.util.Optional
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This interface defines methods to interact with the timeline. It's implemented at the room level.
|
* This interface defines methods to interact with the timeline. It's implemented at the room level.
|
||||||
|
@ -36,5 +37,5 @@ interface TimelineService {
|
||||||
fun getTimeLineEvent(eventId: String): TimelineEvent?
|
fun getTimeLineEvent(eventId: String): TimelineEvent?
|
||||||
|
|
||||||
|
|
||||||
fun getTimeLineEventLive(eventId: String): LiveData<TimelineEvent>
|
fun getTimeLineEventLive(eventId: String): LiveData<Optional<TimelineEvent>>
|
||||||
}
|
}
|
|
@ -31,6 +31,10 @@ data class Optional<T : Any> constructor(private val value: T?) {
|
||||||
return value ?: fn()
|
return value ?: fn()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun hasValue(): Boolean{
|
||||||
|
return value != null
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun <T : Any> from(value: T?): Optional<T> {
|
fun <T : Any> from(value: T?): Optional<T> {
|
||||||
return Optional(value)
|
return Optional(value)
|
||||||
|
|
|
@ -39,9 +39,10 @@ import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Provider
|
||||||
|
|
||||||
internal class DefaultAuthenticator @Inject constructor(@Unauthenticated
|
internal class DefaultAuthenticator @Inject constructor(@Unauthenticated
|
||||||
private val okHttpClient: OkHttpClient,
|
private val okHttpClient: Provider<OkHttpClient>,
|
||||||
private val retrofitFactory: RetrofitFactory,
|
private val retrofitFactory: RetrofitFactory,
|
||||||
private val coroutineDispatchers: MatrixCoroutineDispatchers,
|
private val coroutineDispatchers: MatrixCoroutineDispatchers,
|
||||||
private val sessionParamsStore: SessionParamsStore,
|
private val sessionParamsStore: SessionParamsStore,
|
||||||
|
@ -119,7 +120,7 @@ internal class DefaultAuthenticator @Inject constructor(@Unauthenticated
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun buildAuthAPI(homeServerConnectionConfig: HomeServerConnectionConfig): AuthAPI {
|
private fun buildAuthAPI(homeServerConnectionConfig: HomeServerConnectionConfig): AuthAPI {
|
||||||
val retrofit = retrofitFactory.create(okHttpClient, homeServerConnectionConfig.homeServerUri.toString())
|
val retrofit = retrofitFactory.create(okHttpClient.get(), homeServerConnectionConfig.homeServerUri.toString())
|
||||||
return retrofit.create(AuthAPI::class.java)
|
return retrofit.create(AuthAPI::class.java)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,46 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.matrix.android.internal.database
|
|
||||||
|
|
||||||
import androidx.lifecycle.LiveData
|
|
||||||
import io.realm.*
|
|
||||||
|
|
||||||
class RealmLiveData<T : RealmModel>(private val realmConfiguration: RealmConfiguration,
|
|
||||||
private val query: (Realm) -> RealmQuery<T>) : LiveData<RealmResults<T>>() {
|
|
||||||
|
|
||||||
private val listener = RealmChangeListener<RealmResults<T>> { results ->
|
|
||||||
value = results
|
|
||||||
}
|
|
||||||
|
|
||||||
private var realm: Realm? = null
|
|
||||||
private var results: RealmResults<T>? = null
|
|
||||||
|
|
||||||
override fun onActive() {
|
|
||||||
val realm = Realm.getInstance(realmConfiguration)
|
|
||||||
val results = query.invoke(realm).findAllAsync()
|
|
||||||
results.addChangeListener(listener)
|
|
||||||
this.realm = realm
|
|
||||||
this.results = results
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onInactive() {
|
|
||||||
results?.removeChangeListener(listener)
|
|
||||||
results = null
|
|
||||||
realm?.close()
|
|
||||||
realm = null
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -29,7 +29,8 @@ import im.vector.matrix.android.api.session.room.send.DraftService
|
||||||
import im.vector.matrix.android.api.session.room.send.SendService
|
import im.vector.matrix.android.api.session.room.send.SendService
|
||||||
import im.vector.matrix.android.api.session.room.state.StateService
|
import im.vector.matrix.android.api.session.room.state.StateService
|
||||||
import im.vector.matrix.android.api.session.room.timeline.TimelineService
|
import im.vector.matrix.android.api.session.room.timeline.TimelineService
|
||||||
import im.vector.matrix.android.internal.database.RealmLiveData
|
import im.vector.matrix.android.api.util.Optional
|
||||||
|
import im.vector.matrix.android.api.util.toOptional
|
||||||
import im.vector.matrix.android.internal.database.mapper.RoomSummaryMapper
|
import im.vector.matrix.android.internal.database.mapper.RoomSummaryMapper
|
||||||
import im.vector.matrix.android.internal.database.model.RoomSummaryEntity
|
import im.vector.matrix.android.internal.database.model.RoomSummaryEntity
|
||||||
import im.vector.matrix.android.internal.database.model.RoomSummaryEntityFields
|
import im.vector.matrix.android.internal.database.model.RoomSummaryEntityFields
|
||||||
|
@ -56,19 +57,13 @@ internal class DefaultRoom @Inject constructor(override val roomId: String,
|
||||||
RelationService by relationService,
|
RelationService by relationService,
|
||||||
MembershipService by roomMembersService {
|
MembershipService by roomMembersService {
|
||||||
|
|
||||||
override fun getRoomSummaryLive(): LiveData<RoomSummary> {
|
override fun getRoomSummaryLive(): LiveData<Optional<RoomSummary>> {
|
||||||
val liveRealmData = RealmLiveData<RoomSummaryEntity>(monarchy.realmConfiguration) { realm ->
|
val liveData = monarchy.findAllMappedWithChanges(
|
||||||
RoomSummaryEntity.where(realm, roomId).isNotEmpty(RoomSummaryEntityFields.DISPLAY_NAME)
|
{ realm -> RoomSummaryEntity.where(realm, roomId).isNotEmpty(RoomSummaryEntityFields.DISPLAY_NAME) },
|
||||||
}
|
{ roomSummaryMapper.map(it) }
|
||||||
return Transformations.map(liveRealmData) { results ->
|
)
|
||||||
val roomSummaries = results.map { roomSummaryMapper.map(it) }
|
return Transformations.map(liveData) { results ->
|
||||||
|
results.firstOrNull().toOptional()
|
||||||
if (roomSummaries.isEmpty()) {
|
|
||||||
// Create a dummy RoomSummary to avoid Crash during Sign Out or clear cache
|
|
||||||
RoomSummary(roomId)
|
|
||||||
} else {
|
|
||||||
roomSummaries.first()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,6 @@ import com.zhuinden.monarchy.Monarchy
|
||||||
import im.vector.matrix.android.BuildConfig
|
import im.vector.matrix.android.BuildConfig
|
||||||
import im.vector.matrix.android.api.session.room.send.DraftService
|
import im.vector.matrix.android.api.session.room.send.DraftService
|
||||||
import im.vector.matrix.android.api.session.room.send.UserDraft
|
import im.vector.matrix.android.api.session.room.send.UserDraft
|
||||||
import im.vector.matrix.android.internal.database.RealmLiveData
|
|
||||||
import im.vector.matrix.android.internal.database.mapper.DraftMapper
|
import im.vector.matrix.android.internal.database.mapper.DraftMapper
|
||||||
import im.vector.matrix.android.internal.database.model.DraftEntity
|
import im.vector.matrix.android.internal.database.model.DraftEntity
|
||||||
import im.vector.matrix.android.internal.database.model.RoomSummaryEntity
|
import im.vector.matrix.android.internal.database.model.RoomSummaryEntity
|
||||||
|
@ -51,12 +50,13 @@ internal class DefaultDraftService @AssistedInject constructor(@Assisted private
|
||||||
|
|
||||||
monarchy.writeAsync { realm ->
|
monarchy.writeAsync { realm ->
|
||||||
|
|
||||||
val roomSummaryEntity = RoomSummaryEntity.where(realm, roomId).findFirst() ?: realm.createObject(roomId)
|
val roomSummaryEntity = RoomSummaryEntity.where(realm, roomId).findFirst()
|
||||||
|
?: realm.createObject(roomId)
|
||||||
|
|
||||||
val userDraftsEntity = roomSummaryEntity.userDrafts
|
val userDraftsEntity = roomSummaryEntity.userDrafts
|
||||||
?: realm.createObject<UserDraftsEntity>().also {
|
?: realm.createObject<UserDraftsEntity>().also {
|
||||||
roomSummaryEntity.userDrafts = it
|
roomSummaryEntity.userDrafts = it
|
||||||
}
|
}
|
||||||
|
|
||||||
userDraftsEntity.let { userDraftEntity ->
|
userDraftsEntity.let { userDraftEntity ->
|
||||||
// Save only valid draft
|
// Save only valid draft
|
||||||
|
@ -150,16 +150,16 @@ internal class DefaultDraftService @AssistedInject constructor(@Assisted private
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getDraftsLive(): LiveData<List<UserDraft>> {
|
override fun getDraftsLive(): LiveData<List<UserDraft>> {
|
||||||
val liveData = RealmLiveData(monarchy.realmConfiguration) {
|
val liveData = monarchy.findAllMappedWithChanges(
|
||||||
UserDraftsEntity.where(it, roomId)
|
{ UserDraftsEntity.where(it, roomId) },
|
||||||
}
|
{
|
||||||
|
it.userDrafts.map { draft ->
|
||||||
return Transformations.map(liveData) { userDraftsEntities ->
|
DraftMapper.map(draft)
|
||||||
userDraftsEntities.firstOrNull()?.let { userDraftEntity ->
|
}
|
||||||
userDraftEntity.userDrafts.map { draftEntity ->
|
|
||||||
DraftMapper.map(draftEntity)
|
|
||||||
}
|
}
|
||||||
} ?: emptyList()
|
)
|
||||||
|
return Transformations.map(liveData) {
|
||||||
|
it.firstOrNull() ?: emptyList()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ import im.vector.matrix.android.api.MatrixCallback
|
||||||
import im.vector.matrix.android.api.session.room.model.ReadReceipt
|
import im.vector.matrix.android.api.session.room.model.ReadReceipt
|
||||||
import im.vector.matrix.android.api.session.room.read.ReadService
|
import im.vector.matrix.android.api.session.room.read.ReadService
|
||||||
import im.vector.matrix.android.api.util.Optional
|
import im.vector.matrix.android.api.util.Optional
|
||||||
import im.vector.matrix.android.internal.database.RealmLiveData
|
import im.vector.matrix.android.api.util.toOptional
|
||||||
import im.vector.matrix.android.internal.database.mapper.ReadReceiptsSummaryMapper
|
import im.vector.matrix.android.internal.database.mapper.ReadReceiptsSummaryMapper
|
||||||
import im.vector.matrix.android.internal.database.model.ReadMarkerEntity
|
import im.vector.matrix.android.internal.database.model.ReadMarkerEntity
|
||||||
import im.vector.matrix.android.internal.database.model.ReadReceiptEntity
|
import im.vector.matrix.android.internal.database.model.ReadReceiptEntity
|
||||||
|
@ -82,33 +82,33 @@ internal class DefaultReadService @AssistedInject constructor(@Assisted private
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getReadMarkerLive(): LiveData<Optional<String>> {
|
override fun getReadMarkerLive(): LiveData<Optional<String>> {
|
||||||
val liveRealmData = RealmLiveData(monarchy.realmConfiguration) { realm ->
|
val liveRealmData = monarchy.findAllMappedWithChanges(
|
||||||
ReadMarkerEntity.where(realm, roomId)
|
{ ReadMarkerEntity.where(it, roomId) },
|
||||||
}
|
{ it.eventId }
|
||||||
return Transformations.map(liveRealmData) { results ->
|
)
|
||||||
Optional.from(results.firstOrNull()?.eventId)
|
return Transformations.map(liveRealmData) {
|
||||||
|
it.firstOrNull().toOptional()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getMyReadReceiptLive(): LiveData<Optional<String>> {
|
override fun getMyReadReceiptLive(): LiveData<Optional<String>> {
|
||||||
val liveRealmData = RealmLiveData(monarchy.realmConfiguration) { realm ->
|
val liveRealmData = monarchy.findAllMappedWithChanges(
|
||||||
ReadReceiptEntity.where(realm, roomId = roomId, userId = userId)
|
{ ReadReceiptEntity.where(it, roomId = roomId, userId = userId) },
|
||||||
}
|
{ it.eventId }
|
||||||
return Transformations.map(liveRealmData) { results ->
|
)
|
||||||
Optional.from(results.firstOrNull()?.eventId)
|
return Transformations.map(liveRealmData) {
|
||||||
|
it.firstOrNull().toOptional()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getEventReadReceiptsLive(eventId: String): LiveData<List<ReadReceipt>> {
|
override fun getEventReadReceiptsLive(eventId: String): LiveData<List<ReadReceipt>> {
|
||||||
val liveEntity = RealmLiveData(monarchy.realmConfiguration) { realm ->
|
|
||||||
ReadReceiptsSummaryEntity.where(realm, eventId)
|
val liveRealmData = monarchy.findAllMappedWithChanges(
|
||||||
}
|
{ ReadReceiptsSummaryEntity.where(it, eventId) },
|
||||||
return Transformations.map(liveEntity) { realmResults ->
|
{ readReceiptsSummaryMapper.map(it) }
|
||||||
realmResults.firstOrNull()?.let {
|
)
|
||||||
readReceiptsSummaryMapper.map(it)
|
return Transformations.map(liveRealmData) {
|
||||||
}?.sortedByDescending {
|
it.firstOrNull() ?: emptyList()
|
||||||
it.originServerTs
|
|
||||||
} ?: emptyList()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -30,7 +30,8 @@ import im.vector.matrix.android.api.session.room.model.message.MessageType
|
||||||
import im.vector.matrix.android.api.session.room.model.relation.RelationService
|
import im.vector.matrix.android.api.session.room.model.relation.RelationService
|
||||||
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||||
import im.vector.matrix.android.api.util.Cancelable
|
import im.vector.matrix.android.api.util.Cancelable
|
||||||
import im.vector.matrix.android.internal.database.RealmLiveData
|
import im.vector.matrix.android.api.util.Optional
|
||||||
|
import im.vector.matrix.android.api.util.toOptional
|
||||||
import im.vector.matrix.android.internal.database.helper.addSendingEvent
|
import im.vector.matrix.android.internal.database.helper.addSendingEvent
|
||||||
import im.vector.matrix.android.internal.database.mapper.asDomain
|
import im.vector.matrix.android.internal.database.mapper.asDomain
|
||||||
import im.vector.matrix.android.internal.database.model.EventAnnotationsSummaryEntity
|
import im.vector.matrix.android.internal.database.model.EventAnnotationsSummaryEntity
|
||||||
|
@ -210,13 +211,13 @@ internal class DefaultRelationService @AssistedInject constructor(@Assisted priv
|
||||||
return TimelineSendEventWorkCommon.createWork<SendEventWorker>(sendWorkData, startChain)
|
return TimelineSendEventWorkCommon.createWork<SendEventWorker>(sendWorkData, startChain)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getEventSummaryLive(eventId: String): LiveData<EventAnnotationsSummary> {
|
override fun getEventSummaryLive(eventId: String): LiveData<Optional<EventAnnotationsSummary>> {
|
||||||
val liveEntity = RealmLiveData(monarchy.realmConfiguration) { realm ->
|
val liveData = monarchy.findAllMappedWithChanges(
|
||||||
EventAnnotationsSummaryEntity.where(realm, eventId)
|
{ EventAnnotationsSummaryEntity.where(it, eventId)},
|
||||||
}
|
{ it.asDomain() }
|
||||||
return Transformations.map(liveEntity) { realmResults ->
|
)
|
||||||
realmResults.firstOrNull()?.asDomain()
|
return Transformations.map(liveData) { results ->
|
||||||
?: EventAnnotationsSummary(eventId, emptyList(), null)
|
results.firstOrNull().toOptional()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,8 @@ import im.vector.matrix.android.api.session.room.timeline.Timeline
|
||||||
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||||
import im.vector.matrix.android.api.session.room.timeline.TimelineService
|
import im.vector.matrix.android.api.session.room.timeline.TimelineService
|
||||||
import im.vector.matrix.android.api.session.room.timeline.TimelineSettings
|
import im.vector.matrix.android.api.session.room.timeline.TimelineSettings
|
||||||
import im.vector.matrix.android.internal.database.RealmLiveData
|
import im.vector.matrix.android.api.util.Optional
|
||||||
|
import im.vector.matrix.android.api.util.toOptional
|
||||||
import im.vector.matrix.android.internal.database.mapper.ReadReceiptsSummaryMapper
|
import im.vector.matrix.android.internal.database.mapper.ReadReceiptsSummaryMapper
|
||||||
import im.vector.matrix.android.internal.database.mapper.TimelineEventMapper
|
import im.vector.matrix.android.internal.database.mapper.TimelineEventMapper
|
||||||
import im.vector.matrix.android.internal.database.model.TimelineEventEntity
|
import im.vector.matrix.android.internal.database.model.TimelineEventEntity
|
||||||
|
@ -75,12 +76,13 @@ internal class DefaultTimelineService @AssistedInject constructor(@Assisted priv
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getTimeLineEventLive(eventId: String): LiveData<TimelineEvent> {
|
override fun getTimeLineEventLive(eventId: String): LiveData<Optional<TimelineEvent>> {
|
||||||
val liveData = RealmLiveData(monarchy.realmConfiguration) {
|
val liveData = monarchy.findAllMappedWithChanges(
|
||||||
TimelineEventEntity.where(it, roomId = roomId, eventId = eventId)
|
{ TimelineEventEntity.where(it, roomId = roomId, eventId = eventId) },
|
||||||
}
|
{ timelineEventMapper.map(it) }
|
||||||
|
)
|
||||||
return Transformations.map(liveData) { events ->
|
return Transformations.map(liveData) { events ->
|
||||||
events.firstOrNull()?.let { timelineEventMapper.map(it) }
|
events.firstOrNull().toOptional()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,6 @@ import im.vector.matrix.android.api.session.user.model.User
|
||||||
import im.vector.matrix.android.api.util.Cancelable
|
import im.vector.matrix.android.api.util.Cancelable
|
||||||
import im.vector.matrix.android.api.util.Optional
|
import im.vector.matrix.android.api.util.Optional
|
||||||
import im.vector.matrix.android.api.util.toOptional
|
import im.vector.matrix.android.api.util.toOptional
|
||||||
import im.vector.matrix.android.internal.database.RealmLiveData
|
|
||||||
import im.vector.matrix.android.internal.database.mapper.asDomain
|
import im.vector.matrix.android.internal.database.mapper.asDomain
|
||||||
import im.vector.matrix.android.internal.database.model.UserEntity
|
import im.vector.matrix.android.internal.database.model.UserEntity
|
||||||
import im.vector.matrix.android.internal.database.model.UserEntityFields
|
import im.vector.matrix.android.internal.database.model.UserEntityFields
|
||||||
|
@ -63,20 +62,18 @@ internal class DefaultUserService @Inject constructor(private val monarchy: Mona
|
||||||
|
|
||||||
override fun getUser(userId: String): User? {
|
override fun getUser(userId: String): User? {
|
||||||
val userEntity = monarchy.fetchCopied { UserEntity.where(it, userId).findFirst() }
|
val userEntity = monarchy.fetchCopied { UserEntity.where(it, userId).findFirst() }
|
||||||
?: return null
|
?: return null
|
||||||
|
|
||||||
return userEntity.asDomain()
|
return userEntity.asDomain()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun liveUser(userId: String): LiveData<Optional<User>> {
|
override fun liveUser(userId: String): LiveData<Optional<User>> {
|
||||||
val liveRealmData = RealmLiveData(monarchy.realmConfiguration) { realm ->
|
val liveData = monarchy.findAllMappedWithChanges(
|
||||||
UserEntity.where(realm, userId)
|
{ UserEntity.where(it, userId) },
|
||||||
}
|
{ it.asDomain() }
|
||||||
return Transformations.map(liveRealmData) { results ->
|
)
|
||||||
results
|
return Transformations.map(liveData) { results ->
|
||||||
.map { it.asDomain() }
|
results.firstOrNull().toOptional()
|
||||||
.firstOrNull()
|
|
||||||
.toOptional()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -234,7 +234,9 @@ dependencies {
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta1'
|
implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta1'
|
||||||
implementation 'androidx.core:core-ktx:1.0.2'
|
implementation 'androidx.core:core-ktx:1.0.2'
|
||||||
|
|
||||||
implementation 'com.jakewharton.threetenabp:threetenabp:1.1.1'
|
implementation "org.threeten:threetenbp:1.4.0:no-tzdb"
|
||||||
|
implementation "com.gabrielittner.threetenbp:lazythreetenbp:0.7.0"
|
||||||
|
|
||||||
implementation "com.squareup.moshi:moshi-adapters:$moshi_version"
|
implementation "com.squareup.moshi:moshi-adapters:$moshi_version"
|
||||||
kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshi_version"
|
kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshi_version"
|
||||||
|
|
||||||
|
@ -280,6 +282,7 @@ dependencies {
|
||||||
implementation "ru.noties.markwon:core:$markwon_version"
|
implementation "ru.noties.markwon:core:$markwon_version"
|
||||||
implementation "ru.noties.markwon:html:$markwon_version"
|
implementation "ru.noties.markwon:html:$markwon_version"
|
||||||
implementation 'me.saket:better-link-movement-method:2.2.0'
|
implementation 'me.saket:better-link-movement-method:2.2.0'
|
||||||
|
implementation 'com.google.android:flexbox:1.1.1'
|
||||||
|
|
||||||
// Bus
|
// Bus
|
||||||
implementation 'org.greenrobot:eventbus:3.1.1'
|
implementation 'org.greenrobot:eventbus:3.1.1'
|
||||||
|
|
|
@ -354,8 +354,11 @@ SOFTWARE.
|
||||||
<br/>
|
<br/>
|
||||||
Copyright (C) 2012-2017 Markus Junginger, greenrobot (http://greenrobot.org)
|
Copyright (C) 2012-2017 Markus Junginger, greenrobot (http://greenrobot.org)
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<b>LazyThreeTenBp</b>
|
||||||
|
<br/>
|
||||||
|
Copyright 2017 Gabriel Ittner.
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<pre>
|
<pre>
|
||||||
Apache License
|
Apache License
|
||||||
|
|
|
@ -31,9 +31,9 @@ import androidx.multidex.MultiDex
|
||||||
import com.airbnb.epoxy.EpoxyAsyncUtil
|
import com.airbnb.epoxy.EpoxyAsyncUtil
|
||||||
import com.airbnb.epoxy.EpoxyController
|
import com.airbnb.epoxy.EpoxyController
|
||||||
import com.facebook.stetho.Stetho
|
import com.facebook.stetho.Stetho
|
||||||
|
import com.gabrielittner.threetenbp.LazyThreeTen
|
||||||
import com.github.piasy.biv.BigImageViewer
|
import com.github.piasy.biv.BigImageViewer
|
||||||
import com.github.piasy.biv.loader.glide.GlideImageLoader
|
import com.github.piasy.biv.loader.glide.GlideImageLoader
|
||||||
import com.jakewharton.threetenabp.AndroidThreeTen
|
|
||||||
import im.vector.matrix.android.api.Matrix
|
import im.vector.matrix.android.api.Matrix
|
||||||
import im.vector.matrix.android.api.MatrixConfiguration
|
import im.vector.matrix.android.api.MatrixConfiguration
|
||||||
import im.vector.matrix.android.api.auth.Authenticator
|
import im.vector.matrix.android.api.auth.Authenticator
|
||||||
|
@ -96,7 +96,8 @@ class VectorApplication : Application(), HasVectorInjector, MatrixConfiguration.
|
||||||
Stetho.initializeWithDefaults(this)
|
Stetho.initializeWithDefaults(this)
|
||||||
}
|
}
|
||||||
logInfo()
|
logInfo()
|
||||||
AndroidThreeTen.init(this)
|
LazyThreeTen.init(this)
|
||||||
|
|
||||||
BigImageViewer.initialize(GlideImageLoader.with(applicationContext))
|
BigImageViewer.initialize(GlideImageLoader.with(applicationContext))
|
||||||
EpoxyController.defaultDiffingHandler = EpoxyAsyncUtil.getAsyncBackgroundHandler()
|
EpoxyController.defaultDiffingHandler = EpoxyAsyncUtil.getAsyncBackgroundHandler()
|
||||||
EpoxyController.defaultModelBuildingHandler = EpoxyAsyncUtil.getAsyncBackgroundHandler()
|
EpoxyController.defaultModelBuildingHandler = EpoxyAsyncUtil.getAsyncBackgroundHandler()
|
||||||
|
|
|
@ -70,7 +70,7 @@ class GroupListFragment : VectorBaseFragment(), GroupSummaryController.Callback
|
||||||
is Incomplete -> stateView.state = StateView.State.Loading
|
is Incomplete -> stateView.state = StateView.State.Loading
|
||||||
is Success -> stateView.state = StateView.State.Content
|
is Success -> stateView.state = StateView.State.Content
|
||||||
}
|
}
|
||||||
groupController.setData(state)
|
groupController.update(state)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onGroupSelected(groupSummary: GroupSummary) {
|
override fun onGroupSelected(groupSummary: GroupSummary) {
|
||||||
|
|
|
@ -16,17 +16,29 @@
|
||||||
|
|
||||||
package im.vector.riotx.features.home.group
|
package im.vector.riotx.features.home.group
|
||||||
|
|
||||||
|
import com.airbnb.epoxy.EpoxyController
|
||||||
import com.airbnb.epoxy.TypedEpoxyController
|
import com.airbnb.epoxy.TypedEpoxyController
|
||||||
import im.vector.matrix.android.api.session.group.model.GroupSummary
|
import im.vector.matrix.android.api.session.group.model.GroupSummary
|
||||||
import im.vector.riotx.features.home.AvatarRenderer
|
import im.vector.riotx.features.home.AvatarRenderer
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class GroupSummaryController @Inject constructor(private val avatarRenderer: AvatarRenderer): TypedEpoxyController<GroupListViewState>() {
|
class GroupSummaryController @Inject constructor(private val avatarRenderer: AvatarRenderer) : EpoxyController() {
|
||||||
|
|
||||||
var callback: Callback? = null
|
var callback: Callback? = null
|
||||||
|
private var viewState: GroupListViewState? = null
|
||||||
|
|
||||||
override fun buildModels(viewState: GroupListViewState) {
|
init {
|
||||||
buildGroupModels(viewState.asyncGroups(), viewState.selectedGroup)
|
requestModelBuild()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun update(viewState: GroupListViewState) {
|
||||||
|
this.viewState = viewState
|
||||||
|
requestModelBuild()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun buildModels() {
|
||||||
|
val nonNullViewState = viewState ?: return
|
||||||
|
buildGroupModels(nonNullViewState.asyncGroups(), nonNullViewState.selectedGroup)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun buildGroupModels(summaries: List<GroupSummary>?, selected: GroupSummary?) {
|
private fun buildGroupModels(summaries: List<GroupSummary>?, selected: GroupSummary?) {
|
||||||
|
|
|
@ -50,6 +50,7 @@ import im.vector.matrix.android.api.util.Optional
|
||||||
import im.vector.matrix.android.internal.crypto.attachments.toElementToDecrypt
|
import im.vector.matrix.android.internal.crypto.attachments.toElementToDecrypt
|
||||||
import im.vector.matrix.android.internal.crypto.model.event.EncryptedEventContent
|
import im.vector.matrix.android.internal.crypto.model.event.EncryptedEventContent
|
||||||
import im.vector.matrix.rx.rx
|
import im.vector.matrix.rx.rx
|
||||||
|
import im.vector.matrix.rx.unwrap
|
||||||
import im.vector.riotx.BuildConfig
|
import im.vector.riotx.BuildConfig
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
import im.vector.riotx.core.extensions.postLiveEvent
|
import im.vector.riotx.core.extensions.postLiveEvent
|
||||||
|
@ -719,6 +720,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
|
||||||
|
|
||||||
private fun observeRoomSummary() {
|
private fun observeRoomSummary() {
|
||||||
room.rx().liveRoomSummary()
|
room.rx().liveRoomSummary()
|
||||||
|
.unwrap()
|
||||||
.execute { async ->
|
.execute { async ->
|
||||||
copy(
|
copy(
|
||||||
asyncRoomSummary = async,
|
asyncRoomSummary = async,
|
||||||
|
|
|
@ -27,6 +27,7 @@ import im.vector.matrix.android.api.session.room.model.message.MessageType
|
||||||
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||||
import im.vector.matrix.android.api.session.room.timeline.getLastMessageContent
|
import im.vector.matrix.android.api.session.room.timeline.getLastMessageContent
|
||||||
import im.vector.matrix.rx.RxRoom
|
import im.vector.matrix.rx.RxRoom
|
||||||
|
import im.vector.matrix.rx.unwrap
|
||||||
import im.vector.riotx.core.extensions.canReact
|
import im.vector.riotx.core.extensions.canReact
|
||||||
import im.vector.riotx.core.platform.VectorViewModel
|
import im.vector.riotx.core.platform.VectorViewModel
|
||||||
import im.vector.riotx.features.home.room.detail.timeline.format.NoticeEventFormatter
|
import im.vector.riotx.features.home.room.detail.timeline.format.NoticeEventFormatter
|
||||||
|
@ -51,7 +52,7 @@ data class MessageActionState(
|
||||||
fun senderName(): String = informationData.memberName?.toString() ?: ""
|
fun senderName(): String = informationData.memberName?.toString() ?: ""
|
||||||
|
|
||||||
fun time(): String? = timelineEvent()?.root?.originServerTs?.let { dateFormat.format(Date(it)) }
|
fun time(): String? = timelineEvent()?.root?.originServerTs?.let { dateFormat.format(Date(it)) }
|
||||||
?: ""
|
?: ""
|
||||||
|
|
||||||
fun canReact() = timelineEvent()?.canReact() == true
|
fun canReact() = timelineEvent()?.canReact() == true
|
||||||
|
|
||||||
|
@ -61,7 +62,7 @@ data class MessageActionState(
|
||||||
val messageContent: MessageContent? = timelineEvent()?.getLastMessageContent()
|
val messageContent: MessageContent? = timelineEvent()?.getLastMessageContent()
|
||||||
if (messageContent is MessageTextContent && messageContent.format == MessageType.FORMAT_MATRIX_HTML) {
|
if (messageContent is MessageTextContent && messageContent.format == MessageType.FORMAT_MATRIX_HTML) {
|
||||||
eventHtmlRenderer?.render(messageContent.formattedBody
|
eventHtmlRenderer?.render(messageContent.formattedBody
|
||||||
?: messageContent.body)
|
?: messageContent.body)
|
||||||
} else {
|
} else {
|
||||||
messageContent?.body
|
messageContent?.body
|
||||||
}
|
}
|
||||||
|
@ -116,6 +117,7 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted
|
||||||
if (room == null) return
|
if (room == null) return
|
||||||
RxRoom(room)
|
RxRoom(room)
|
||||||
.liveTimelineEvent(eventId)
|
.liveTimelineEvent(eventId)
|
||||||
|
.unwrap()
|
||||||
.execute {
|
.execute {
|
||||||
copy(timelineEvent = it)
|
copy(timelineEvent = it)
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@ import im.vector.matrix.android.api.session.room.model.message.MessageType
|
||||||
import im.vector.matrix.android.api.session.room.send.SendState
|
import im.vector.matrix.android.api.session.room.send.SendState
|
||||||
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||||
import im.vector.matrix.android.api.session.room.timeline.hasBeenEdited
|
import im.vector.matrix.android.api.session.room.timeline.hasBeenEdited
|
||||||
|
import im.vector.matrix.android.api.util.Optional
|
||||||
import im.vector.matrix.rx.RxRoom
|
import im.vector.matrix.rx.RxRoom
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
import im.vector.riotx.core.extensions.canReact
|
import im.vector.riotx.core.extensions.canReact
|
||||||
|
@ -110,7 +111,9 @@ class MessageMenuViewModel @AssistedInject constructor(@Assisted initialState: M
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun actionsForEvent(event: TimelineEvent): List<SimpleAction> {
|
private fun actionsForEvent(optionalEvent: Optional<TimelineEvent>): List<SimpleAction> {
|
||||||
|
val event = optionalEvent.getOrNull() ?: return emptyList()
|
||||||
|
|
||||||
val messageContent: MessageContent? = event.annotations?.editSummary?.aggregatedContent.toModel()
|
val messageContent: MessageContent? = event.annotations?.editSummary?.aggregatedContent.toModel()
|
||||||
?: event.root.getClearContent().toModel()
|
?: event.root.getClearContent().toModel()
|
||||||
val type = messageContent?.type
|
val type = messageContent?.type
|
||||||
|
|
|
@ -77,7 +77,7 @@ class QuickReactionViewModel @AssistedInject constructor(@Assisted initialState:
|
||||||
.liveAnnotationSummary(eventId)
|
.liveAnnotationSummary(eventId)
|
||||||
.map { annotations ->
|
.map { annotations ->
|
||||||
quickEmojis.map { emoji ->
|
quickEmojis.map { emoji ->
|
||||||
ToggleState(emoji, annotations.reactionsSummary.firstOrNull { it.key == emoji }?.addedByMe
|
ToggleState(emoji, annotations.getOrNull()?.reactionsSummary?.firstOrNull { it.key == emoji }?.addedByMe
|
||||||
?: false)
|
?: false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ import com.squareup.inject.assisted.AssistedInject
|
||||||
import im.vector.matrix.android.api.session.Session
|
import im.vector.matrix.android.api.session.Session
|
||||||
import im.vector.matrix.android.api.session.room.model.ReactionAggregatedSummary
|
import im.vector.matrix.android.api.session.room.model.ReactionAggregatedSummary
|
||||||
import im.vector.matrix.rx.RxRoom
|
import im.vector.matrix.rx.RxRoom
|
||||||
|
import im.vector.matrix.rx.unwrap
|
||||||
import im.vector.riotx.core.platform.VectorViewModel
|
import im.vector.riotx.core.platform.VectorViewModel
|
||||||
import im.vector.riotx.core.utils.isSingleEmoji
|
import im.vector.riotx.core.utils.isSingleEmoji
|
||||||
import im.vector.riotx.core.date.VectorDateFormatter
|
import im.vector.riotx.core.date.VectorDateFormatter
|
||||||
|
@ -87,6 +88,7 @@ class ViewReactionViewModel @AssistedInject constructor(@Assisted
|
||||||
private fun observeEventAnnotationSummaries() {
|
private fun observeEventAnnotationSummaries() {
|
||||||
RxRoom(room)
|
RxRoom(room)
|
||||||
.liveAnnotationSummary(eventId)
|
.liveAnnotationSummary(eventId)
|
||||||
|
.unwrap()
|
||||||
.flatMapSingle { summaries ->
|
.flatMapSingle { summaries ->
|
||||||
Observable
|
Observable
|
||||||
.fromIterable(summaries.reactionsSummary)
|
.fromIterable(summaries.reactionsSummary)
|
||||||
|
|
|
@ -250,6 +250,7 @@ class MessageItemFactory @Inject constructor(
|
||||||
callback: TimelineEventController.Callback?,
|
callback: TimelineEventController.Callback?,
|
||||||
attributes: AbsMessageItem.Attributes): MessageTextItem? {
|
attributes: AbsMessageItem.Attributes): MessageTextItem? {
|
||||||
|
|
||||||
|
val isFormatted = messageContent.formattedBody.isNullOrBlank().not()
|
||||||
val bodyToUse = messageContent.formattedBody?.let {
|
val bodyToUse = messageContent.formattedBody?.let {
|
||||||
htmlRenderer.get().render(it.trim())
|
htmlRenderer.get().render(it.trim())
|
||||||
} ?: messageContent.body
|
} ?: messageContent.body
|
||||||
|
@ -265,6 +266,7 @@ class MessageItemFactory @Inject constructor(
|
||||||
message(linkifiedBody)
|
message(linkifiedBody)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.searchForPills(isFormatted)
|
||||||
.leftGuideline(avatarSizeProvider.leftGuideline)
|
.leftGuideline(avatarSizeProvider.leftGuideline)
|
||||||
.attributes(attributes)
|
.attributes(attributes)
|
||||||
.highlighted(highlight)
|
.highlighted(highlight)
|
||||||
|
|
|
@ -17,16 +17,11 @@
|
||||||
package im.vector.riotx.features.home.room.detail.timeline.item
|
package im.vector.riotx.features.home.room.detail.timeline.item
|
||||||
|
|
||||||
import android.graphics.Typeface
|
import android.graphics.Typeface
|
||||||
import android.os.Build
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.view.ViewStub
|
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.annotation.IdRes
|
import androidx.annotation.IdRes
|
||||||
import androidx.constraintlayout.helper.widget.Flow
|
|
||||||
import androidx.core.view.children
|
|
||||||
import androidx.core.view.isGone
|
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import com.airbnb.epoxy.EpoxyAttribute
|
import com.airbnb.epoxy.EpoxyAttribute
|
||||||
import im.vector.matrix.android.api.session.room.send.SendState
|
import im.vector.matrix.android.api.session.room.send.SendState
|
||||||
|
@ -122,38 +117,23 @@ abstract class AbsMessageItem<H : AbsMessageItem.Holder> : BaseEventItem<H>() {
|
||||||
_readMarkerCallback
|
_readMarkerCallback
|
||||||
)
|
)
|
||||||
|
|
||||||
if (!shouldShowReactionAtBottom() || attributes.informationData.orderedReactionList.isNullOrEmpty()) {
|
val reactions = attributes.informationData.orderedReactionList
|
||||||
holder.reactionWrapper?.isVisible = false
|
if (!shouldShowReactionAtBottom() || reactions.isNullOrEmpty()) {
|
||||||
|
holder.reactionsContainer.isVisible = false
|
||||||
} else {
|
} else {
|
||||||
//inflate if needed
|
holder.reactionsContainer.isVisible = true
|
||||||
if (holder.reactionFlowHelper == null) {
|
holder.reactionsContainer.removeAllViews()
|
||||||
holder.reactionWrapper = holder.view.findViewById<ViewStub>(R.id.messageBottomInfo).inflate() as? ViewGroup
|
reactions.take(8).forEach { reaction ->
|
||||||
holder.reactionFlowHelper = holder.view.findViewById(R.id.reactionsFlowHelper)
|
val reactionButton = ReactionButton(holder.view.context)
|
||||||
|
reactionButton.reactedListener = reactionClickListener
|
||||||
|
reactionButton.setTag(R.id.reactionsContainer, reaction.key)
|
||||||
|
reactionButton.reactionString = reaction.key
|
||||||
|
reactionButton.reactionCount = reaction.count
|
||||||
|
reactionButton.setChecked(reaction.addedByMe)
|
||||||
|
reactionButton.isEnabled = reaction.synced
|
||||||
|
holder.reactionsContainer.addView(reactionButton)
|
||||||
}
|
}
|
||||||
holder.reactionWrapper?.isVisible = true
|
holder.reactionsContainer.setOnLongClickListener(attributes.itemLongClickListener)
|
||||||
//clear all reaction buttons (but not the Flow helper!)
|
|
||||||
holder.reactionWrapper?.children?.forEach { (it as? ReactionButton)?.isGone = true }
|
|
||||||
val idToRefInFlow = ArrayList<Int>()
|
|
||||||
attributes.informationData.orderedReactionList?.chunked(8)?.firstOrNull()?.forEachIndexed { index, reaction ->
|
|
||||||
(holder.reactionWrapper?.children?.elementAtOrNull(index) as? ReactionButton)?.let { reactionButton ->
|
|
||||||
reactionButton.isVisible = true
|
|
||||||
reactionButton.reactedListener = reactionClickListener
|
|
||||||
reactionButton.setTag(R.id.messageBottomInfo, reaction.key)
|
|
||||||
idToRefInFlow.add(reactionButton.id)
|
|
||||||
reactionButton.reactionString = reaction.key
|
|
||||||
reactionButton.reactionCount = reaction.count
|
|
||||||
reactionButton.setChecked(reaction.addedByMe)
|
|
||||||
reactionButton.isEnabled = reaction.synced
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Just setting the view as gone will break the FlowHelper (and invisible will take too much space),
|
|
||||||
// so have to update ref ids
|
|
||||||
holder.reactionFlowHelper?.referencedIds = idToRefInFlow.toIntArray()
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2 && !holder.view.isInLayout) {
|
|
||||||
holder.reactionFlowHelper?.requestLayout()
|
|
||||||
}
|
|
||||||
holder.reactionWrapper?.setOnLongClickListener(attributes.itemLongClickListener)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,8 +161,7 @@ abstract class AbsMessageItem<H : AbsMessageItem.Holder> : BaseEventItem<H>() {
|
||||||
val avatarImageView by bind<ImageView>(R.id.messageAvatarImageView)
|
val avatarImageView by bind<ImageView>(R.id.messageAvatarImageView)
|
||||||
val memberNameView by bind<TextView>(R.id.messageMemberNameView)
|
val memberNameView by bind<TextView>(R.id.messageMemberNameView)
|
||||||
val timeView by bind<TextView>(R.id.messageTimeView)
|
val timeView by bind<TextView>(R.id.messageTimeView)
|
||||||
var reactionWrapper: ViewGroup? = null
|
val reactionsContainer by bind<ViewGroup>(R.id.reactionsContainer)
|
||||||
var reactionFlowHelper: Flow? = null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -17,8 +17,11 @@ package im.vector.riotx.features.home.room.detail.timeline.item
|
||||||
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewStub
|
import android.view.ViewStub
|
||||||
|
import android.widget.RelativeLayout
|
||||||
import androidx.annotation.IdRes
|
import androidx.annotation.IdRes
|
||||||
import androidx.constraintlayout.widget.Guideline
|
import androidx.constraintlayout.widget.Guideline
|
||||||
|
import androidx.core.view.marginStart
|
||||||
|
import androidx.core.view.updateLayoutParams
|
||||||
import com.airbnb.epoxy.EpoxyAttribute
|
import com.airbnb.epoxy.EpoxyAttribute
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
import im.vector.riotx.core.epoxy.VectorEpoxyHolder
|
import im.vector.riotx.core.epoxy.VectorEpoxyHolder
|
||||||
|
@ -44,7 +47,9 @@ abstract class BaseEventItem<H : BaseEventItem.BaseHolder> : VectorEpoxyModel<H>
|
||||||
|
|
||||||
override fun bind(holder: H) {
|
override fun bind(holder: H) {
|
||||||
super.bind(holder)
|
super.bind(holder)
|
||||||
holder.leftGuideline.setGuidelineBegin(leftGuideline)
|
holder.leftGuideline.updateLayoutParams<RelativeLayout.LayoutParams> {
|
||||||
|
this.marginStart = leftGuideline
|
||||||
|
}
|
||||||
holder.checkableBackground.isChecked = highlighted
|
holder.checkableBackground.isChecked = highlighted
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,7 +60,7 @@ abstract class BaseEventItem<H : BaseEventItem.BaseHolder> : VectorEpoxyModel<H>
|
||||||
abstract fun getEventIds(): List<String>
|
abstract fun getEventIds(): List<String>
|
||||||
|
|
||||||
abstract class BaseHolder(@IdRes val stubId: Int) : VectorEpoxyHolder() {
|
abstract class BaseHolder(@IdRes val stubId: Int) : VectorEpoxyHolder() {
|
||||||
val leftGuideline by bind<Guideline>(R.id.messageStartGuideline)
|
val leftGuideline by bind<View>(R.id.messageStartGuideline)
|
||||||
val checkableBackground by bind<CheckableView>(R.id.messageSelectedBackground)
|
val checkableBackground by bind<CheckableView>(R.id.messageSelectedBackground)
|
||||||
val readReceiptsView by bind<ReadReceiptsView>(R.id.readReceiptsView)
|
val readReceiptsView by bind<ReadReceiptsView>(R.id.readReceiptsView)
|
||||||
val readMarkerView by bind<ReadMarkerView>(R.id.readMarkerView)
|
val readMarkerView by bind<ReadMarkerView>(R.id.readMarkerView)
|
||||||
|
|
|
@ -35,6 +35,8 @@ import me.saket.bettermovementmethod.BetterLinkMovementMethod
|
||||||
@EpoxyModelClass(layout = R.layout.item_timeline_event_base)
|
@EpoxyModelClass(layout = R.layout.item_timeline_event_base)
|
||||||
abstract class MessageTextItem : AbsMessageItem<MessageTextItem.Holder>() {
|
abstract class MessageTextItem : AbsMessageItem<MessageTextItem.Holder>() {
|
||||||
|
|
||||||
|
@EpoxyAttribute
|
||||||
|
var searchForPills: Boolean = false
|
||||||
@EpoxyAttribute
|
@EpoxyAttribute
|
||||||
var message: CharSequence? = null
|
var message: CharSequence? = null
|
||||||
@EpoxyAttribute
|
@EpoxyAttribute
|
||||||
|
@ -65,22 +67,22 @@ abstract class MessageTextItem : AbsMessageItem<MessageTextItem.Holder>() {
|
||||||
override fun bind(holder: Holder) {
|
override fun bind(holder: Holder) {
|
||||||
super.bind(holder)
|
super.bind(holder)
|
||||||
holder.messageView.movementMethod = mvmtMethod
|
holder.messageView.movementMethod = mvmtMethod
|
||||||
|
|
||||||
if (useBigFont) {
|
if (useBigFont) {
|
||||||
holder.messageView.textSize = 44F
|
holder.messageView.textSize = 44F
|
||||||
} else {
|
} else {
|
||||||
holder.messageView.textSize = 14F
|
holder.messageView.textSize = 14F
|
||||||
}
|
}
|
||||||
|
|
||||||
val textFuture = PrecomputedTextCompat.getTextFuture(message ?: "",
|
|
||||||
TextViewCompat.getTextMetricsParams(holder.messageView),
|
|
||||||
null)
|
|
||||||
|
|
||||||
holder.messageView.setTextFuture(textFuture)
|
|
||||||
renderSendState(holder.messageView, holder.messageView)
|
renderSendState(holder.messageView, holder.messageView)
|
||||||
holder.messageView.setOnClickListener(attributes.itemClickListener)
|
holder.messageView.setOnClickListener(attributes.itemClickListener)
|
||||||
holder.messageView.setOnLongClickListener(attributes.itemLongClickListener)
|
holder.messageView.setOnLongClickListener(attributes.itemLongClickListener)
|
||||||
findPillsAndProcess { it.bind(holder.messageView) }
|
if (searchForPills) {
|
||||||
|
findPillsAndProcess { it.bind(holder.messageView) }
|
||||||
|
}
|
||||||
|
val textFuture = PrecomputedTextCompat.getTextFuture(
|
||||||
|
message ?: "",
|
||||||
|
TextViewCompat.getTextMetricsParams(holder.messageView),
|
||||||
|
null)
|
||||||
|
holder.messageView.setTextFuture(textFuture)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun findPillsAndProcess(processBlock: (span: PillImageSpan) -> Unit) {
|
private fun findPillsAndProcess(processBlock: (span: PillImageSpan) -> Unit) {
|
||||||
|
|
|
@ -41,6 +41,7 @@ import im.vector.riotx.features.home.room.list.widget.FabMenuView
|
||||||
import im.vector.riotx.features.notifications.NotificationDrawerManager
|
import im.vector.riotx.features.notifications.NotificationDrawerManager
|
||||||
import kotlinx.android.parcel.Parcelize
|
import kotlinx.android.parcel.Parcelize
|
||||||
import kotlinx.android.synthetic.main.fragment_room_list.*
|
import kotlinx.android.synthetic.main.fragment_room_list.*
|
||||||
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
|
@ -180,7 +181,7 @@ class RoomListFragment : VectorBaseFragment(), RoomSummaryController.Listener, O
|
||||||
is Success -> renderSuccess(state)
|
is Success -> renderSuccess(state)
|
||||||
is Fail -> renderFailure(state.asyncFilteredRooms.error)
|
is Fail -> renderFailure(state.asyncFilteredRooms.error)
|
||||||
}
|
}
|
||||||
roomController.setData(state)
|
roomController.update(state)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun renderSuccess(state: RoomListViewState) {
|
private fun renderSuccess(state: RoomListViewState) {
|
||||||
|
|
|
@ -17,40 +17,56 @@
|
||||||
package im.vector.riotx.features.home.room.list
|
package im.vector.riotx.features.home.room.list
|
||||||
|
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
|
import com.airbnb.epoxy.EpoxyController
|
||||||
import com.airbnb.epoxy.TypedEpoxyController
|
import com.airbnb.epoxy.TypedEpoxyController
|
||||||
import im.vector.matrix.android.api.session.room.model.Membership
|
import im.vector.matrix.android.api.session.room.model.Membership
|
||||||
import im.vector.matrix.android.api.session.room.model.RoomSummary
|
import im.vector.matrix.android.api.session.room.model.RoomSummary
|
||||||
import im.vector.riotx.core.resources.StringProvider
|
import im.vector.riotx.core.resources.StringProvider
|
||||||
import im.vector.riotx.features.home.room.filtered.FilteredRoomFooterItem
|
import im.vector.riotx.features.home.room.filtered.FilteredRoomFooterItem
|
||||||
import im.vector.riotx.features.home.room.filtered.filteredRoomFooterItem
|
import im.vector.riotx.features.home.room.filtered.filteredRoomFooterItem
|
||||||
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class RoomSummaryController @Inject constructor(private val stringProvider: StringProvider,
|
class RoomSummaryController @Inject constructor(private val stringProvider: StringProvider,
|
||||||
private val roomSummaryItemFactory: RoomSummaryItemFactory,
|
private val roomSummaryItemFactory: RoomSummaryItemFactory,
|
||||||
private val roomListNameFilter: RoomListNameFilter
|
private val roomListNameFilter: RoomListNameFilter
|
||||||
) : TypedEpoxyController<RoomListViewState>() {
|
) : EpoxyController() {
|
||||||
|
|
||||||
var listener: Listener? = null
|
var listener: Listener? = null
|
||||||
|
|
||||||
override fun buildModels(viewState: RoomListViewState) {
|
private var viewState: RoomListViewState? = null
|
||||||
if (viewState.displayMode == RoomListFragment.DisplayMode.FILTERED) {
|
|
||||||
buildFilteredRooms(viewState)
|
init {
|
||||||
|
// We are requesting a model build directly as the first build of epoxy is on the main thread.
|
||||||
|
// It avoids to build the the whole list of rooms on the main thread.
|
||||||
|
requestModelBuild()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun update(viewState: RoomListViewState) {
|
||||||
|
this.viewState = viewState
|
||||||
|
requestModelBuild()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun buildModels() {
|
||||||
|
val nonNullViewState = viewState ?: return
|
||||||
|
if (nonNullViewState.displayMode == RoomListFragment.DisplayMode.FILTERED) {
|
||||||
|
buildFilteredRooms(nonNullViewState)
|
||||||
} else {
|
} else {
|
||||||
val roomSummaries = viewState.asyncFilteredRooms()
|
val roomSummaries = nonNullViewState.asyncFilteredRooms()
|
||||||
roomSummaries?.forEach { (category, summaries) ->
|
roomSummaries?.forEach { (category, summaries) ->
|
||||||
if (summaries.isEmpty()) {
|
if (summaries.isEmpty()) {
|
||||||
return@forEach
|
return@forEach
|
||||||
} else {
|
} else {
|
||||||
val isExpanded = viewState.isCategoryExpanded(category)
|
val isExpanded = nonNullViewState.isCategoryExpanded(category)
|
||||||
buildRoomCategory(viewState, summaries, category.titleRes, viewState.isCategoryExpanded(category)) {
|
buildRoomCategory(nonNullViewState, summaries, category.titleRes, nonNullViewState.isCategoryExpanded(category)) {
|
||||||
listener?.onToggleRoomCategory(category)
|
listener?.onToggleRoomCategory(category)
|
||||||
}
|
}
|
||||||
if (isExpanded) {
|
if (isExpanded) {
|
||||||
buildRoomModels(summaries,
|
buildRoomModels(summaries,
|
||||||
viewState.joiningRoomsIds,
|
nonNullViewState.joiningRoomsIds,
|
||||||
viewState.joiningErrorRoomsIds,
|
nonNullViewState.joiningErrorRoomsIds,
|
||||||
viewState.rejectingRoomsIds,
|
nonNullViewState.rejectingRoomsIds,
|
||||||
viewState.rejectingErrorRoomsIds)
|
nonNullViewState.rejectingErrorRoomsIds)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,10 +82,10 @@ class RoomSummaryController @Inject constructor(private val stringProvider: Stri
|
||||||
.filter { it.membership == Membership.JOIN && roomListNameFilter.test(it) }
|
.filter { it.membership == Membership.JOIN && roomListNameFilter.test(it) }
|
||||||
|
|
||||||
buildRoomModels(filteredSummaries,
|
buildRoomModels(filteredSummaries,
|
||||||
viewState.joiningRoomsIds,
|
viewState.joiningRoomsIds,
|
||||||
viewState.joiningErrorRoomsIds,
|
viewState.joiningErrorRoomsIds,
|
||||||
viewState.rejectingRoomsIds,
|
viewState.rejectingRoomsIds,
|
||||||
viewState.rejectingErrorRoomsIds)
|
viewState.rejectingErrorRoomsIds)
|
||||||
|
|
||||||
addFilterFooter(viewState)
|
addFilterFooter(viewState)
|
||||||
}
|
}
|
||||||
|
@ -105,7 +121,7 @@ class RoomSummaryController @Inject constructor(private val stringProvider: Stri
|
||||||
showHighlighted(showHighlighted)
|
showHighlighted(showHighlighted)
|
||||||
listener {
|
listener {
|
||||||
mutateExpandedState()
|
mutateExpandedState()
|
||||||
setData(viewState)
|
update(viewState)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
|
<size
|
||||||
|
android:width="8dp"
|
||||||
|
android:height="8dp" />
|
||||||
|
<solid android:color="#00000000" />
|
||||||
|
</shape>
|
|
@ -1,5 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
@ -9,139 +9,135 @@
|
||||||
|
|
||||||
<im.vector.riotx.core.platform.CheckableView
|
<im.vector.riotx.core.platform.CheckableView
|
||||||
android:id="@+id/messageSelectedBackground"
|
android:id="@+id/messageSelectedBackground"
|
||||||
android:layout_width="0dp"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="0dp"
|
android:layout_height="match_parent"
|
||||||
android:background="?riotx_highlighted_message_background"
|
android:layout_alignBottom="@+id/readMarkerView"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
android:layout_alignParentTop="true"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
android:background="?riotx_highlighted_message_background" />
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/messageAvatarImageView"
|
android:id="@+id/messageAvatarImageView"
|
||||||
android:layout_width="44dp"
|
android:layout_width="44dp"
|
||||||
android:layout_height="44dp"
|
android:layout_height="44dp"
|
||||||
android:layout_marginStart="8dp"
|
android:layout_marginStart="8dp"
|
||||||
android:layout_marginLeft="8dp"
|
|
||||||
android:layout_marginTop="4dp"
|
android:layout_marginTop="4dp"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
tools:src="@tools:sample/avatars" />
|
tools:src="@tools:sample/avatars" />
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.Guideline
|
|
||||||
android:id="@+id/messageStartGuideline"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical"
|
|
||||||
tools:layout_constraintGuide_begin="52dp" />
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/messageMemberNameView"
|
android:id="@+id/messageMemberNameView"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentTop="true"
|
||||||
android:layout_marginStart="8dp"
|
android:layout_marginStart="8dp"
|
||||||
android:layout_marginLeft="8dp"
|
android:layout_marginLeft="8dp"
|
||||||
android:layout_marginTop="4dp"
|
android:layout_marginTop="4dp"
|
||||||
android:layout_marginEnd="4dp"
|
android:layout_marginEnd="4dp"
|
||||||
android:layout_marginRight="8dp"
|
android:layout_marginRight="8dp"
|
||||||
|
android:layout_toStartOf="@+id/messageTimeView"
|
||||||
|
android:layout_toEndOf="@+id/messageStartGuideline"
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
android:textColor="?riotx_text_primary"
|
android:textColor="?riotx_text_primary"
|
||||||
android:textSize="15sp"
|
android:textSize="15sp"
|
||||||
android:textStyle="bold"
|
android:textStyle="bold"
|
||||||
app:layout_constrainedWidth="true"
|
|
||||||
app:layout_constraintEnd_toStartOf="@+id/messageTimeView"
|
|
||||||
app:layout_constraintHorizontal_bias="0"
|
|
||||||
app:layout_constraintHorizontal_chainStyle="spread_inside"
|
|
||||||
app:layout_constraintStart_toEndOf="@id/messageStartGuideline"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
tools:text="@sample/matrix.json/data/displayName" />
|
tools:text="@sample/matrix.json/data/displayName" />
|
||||||
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/messageTimeView"
|
android:id="@+id/messageTimeView"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignBaseline="@+id/messageMemberNameView"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
android:layout_marginStart="8dp"
|
android:layout_marginStart="8dp"
|
||||||
android:layout_marginLeft="8dp"
|
android:layout_marginLeft="8dp"
|
||||||
android:layout_marginEnd="8dp"
|
android:layout_marginEnd="8dp"
|
||||||
android:layout_marginRight="8dp"
|
android:layout_marginRight="8dp"
|
||||||
android:textColor="?riotx_text_secondary"
|
android:textColor="?riotx_text_secondary"
|
||||||
android:textSize="12sp"
|
android:textSize="12sp"
|
||||||
app:layout_constraintBaseline_toBaselineOf="@id/messageMemberNameView"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toEndOf="@id/messageMemberNameView"
|
|
||||||
tools:text="@tools:sample/date/hhmm" />
|
tools:text="@tools:sample/date/hhmm" />
|
||||||
|
|
||||||
|
<View
|
||||||
<ViewStub
|
android:id="@+id/messageStartGuideline"
|
||||||
android:id="@+id/messageContentTextStub"
|
|
||||||
style="@style/TimelineContentStubLayoutParams"
|
|
||||||
android:inflatedId="@id/messageTextView"
|
|
||||||
android:layout="@layout/item_timeline_event_text_message_stub"
|
|
||||||
tools:ignore="MissingConstraints"
|
|
||||||
tools:visibility="visible" />
|
|
||||||
|
|
||||||
<ViewStub
|
|
||||||
android:id="@+id/messageContentMediaStub"
|
|
||||||
style="@style/TimelineContentStubLayoutParams"
|
|
||||||
android:inflatedId="@+id/messageContentMedia"
|
|
||||||
android:layout="@layout/item_timeline_event_media_message_stub"
|
|
||||||
tools:ignore="MissingConstraints" />
|
|
||||||
|
|
||||||
<ViewStub
|
|
||||||
android:id="@+id/messageContentFileStub"
|
|
||||||
style="@style/TimelineContentStubLayoutParams"
|
|
||||||
android:layout="@layout/item_timeline_event_file_stub"
|
|
||||||
tools:ignore="MissingConstraints" />
|
|
||||||
|
|
||||||
<ViewStub
|
|
||||||
android:id="@+id/messageContentRedactedStub"
|
|
||||||
style="@style/TimelineContentStubLayoutParams"
|
|
||||||
android:layout_height="20dp"
|
|
||||||
android:layout_marginEnd="56dp"
|
|
||||||
android:layout_marginRight="56dp"
|
|
||||||
android:layout="@layout/item_timeline_event_redacted_stub"
|
|
||||||
tools:ignore="MissingConstraints" />
|
|
||||||
|
|
||||||
|
|
||||||
<!-- TODO: For now we show 8 reactions maximum, this will need rework when needed-->
|
|
||||||
<ViewStub
|
|
||||||
android:id="@+id/messageBottomInfo"
|
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="0dp"
|
||||||
android:layout_marginBottom="4dp"
|
tools:layout_marginStart="52dp" />
|
||||||
android:inflatedId="@+id/messageBottomInfo"
|
|
||||||
android:layout="@layout/item_timeline_event_bottom_reactions_stub"
|
|
||||||
android:visibility="gone"
|
|
||||||
app:layout_constraintBottom_toTopOf="@+id/readReceiptsView"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toEndOf="@id/messageStartGuideline"
|
|
||||||
app:layout_constraintVertical_chainStyle="packed"
|
|
||||||
tools:visibility="visible">
|
|
||||||
|
|
||||||
</ViewStub>
|
<FrameLayout
|
||||||
|
android:id="@+id/viewStubContainer"
|
||||||
<im.vector.riotx.core.ui.views.ReadReceiptsView
|
android:layout_width="match_parent"
|
||||||
android:id="@+id/readReceiptsView"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginEnd="8dp"
|
android:layout_below="@id/messageMemberNameView"
|
||||||
android:layout_marginBottom="4dp"
|
android:layout_toEndOf="@id/messageStartGuideline"
|
||||||
app:layout_constraintBottom_toTopOf="@+id/readMarkerView"
|
android:addStatesFromChildren="true">
|
||||||
app:layout_constraintEnd_toEndOf="parent" />
|
|
||||||
|
<ViewStub
|
||||||
|
android:id="@+id/messageContentTextStub"
|
||||||
|
style="@style/TimelineContentStubBaseParams"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:inflatedId="@id/messageTextView"
|
||||||
|
android:layout="@layout/item_timeline_event_text_message_stub"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<ViewStub
|
||||||
|
android:id="@+id/messageContentMediaStub"
|
||||||
|
style="@style/TimelineContentStubBaseParams"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:inflatedId="@+id/messageContentMedia"
|
||||||
|
android:layout="@layout/item_timeline_event_media_message_stub" />
|
||||||
|
|
||||||
|
<ViewStub
|
||||||
|
android:id="@+id/messageContentFileStub"
|
||||||
|
style="@style/TimelineContentStubBaseParams"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout="@layout/item_timeline_event_file_stub" />
|
||||||
|
|
||||||
|
<ViewStub
|
||||||
|
android:id="@+id/messageContentRedactedStub"
|
||||||
|
style="@style/TimelineContentStubBaseParams"
|
||||||
|
android:layout_height="20dp"
|
||||||
|
android:layout_marginEnd="56dp"
|
||||||
|
android:layout="@layout/item_timeline_event_redacted_stub" />
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/informationBottom"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_below="@+id/viewStubContainer"
|
||||||
|
android:layout_toEndOf="@+id/messageStartGuideline"
|
||||||
|
android:addStatesFromChildren="true"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<com.google.android.flexbox.FlexboxLayout
|
||||||
|
android:id="@+id/reactionsContainer"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:layout_marginBottom="4dp"
|
||||||
|
app:dividerDrawable="@drawable/reaction_divider"
|
||||||
|
app:flexWrap="wrap"
|
||||||
|
app:showDivider="middle" />
|
||||||
|
|
||||||
|
<im.vector.riotx.core.ui.views.ReadReceiptsView
|
||||||
|
android:id="@+id/readReceiptsView"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="end"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:layout_marginBottom="4dp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<im.vector.riotx.core.ui.views.ReadMarkerView
|
<im.vector.riotx.core.ui.views.ReadMarkerView
|
||||||
android:id="@+id/readMarkerView"
|
android:id="@+id/readMarkerView"
|
||||||
android:layout_width="0dp"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="2dp"
|
android:layout_height="2dp"
|
||||||
android:background="?attr/vctr_unread_marker_line_color"
|
android:layout_below="@+id/informationBottom"
|
||||||
android:layout_marginBottom="2dp"
|
|
||||||
android:visibility="invisible"
|
|
||||||
android:layout_marginEnd="8dp"
|
|
||||||
android:layout_marginStart="8dp"
|
android:layout_marginStart="8dp"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
android:layout_marginEnd="8dp"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
android:layout_marginBottom="2dp"
|
||||||
app:layout_constraintStart_toStartOf="parent" />
|
android:background="?attr/vctr_unread_marker_line_color"
|
||||||
|
android:visibility="invisible" />
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</RelativeLayout>
|
|
@ -1,6 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<RelativeLayout 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"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
@ -9,70 +8,76 @@
|
||||||
|
|
||||||
<im.vector.riotx.core.platform.CheckableView
|
<im.vector.riotx.core.platform.CheckableView
|
||||||
android:id="@+id/messageSelectedBackground"
|
android:id="@+id/messageSelectedBackground"
|
||||||
android:layout_width="0dp"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="0dp"
|
android:layout_height="match_parent"
|
||||||
android:background="?riotx_highlighted_message_background"
|
android:layout_alignBottom="@+id/informationBottom"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
android:layout_alignParentTop="true"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
android:background="?riotx_highlighted_message_background" />
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.Guideline
|
<View
|
||||||
android:id="@+id/messageStartGuideline"
|
android:id="@+id/messageStartGuideline"
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical"
|
|
||||||
tools:layout_constraintGuide_begin="52dp" />
|
|
||||||
|
|
||||||
<ViewStub
|
|
||||||
android:id="@+id/messageContentNoticeStub"
|
|
||||||
style="@style/TimelineContentStubNoInfoLayoutParams"
|
|
||||||
android:layout="@layout/item_timeline_event_notice_stub"
|
|
||||||
tools:ignore="MissingConstraints"
|
|
||||||
tools:visibility="visible" />
|
|
||||||
|
|
||||||
<ViewStub
|
|
||||||
android:id="@+id/messageContentDefaultStub"
|
|
||||||
style="@style/TimelineContentStubNoInfoLayoutParams"
|
|
||||||
android:inflatedId="@+id/stateMessageView"
|
|
||||||
android:layout="@layout/item_timeline_event_default_stub"
|
|
||||||
tools:ignore="MissingConstraints" />
|
|
||||||
|
|
||||||
<ViewStub
|
|
||||||
android:id="@+id/messageContentBlankStub"
|
|
||||||
style="@style/TimelineContentStubNoInfoLayoutParams"
|
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
android:layout="@layout/item_timeline_event_blank_stub"
|
android:layout_marginStart="52dp" />
|
||||||
tools:ignore="MissingConstraints" />
|
|
||||||
|
|
||||||
<ViewStub
|
<FrameLayout
|
||||||
android:id="@+id/messageContentMergedheaderStub"
|
android:id="@+id/viewStubContainer"
|
||||||
style="@style/TimelineContentStubNoInfoLayoutParams"
|
android:layout_width="match_parent"
|
||||||
android:layout="@layout/item_timeline_event_merged_header_stub"
|
|
||||||
tools:ignore="MissingConstraints" />
|
|
||||||
|
|
||||||
<im.vector.riotx.core.ui.views.ReadReceiptsView
|
|
||||||
android:id="@+id/readReceiptsView"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginEnd="8dp"
|
android:layout_alignParentTop="true"
|
||||||
android:layout_marginBottom="4dp"
|
android:layout_toEndOf="@id/messageStartGuideline">
|
||||||
app:layout_constraintBottom_toTopOf="@+id/readMarkerView"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent" />
|
|
||||||
|
|
||||||
<im.vector.riotx.core.ui.views.ReadMarkerView
|
<ViewStub
|
||||||
android:id="@+id/readMarkerView"
|
android:id="@+id/messageContentNoticeStub"
|
||||||
android:layout_width="0dp"
|
style="@style/TimelineContentStubBaseParams"
|
||||||
android:layout_height="2dp"
|
android:layout="@layout/item_timeline_event_notice_stub"
|
||||||
android:layout_marginStart="8dp"
|
tools:visibility="visible" />
|
||||||
android:layout_marginEnd="8dp"
|
|
||||||
android:layout_marginBottom="2dp"
|
|
||||||
android:background="?attr/vctr_unread_marker_line_color"
|
|
||||||
android:visibility="invisible"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent" />
|
|
||||||
|
|
||||||
|
<ViewStub
|
||||||
|
android:id="@+id/messageContentDefaultStub"
|
||||||
|
style="@style/TimelineContentStubBaseParams"
|
||||||
|
android:inflatedId="@+id/stateMessageView"
|
||||||
|
android:layout="@layout/item_timeline_event_default_stub" />
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
<ViewStub
|
||||||
|
android:id="@+id/messageContentBlankStub"
|
||||||
|
style="@style/TimelineContentStubBaseParams"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout="@layout/item_timeline_event_blank_stub" />
|
||||||
|
|
||||||
|
<ViewStub
|
||||||
|
android:id="@+id/messageContentMergedheaderStub"
|
||||||
|
style="@style/TimelineContentStubBaseParams"
|
||||||
|
android:layout="@layout/item_timeline_event_merged_header_stub" />
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/informationBottom"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_below="@id/viewStubContainer"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<im.vector.riotx.core.ui.views.ReadReceiptsView
|
||||||
|
android:id="@+id/readReceiptsView"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="end"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:layout_marginBottom="4dp" />
|
||||||
|
|
||||||
|
<im.vector.riotx.core.ui.views.ReadMarkerView
|
||||||
|
android:id="@+id/readMarkerView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="2dp"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:layout_marginBottom="2dp"
|
||||||
|
android:background="?attr/vctr_unread_marker_line_color"
|
||||||
|
android:visibility="invisible" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</RelativeLayout>
|
|
@ -1,102 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout 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/messageBottomInfo"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content">
|
|
||||||
|
|
||||||
<im.vector.riotx.features.reactions.widget.ReactionButton
|
|
||||||
android:id="@+id/messageBottomReaction1"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:visibility="gone"
|
|
||||||
tools:emoji="👍"
|
|
||||||
tools:ignore="MissingConstraints"
|
|
||||||
tools:reaction_count="3"
|
|
||||||
tools:visibility="visible" />
|
|
||||||
|
|
||||||
<im.vector.riotx.features.reactions.widget.ReactionButton
|
|
||||||
android:id="@+id/messageBottomReaction2"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:visibility="gone"
|
|
||||||
tools:emoji="👎"
|
|
||||||
tools:ignore="MissingConstraints"
|
|
||||||
tools:reaction_count="10"
|
|
||||||
tools:visibility="visible" />
|
|
||||||
|
|
||||||
<im.vector.riotx.features.reactions.widget.ReactionButton
|
|
||||||
android:id="@+id/messageBottomReaction3"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:visibility="gone"
|
|
||||||
tools:emoji="😀"
|
|
||||||
tools:ignore="MissingConstraints"
|
|
||||||
tools:visibility="visible" />
|
|
||||||
|
|
||||||
<im.vector.riotx.features.reactions.widget.ReactionButton
|
|
||||||
android:id="@+id/messageBottomReaction4"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:visibility="gone"
|
|
||||||
tools:emoji="☹️"
|
|
||||||
tools:ignore="MissingConstraints"
|
|
||||||
tools:visibility="visible" />
|
|
||||||
|
|
||||||
<im.vector.riotx.features.reactions.widget.ReactionButton
|
|
||||||
android:id="@+id/messageBottomReaction5"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:visibility="gone"
|
|
||||||
tools:emoji="😱"
|
|
||||||
tools:ignore="MissingConstraints"
|
|
||||||
tools:visibility="visible" />
|
|
||||||
|
|
||||||
|
|
||||||
<im.vector.riotx.features.reactions.widget.ReactionButton
|
|
||||||
android:id="@+id/messageBottomReaction6"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:visibility="gone"
|
|
||||||
tools:emoji="❌"
|
|
||||||
tools:ignore="MissingConstraints"
|
|
||||||
tools:visibility="visible" />
|
|
||||||
|
|
||||||
|
|
||||||
<im.vector.riotx.features.reactions.widget.ReactionButton
|
|
||||||
android:id="@+id/messageBottomReaction7"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:visibility="gone"
|
|
||||||
tools:emoji="✔️"
|
|
||||||
tools:ignore="MissingConstraints"
|
|
||||||
tools:visibility="visible" />
|
|
||||||
|
|
||||||
<im.vector.riotx.features.reactions.widget.ReactionButton
|
|
||||||
android:id="@+id/messageBottomReaction8"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:visibility="gone"
|
|
||||||
tools:emoji="♥️"
|
|
||||||
tools:ignore="MissingConstraints"
|
|
||||||
tools:visibility="visible" />
|
|
||||||
|
|
||||||
|
|
||||||
<androidx.constraintlayout.helper.widget.Flow
|
|
||||||
android:id="@+id/reactionsFlowHelper"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:padding="2dp"
|
|
||||||
app:constraint_referenced_ids="messageBottomReaction1,messageBottomReaction2,messageBottomReaction3,messageBottomReaction4,messageBottomReaction5,messageBottomReaction6,messageBottomReaction7,messageBottomReaction8"
|
|
||||||
app:flow_horizontalBias="0"
|
|
||||||
app:flow_horizontalGap="8dp"
|
|
||||||
app:flow_horizontalStyle="packed"
|
|
||||||
app:flow_verticalBias="0"
|
|
||||||
app:flow_verticalGap="4dp"
|
|
||||||
app:flow_wrapMode="chain"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
|
@ -1,61 +1,58 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<LinearLayout 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"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
<include
|
<RelativeLayout
|
||||||
android:id="@+id/itemMergedAvatarListView"
|
android:layout_width="match_parent"
|
||||||
layout="@layout/vector_message_merge_avatar_list"
|
android:layout_height="wrap_content">
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:layout_marginEnd="16dp"
|
|
||||||
android:layout_marginRight="16dp"
|
|
||||||
app:layout_constraintEnd_toStartOf="@+id/itemMergedExpandTextView"
|
|
||||||
app:layout_constraintHorizontal_bias="0.0"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<TextView
|
<include
|
||||||
android:id="@+id/itemMergedExpandTextView"
|
android:id="@+id/itemMergedAvatarListView"
|
||||||
android:layout_width="0dp"
|
layout="@layout/vector_message_merge_avatar_list"
|
||||||
android:layout_height="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:paddingRight="8dp"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="2dp"
|
android:layout_marginTop="8dp"
|
||||||
android:paddingLeft="8dp"
|
android:layout_alignParentStart="true"
|
||||||
android:paddingTop="4dp"
|
android:layout_toLeftOf="@+id/itemMergedExpandTextView"
|
||||||
android:paddingBottom="4dp"
|
android:layout_marginEnd="16dp" />
|
||||||
android:text="@string/merged_events_expand"
|
|
||||||
android:textColor="?attr/colorAccent"
|
<TextView
|
||||||
android:textSize="14sp"
|
android:id="@+id/itemMergedExpandTextView"
|
||||||
android:textStyle="italic"
|
android:layout_width="wrap_content"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
android:layout_height="wrap_content"
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
android:layout_marginTop="2dp"
|
||||||
|
android:paddingLeft="8dp"
|
||||||
|
android:paddingTop="4dp"
|
||||||
|
android:paddingRight="8dp"
|
||||||
|
android:paddingBottom="4dp"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:text="@string/merged_events_expand"
|
||||||
|
android:textColor="?attr/colorAccent"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:textStyle="italic" />
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
<View
|
<View
|
||||||
android:id="@+id/itemMergedSeparatorView"
|
android:id="@+id/itemMergedSeparatorView"
|
||||||
android:layout_width="0dp"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="1dp"
|
android:layout_height="1dp"
|
||||||
android:layout_marginTop="4dp"
|
android:layout_marginTop="4dp"
|
||||||
android:background="?attr/riotx_header_panel_background"
|
android:background="?attr/riotx_header_panel_background"/>
|
||||||
app:layout_constraintEnd_toEndOf="@id/itemMergedExpandTextView"
|
|
||||||
app:layout_constraintStart_toStartOf="@id/itemMergedAvatarListView"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/itemMergedExpandTextView" />
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/itemMergedSummaryTextView"
|
android:id="@+id/itemMergedSummaryTextView"
|
||||||
android:layout_width="0dp"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="4dp"
|
android:layout_marginTop="4dp"
|
||||||
android:textColor="?riotx_text_secondary"
|
android:textColor="?riotx_text_secondary"
|
||||||
android:textIsSelectable="false"
|
android:textIsSelectable="false"
|
||||||
android:textSize="14sp"
|
android:textSize="14sp"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="@id/itemMergedAvatarListView"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/itemMergedSeparatorView"
|
|
||||||
tools:text="3 membership changes" />
|
tools:text="3 membership changes" />
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</LinearLayout>
|
|
@ -282,7 +282,7 @@
|
||||||
|
|
||||||
|
|
||||||
<style name="TimelineContentStubBaseParams">
|
<style name="TimelineContentStubBaseParams">
|
||||||
<item name="android:layout_width">0dp</item>
|
<item name="android:layout_width">match_parent</item>
|
||||||
<item name="android:layout_height">wrap_content</item>
|
<item name="android:layout_height">wrap_content</item>
|
||||||
<item name="android:layout_marginStart">8dp</item>
|
<item name="android:layout_marginStart">8dp</item>
|
||||||
<item name="android:layout_marginLeft">8dp</item>
|
<item name="android:layout_marginLeft">8dp</item>
|
||||||
|
@ -290,20 +290,6 @@
|
||||||
<item name="android:layout_marginRight">8dp</item>
|
<item name="android:layout_marginRight">8dp</item>
|
||||||
<item name="android:layout_marginBottom">4dp</item>
|
<item name="android:layout_marginBottom">4dp</item>
|
||||||
<item name="android:layout_marginTop">4dp</item>
|
<item name="android:layout_marginTop">4dp</item>
|
||||||
<item name="layout_constraintBottom_toBottomOf">parent</item>
|
|
||||||
<item name="layout_constraintEnd_toEndOf">parent</item>
|
|
||||||
<item name="layout_constraintStart_toEndOf">@id/messageStartGuideline</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style name="TimelineContentStubNoInfoLayoutParams" parent="TimelineContentStubBaseParams">
|
|
||||||
<item name="layout_constraintTop_toTopOf">parent</item>
|
|
||||||
<item name="layout_constraintBottom_toTopOf">@id/readReceiptsView</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
|
|
||||||
<style name="TimelineContentStubLayoutParams" parent="TimelineContentStubBaseParams">
|
|
||||||
<item name="layout_constraintTop_toBottomOf">@id/messageMemberNameView</item>
|
|
||||||
<item name="layout_constraintBottom_toTopOf">@id/messageBottomInfo</item>
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="VectorLabel">
|
<style name="VectorLabel">
|
||||||
|
|
Loading…
Reference in New Issue