1
0
mirror of https://github.com/TwidereProject/Twidere-Android synced 2025-02-16 19:50:53 +01:00

fixed paged database loading

improved promotion offer card
This commit is contained in:
Mariotaku Lee 2017-12-28 18:26:06 +08:00
parent f9874588bd
commit 0e8fc18801
No known key found for this signature in database
GPG Key ID: 15C10F89D7C33535
6 changed files with 130 additions and 52 deletions

View File

@ -16,7 +16,7 @@ allprojects {
ext {
projectGroupId = 'org.mariotaku.twidere'
projectVersionCode = 398
projectVersionCode = 397
projectVersionName = '4.0.0-SNAPSHOT'
globalCompileSdkVersion = 27
@ -47,7 +47,7 @@ subprojects {
MariotakuCommons : '0.9.22',
RestFu : '0.9.61',
ObjectCursor : '0.9.21',
PlayServices : '11.6.2',
PlayServices : '11.8.0',
PlayBilling : '1.0',
MapsUtils : '0.5',
DropboxCoreSdk : '3.0.3',

View File

@ -20,7 +20,7 @@
package org.mariotaku.twidere.data
import android.arch.paging.DataSource
import android.arch.paging.PositionalDataSource
import android.arch.paging.PageKeyedDataSource
import android.content.ContentResolver
import android.database.ContentObserver
import android.net.Uri
@ -59,10 +59,10 @@ class CursorObjectDataSourceFactory<T : Any>(
val sortOrder: String? = null,
val cls: Class<T>,
val processor: DataSourceItemProcessor<T>?
) : PositionalDataSource<T>() {
) : PageKeyedDataSource<Int, T>() {
private val lazyCount: Int by lazy { resolver.queryCount(uri, selection, selectionArgs) }
private val filterStates: BooleanArray by lazy { BooleanArray(lazyCount) }
private val totalCount: Int by lazy { resolver.queryCount(uri, selection, selectionArgs) }
private val filterStates: BooleanArray by lazy { BooleanArray(totalCount) }
init {
val weakThis by weak(this)
@ -76,24 +76,62 @@ class CursorObjectDataSourceFactory<T : Any>(
processor?.init(resolver)
}
override fun loadInitial(params: LoadInitialParams, callback: LoadInitialCallback<T>) {
loadRange(0, params.pageSize) { data ->
callback.onResult(data, 0, lazyCount)
override fun loadInitial(params: LoadInitialParams<Int>, callback: LoadInitialCallback<Int, T>) {
val totalCount = this.totalCount
val data = loadRange(0, params.requestedLoadSize)
if (data == null) {
invalidate()
return
}
callback.onResult(data, 0, totalCount, null, params.requestedLoadSize)
}
override fun loadBefore(params: LoadParams<Int>, callback: LoadCallback<Int, T>) {
val data = loadRange(params.key, params.requestedLoadSize)
if (data == null) {
invalidate()
return
}
if (params.key == 0) {
callback.onResult(data, null)
} else {
callback.onResult(data, (params.key - params.requestedLoadSize).coerceAtLeast(0))
}
}
override fun loadRange(params: LoadRangeParams, callback: LoadRangeCallback<T>) {
loadRange(params.startPosition, params.loadSize, callback::onResult)
override fun loadAfter(params: LoadParams<Int>, callback: LoadCallback<Int, T>) {
val data = loadRange(params.key, params.requestedLoadSize)
if (data == null) {
invalidate()
return
}
val nextKey = params.key + params.requestedLoadSize
if (nextKey >= totalCount) {
callback.onResult(data, null)
} else {
callback.onResult(data, nextKey)
}
}
private fun loadRange(startPosition: Int, count: Int, callback: (List<T>) -> Unit) {
private fun loadRange(startPosition: Int, count: Int): List<T>? {
val start = System.currentTimeMillis()
val result = resolver.queryAll(uri, projection, selection, selectionArgs, sortOrder,
"$startPosition,$count", cls) ?: return null
DebugLog.d(msg = "Querying $uri:$startPosition,$count took ${System.currentTimeMillis() - start} ms.")
if (processor != null) {
return result.mapNotNull(processor::process)
}
return result
}
private fun loadRangePositional(startPosition: Int, count: Int, callback: (List<T>) -> Unit): Boolean {
if (processor == null) {
val start = System.currentTimeMillis()
val result = resolver.queryAll(uri, projection, selection, selectionArgs, sortOrder,
"$startPosition,$count", cls)!!
DebugLog.d(msg = "Querying $uri took ${System.currentTimeMillis() - start} ms.")
"$startPosition,$count", cls) ?: return false
DebugLog.d(msg = "Querying $uri:$startPosition,$count took ${System.currentTimeMillis() - start} ms.")
callback(result)
return
return true
}
val result = ArrayList<T>()
var offset = filterStates.actualIndex(startPosition, startPosition)
@ -101,8 +139,8 @@ class CursorObjectDataSourceFactory<T : Any>(
do {
val start = System.currentTimeMillis()
val list = resolver.queryAll(uri, projection, selection, selectionArgs, sortOrder,
"$offset,$limit", cls).orEmpty()
DebugLog.d(msg = "Querying $uri took ${System.currentTimeMillis() - start} ms.")
"$offset,$limit", cls) ?: return false
DebugLog.d(msg = "Querying $uri:$startPosition,$count took ${System.currentTimeMillis() - start} ms.")
val reachedEnd = list.size < count
list.mapIndexedNotNullTo(result) lambda@ { index, item ->
val processed = processor.process(item)
@ -114,6 +152,7 @@ class CursorObjectDataSourceFactory<T : Any>(
limit = count - result.size
} while (result.size < count)
callback(result)
return true
}
override fun invalidate() {

View File

@ -20,6 +20,7 @@
package org.mariotaku.twidere.data
import android.arch.lifecycle.LiveData
import android.arch.lifecycle.Observer
import org.mariotaku.twidere.model.SingleResponse
abstract class ExceptionLiveData<T> : LiveData<SingleResponse<T>>() {
@ -42,15 +43,23 @@ abstract class ExceptionLiveData<T> : LiveData<SingleResponse<T>>() {
companion object {
fun <T> wrap(liveData: LiveData<T>): ExceptionLiveData<T> {
val wrapped = object : ExceptionLiveData<T>() {}
liveData.observeForever {
if (it != null) {
wrapped.postData(it)
} else {
wrapped.postException(NullPointerException())
return object : ExceptionLiveData<T>() {
val observer = Observer<T> {
if (it != null) {
setData(it)
} else {
setException(NullPointerException())
}
}
override fun onActive() {
liveData.observeForever(observer)
}
override fun onInactive() {
liveData.removeObserver(observer)
}
}
return wrapped
}
}

View File

@ -383,8 +383,7 @@ abstract class AbsActivitiesFragment : AbsContentRecyclerViewFragment<Parcelable
expressionArgs.toTypedArray(), Activities.DEFAULT_SORT_ORDER,
ParcelableActivity::class.java, onCreateCursorObjectProcessor())
// dataController = factory.obtainDataController()
return LivePagedListBuilder(factory, PagedList.Config.Builder()
.setPageSize(50).setEnablePlaceholders(false).build()).build()
return LivePagedListBuilder(factory, AbsTimelineFragment.databasePagedListConfig).build()
}
private fun getFullActivity(position: Int): ParcelableActivity {

View File

@ -152,6 +152,7 @@ abstract class AbsTimelineFragment : AbsContentRecyclerViewFragment<ParcelableSt
private val busEventHandler = BusEventHandler()
private val scrollHandler = ScrollHandler()
private val timelineBoundaryCallback = StatusesBoundaryCallback()
private var dataController: ExtendedPagedListProvider.DataController? = null
override fun onActivityCreated(savedInstanceState: Bundle?) {
@ -268,27 +269,7 @@ abstract class AbsTimelineFragment : AbsContentRecyclerViewFragment<ParcelableSt
}
override fun onLoadMoreContents(position: Int) {
if (isStandalone || !refreshEnabled || position != LoadMorePosition.END) return
val started = getStatuses(object : ContentRefreshParam {
override val accountKeys by lazy {
this@AbsTimelineFragment.accountKeys
}
override val pagination by lazy {
val context = context!!
val keys = accountKeys.toNulls()
val maxIds = DataStoreUtils.getOldestStatusIds(context, contentUri, keys)
val maxSortIds = DataStoreUtils.getOldestStatusSortIds(context, contentUri, keys)
return@lazy Array(keys.size) { idx ->
SinceMaxPagination.maxId(maxIds[idx], maxSortIds[idx])
}
}
override val tabId: Long
get() = this@AbsTimelineFragment.tabId
})
if (!started) return
super.onLoadMoreContents(position)
// No-op
}
override fun scrollToPositionWithOffset(position: Int, offset: Int) {
@ -468,8 +449,8 @@ abstract class AbsTimelineFragment : AbsContentRecyclerViewFragment<ParcelableSt
expressionArgs.toTypedArray(), Statuses.DEFAULT_SORT_ORDER,
ParcelableStatus::class.java)
// dataController = factory.obtainDataController()
return ExceptionLiveData.wrap(LivePagedListBuilder(factory, PagedList.Config.Builder()
.setPageSize(50).setEnablePlaceholders(false).build()).build())
return ExceptionLiveData.wrap(LivePagedListBuilder(factory, databasePagedListConfig)
.setBoundaryCallback(timelineBoundaryCallback).build())
}
private fun getFullStatus(position: Int): ParcelableStatus {
@ -620,6 +601,30 @@ abstract class AbsTimelineFragment : AbsContentRecyclerViewFragment<ParcelableSt
}
}
private inner class StatusesBoundaryCallback : PagedList.BoundaryCallback<ParcelableStatus>() {
override fun onItemAtEndLoaded(itemAtEnd: ParcelableStatus) {
adapter.loadMoreIndicatorPosition = LoadMorePosition.END
// val started = getStatuses(object : ContentRefreshParam {
// override val accountKeys by lazy {
// this@AbsTimelineFragment.accountKeys
// }
// override val pagination by lazy {
// val context = context!!
// val keys = accountKeys.toNulls()
// val maxIds = DataStoreUtils.getOldestStatusIds(context, contentUri, keys)
// val maxSortIds = DataStoreUtils.getOldestStatusSortIds(context, contentUri, keys)
// return@lazy Array(keys.size) { idx ->
// SinceMaxPagination.maxId(maxIds[idx], maxSortIds[idx])
// }
// }
//
// override val tabId: Long
// get() = this@AbsTimelineFragment.tabId
//
// })
}
}
class DefaultOnLikedListener(
private val context: Context,
private val status: ParcelableStatus,
@ -809,6 +814,12 @@ abstract class AbsTimelineFragment : AbsContentRecyclerViewFragment<ParcelableSt
return intent
}
val databasePagedListConfig: PagedList.Config = PagedList.Config.Builder()
.setPageSize(50)
.setInitialLoadSizeHint(50)
.setEnablePlaceholders(false)
.build()
}
}

View File

@ -19,6 +19,7 @@
package org.mariotaku.twidere.view.controller.premium
import android.content.Intent
import android.view.View
import org.mariotaku.chameleon.ChameleonUtils
import org.mariotaku.kpreferences.get
@ -34,20 +35,35 @@ class PromotionOfferViewController : PremiumDashboardActivity.ExtraFeatureViewCo
super.onCreate()
titleView.setText(R.string.title_promotions_reward)
messageView.text = context.getString(R.string.message_promotions_reward)
button1.setText(R.string.action_purchase_features_pack)
if (preferences[promotionsEnabledKey]) {
button1.setText(R.string.action_disable)
button2.setText(R.string.action_disable)
} else {
button1.setText(R.string.action_enable)
button2.setText(R.string.action_enable_promotions)
}
button1.visibility = View.VISIBLE
button2.visibility = View.GONE
button2.visibility = View.VISIBLE
button1.setOnClickListener {
val purchaseIntent = extraFeaturesService.createPurchaseIntent(context,
ExtraFeaturesService.FEATURE_FEATURES_PACK) ?: return@setOnClickListener
activity.startActivityForControllerResult(purchaseIntent, position,
REQUEST_PURCHASE_FEATURES_PACK)
}
button2.setOnClickListener {
togglePromotions()
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
when (requestCode) {
REQUEST_PURCHASE_FEATURES_PACK -> {
activity.recreate()
}
}
}
override fun onViewCreated(view: View) {
if (preferences[promotionsEnabledKey] || !extraFeaturesService.isPurchased(ExtraFeaturesService.FEATURE_FEATURES_PACK)) {
view.visibility = View.VISIBLE
@ -60,4 +76,8 @@ class PromotionOfferViewController : PremiumDashboardActivity.ExtraFeatureViewCo
preferences[promotionsEnabledKey] = !preferences[promotionsEnabledKey]
ChameleonUtils.getActivity(context)?.recreate()
}
companion object {
private const val REQUEST_PURCHASE_FEATURES_PACK = 101
}
}