That fuck shit the fascists are using
1/*
2 * Copyright 2023 Signal Messenger, LLC
3 * SPDX-License-Identifier: AGPL-3.0-only
4 */
5
6package androidx.recyclerview.widget
7
8import android.content.Context
9import org.signal.core.util.logging.Log
10
11/**
12 * Variation of a vertical, reversed [LinearLayoutManager] that makes specific assumptions in how it will
13 * be used by Conversation view to support easier scrolling to the initial start position.
14 *
15 * Primarily, it assumes that an initial scroll to position call will always happen and that the implementation
16 * of [LinearLayoutManager] remains unchanged with respect to how it assigns [mPendingScrollPosition] and
17 * [mPendingScrollPositionOffset] in [LinearLayoutManager.scrollToPositionWithOffset] and how it always clears
18 * the pending state variables in every call to [LinearLayoutManager.onLayoutCompleted].
19 *
20 * The assumptions are necessary to force the requested scroll position/layout to occur even if the request
21 * happens prior to the data source populating the recycler view/adapter.
22 */
23class ConversationLayoutManager(context: Context) : LinearLayoutManager(context, RecyclerView.VERTICAL, true) {
24
25 private var afterScroll: (() -> Unit)? = null
26
27 override fun supportsPredictiveItemAnimations(): Boolean {
28 return false
29 }
30
31 /**
32 * Scroll to the desired position and be notified when the layout manager has completed the request
33 * via [afterScroll] callback.
34 */
35 fun scrollToPositionWithOffset(position: Int, offset: Int, afterScroll: () -> Unit) {
36 this.afterScroll = afterScroll
37 super.scrollToPositionWithOffset(position, offset)
38 }
39
40 /**
41 * If a scroll to position request is made and a layout pass occurs prior to the list being populated with via the data source,
42 * the base implementation clears the request as if it was never made.
43 *
44 * This override will capture the pending scroll position and offset, determine if the scroll request was satisfied, and
45 * re-request the scroll to position to force another attempt if not satisfied.
46 *
47 * A pending scroll request will be re-requested if the pending scroll position is outside the bounds of the current known size of
48 * items in the list.
49 */
50 override fun onLayoutCompleted(state: RecyclerView.State?) {
51 val pendingScrollPosition = mPendingScrollPosition
52 val pendingScrollOffset = mPendingScrollPositionOffset
53
54 val reRequestPendingPosition = pendingScrollPosition >= (state?.mItemCount ?: 0)
55
56 // Base implementation always clears mPendingScrollPosition+mPendingScrollPositionOffset
57 super.onLayoutCompleted(state)
58
59 // Re-request scroll to position request if necessary thus forcing mPendingScrollPosition+mPendingScrollPositionOffset to be re-assigned
60 if (reRequestPendingPosition) {
61 Log.d(TAG, "Re-requesting pending scroll position: $pendingScrollPosition offset: $pendingScrollOffset")
62 if (pendingScrollOffset != INVALID_OFFSET) {
63 scrollToPositionWithOffset(pendingScrollPosition, pendingScrollOffset)
64 } else {
65 scrollToPosition(pendingScrollPosition)
66 }
67 } else {
68 afterScroll?.invoke()
69 afterScroll = null
70 }
71 }
72
73 companion object {
74 private val TAG = Log.tag(ConversationLayoutManager::class.java)
75 }
76}