Merge pull request #2165 from vector-im/feature/timeline_scroll_opti
Feature/timeline scroll opti
This commit is contained in:
commit
ab74f6c1a8
|
@ -6,6 +6,7 @@ Features ✨:
|
|||
|
||||
Improvements 🙌:
|
||||
- PIN code: request PIN code if phone has been locked
|
||||
- Small optimisation of scrolling experience in timeline (#2114)
|
||||
|
||||
Bugfix 🐛:
|
||||
- Fix Splash layout on small screens
|
||||
|
|
|
@ -326,6 +326,7 @@ dependencies {
|
|||
implementation 'com.jakewharton.rxbinding3:rxbinding-material:3.0.0'
|
||||
|
||||
implementation("com.airbnb.android:epoxy:$epoxy_version")
|
||||
implementation "com.airbnb.android:epoxy-glide-preloading:$epoxy_version"
|
||||
kapt "com.airbnb.android:epoxy-processor:$epoxy_version"
|
||||
implementation "com.airbnb.android:epoxy-paging:$epoxy_version"
|
||||
implementation 'com.airbnb.android:mvrx:1.3.0'
|
||||
|
|
|
@ -53,6 +53,8 @@ import androidx.recyclerview.widget.RecyclerView
|
|||
import butterknife.BindView
|
||||
import com.airbnb.epoxy.EpoxyModel
|
||||
import com.airbnb.epoxy.OnModelBuildFinishedListener
|
||||
import com.airbnb.epoxy.addGlidePreloader
|
||||
import com.airbnb.epoxy.glidePreloader
|
||||
import com.airbnb.mvrx.Async
|
||||
import com.airbnb.mvrx.Fail
|
||||
import com.airbnb.mvrx.Loading
|
||||
|
@ -75,6 +77,7 @@ import im.vector.app.core.extensions.setTextOrHide
|
|||
import im.vector.app.core.extensions.showKeyboard
|
||||
import im.vector.app.core.extensions.trackItemsVisibilityChange
|
||||
import im.vector.app.core.glide.GlideApp
|
||||
import im.vector.app.core.glide.GlideRequests
|
||||
import im.vector.app.core.intent.getMimeTypeFromUri
|
||||
import im.vector.app.core.platform.VectorBaseFragment
|
||||
import im.vector.app.core.resources.ColorProvider
|
||||
|
@ -218,7 +221,8 @@ class RoomDetailFragment @Inject constructor(
|
|||
private val colorProvider: ColorProvider,
|
||||
private val notificationUtils: NotificationUtils,
|
||||
private val webRtcPeerConnectionManager: WebRtcPeerConnectionManager,
|
||||
private val matrixItemColorProvider: MatrixItemColorProvider
|
||||
private val matrixItemColorProvider: MatrixItemColorProvider,
|
||||
private val imageContentRenderer: ImageContentRenderer
|
||||
) :
|
||||
VectorBaseFragment(),
|
||||
TimelineEventController.Callback,
|
||||
|
@ -921,6 +925,16 @@ class RoomDetailFragment @Inject constructor(
|
|||
val touchHelper = ItemTouchHelper(swipeCallback)
|
||||
touchHelper.attachToRecyclerView(recyclerView)
|
||||
}
|
||||
recyclerView.addGlidePreloader(
|
||||
epoxyController = timelineEventController,
|
||||
requestManager = GlideApp.with(this),
|
||||
preloader = glidePreloader { requestManager, epoxyModel: MessageImageVideoItem, _ ->
|
||||
imageContentRenderer.createGlideRequest(
|
||||
epoxyModel.mediaData,
|
||||
ImageContentRenderer.Mode.THUMBNAIL,
|
||||
requestManager as GlideRequests
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
private fun updateJumpToReadMarkerViewVisibility() {
|
||||
|
|
|
@ -56,6 +56,8 @@ import org.matrix.android.sdk.api.session.room.timeline.Timeline
|
|||
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||
import javax.inject.Inject
|
||||
|
||||
private const val DEFAULT_PREFETCH_THRESHOLD = 30
|
||||
|
||||
class TimelineEventController @Inject constructor(private val dateFormatter: VectorDateFormatter,
|
||||
private val contentUploadStateTrackerBinder: ContentUploadStateTrackerBinder,
|
||||
private val contentDownloadStateTrackerBinder: ContentDownloadStateTrackerBinder,
|
||||
|
@ -116,6 +118,7 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
|
|||
private var unreadState: UnreadState = UnreadState.Unknown
|
||||
private var positionOfReadMarker: Int? = null
|
||||
private var eventIdToHighlight: String? = null
|
||||
private var previousModelsSize = 0
|
||||
|
||||
var callback: Callback? = null
|
||||
var timeline: Timeline? = null
|
||||
|
@ -191,6 +194,29 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
|
|||
models.add(position, readMarker)
|
||||
}
|
||||
}
|
||||
val shouldAddBackwardPrefetch = timeline?.hasMoreToLoad(Timeline.Direction.BACKWARDS) ?: false
|
||||
if (shouldAddBackwardPrefetch) {
|
||||
val indexOfPrefetchBackward = (previousModelsSize - 1)
|
||||
.coerceAtMost(models.size - DEFAULT_PREFETCH_THRESHOLD)
|
||||
.coerceAtLeast(0)
|
||||
|
||||
val loadingItem = LoadingItem_()
|
||||
.id("prefetch_backward_loading${System.currentTimeMillis()}")
|
||||
.showLoader(false)
|
||||
.setVisibilityStateChangedListener(Timeline.Direction.BACKWARDS)
|
||||
|
||||
models.add(indexOfPrefetchBackward, loadingItem)
|
||||
}
|
||||
val shouldAddForwardPrefetch = timeline?.hasMoreToLoad(Timeline.Direction.FORWARDS) ?: false
|
||||
if (shouldAddForwardPrefetch) {
|
||||
val indexOfPrefetchForward = DEFAULT_PREFETCH_THRESHOLD.coerceAtMost(models.size - 1)
|
||||
val loadingItem = LoadingItem_()
|
||||
.id("prefetch_forward_loading${System.currentTimeMillis()}")
|
||||
.showLoader(false)
|
||||
.setVisibilityStateChangedListener(Timeline.Direction.FORWARDS)
|
||||
models.add(indexOfPrefetchForward, loadingItem)
|
||||
}
|
||||
previousModelsSize = models.size
|
||||
}
|
||||
|
||||
fun update(viewState: RoomDetailViewState) {
|
||||
|
@ -355,9 +381,6 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
|
|||
return shouldAdd
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if added
|
||||
*/
|
||||
private fun LoadingItem_.setVisibilityStateChangedListener(direction: Timeline.Direction): LoadingItem_ {
|
||||
return onVisibilityStateChanged { _, _, visibilityState ->
|
||||
if (visibilityState == VisibilityState.VISIBLE) {
|
||||
|
|
|
@ -33,6 +33,7 @@ import im.vector.app.R
|
|||
import im.vector.app.core.di.ActiveSessionHolder
|
||||
import im.vector.app.core.glide.GlideApp
|
||||
import im.vector.app.core.glide.GlideRequest
|
||||
import im.vector.app.core.glide.GlideRequests
|
||||
import im.vector.app.core.ui.model.Size
|
||||
import im.vector.app.core.utils.DimensionConverter
|
||||
import im.vector.app.core.utils.isLocalFile
|
||||
|
@ -206,12 +207,14 @@ class ImageContentRenderer @Inject constructor(private val activeSessionHolder:
|
|||
.into(imageView)
|
||||
}
|
||||
|
||||
private fun createGlideRequest(data: Data, mode: Mode, imageView: ImageView, size: Size): GlideRequest<Drawable> {
|
||||
fun createGlideRequest(data: Data, mode: Mode, imageView: ImageView, size: Size): GlideRequest<Drawable> {
|
||||
return createGlideRequest(data, mode, GlideApp.with(imageView), size)
|
||||
}
|
||||
|
||||
fun createGlideRequest(data: Data, mode: Mode, glideRequests: GlideRequests, size: Size = processSize(data, mode)): GlideRequest<Drawable> {
|
||||
return if (data.elementToDecrypt != null) {
|
||||
// Encrypted image
|
||||
GlideApp
|
||||
.with(imageView)
|
||||
.load(data)
|
||||
glideRequests.load(data)
|
||||
} else {
|
||||
// Clear image
|
||||
val contentUrlResolver = activeSessionHolder.getActiveSession().contentUrlResolver()
|
||||
|
@ -223,15 +226,12 @@ class ImageContentRenderer @Inject constructor(private val activeSessionHolder:
|
|||
// Fallback to base url
|
||||
?: data.url.takeIf { it?.startsWith("content://") == true }
|
||||
|
||||
GlideApp
|
||||
.with(imageView)
|
||||
glideRequests
|
||||
.load(resolvedUrl)
|
||||
.apply {
|
||||
if (mode == Mode.THUMBNAIL) {
|
||||
error(
|
||||
GlideApp
|
||||
.with(imageView)
|
||||
.load(resolveUrl(data))
|
||||
glideRequests.load(resolveUrl(data))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue