Twidere-App-Android-Twitter.../twidere/src/main/kotlin/org/mariotaku/twidere/task/twitter/GetActivitiesTask.kt

210 lines
9.7 KiB
Kotlin
Raw Normal View History

2016-09-09 05:58:26 +02:00
package org.mariotaku.twidere.task.twitter
2016-12-04 04:58:03 +01:00
import android.accounts.AccountManager
2016-09-09 05:58:26 +02:00
import android.content.ContentResolver
import android.content.ContentValues
import android.content.Context
import android.net.Uri
import android.support.annotation.UiThread
import org.mariotaku.kpreferences.get
2016-09-09 05:58:26 +02:00
import org.mariotaku.microblog.library.MicroBlog
import org.mariotaku.microblog.library.MicroBlogException
import org.mariotaku.microblog.library.twitter.model.Activity
import org.mariotaku.microblog.library.twitter.model.Paging
import org.mariotaku.microblog.library.twitter.model.ResponseList
import org.mariotaku.sqliteqb.library.Expression
2017-03-02 03:42:40 +01:00
import org.mariotaku.twidere.R
2016-09-09 05:58:26 +02:00
import org.mariotaku.twidere.TwidereConstants.LOGTAG
2017-03-03 05:20:52 +01:00
import org.mariotaku.twidere.TwidereConstants.QUERY_PARAM_NOTIFY_CHANGE
2016-12-14 09:02:50 +01:00
import org.mariotaku.twidere.constant.loadItemLimitKey
2016-12-08 16:45:07 +01:00
import org.mariotaku.twidere.extension.model.newMicroBlogInstance
2016-12-04 04:58:03 +01:00
import org.mariotaku.twidere.model.AccountDetails
2016-09-09 05:58:26 +02:00
import org.mariotaku.twidere.model.RefreshTaskParam
import org.mariotaku.twidere.model.UserKey
2017-02-09 09:00:12 +01:00
import org.mariotaku.twidere.model.event.GetActivitiesTaskEvent
2016-12-04 04:58:03 +01:00
import org.mariotaku.twidere.model.util.AccountUtils
2016-09-09 05:58:26 +02:00
import org.mariotaku.twidere.model.util.ParcelableActivityUtils
import org.mariotaku.twidere.provider.TwidereDataStore.Activities
import org.mariotaku.twidere.task.BaseAbstractTask
2016-09-09 05:58:26 +02:00
import org.mariotaku.twidere.util.*
2017-02-04 15:47:50 +01:00
import org.mariotaku.twidere.util.TwitterWrapper.TwitterListResponse
2016-09-09 05:58:26 +02:00
import org.mariotaku.twidere.util.content.ContentResolverUtils
import java.util.*
/**
* Created by mariotaku on 16/1/4.
*/
2016-12-15 01:13:09 +01:00
abstract class GetActivitiesTask(
context: Context
) : BaseAbstractTask<RefreshTaskParam, List<TwitterListResponse<Activity>>, (Boolean) -> Unit>(context) {
2017-02-02 11:18:34 +01:00
2017-03-02 03:42:40 +01:00
private val profileImageSize = context.getString(R.string.profile_image_size)
2017-02-02 11:18:34 +01:00
protected abstract val errorInfoKey: String
protected abstract val contentUri: Uri
2016-09-09 05:58:26 +02:00
2017-02-04 15:47:50 +01:00
override fun doLongOperation(param: RefreshTaskParam): List<TwitterListResponse<Activity>> {
if (!initialized || param.shouldAbort) return emptyList()
2016-09-09 05:58:26 +02:00
val accountIds = param.accountKeys
val maxIds = param.maxIds
val maxSortIds = param.maxSortIds
val sinceIds = param.sinceIds
val cr = context.contentResolver
2017-02-04 15:47:50 +01:00
val result = ArrayList<TwitterListResponse<Activity>>()
2016-12-14 09:02:50 +01:00
val loadItemLimit = preferences[loadItemLimitKey]
2016-09-09 05:58:26 +02:00
var saveReadPosition = false
for (i in accountIds.indices) {
val accountKey = accountIds[i]
2016-12-14 09:02:50 +01:00
val noItemsBefore = DataStoreUtils.getActivitiesCount(context, contentUri, accountKey) <= 0
2016-12-15 06:11:32 +01:00
val credentials = AccountUtils.getAccountDetails(AccountManager.get(context), accountKey, true) ?: continue
2016-12-08 16:45:07 +01:00
val microBlog = credentials.newMicroBlogInstance(context = context, cls = MicroBlog::class.java)
2016-09-09 05:58:26 +02:00
val paging = Paging()
paging.count(loadItemLimit)
var maxId: String? = null
var maxSortId: Long = -1
if (maxIds != null) {
maxId = maxIds[i]
if (maxSortIds != null) {
maxSortId = maxSortIds[i]
}
if (maxId != null) {
paging.maxId(maxId)
}
}
var sinceId: String? = null
if (sinceIds != null) {
sinceId = sinceIds[i]
if (sinceId != null) {
paging.sinceId(sinceId)
if (maxIds == null || maxId == null) {
paging.setLatestResults(true)
saveReadPosition = true
}
}
}
// We should delete old activities has intersection with new items
try {
2016-12-04 04:58:03 +01:00
val activities = getActivities(microBlog, credentials, paging)
2017-02-04 15:47:50 +01:00
val storeResult = storeActivities(cr, loadItemLimit, credentials, noItemsBefore, activities, sinceId,
2016-09-09 05:58:26 +02:00
maxId, false)
if (saveReadPosition) {
2016-12-04 04:58:03 +01:00
saveReadPosition(accountKey, credentials, microBlog)
2016-09-09 05:58:26 +02:00
}
errorInfoStore.remove(errorInfoKey, accountKey)
2017-02-04 15:47:50 +01:00
if (storeResult != 0) {
throw GetStatusesTask.GetTimelineException(storeResult)
}
2016-09-09 05:58:26 +02:00
} catch (e: MicroBlogException) {
2017-01-26 16:15:05 +01:00
DebugLog.w(LOGTAG, tr = e)
2016-09-09 05:58:26 +02:00
if (e.errorCode == 220) {
2016-12-18 03:04:02 +01:00
errorInfoStore[errorInfoKey, accountKey] = ErrorInfoStore.CODE_NO_ACCESS_FOR_CREDENTIALS
2016-09-09 05:58:26 +02:00
} else if (e.isCausedByNetworkIssue) {
2016-12-18 03:04:02 +01:00
errorInfoStore[errorInfoKey, accountKey] = ErrorInfoStore.CODE_NETWORK_ERROR
2016-09-09 05:58:26 +02:00
}
2017-02-04 15:47:50 +01:00
} catch (e: GetStatusesTask.GetTimelineException) {
result.add(TwitterListResponse(accountKey, e))
2016-09-09 05:58:26 +02:00
}
}
2017-02-04 15:47:50 +01:00
return result
2016-09-09 05:58:26 +02:00
}
2017-02-04 15:47:50 +01:00
override fun afterExecute(handler: ((Boolean) -> Unit)?, result: List<TwitterListResponse<Activity>>) {
2017-02-02 11:18:34 +01:00
if (!initialized) return
context.contentResolver.notifyChange(contentUri, null)
2017-02-04 15:47:50 +01:00
val exception = AsyncTwitterWrapper.getException(result)
bus.post(GetActivitiesTaskEvent(contentUri, false, exception))
2017-02-02 11:18:34 +01:00
handler?.invoke(true)
}
2016-09-09 05:58:26 +02:00
2016-12-04 04:58:03 +01:00
private fun storeActivities(cr: ContentResolver, loadItemLimit: Int, details: AccountDetails,
2017-03-02 03:42:40 +01:00
noItemsBefore: Boolean, activities: ResponseList<Activity>,
sinceId: String?, maxId: String?, notify: Boolean): Int {
2016-12-14 09:02:50 +01:00
val deleteBound = LongArray(2) { -1 }
2016-09-09 05:58:26 +02:00
val valuesList = ArrayList<ContentValues>()
var minIdx = -1
var minPositionKey: Long = -1
if (!activities.isEmpty()) {
val firstSortId = activities.first().createdAt.time
val lastSortId = activities.last().createdAt.time
// Get id diff of first and last item
val sortDiff = firstSortId - lastSortId
for (i in activities.indices) {
val item = activities[i]
2017-03-02 03:42:40 +01:00
val activity = ParcelableActivityUtils.fromActivity(item, details.key, false,
profileImageSize)
mediaPreloader.preloadActivity(activity)
2016-09-09 05:58:26 +02:00
activity.position_key = GetStatusesTask.getPositionKey(activity.timestamp,
activity.timestamp, lastSortId, sortDiff, i, activities.size)
if (deleteBound[0] < 0) {
deleteBound[0] = activity.min_sort_position
} else {
deleteBound[0] = Math.min(deleteBound[0], activity.min_sort_position)
}
if (deleteBound[1] < 0) {
deleteBound[1] = activity.max_sort_position
} else {
deleteBound[1] = Math.max(deleteBound[1], activity.max_sort_position)
}
if (minIdx == -1 || item < activities[minIdx]) {
minIdx = i
minPositionKey = activity.position_key
}
activity.inserted_date = System.currentTimeMillis()
2017-02-09 07:34:50 +01:00
val values = ContentValuesCreator.createActivity(activity, details)
2016-09-09 05:58:26 +02:00
valuesList.add(values)
}
}
var olderCount = -1
if (minPositionKey > 0) {
olderCount = DataStoreUtils.getActivitiesCount(context, contentUri, minPositionKey,
2017-02-10 16:38:59 +01:00
Activities.POSITION_KEY, false, arrayOf(details.key))
2016-09-09 05:58:26 +02:00
}
2017-03-03 05:20:52 +01:00
val writeUri = UriUtils.appendQueryParameters(contentUri, QUERY_PARAM_NOTIFY_CHANGE, notify)
2016-09-09 05:58:26 +02:00
if (deleteBound[0] > 0 && deleteBound[1] > 0) {
val where = Expression.and(
Expression.equalsArgs(Activities.ACCOUNT_KEY),
Expression.greaterEqualsArgs(Activities.MIN_SORT_POSITION),
Expression.lesserEqualsArgs(Activities.MAX_SORT_POSITION))
2016-12-04 04:58:03 +01:00
val whereArgs = arrayOf(details.key.toString(), deleteBound[0].toString(), deleteBound[1].toString())
2016-09-09 05:58:26 +02:00
val rowsDeleted = cr.delete(writeUri, where.sql, whereArgs)
// Why loadItemLimit / 2? because it will not acting strange in most cases
2016-12-04 04:58:03 +01:00
val insertGap = !noItemsBefore && olderCount > 0 && rowsDeleted <= 0 && activities.size > loadItemLimit / 2
2016-09-09 05:58:26 +02:00
if (insertGap && !valuesList.isEmpty()) {
valuesList[valuesList.size - 1].put(Activities.IS_GAP, true)
}
}
2017-02-04 15:47:50 +01:00
// Insert previously fetched items.
2016-09-09 05:58:26 +02:00
ContentResolverUtils.bulkInsert(cr, writeUri, valuesList)
2017-02-04 15:47:50 +01:00
// Remove gap flag
2016-09-09 05:58:26 +02:00
if (maxId != null && sinceId == null) {
2017-02-04 15:47:50 +01:00
if (activities.isNotEmpty()) {
// Only remove when actual result returned, otherwise it seems that gap is too old to load
val noGapValues = ContentValues()
noGapValues.put(Activities.IS_GAP, false)
val noGapWhere = Expression.and(Expression.equalsArgs(Activities.ACCOUNT_KEY),
Expression.equalsArgs(Activities.MIN_REQUEST_POSITION),
Expression.equalsArgs(Activities.MAX_REQUEST_POSITION)).sql
val noGapWhereArgs = arrayOf(details.key.toString(), maxId, maxId)
cr.update(writeUri, noGapValues, noGapWhere, noGapWhereArgs)
} else {
return GetStatusesTask.ERROR_LOAD_GAP
}
2016-09-09 05:58:26 +02:00
}
2017-02-04 15:47:50 +01:00
return 0
2016-09-09 05:58:26 +02:00
}
@UiThread
override fun beforeExecute() {
2017-02-02 11:18:34 +01:00
if (!initialized) return
2016-09-09 05:58:26 +02:00
bus.post(GetActivitiesTaskEvent(contentUri, true, null))
}
2017-02-02 11:18:34 +01:00
protected abstract fun saveReadPosition(accountKey: UserKey, details: AccountDetails, twitter: MicroBlog)
@Throws(MicroBlogException::class)
protected abstract fun getActivities(twitter: MicroBlog, details: AccountDetails, paging: Paging): ResponseList<Activity>
2016-09-09 05:58:26 +02:00
}