improved timeline position after load
This commit is contained in:
parent
d728a86f17
commit
e8774425a7
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Twidere - Twitter client for Android
|
||||
*
|
||||
* Copyright (C) 2012-2017 Mariotaku Lee <mariotaku.lee@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package android.arch.paging
|
||||
|
||||
fun <T> PagedListAdapterHelper<T>.setPagedListListener(listener: ((list: PagedList<T>?) -> Unit)?) {
|
||||
mListener = if (listener != null) PagedListAdapterHelper.PagedListListener { pagedList ->
|
||||
listener(pagedList)
|
||||
} else null
|
||||
}
|
|
@ -22,6 +22,7 @@ package org.mariotaku.twidere.adapter
|
|||
import android.annotation.SuppressLint
|
||||
import android.arch.paging.PagedList
|
||||
import android.arch.paging.PagedListAdapterHelper
|
||||
import android.arch.paging.setPagedListListener
|
||||
import android.content.Context
|
||||
import android.support.v7.recyclerview.extensions.ListAdapterConfig
|
||||
import android.support.v7.widget.RecyclerView
|
||||
|
@ -44,7 +45,11 @@ import org.mariotaku.twidere.annotation.TimelineStyle
|
|||
import org.mariotaku.twidere.constant.newDocumentApiKey
|
||||
import org.mariotaku.twidere.exception.UnsupportedCountIndexException
|
||||
import org.mariotaku.twidere.extension.model.activityStatus
|
||||
import org.mariotaku.twidere.model.*
|
||||
import org.mariotaku.twidere.model.ItemCounts
|
||||
import org.mariotaku.twidere.model.ObjectId
|
||||
import org.mariotaku.twidere.model.ParcelableActivity
|
||||
import org.mariotaku.twidere.model.ParcelableMedia
|
||||
import org.mariotaku.twidere.model.placeholder.ParcelableActivityPlaceholder
|
||||
import org.mariotaku.twidere.util.IntentUtils
|
||||
import org.mariotaku.twidere.util.OnLinkClickHandler
|
||||
import org.mariotaku.twidere.util.TwidereLinkify
|
||||
|
@ -119,6 +124,12 @@ class ParcelableActivitiesAdapter(
|
|||
|
||||
var activityClickListener: ActivityAdapterListener? = null
|
||||
|
||||
var pagedListListener: ((list: PagedList<ParcelableActivity>?) -> Unit)? = null
|
||||
set(value) {
|
||||
field = value
|
||||
pagedActivitiesHelper.setPagedListListener(value)
|
||||
}
|
||||
|
||||
private val inflater = LayoutInflater.from(context)
|
||||
private val twidereLinkify = TwidereLinkify(OnLinkClickHandler(context, null, preferences))
|
||||
private val statusAdapterDelegate = DummyItemAdapter(context, twidereLinkify, this, requestManager)
|
||||
|
@ -400,18 +411,6 @@ class ParcelableActivitiesAdapter(
|
|||
}
|
||||
}
|
||||
|
||||
object ParcelableActivityPlaceholder : ParcelableActivity() {
|
||||
init {
|
||||
id = "none"
|
||||
account_key = UserKey.INVALID
|
||||
user_key = UserKey.INVALID
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val ITEM_VIEW_TYPE_STUB = 0
|
||||
const val ITEM_VIEW_TYPE_GAP = 1
|
||||
|
|
|
@ -21,6 +21,7 @@ package org.mariotaku.twidere.adapter
|
|||
|
||||
import android.arch.paging.PagedList
|
||||
import android.arch.paging.PagedListAdapterHelper
|
||||
import android.arch.paging.setPagedListListener
|
||||
import android.content.Context
|
||||
import android.support.v4.widget.Space
|
||||
import android.support.v7.recyclerview.extensions.ListAdapterConfig
|
||||
|
@ -99,6 +100,20 @@ class ParcelableStatusesAdapter(
|
|||
|
||||
override val itemCounts = ItemCounts(5)
|
||||
|
||||
override var loadMoreIndicatorPosition: Int
|
||||
get() = super.loadMoreIndicatorPosition
|
||||
set(value) {
|
||||
super.loadMoreIndicatorPosition = value
|
||||
updateItemCounts()
|
||||
}
|
||||
|
||||
override var loadMoreSupportedPosition: Int
|
||||
get() = super.loadMoreSupportedPosition
|
||||
set(value) {
|
||||
super.loadMoreSupportedPosition = value
|
||||
updateItemCounts()
|
||||
}
|
||||
|
||||
var isShowInReplyTo: Boolean = false
|
||||
set(value) {
|
||||
if (field == value) return
|
||||
|
@ -134,18 +149,10 @@ class ParcelableStatusesAdapter(
|
|||
val statusStartIndex: Int
|
||||
get() = getItemStartPosition(ITEM_INDEX_STATUS)
|
||||
|
||||
override var loadMoreIndicatorPosition: Int
|
||||
get() = super.loadMoreIndicatorPosition
|
||||
var pagedListListener: ((list: PagedList<ParcelableStatus>?) -> Unit)? = null
|
||||
set(value) {
|
||||
super.loadMoreIndicatorPosition = value
|
||||
updateItemCounts()
|
||||
}
|
||||
|
||||
override var loadMoreSupportedPosition: Int
|
||||
get() = super.loadMoreSupportedPosition
|
||||
set(value) {
|
||||
super.loadMoreSupportedPosition = value
|
||||
updateItemCounts()
|
||||
field = value
|
||||
pagedStatusesHelper.setPagedListListener(value)
|
||||
}
|
||||
|
||||
private val inflater: LayoutInflater = LayoutInflater.from(context)
|
||||
|
|
|
@ -60,7 +60,9 @@ import org.mariotaku.twidere.data.processor.DataSourceItemProcessor
|
|||
import org.mariotaku.twidere.extension.model.activityStatus
|
||||
import org.mariotaku.twidere.extension.queryOne
|
||||
import org.mariotaku.twidere.extension.showContextMenuForChild
|
||||
import org.mariotaku.twidere.extension.view.PositionWithOffset
|
||||
import org.mariotaku.twidere.extension.view.firstVisibleItemPosition
|
||||
import org.mariotaku.twidere.extension.view.firstVisibleItemPositionWithOffset
|
||||
import org.mariotaku.twidere.extension.view.lastVisibleItemPosition
|
||||
import org.mariotaku.twidere.fragment.AbsContentRecyclerViewFragment
|
||||
import org.mariotaku.twidere.fragment.timeline.AbsTimelineFragment
|
||||
|
@ -82,6 +84,7 @@ import org.mariotaku.twidere.view.ExtendedRecyclerView
|
|||
import org.mariotaku.twidere.view.holder.ActivityTitleSummaryViewHolder
|
||||
import org.mariotaku.twidere.view.holder.GapViewHolder
|
||||
import org.mariotaku.twidere.view.holder.iface.IStatusViewHolder
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
|
||||
abstract class AbsActivitiesFragment : AbsContentRecyclerViewFragment<ParcelableActivitiesAdapter, RecyclerView.LayoutManager>() {
|
||||
|
||||
|
@ -125,12 +128,15 @@ abstract class AbsActivitiesFragment : AbsContentRecyclerViewFragment<Parcelable
|
|||
|
||||
private val busEventHandler = BusEventHandler()
|
||||
private val scrollHandler = ScrollHandler()
|
||||
private val timelineBoundaryCallback = ActivitiesBoundaryCallback()
|
||||
private val positionBackup: AtomicReference<PositionWithOffset> = AtomicReference()
|
||||
private var dataController: ExtendedPagedListProvider.DataController? = null
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
registerForContextMenu(recyclerView)
|
||||
adapter.activityClickListener = ActivityClickHandler()
|
||||
adapter.pagedListListener = this::onPagedListChanged
|
||||
adapter.loadMoreSupportedPosition = if (isStandalone) {
|
||||
LoadMorePosition.NONE
|
||||
} else {
|
||||
|
@ -235,26 +241,7 @@ abstract class AbsActivitiesFragment : AbsContentRecyclerViewFragment<Parcelable
|
|||
}
|
||||
|
||||
override fun onLoadMoreContents(position: Int) {
|
||||
if (isStandalone || !refreshEnabled || position != LoadMorePosition.END) return
|
||||
val started = getActivities(object : ContentRefreshParam {
|
||||
override val accountKeys by lazy {
|
||||
this@AbsActivitiesFragment.accountKeys
|
||||
}
|
||||
override val pagination by lazy {
|
||||
val keys = accountKeys.toNulls()
|
||||
val maxIds = DataStoreUtils.getRefreshOldestActivityMaxPositions(context!!, contentUri, keys)
|
||||
val maxSortIds = DataStoreUtils.getRefreshOldestActivityMaxSortPositions(context!!, contentUri, keys)
|
||||
return@lazy Array(keys.size) { idx ->
|
||||
SinceMaxPagination.maxId(maxIds[idx], maxSortIds[idx])
|
||||
}
|
||||
}
|
||||
|
||||
override val tabId: Long
|
||||
get() = this@AbsActivitiesFragment.tabId
|
||||
|
||||
})
|
||||
if (!started) return
|
||||
super.onLoadMoreContents(position)
|
||||
// No-op
|
||||
}
|
||||
|
||||
override fun scrollToPositionWithOffset(position: Int, offset: Int) {
|
||||
|
@ -269,6 +256,9 @@ abstract class AbsActivitiesFragment : AbsContentRecyclerViewFragment<Parcelable
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scroll to start of the timeline. This also updates read position
|
||||
*/
|
||||
override fun scrollToStart(): Boolean {
|
||||
val result = super.scrollToStart()
|
||||
if (result) saveReadPosition(0)
|
||||
|
@ -288,7 +278,7 @@ abstract class AbsActivitiesFragment : AbsContentRecyclerViewFragment<Parcelable
|
|||
|
||||
|
||||
protected open fun onDataLoaded(data: PagedList<ParcelableActivity>?) {
|
||||
val firstVisiblePosition = layoutManager.firstVisibleItemPosition
|
||||
val firstVisiblePosition = layoutManager.firstVisibleItemPositionWithOffset
|
||||
adapter.showAccountsColor = accountKeys.size > 1
|
||||
adapter.activities = data
|
||||
when {
|
||||
|
@ -299,12 +289,15 @@ abstract class AbsActivitiesFragment : AbsContentRecyclerViewFragment<Parcelable
|
|||
showContent()
|
||||
}
|
||||
}
|
||||
if (firstVisiblePosition == 0 && !preferences[readFromBottomKey]) {
|
||||
val weakThis by weak(this)
|
||||
recyclerView.post {
|
||||
val f = weakThis?.takeIf { !it.isDetached } ?: return@post
|
||||
f.scrollToStart()
|
||||
}
|
||||
positionBackup.set(firstVisiblePosition)
|
||||
}
|
||||
|
||||
protected open fun onPagedListChanged(data: PagedList<ParcelableActivity>?) {
|
||||
val firstVisiblePosition = positionBackup.getAndSet(null) ?: return
|
||||
if (firstVisiblePosition.position == 0 && !preferences[readFromBottomKey]) {
|
||||
scrollToPositionWithOffset(0, 0)
|
||||
} else {
|
||||
scrollToPositionWithOffset(firstVisiblePosition.position, firstVisiblePosition.offset)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -533,6 +526,31 @@ abstract class AbsActivitiesFragment : AbsContentRecyclerViewFragment<Parcelable
|
|||
}
|
||||
}
|
||||
|
||||
private inner class ActivitiesBoundaryCallback : PagedList.BoundaryCallback<ParcelableStatus>() {
|
||||
override fun onItemAtEndLoaded(itemAtEnd: ParcelableStatus) {
|
||||
val started = getActivities(object : ContentRefreshParam {
|
||||
override val accountKeys by lazy {
|
||||
this@AbsActivitiesFragment.accountKeys
|
||||
}
|
||||
override val pagination by lazy {
|
||||
val keys = accountKeys.toNulls()
|
||||
val maxIds = DataStoreUtils.getRefreshOldestActivityMaxPositions(context!!, contentUri, keys)
|
||||
val maxSortIds = DataStoreUtils.getRefreshOldestActivityMaxSortPositions(context!!, contentUri, keys)
|
||||
return@lazy Array(keys.size) { idx ->
|
||||
SinceMaxPagination.maxId(maxIds[idx], maxSortIds[idx])
|
||||
}
|
||||
}
|
||||
|
||||
override val tabId: Long
|
||||
get() = this@AbsActivitiesFragment.tabId
|
||||
|
||||
})
|
||||
if (started) {
|
||||
adapter.loadMoreIndicatorPosition = LoadMorePosition.END
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val activityColumnsLite = Activities.COLUMNS - arrayOf(Activities.SOURCES, Activities.TARGETS,
|
||||
Activities.TARGET_OBJECTS, Activities.MENTIONS_JSON, Activities.CARD,
|
||||
|
|
|
@ -70,6 +70,7 @@ import org.mariotaku.twidere.data.fetcher.StatusesFetcher
|
|||
import org.mariotaku.twidere.extension.*
|
||||
import org.mariotaku.twidere.extension.adapter.removeStatuses
|
||||
import org.mariotaku.twidere.extension.data.observe
|
||||
import org.mariotaku.twidere.extension.view.PositionWithOffset
|
||||
import org.mariotaku.twidere.extension.view.firstVisibleItemPosition
|
||||
import org.mariotaku.twidere.extension.view.firstVisibleItemPositionWithOffset
|
||||
import org.mariotaku.twidere.extension.view.lastVisibleItemPosition
|
||||
|
@ -99,6 +100,7 @@ import org.mariotaku.twidere.view.holder.GapViewHolder
|
|||
import org.mariotaku.twidere.view.holder.TimelineFilterHeaderViewHolder
|
||||
import org.mariotaku.twidere.view.holder.iface.IStatusViewHolder
|
||||
import org.mariotaku.twidere.view.holder.status.StatusViewHolder
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
|
||||
abstract class AbsTimelineFragment : AbsContentRecyclerViewFragment<ParcelableStatusesAdapter, LayoutManager>(),
|
||||
IFloatingActionButtonFragment {
|
||||
|
@ -154,12 +156,14 @@ abstract class AbsTimelineFragment : AbsContentRecyclerViewFragment<ParcelableSt
|
|||
private val busEventHandler = BusEventHandler()
|
||||
private val scrollHandler = ScrollHandler()
|
||||
private val timelineBoundaryCallback = StatusesBoundaryCallback()
|
||||
private val positionBackup: AtomicReference<PositionWithOffset> = AtomicReference()
|
||||
private var dataController: ExtendedPagedListProvider.DataController? = null
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
registerForContextMenu(recyclerView)
|
||||
adapter.statusClickListener = StatusClickHandler()
|
||||
adapter.pagedListListener = this::onPagedListChanged
|
||||
adapter.loadMoreSupportedPosition = if (isStandalone) {
|
||||
LoadMorePosition.NONE
|
||||
} else {
|
||||
|
@ -285,6 +289,9 @@ abstract class AbsTimelineFragment : AbsContentRecyclerViewFragment<ParcelableSt
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scroll to start of the timeline. This also updates read position
|
||||
*/
|
||||
override fun scrollToStart(): Boolean {
|
||||
val result = super.scrollToStart()
|
||||
if (result) saveReadPosition(0)
|
||||
|
@ -317,7 +324,6 @@ abstract class AbsTimelineFragment : AbsContentRecyclerViewFragment<ParcelableSt
|
|||
showProgress()
|
||||
}
|
||||
|
||||
|
||||
protected open fun onDataLoaded(data: PagedList<ParcelableStatus>?) {
|
||||
val firstVisiblePosition = layoutManager.firstVisibleItemPositionWithOffset
|
||||
adapter.showAccountsColor = accountKeys.size > 1
|
||||
|
@ -331,18 +337,15 @@ abstract class AbsTimelineFragment : AbsContentRecyclerViewFragment<ParcelableSt
|
|||
showContent()
|
||||
}
|
||||
}
|
||||
if (firstVisiblePosition == null) return
|
||||
positionBackup.set(firstVisiblePosition)
|
||||
}
|
||||
|
||||
protected open fun onPagedListChanged(data: PagedList<ParcelableStatus>?) {
|
||||
val firstVisiblePosition = positionBackup.getAndSet(null) ?: return
|
||||
if (firstVisiblePosition.position == 0 && !preferences[readFromBottomKey]) {
|
||||
val weakThis by weak(this)
|
||||
recyclerView.post {
|
||||
val f = weakThis?.takeIf { !it.isDetached && it.view != null } ?: return@post
|
||||
f.scrollToStart()
|
||||
}
|
||||
scrollToPositionWithOffset(0, 0)
|
||||
} else {
|
||||
val lm = loaderManager
|
||||
if (lm is LinearLayoutManager) {
|
||||
lm.scrollToPositionWithOffset(firstVisiblePosition.position, firstVisiblePosition.offset)
|
||||
}
|
||||
scrollToPositionWithOffset(firstVisiblePosition.position, firstVisiblePosition.offset)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -400,7 +403,7 @@ abstract class AbsTimelineFragment : AbsContentRecyclerViewFragment<ParcelableSt
|
|||
|
||||
private fun setupLiveData() {
|
||||
statuses = if (isStandalone) onCreateStandaloneLiveData() else onCreateDatabaseLiveData()
|
||||
statuses?.observe(this, success = { onDataLoaded(it) }, fail = {
|
||||
statuses?.observe(this, success = this::onDataLoaded, fail = {
|
||||
showError(R.drawable.ic_info_error_generic, it.getErrorMessage(context!!))
|
||||
})
|
||||
}
|
||||
|
@ -487,6 +490,19 @@ abstract class AbsTimelineFragment : AbsContentRecyclerViewFragment<ParcelableSt
|
|||
}
|
||||
}
|
||||
|
||||
class DefaultOnLikedListener(
|
||||
private val context: Context,
|
||||
private val status: ParcelableStatus,
|
||||
private val accountKey: UserKey? = null
|
||||
) : LikeAnimationDrawable.OnLikedListener {
|
||||
|
||||
override fun onLiked(): Boolean {
|
||||
if (status.is_favorite) return false
|
||||
CreateFavoriteTask(context, accountKey ?: status.account_key, status).promise()
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
internal inner class BusEventHandler {
|
||||
|
||||
@Subscribe
|
||||
|
@ -634,19 +650,6 @@ abstract class AbsTimelineFragment : AbsContentRecyclerViewFragment<ParcelableSt
|
|||
}
|
||||
}
|
||||
|
||||
class DefaultOnLikedListener(
|
||||
private val context: Context,
|
||||
private val status: ParcelableStatus,
|
||||
private val accountKey: UserKey? = null
|
||||
) : LikeAnimationDrawable.OnLikedListener {
|
||||
|
||||
override fun onLiked(): Boolean {
|
||||
if (status.is_favorite) return false
|
||||
CreateFavoriteTask(context, accountKey ?: status.account_key, status).promise()
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
const val REQUEST_FAVORITE_SELECT_ACCOUNT = 101
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Twidere - Twitter client for Android
|
||||
*
|
||||
* Copyright (C) 2012-2017 Mariotaku Lee <mariotaku.lee@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.mariotaku.twidere.model.placeholder
|
||||
|
||||
import org.mariotaku.twidere.model.ParcelableActivity
|
||||
import org.mariotaku.twidere.model.UserKey
|
||||
|
||||
object ParcelableActivityPlaceholder : ParcelableActivity() {
|
||||
init {
|
||||
id = "none"
|
||||
account_key = UserKey.INVALID
|
||||
user_key = UserKey.INVALID
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return -1
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue