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:
parent
f9874588bd
commit
0e8fc18801
@ -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',
|
||||
|
@ -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() {
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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()
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user