mirror of
https://github.com/vector-im/element-android.git
synced 2024-11-16 02:05:06 +08:00
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 🙌:
|
Improvements 🙌:
|
||||||
- PIN code: request PIN code if phone has been locked
|
- PIN code: request PIN code if phone has been locked
|
||||||
|
- Small optimisation of scrolling experience in timeline (#2114)
|
||||||
|
|
||||||
Bugfix 🐛:
|
Bugfix 🐛:
|
||||||
- Fix Splash layout on small screens
|
- Fix Splash layout on small screens
|
||||||
|
@ -326,6 +326,7 @@ dependencies {
|
|||||||
implementation 'com.jakewharton.rxbinding3:rxbinding-material:3.0.0'
|
implementation 'com.jakewharton.rxbinding3:rxbinding-material:3.0.0'
|
||||||
|
|
||||||
implementation("com.airbnb.android:epoxy:$epoxy_version")
|
implementation("com.airbnb.android:epoxy:$epoxy_version")
|
||||||
|
implementation "com.airbnb.android:epoxy-glide-preloading:$epoxy_version"
|
||||||
kapt "com.airbnb.android:epoxy-processor:$epoxy_version"
|
kapt "com.airbnb.android:epoxy-processor:$epoxy_version"
|
||||||
implementation "com.airbnb.android:epoxy-paging:$epoxy_version"
|
implementation "com.airbnb.android:epoxy-paging:$epoxy_version"
|
||||||
implementation 'com.airbnb.android:mvrx:1.3.0'
|
implementation 'com.airbnb.android:mvrx:1.3.0'
|
||||||
|
@ -53,6 +53,8 @@ import androidx.recyclerview.widget.RecyclerView
|
|||||||
import butterknife.BindView
|
import butterknife.BindView
|
||||||
import com.airbnb.epoxy.EpoxyModel
|
import com.airbnb.epoxy.EpoxyModel
|
||||||
import com.airbnb.epoxy.OnModelBuildFinishedListener
|
import com.airbnb.epoxy.OnModelBuildFinishedListener
|
||||||
|
import com.airbnb.epoxy.addGlidePreloader
|
||||||
|
import com.airbnb.epoxy.glidePreloader
|
||||||
import com.airbnb.mvrx.Async
|
import com.airbnb.mvrx.Async
|
||||||
import com.airbnb.mvrx.Fail
|
import com.airbnb.mvrx.Fail
|
||||||
import com.airbnb.mvrx.Loading
|
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.showKeyboard
|
||||||
import im.vector.app.core.extensions.trackItemsVisibilityChange
|
import im.vector.app.core.extensions.trackItemsVisibilityChange
|
||||||
import im.vector.app.core.glide.GlideApp
|
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.intent.getMimeTypeFromUri
|
||||||
import im.vector.app.core.platform.VectorBaseFragment
|
import im.vector.app.core.platform.VectorBaseFragment
|
||||||
import im.vector.app.core.resources.ColorProvider
|
import im.vector.app.core.resources.ColorProvider
|
||||||
@ -218,7 +221,8 @@ class RoomDetailFragment @Inject constructor(
|
|||||||
private val colorProvider: ColorProvider,
|
private val colorProvider: ColorProvider,
|
||||||
private val notificationUtils: NotificationUtils,
|
private val notificationUtils: NotificationUtils,
|
||||||
private val webRtcPeerConnectionManager: WebRtcPeerConnectionManager,
|
private val webRtcPeerConnectionManager: WebRtcPeerConnectionManager,
|
||||||
private val matrixItemColorProvider: MatrixItemColorProvider
|
private val matrixItemColorProvider: MatrixItemColorProvider,
|
||||||
|
private val imageContentRenderer: ImageContentRenderer
|
||||||
) :
|
) :
|
||||||
VectorBaseFragment(),
|
VectorBaseFragment(),
|
||||||
TimelineEventController.Callback,
|
TimelineEventController.Callback,
|
||||||
@ -921,6 +925,16 @@ class RoomDetailFragment @Inject constructor(
|
|||||||
val touchHelper = ItemTouchHelper(swipeCallback)
|
val touchHelper = ItemTouchHelper(swipeCallback)
|
||||||
touchHelper.attachToRecyclerView(recyclerView)
|
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() {
|
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 org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
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 contentUploadStateTrackerBinder: ContentUploadStateTrackerBinder,
|
private val contentUploadStateTrackerBinder: ContentUploadStateTrackerBinder,
|
||||||
private val contentDownloadStateTrackerBinder: ContentDownloadStateTrackerBinder,
|
private val contentDownloadStateTrackerBinder: ContentDownloadStateTrackerBinder,
|
||||||
@ -116,6 +118,7 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
|
|||||||
private var unreadState: UnreadState = UnreadState.Unknown
|
private var unreadState: UnreadState = UnreadState.Unknown
|
||||||
private var positionOfReadMarker: Int? = null
|
private var positionOfReadMarker: Int? = null
|
||||||
private var eventIdToHighlight: String? = null
|
private var eventIdToHighlight: String? = null
|
||||||
|
private var previousModelsSize = 0
|
||||||
|
|
||||||
var callback: Callback? = null
|
var callback: Callback? = null
|
||||||
var timeline: Timeline? = null
|
var timeline: Timeline? = null
|
||||||
@ -191,6 +194,29 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
|
|||||||
models.add(position, readMarker)
|
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) {
|
fun update(viewState: RoomDetailViewState) {
|
||||||
@ -355,9 +381,6 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
|
|||||||
return shouldAdd
|
return shouldAdd
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Return true if added
|
|
||||||
*/
|
|
||||||
private fun LoadingItem_.setVisibilityStateChangedListener(direction: Timeline.Direction): LoadingItem_ {
|
private fun LoadingItem_.setVisibilityStateChangedListener(direction: Timeline.Direction): LoadingItem_ {
|
||||||
return onVisibilityStateChanged { _, _, visibilityState ->
|
return onVisibilityStateChanged { _, _, visibilityState ->
|
||||||
if (visibilityState == VisibilityState.VISIBLE) {
|
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.di.ActiveSessionHolder
|
||||||
import im.vector.app.core.glide.GlideApp
|
import im.vector.app.core.glide.GlideApp
|
||||||
import im.vector.app.core.glide.GlideRequest
|
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.ui.model.Size
|
||||||
import im.vector.app.core.utils.DimensionConverter
|
import im.vector.app.core.utils.DimensionConverter
|
||||||
import im.vector.app.core.utils.isLocalFile
|
import im.vector.app.core.utils.isLocalFile
|
||||||
@ -206,12 +207,14 @@ class ImageContentRenderer @Inject constructor(private val activeSessionHolder:
|
|||||||
.into(imageView)
|
.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) {
|
return if (data.elementToDecrypt != null) {
|
||||||
// Encrypted image
|
// Encrypted image
|
||||||
GlideApp
|
glideRequests.load(data)
|
||||||
.with(imageView)
|
|
||||||
.load(data)
|
|
||||||
} else {
|
} else {
|
||||||
// Clear image
|
// Clear image
|
||||||
val contentUrlResolver = activeSessionHolder.getActiveSession().contentUrlResolver()
|
val contentUrlResolver = activeSessionHolder.getActiveSession().contentUrlResolver()
|
||||||
@ -223,15 +226,12 @@ class ImageContentRenderer @Inject constructor(private val activeSessionHolder:
|
|||||||
// Fallback to base url
|
// Fallback to base url
|
||||||
?: data.url.takeIf { it?.startsWith("content://") == true }
|
?: data.url.takeIf { it?.startsWith("content://") == true }
|
||||||
|
|
||||||
GlideApp
|
glideRequests
|
||||||
.with(imageView)
|
|
||||||
.load(resolvedUrl)
|
.load(resolvedUrl)
|
||||||
.apply {
|
.apply {
|
||||||
if (mode == Mode.THUMBNAIL) {
|
if (mode == Mode.THUMBNAIL) {
|
||||||
error(
|
error(
|
||||||
GlideApp
|
glideRequests.load(resolveUrl(data))
|
||||||
.with(imageView)
|
|
||||||
.load(resolveUrl(data))
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user