UTD pagination: clean up and add developer settings to enable complete history

This commit is contained in:
ganfra 2020-09-30 15:23:46 +02:00 committed by Benoit Marty
parent 0a0330a48c
commit f0272fd283
7 changed files with 17 additions and 224 deletions

View File

@ -50,6 +50,7 @@ import im.vector.app.features.home.room.detail.timeline.item.ReadReceiptData
import im.vector.app.features.home.room.detail.timeline.item.TimelineReadMarkerItem_ import im.vector.app.features.home.room.detail.timeline.item.TimelineReadMarkerItem_
import im.vector.app.features.media.ImageContentRenderer import im.vector.app.features.media.ImageContentRenderer
import im.vector.app.features.media.VideoContentRenderer import im.vector.app.features.media.VideoContentRenderer
import im.vector.app.features.settings.VectorPreferences
import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.events.model.toModel
@ -64,6 +65,7 @@ import javax.inject.Inject
private const val DEFAULT_PREFETCH_THRESHOLD = 30 private const val DEFAULT_PREFETCH_THRESHOLD = 30
class TimelineEventController @Inject constructor(private val dateFormatter: VectorDateFormatter, class TimelineEventController @Inject constructor(private val dateFormatter: VectorDateFormatter,
private val vectorPreferences: VectorPreferences,
private val contentUploadStateTrackerBinder: ContentUploadStateTrackerBinder, private val contentUploadStateTrackerBinder: ContentUploadStateTrackerBinder,
private val contentDownloadStateTrackerBinder: ContentDownloadStateTrackerBinder, private val contentDownloadStateTrackerBinder: ContentDownloadStateTrackerBinder,
private val timelineItemFactory: TimelineItemFactory, private val timelineItemFactory: TimelineItemFactory,
@ -394,6 +396,9 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
} }
private fun updateUTDStates(event: TimelineEvent, nextEvent: TimelineEvent?) { private fun updateUTDStates(event: TimelineEvent, nextEvent: TimelineEvent?) {
if (vectorPreferences.labShowCompleteHistoryInEncryptedRoom()) {
return
}
if (event.root.type == EventType.STATE_ROOM_MEMBER if (event.root.type == EventType.STATE_ROOM_MEMBER
&& event.root.stateKey == session.myUserId) { && event.root.stateKey == session.myUserId) {
val content = event.root.content.toModel<RoomMemberContent>() val content = event.root.content.toModel<RoomMemberContent>()

View File

@ -31,9 +31,6 @@ import im.vector.app.features.home.room.detail.timeline.item.MergedMembershipEve
import im.vector.app.features.home.room.detail.timeline.item.MergedMembershipEventsItem_ import im.vector.app.features.home.room.detail.timeline.item.MergedMembershipEventsItem_
import im.vector.app.features.home.room.detail.timeline.item.MergedRoomCreationItem import im.vector.app.features.home.room.detail.timeline.item.MergedRoomCreationItem
import im.vector.app.features.home.room.detail.timeline.item.MergedRoomCreationItem_ import im.vector.app.features.home.room.detail.timeline.item.MergedRoomCreationItem_
import im.vector.app.features.home.room.detail.timeline.item.MergedUTDItem
import im.vector.app.features.home.room.detail.timeline.item.MergedUTDItem_
import im.vector.app.features.settings.VectorPreferences
import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.events.model.toModel
@ -41,14 +38,12 @@ import org.matrix.android.sdk.api.session.room.model.create.RoomCreateContent
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM
import org.matrix.android.sdk.internal.crypto.model.event.EncryptionEventContent import org.matrix.android.sdk.internal.crypto.model.event.EncryptionEventContent
import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
class MergedHeaderItemFactory @Inject constructor(private val activeSessionHolder: ActiveSessionHolder, class MergedHeaderItemFactory @Inject constructor(private val activeSessionHolder: ActiveSessionHolder,
private val avatarRenderer: AvatarRenderer, private val avatarRenderer: AvatarRenderer,
private val avatarSizeProvider: AvatarSizeProvider, private val avatarSizeProvider: AvatarSizeProvider,
private val roomSummaryHolder: RoomSummaryHolder, private val roomSummaryHolder: RoomSummaryHolder) {
private val vectorPreferences: VectorPreferences) {
private val collapsedEventIds = linkedSetOf<Long>() private val collapsedEventIds = linkedSetOf<Long>()
private val mergeItemCollapseStates = HashMap<Long, Boolean>() private val mergeItemCollapseStates = HashMap<Long, Boolean>()
@ -66,10 +61,7 @@ class MergedHeaderItemFactory @Inject constructor(private val activeSessionHolde
callback: TimelineEventController.Callback?, callback: TimelineEventController.Callback?,
requestModelBuild: () -> Unit) requestModelBuild: () -> Unit)
: BasedMergedItem<*>? { : BasedMergedItem<*>? {
return if (shouldMergedAsCannotDecryptGroup(event, nextEvent)) { return if (nextEvent?.root?.getClearType() == EventType.STATE_ROOM_CREATE
Timber.v("## MERGE: Candidate for merge, top event ${event.eventId}")
buildUTDMergedSummary(currentPosition, items, event, eventIdToHighlight, /*requestModelBuild,*/ callback)
} else if (nextEvent?.root?.getClearType() == EventType.STATE_ROOM_CREATE
&& event.isRoomConfiguration(nextEvent.root.getClearContent()?.toModel<RoomCreateContent>()?.creator)) { && event.isRoomConfiguration(nextEvent.root.getClearContent()?.toModel<RoomCreateContent>()?.creator)) {
// It's the first item before room.create // It's the first item before room.create
// Collapse all room configuration events // Collapse all room configuration events
@ -144,83 +136,6 @@ class MergedHeaderItemFactory @Inject constructor(private val activeSessionHolde
} }
} }
// Event should be UTD
// Next event should not
private fun shouldMergedAsCannotDecryptGroup(event: TimelineEvent, nextEvent: TimelineEvent?): Boolean {
if (!vectorPreferences.mergeUTDinTimeline()) return false
// if event is not UTD return false
if (!isEventUTD(event)) return false
// At this point event cannot be decrypted
// Let's check if older event is not UTD
return nextEvent == null || !isEventUTD(event)
}
private fun isEventUTD(event: TimelineEvent): Boolean {
return event.root.getClearType() == EventType.ENCRYPTED && !event.root.isRedacted()
}
private fun buildUTDMergedSummary(currentPosition: Int,
items: List<TimelineEvent>,
event: TimelineEvent,
eventIdToHighlight: String?,
// requestModelBuild: () -> Unit,
callback: TimelineEventController.Callback?): MergedUTDItem_? {
Timber.v("## MERGE: buildUTDMergedSummary from position $currentPosition")
var prevEvent = items.prevOrNull(currentPosition)
var tmpPos = currentPosition - 1
val mergedEvents = ArrayList<TimelineEvent>().also { it.add(event) }
while (prevEvent != null && isEventUTD(prevEvent)) {
mergedEvents.add(prevEvent)
tmpPos--
prevEvent = if (tmpPos >= 0) items[tmpPos] else null
}
Timber.v("## MERGE: buildUTDMergedSummary merge group size ${mergedEvents.size}")
if (mergedEvents.size < 3) return null
var highlighted = false
val mergedData = ArrayList<BasedMergedItem.Data>(mergedEvents.size)
mergedEvents.reversed()
.forEach { mergedEvent ->
if (!highlighted && mergedEvent.root.eventId == eventIdToHighlight) {
highlighted = true
}
val senderAvatar = mergedEvent.senderInfo.avatarUrl
val senderName = mergedEvent.senderInfo.disambiguatedDisplayName
val data = BasedMergedItem.Data(
userId = mergedEvent.root.senderId ?: "",
avatarUrl = senderAvatar,
memberName = senderName,
localId = mergedEvent.localId,
eventId = mergedEvent.root.eventId ?: "",
isDirectRoom = isDirectRoom()
)
mergedData.add(data)
}
val mergedEventIds = mergedEvents.map { it.localId }
collapsedEventIds.addAll(mergedEventIds)
val mergeId = mergedEventIds.joinToString(separator = "_") { it.toString() }
val attributes = MergedUTDItem.Attributes(
isCollapsed = true,
mergeData = mergedData,
avatarRenderer = avatarRenderer,
onCollapsedStateChanged = {}
)
return MergedUTDItem_()
.id(mergeId)
.big(mergedEventIds.size > 5)
.leftGuideline(avatarSizeProvider.leftGuideline)
.highlighted(highlighted)
.attributes(attributes)
.also {
it.setOnVisibilityStateChanged(MergedTimelineEventVisibilityStateChangedListener(callback, mergedEvents))
}
}
private fun buildRoomCreationMergedSummary(currentPosition: Int, private fun buildRoomCreationMergedSummary(currentPosition: Int,
items: List<TimelineEvent>, items: List<TimelineEvent>,
event: TimelineEvent, event: TimelineEvent,

View File

@ -1,127 +0,0 @@
/*
* Copyright (c) 2020 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.app.features.home.room.detail.timeline.item
import android.util.TypedValue
import android.view.ViewGroup
import android.widget.LinearLayout
import android.widget.RelativeLayout
import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams
import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
import im.vector.app.R
import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.home.room.detail.timeline.TimelineEventController
@EpoxyModelClass(layout = R.layout.item_timeline_event_base_noinfo)
abstract class MergedUTDItem : BasedMergedItem<MergedUTDItem.Holder>() {
@EpoxyAttribute
override lateinit var attributes: Attributes
@EpoxyAttribute
var big: Boolean? = false
override fun getViewType() = STUB_ID
override fun bind(holder: Holder) {
super.bind(holder)
holder.mergedTile.updateLayoutParams<RelativeLayout.LayoutParams> {
this.marginEnd = leftGuideline
if (big == true) {
this.height = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
800f,
holder.view.context.resources.displayMetrics
).toInt()
} else {
this.height = LinearLayout.LayoutParams.WRAP_CONTENT
}
}
// if (attributes.isCollapsed) {
// // Take the oldest data
// val data = distinctMergeData.lastOrNull()
//
// val summary = holder.expandView.resources.getString(R.string.room_created_summary_item,
// data?.memberName ?: data?.userId ?: "")
// holder.summaryView.text = summary
// holder.summaryView.visibility = View.VISIBLE
// holder.avatarView.visibility = View.VISIBLE
// if (data != null) {
// holder.avatarView.visibility = View.VISIBLE
// attributes.avatarRenderer.render(data.toMatrixItem(), holder.avatarView)
// } else {
// holder.avatarView.visibility = View.GONE
// }
//
// if (attributes.hasEncryptionEvent) {
// holder.encryptionTile.isVisible = true
// holder.encryptionTile.updateLayoutParams<RelativeLayout.LayoutParams> {
// this.marginEnd = leftGuideline
// }
// if (attributes.isEncryptionAlgorithmSecure) {
// holder.e2eTitleTextView.text = holder.expandView.resources.getString(R.string.encryption_enabled)
// holder.e2eTitleDescriptionView.text = holder.expandView.resources.getString(R.string.encryption_enabled_tile_description)
// holder.e2eTitleDescriptionView.textAlignment = View.TEXT_ALIGNMENT_CENTER
// holder.e2eTitleTextView.setCompoundDrawablesWithIntrinsicBounds(
// ContextCompat.getDrawable(holder.view.context, R.drawable.ic_shield_black),
// null, null, null
// )
// } else {
// holder.e2eTitleTextView.text = holder.expandView.resources.getString(R.string.encryption_not_enabled)
// holder.e2eTitleDescriptionView.text = holder.expandView.resources.getString(R.string.encryption_unknown_algorithm_tile_description)
// holder.e2eTitleTextView.setCompoundDrawablesWithIntrinsicBounds(
// ContextCompat.getDrawable(holder.view.context, R.drawable.ic_shield_warning),
// null, null, null
// )
// }
// } else {
// holder.encryptionTile.isVisible = false
// }
// } else {
// holder.avatarView.visibility = View.INVISIBLE
// holder.summaryView.visibility = View.GONE
// holder.encryptionTile.isGone = true
// }
// No read receipt for this item
holder.readReceiptsView.isVisible = false
}
class Holder : BasedMergedItem.Holder(STUB_ID) {
// val summaryView by bind<TextView>(R.id.itemNoticeTextView)
// val avatarView by bind<ImageView>(R.id.itemNoticeAvatarView)
val mergedTile by bind<ViewGroup>(R.id.mergedUTDTile)
//
// val e2eTitleTextView by bind<TextView>(R.id.itemVerificationDoneTitleTextView)
// val e2eTitleDescriptionView by bind<TextView>(R.id.itemVerificationDoneDetailTextView)
}
companion object {
private const val STUB_ID = R.id.messageContentMergedUTDStub
}
data class Attributes(
override val isCollapsed: Boolean,
override val mergeData: List<Data>,
override val avatarRenderer: AvatarRenderer,
override val readReceiptsCallback: TimelineEventController.ReadReceiptsCallback? = null,
override val onCollapsedStateChanged: (Boolean) -> Unit
) : BasedMergedItem.Attributes
}

View File

@ -153,7 +153,7 @@ class VectorPreferences @Inject constructor(private val context: Context) {
private const val SETTINGS_DEVELOPER_MODE_FAIL_FAST_PREFERENCE_KEY = "SETTINGS_DEVELOPER_MODE_FAIL_FAST_PREFERENCE_KEY" private const val SETTINGS_DEVELOPER_MODE_FAIL_FAST_PREFERENCE_KEY = "SETTINGS_DEVELOPER_MODE_FAIL_FAST_PREFERENCE_KEY"
// SETTINGS_LABS_HIDE_TECHNICAL_E2E_ERRORS // SETTINGS_LABS_HIDE_TECHNICAL_E2E_ERRORS
private const val SETTINGS_LABS_MERGE_E2E_ERRORS = "SETTINGS_LABS_MERGE_E2E_ERRORS" private const val SETTINGS_LABS_SHOW_COMPLETE_HISTORY_IN_ENCRYPTED_ROOM = "SETTINGS_LABS_SHOW_COMPLETE_HISTORY_IN_ENCRYPTED_ROOM"
const val SETTINGS_LABS_UNREAD_NOTIFICATIONS_AS_TAB = "SETTINGS_LABS_UNREAD_NOTIFICATIONS_AS_TAB" const val SETTINGS_LABS_UNREAD_NOTIFICATIONS_AS_TAB = "SETTINGS_LABS_UNREAD_NOTIFICATIONS_AS_TAB"
// analytics // analytics
@ -285,8 +285,8 @@ class VectorPreferences @Inject constructor(private val context: Context) {
return defaultPrefs.getBoolean(SETTINGS_LABS_ENABLE_SWIPE_TO_REPLY, true) return defaultPrefs.getBoolean(SETTINGS_LABS_ENABLE_SWIPE_TO_REPLY, true)
} }
fun mergeUTDinTimeline(): Boolean { fun labShowCompleteHistoryInEncryptedRoom(): Boolean {
return defaultPrefs.getBoolean(SETTINGS_LABS_MERGE_E2E_ERRORS, false) return defaultPrefs.getBoolean(SETTINGS_LABS_SHOW_COMPLETE_HISTORY_IN_ENCRYPTED_ROOM, false)
} }
fun labAllowedExtendedLogging(): Boolean { fun labAllowedExtendedLogging(): Boolean {

View File

@ -1710,6 +1710,7 @@
<string name="send_suggestion_failed">The suggestion failed to be sent (%s)</string> <string name="send_suggestion_failed">The suggestion failed to be sent (%s)</string>
<string name="settings_labs_show_hidden_events_in_timeline">Show hidden events in timeline</string> <string name="settings_labs_show_hidden_events_in_timeline">Show hidden events in timeline</string>
<string name="settings_labs_show_complete_history_in_encrypted_room">"Show complete history in encrypted rooms"</string>
<string name="bottom_action_people_x">Direct Messages</string> <string name="bottom_action_people_x">Direct Messages</string>

View File

@ -16,6 +16,12 @@
android:key="SETTINGS_LABS_SHOW_HIDDEN_EVENTS_PREFERENCE_KEY" android:key="SETTINGS_LABS_SHOW_HIDDEN_EVENTS_PREFERENCE_KEY"
android:title="@string/settings_labs_show_hidden_events_in_timeline" /> android:title="@string/settings_labs_show_hidden_events_in_timeline" />
<im.vector.app.core.preference.VectorSwitchPreference
android:defaultValue="false"
android:dependency="SETTINGS_DEVELOPER_MODE_PREFERENCE_KEY"
android:key="SETTINGS_LABS_SHOW_COMPLETE_HISTORY_IN_ENCRYPTED_ROOM"
android:title="@string/settings_labs_show_complete_history_in_encrypted_room" />
<im.vector.app.core.preference.VectorSwitchPreference <im.vector.app.core.preference.VectorSwitchPreference
android:defaultValue="false" android:defaultValue="false"
android:dependency="SETTINGS_DEVELOPER_MODE_PREFERENCE_KEY" android:dependency="SETTINGS_DEVELOPER_MODE_PREFERENCE_KEY"

View File

@ -39,13 +39,6 @@
android:key="SETTINGS_LABS_ENABLE_SWIPE_TO_REPLY" android:key="SETTINGS_LABS_ENABLE_SWIPE_TO_REPLY"
android:title="@string/labs_swipe_to_reply_in_timeline" /> android:title="@string/labs_swipe_to_reply_in_timeline" />
<im.vector.app.core.preference.VectorSwitchPreference
android:defaultValue="false"
android:key="SETTINGS_LABS_MERGE_E2E_ERRORS"
android:title="@string/labs_merge_e2e_in_timeline" />
<im.vector.app.core.preference.VectorSwitchPreference <im.vector.app.core.preference.VectorSwitchPreference
android:defaultValue="false" android:defaultValue="false"
android:key="SETTINGS_LABS_UNREAD_NOTIFICATIONS_AS_TAB" android:key="SETTINGS_LABS_UNREAD_NOTIFICATIONS_AS_TAB"