implemented mastodon interactions timeline

This commit is contained in:
Mariotaku Lee 2017-04-22 01:38:47 +08:00
parent dad16bdc3b
commit d1b175b524
No known key found for this signature in database
GPG Key ID: 15C10F89D7C33535
12 changed files with 251 additions and 108 deletions

View File

@ -18,9 +18,18 @@
package org.mariotaku.microblog.library.mastodon.api; package org.mariotaku.microblog.library.mastodon.api;
import org.mariotaku.microblog.library.MicroBlogException;
import org.mariotaku.microblog.library.mastodon.model.LinkHeaderList;
import org.mariotaku.microblog.library.mastodon.model.Notification;
import org.mariotaku.microblog.library.twitter.model.Paging;
import org.mariotaku.restfu.annotation.method.GET;
import org.mariotaku.restfu.annotation.param.Query;
/** /**
* Created by mariotaku on 2017/4/17. * Created by mariotaku on 2017/4/17.
*/ */
public interface NotificationsResources { public interface NotificationsResources {
@GET("/v1/notifications")
LinkHeaderList<Notification> getNotifications(@Query Paging paging) throws MicroBlogException;
} }

View File

@ -18,6 +18,8 @@
package org.mariotaku.microblog.library.mastodon.model; package org.mariotaku.microblog.library.mastodon.model;
import android.support.annotation.StringDef;
import com.bluelinelabs.logansquare.annotation.JsonField; import com.bluelinelabs.logansquare.annotation.JsonField;
import com.bluelinelabs.logansquare.annotation.JsonObject; import com.bluelinelabs.logansquare.annotation.JsonObject;
@ -36,7 +38,8 @@ public class Notification {
@JsonField(name = "id") @JsonField(name = "id")
String id; String id;
/** /**
* One of: {@code mention}, {@code reblog}, {@code favourite}, {@code follow} * One of: {@link Type#MENTION}, {@link Type#REBLOG}, {@link Type#FAVOURITE},
* {@link Type#FOLLOW}
*/ */
@JsonField(name = "type") @JsonField(name = "type")
String type; String type;
@ -86,4 +89,9 @@ public class Notification {
", status=" + status + ", status=" + status +
'}'; '}';
} }
@StringDef({Type.MENTION, Type.REBLOG, Type.FAVOURITE, Type.FOLLOW})
public @interface Type {
String MENTION = "mention", REBLOG = "reblog", FAVOURITE = "favourite", FOLLOW = "follow";
}
} }

View File

@ -97,7 +97,7 @@ public class ParcelableActivity implements Comparable<ParcelableActivity>, Parce
@ParcelableThisPlease @ParcelableThisPlease
@JsonField(name = "source_ids", typeConverter = UserKeysConverter.class) @JsonField(name = "source_ids", typeConverter = UserKeysConverter.class)
@CursorField(value = Activities.SOURCE_IDS, converter = UserKeysCursorFieldConverter.class) @CursorField(value = Activities.SOURCE_IDS, converter = UserKeysCursorFieldConverter.class)
public UserKey[] source_ids; public UserKey[] source_keys;
@ParcelableThisPlease @ParcelableThisPlease
@JsonField(name = "sources") @JsonField(name = "sources")
@ -215,7 +215,7 @@ public class ParcelableActivity implements Comparable<ParcelableActivity>, Parce
", max_position='" + max_position + '\'' + ", max_position='" + max_position + '\'' +
", min_position='" + min_position + '\'' + ", min_position='" + min_position + '\'' +
", action='" + action + '\'' + ", action='" + action + '\'' +
", source_ids=" + Arrays.toString(source_ids) + ", source_ids=" + Arrays.toString(source_keys) +
", sources=" + Arrays.toString(sources) + ", sources=" + Arrays.toString(sources) +
", target_users=" + Arrays.toString(target_users) + ", target_users=" + Arrays.toString(target_users) +
", target_statuses=" + Arrays.toString(target_statuses) + ", target_statuses=" + Arrays.toString(target_statuses) +

View File

@ -34,12 +34,12 @@ import org.mariotaku.microblog.library.twitter.model.Activity
import org.mariotaku.microblog.library.twitter.model.DirectMessage import org.mariotaku.microblog.library.twitter.model.DirectMessage
import org.mariotaku.microblog.library.twitter.model.Status import org.mariotaku.microblog.library.twitter.model.Status
import org.mariotaku.twidere.annotation.AccountType import org.mariotaku.twidere.annotation.AccountType
import org.mariotaku.twidere.extension.model.api.microblog.toParcelable
import org.mariotaku.twidere.extension.model.newMicroBlogInstance import org.mariotaku.twidere.extension.model.newMicroBlogInstance
import org.mariotaku.twidere.model.AccountDetails import org.mariotaku.twidere.model.AccountDetails
import org.mariotaku.twidere.model.ActivityTitleSummaryMessage import org.mariotaku.twidere.model.ActivityTitleSummaryMessage
import org.mariotaku.twidere.model.UserKey import org.mariotaku.twidere.model.UserKey
import org.mariotaku.twidere.model.util.AccountUtils import org.mariotaku.twidere.model.util.AccountUtils
import org.mariotaku.twidere.model.util.ParcelableActivityUtils
import org.mariotaku.twidere.util.UserColorNameManager import org.mariotaku.twidere.util.UserColorNameManager
import org.mariotaku.twidere.util.dagger.DependencyHolder import org.mariotaku.twidere.util.dagger.DependencyHolder
import org.mariotaku.twidere.util.streaming.FanfouTimelineStreamCallback import org.mariotaku.twidere.util.streaming.FanfouTimelineStreamCallback
@ -118,8 +118,7 @@ class UserStreamDumperPlugin(val context: Context) : DumperPlugin {
if (verboseMode) { if (verboseMode) {
dumpContext.stdout.println("Activity: @${activity.toString().trim('\n')}") dumpContext.stdout.println("Activity: @${activity.toString().trim('\n')}")
} else { } else {
val pActivity = ParcelableActivityUtils.fromActivity(activity, account.key, val pActivity = activity.toParcelable(account.key, account.type)
account.type)
val message = ActivityTitleSummaryMessage.get(context, manager, pActivity, val message = ActivityTitleSummaryMessage.get(context, manager, pActivity,
pActivity.sources, 0, true, true) pActivity.sources, 0, true, true)
if (message != null) { if (message != null) {
@ -173,8 +172,7 @@ class UserStreamDumperPlugin(val context: Context) : DumperPlugin {
override fun onActivityAboutMe(activity: Activity): Boolean { override fun onActivityAboutMe(activity: Activity): Boolean {
if (!includeInteractions && includeTimeline) return true if (!includeInteractions && includeTimeline) return true
val pActivity = ParcelableActivityUtils.fromActivity(activity, account.key, val pActivity = activity.toParcelable(account.key, account.type)
account.type)
val message = ActivityTitleSummaryMessage.get(context, manager, pActivity, pActivity.sources, 0, val message = ActivityTitleSummaryMessage.get(context, manager, pActivity, pActivity.sources, 0,
true, true) true, true)
if (message != null) { if (message != null) {

View File

@ -0,0 +1,74 @@
/*
* 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.extension.model.api.mastodon
import org.mariotaku.ktextension.mapToArray
import org.mariotaku.microblog.library.mastodon.model.Notification
import org.mariotaku.microblog.library.twitter.model.Activity
import org.mariotaku.twidere.model.ParcelableActivity
import org.mariotaku.twidere.model.ParcelableStatus
import org.mariotaku.twidere.model.ParcelableUser
import org.mariotaku.twidere.model.UserKey
/**
* Created by mariotaku on 2017/4/22.
*/
fun Notification.toParcelable(accountKey: UserKey): ParcelableActivity {
val result = ParcelableActivity()
result.account_key = accountKey
result.timestamp = createdAt.time
result.min_position = id
result.max_position = id
result.min_sort_position = result.timestamp
result.max_sort_position = result.timestamp
result.sources = toSources(accountKey)
when (type) {
Notification.Type.MENTION -> {
result.action = Activity.Action.MENTION
result.target_object_statuses = toStatuses(accountKey)
}
Notification.Type.REBLOG -> {
result.action = Activity.Action.RETWEET
result.target_object_statuses = toStatuses(accountKey)
}
Notification.Type.FAVOURITE -> {
result.action = Activity.Action.FAVORITE
result.target_statuses = toStatuses(accountKey)
}
Notification.Type.FOLLOW -> {
result.action = Activity.Action.FOLLOW
}
else -> {
result.action = type
}
}
result.source_keys = result.sources?.mapToArray { it.key }
return result
}
private fun Notification.toSources(accountKey: UserKey): Array<ParcelableUser>? {
val account = this.account ?: return null
return arrayOf(account.toParcelable(accountKey))
}
private fun Notification.toStatuses(accountKey: UserKey): Array<ParcelableStatus>? {
val status = this.status ?: return null
return arrayOf(status.toParcelable(accountKey))
}

View File

@ -0,0 +1,65 @@
/*
* 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.extension.model.api.microblog
import org.mariotaku.ktextension.mapToArray
import org.mariotaku.microblog.library.twitter.model.Activity
import org.mariotaku.twidere.extension.model.toParcelables
import org.mariotaku.twidere.model.ParcelableActivity
import org.mariotaku.twidere.model.UserKey
/**
* Created by mariotaku on 2017/4/22.
*/
fun Activity.toParcelable(accountKey: UserKey, accountType: String, isGap: Boolean = false,
profileImageSize: String = "normal"): ParcelableActivity {
val result = ParcelableActivity()
result.account_key = accountKey
result.timestamp = createdAt.time
result.action = action
result.max_sort_position = maxSortPosition
result.min_sort_position = minSortPosition
result.max_position = maxPosition
result.min_position = minPosition
result.sources = sources?.toParcelables(accountKey, accountType,
profileImageSize)
result.target_users = targetUsers?.toParcelables(accountKey,
accountType, profileImageSize)
result.target_user_lists = targetUserLists?.toParcelables(accountKey,
profileImageSize)
result.target_statuses = targetStatuses?.toParcelables(accountKey,
accountType, profileImageSize)
result.target_object_statuses = targetObjectStatuses?.toParcelables(accountKey,
accountType, profileImageSize)
result.target_object_user_lists = targetObjectUserLists?.toParcelables(accountKey,
profileImageSize)
result.target_object_users = targetObjectUsers?.toParcelables(accountKey, accountType,
profileImageSize)
result.has_following_source = sources?.fold(false) { folded, item ->
if (item.isFollowing == true) {
return@fold true
}
return@fold folded
} ?: false
result.source_keys = result.sources?.mapToArray { it.key }
result.is_gap = isGap
return result
}

View File

@ -0,0 +1,22 @@
/*
* 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.task
data class GetTimelineResult(val exception: Exception?)

View File

@ -1,7 +1,5 @@
package org.mariotaku.twidere.model.util package org.mariotaku.twidere.model.util
import org.mariotaku.microblog.library.twitter.model.Activity
import org.mariotaku.twidere.extension.model.toParcelables
import org.mariotaku.twidere.model.ParcelableActivity import org.mariotaku.twidere.model.ParcelableActivity
import org.mariotaku.twidere.model.ParcelableUser import org.mariotaku.twidere.model.ParcelableUser
import org.mariotaku.twidere.model.UserKey import org.mariotaku.twidere.model.UserKey
@ -41,7 +39,7 @@ object ParcelableActivityUtils {
activity.after_filtered_source_ids = list.toTypedArray() activity.after_filtered_source_ids = list.toTypedArray()
return true return true
} else { } else {
activity.after_filtered_source_ids = activity.source_ids activity.after_filtered_source_ids = activity.source_keys
return false return false
} }
} }
@ -58,45 +56,5 @@ object ParcelableActivityUtils {
return result return result
} }
fun fromActivity(activity: Activity, accountKey: UserKey, accountType: String,
isGap: Boolean = false, profileImageSize: String = "normal"): ParcelableActivity {
val result = ParcelableActivity()
result.account_key = accountKey
result.timestamp = activity.createdAt.time
result.action = activity.action
result.max_sort_position = activity.maxSortPosition
result.min_sort_position = activity.minSortPosition
result.max_position = activity.maxPosition
result.min_position = activity.minPosition
result.sources = activity.sources?.toParcelables(accountKey, accountType,
profileImageSize)
result.target_users = activity.targetUsers?.toParcelables(accountKey,
accountType, profileImageSize)
result.target_user_lists = activity.targetUserLists?.toParcelables(accountKey,
profileImageSize)
result.target_statuses = activity.targetStatuses?.toParcelables(accountKey,
accountType, profileImageSize)
result.target_object_statuses = activity.targetObjectStatuses?.toParcelables(accountKey,
accountType, profileImageSize)
result.target_object_user_lists = activity.targetObjectUserLists?.toParcelables(accountKey,
profileImageSize)
result.target_object_users = activity.targetObjectUsers?.toParcelables(accountKey, accountType,
profileImageSize)
result.has_following_source = activity.sources?.fold(false) { folded, item ->
if (item.isFollowing == true) {
return@fold true
}
return@fold folded
} ?: false
if (result.sources != null) {
result.source_ids = arrayOfNulls<UserKey>(result.sources.size)
for (i in result.sources.indices) {
result.source_ids[i] = result.sources[i].key
}
}
result.is_gap = isGap
return result
}
} }

View File

@ -32,6 +32,7 @@ import org.mariotaku.twidere.constant.streamingEnabledKey
import org.mariotaku.twidere.constant.streamingNonMeteredNetworkKey import org.mariotaku.twidere.constant.streamingNonMeteredNetworkKey
import org.mariotaku.twidere.constant.streamingPowerSavingKey import org.mariotaku.twidere.constant.streamingPowerSavingKey
import org.mariotaku.twidere.extension.model.* import org.mariotaku.twidere.extension.model.*
import org.mariotaku.twidere.extension.model.api.microblog.toParcelable
import org.mariotaku.twidere.extension.model.api.toParcelable import org.mariotaku.twidere.extension.model.api.toParcelable
import org.mariotaku.twidere.model.* import org.mariotaku.twidere.model.*
import org.mariotaku.twidere.model.pagination.SinceMaxPagination import org.mariotaku.twidere.model.pagination.SinceMaxPagination
@ -315,8 +316,8 @@ class StreamingService : BaseService() {
} else { } else {
insertGap = false insertGap = false
} }
val curActivity = ParcelableActivityUtils.fromActivity(activity, account.key, val curActivity = activity.toParcelable(account.key, account.type, insertGap,
account.type, insertGap, profileImageSize) profileImageSize)
curActivity.account_color = account.color curActivity.account_color = account.color
curActivity.position_key = curActivity.timestamp curActivity.position_key = curActivity.timestamp
var updateId = -1L var updateId = -1L

View File

@ -23,12 +23,18 @@ import android.content.Context
import android.net.Uri import android.net.Uri
import org.mariotaku.microblog.library.MicroBlog import org.mariotaku.microblog.library.MicroBlog
import org.mariotaku.microblog.library.MicroBlogException import org.mariotaku.microblog.library.MicroBlogException
import org.mariotaku.microblog.library.mastodon.Mastodon
import org.mariotaku.microblog.library.twitter.model.* import org.mariotaku.microblog.library.twitter.model.*
import org.mariotaku.twidere.R
import org.mariotaku.twidere.annotation.AccountType import org.mariotaku.twidere.annotation.AccountType
import org.mariotaku.twidere.annotation.ReadPositionTag import org.mariotaku.twidere.annotation.ReadPositionTag
import org.mariotaku.twidere.extension.model.api.mastodon.toParcelable
import org.mariotaku.twidere.extension.model.api.microblog.toParcelable
import org.mariotaku.twidere.extension.model.isOfficial import org.mariotaku.twidere.extension.model.isOfficial
import org.mariotaku.twidere.extension.model.newMicroBlogInstance
import org.mariotaku.twidere.fragment.InteractionsTimelineFragment import org.mariotaku.twidere.fragment.InteractionsTimelineFragment
import org.mariotaku.twidere.model.AccountDetails import org.mariotaku.twidere.model.AccountDetails
import org.mariotaku.twidere.model.ParcelableActivity
import org.mariotaku.twidere.model.UserKey import org.mariotaku.twidere.model.UserKey
import org.mariotaku.twidere.provider.TwidereDataStore.Activities import org.mariotaku.twidere.provider.TwidereDataStore.Activities
import org.mariotaku.twidere.util.ErrorInfoStore import org.mariotaku.twidere.util.ErrorInfoStore
@ -45,24 +51,38 @@ class GetActivitiesAboutMeTask(context: Context) : GetActivitiesTask(context) {
override val contentUri: Uri override val contentUri: Uri
get() = Activities.AboutMe.CONTENT_URI get() = Activities.AboutMe.CONTENT_URI
private val profileImageSize = context.getString(R.string.profile_image_size)
@Throws(MicroBlogException::class) @Throws(MicroBlogException::class)
override fun getActivities(twitter: MicroBlog, details: AccountDetails, paging: Paging): override fun getActivities(account: AccountDetails, paging: Paging): List<ParcelableActivity> {
ResponseList<Activity> { when (account.type) {
if (details.isOfficial(context)) { AccountType.MASTODON -> {
return twitter.getActivitiesAboutMe(paging) val mastodon = account.newMicroBlogInstance(context, Mastodon::class.java)
return mastodon.getNotifications(paging).map {
it.toParcelable(account.key)
}
} }
val activities = ResponseList<Activity>()
val statuses: ResponseList<Status>
when (details.type) {
AccountType.FANFOU -> { AccountType.FANFOU -> {
statuses = twitter.getMentions(paging) val microBlog = account.newMicroBlogInstance(context, MicroBlog::class.java)
if (account.isOfficial(context)) {
return microBlog.getActivitiesAboutMe(paging).map {
it.toParcelable(account.key, account.type, profileImageSize = profileImageSize)
}
}
return microBlog.getMentions(paging).map {
InternalActivityCreator.status(it, account.key.id).toParcelable(account.key,
account.type, profileImageSize = profileImageSize)
}
} }
else -> { else -> {
statuses = twitter.getMentionsTimeline(paging) val microBlog = account.newMicroBlogInstance(context, MicroBlog::class.java)
return microBlog.getHomeTimeline(paging).map {
InternalActivityCreator.status(it, account.key.id).toParcelable(account.key,
account.type, profileImageSize = profileImageSize)
}
} }
} }
statuses.mapTo(activities) { InternalActivityCreator.status(it, details.key.id) }
return activities
} }

View File

@ -1,17 +1,13 @@
package org.mariotaku.twidere.task.twitter package org.mariotaku.twidere.task.twitter
import android.accounts.AccountManager import android.accounts.AccountManager
import android.content.ContentResolver
import android.content.ContentValues import android.content.ContentValues
import android.content.Context import android.content.Context
import android.net.Uri import android.net.Uri
import android.support.annotation.UiThread import android.support.annotation.UiThread
import org.mariotaku.kpreferences.get import org.mariotaku.kpreferences.get
import org.mariotaku.microblog.library.MicroBlog
import org.mariotaku.microblog.library.MicroBlogException 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.Paging
import org.mariotaku.microblog.library.twitter.model.ResponseList
import org.mariotaku.sqliteqb.library.Expression import org.mariotaku.sqliteqb.library.Expression
import org.mariotaku.twidere.R import org.mariotaku.twidere.R
import org.mariotaku.twidere.TwidereConstants.LOGTAG import org.mariotaku.twidere.TwidereConstants.LOGTAG
@ -20,14 +16,13 @@ import org.mariotaku.twidere.constant.loadItemLimitKey
import org.mariotaku.twidere.extension.model.getMaxId import org.mariotaku.twidere.extension.model.getMaxId
import org.mariotaku.twidere.extension.model.getMaxSortId import org.mariotaku.twidere.extension.model.getMaxSortId
import org.mariotaku.twidere.extension.model.getSinceId import org.mariotaku.twidere.extension.model.getSinceId
import org.mariotaku.twidere.extension.model.newMicroBlogInstance
import org.mariotaku.twidere.model.AccountDetails import org.mariotaku.twidere.model.AccountDetails
import org.mariotaku.twidere.model.ParcelableActivity
import org.mariotaku.twidere.model.RefreshTaskParam import org.mariotaku.twidere.model.RefreshTaskParam
import org.mariotaku.twidere.model.TwitterListResponse
import org.mariotaku.twidere.model.UserKey import org.mariotaku.twidere.model.UserKey
import org.mariotaku.twidere.model.event.GetActivitiesTaskEvent import org.mariotaku.twidere.model.event.GetActivitiesTaskEvent
import org.mariotaku.twidere.model.task.GetTimelineResult
import org.mariotaku.twidere.model.util.AccountUtils import org.mariotaku.twidere.model.util.AccountUtils
import org.mariotaku.twidere.model.util.ParcelableActivityUtils
import org.mariotaku.twidere.provider.TwidereDataStore.Activities import org.mariotaku.twidere.provider.TwidereDataStore.Activities
import org.mariotaku.twidere.task.BaseAbstractTask import org.mariotaku.twidere.task.BaseAbstractTask
import org.mariotaku.twidere.util.* import org.mariotaku.twidere.util.*
@ -39,26 +34,21 @@ import java.util.*
*/ */
abstract class GetActivitiesTask( abstract class GetActivitiesTask(
context: Context context: Context
) : BaseAbstractTask<RefreshTaskParam, List<TwitterListResponse<Activity>>, (Boolean) -> Unit>(context) { ) : BaseAbstractTask<RefreshTaskParam, List<GetTimelineResult?>, (Boolean) -> Unit>(context) {
private val profileImageSize = context.getString(R.string.profile_image_size)
protected abstract val errorInfoKey: String protected abstract val errorInfoKey: String
protected abstract val contentUri: Uri protected abstract val contentUri: Uri
override fun doLongOperation(param: RefreshTaskParam): List<TwitterListResponse<Activity>> { override fun doLongOperation(param: RefreshTaskParam): List<GetTimelineResult?> {
if (param.shouldAbort) return emptyList() if (param.shouldAbort) return emptyList()
val accountKeys = param.accountKeys val accountKeys = param.accountKeys
val cr = context.contentResolver
val result = ArrayList<TwitterListResponse<Activity>>()
val loadItemLimit = preferences[loadItemLimitKey] val loadItemLimit = preferences[loadItemLimitKey]
val saveReadPosition = BooleanArray(accountKeys.size) val saveReadPosition = BooleanArray(accountKeys.size)
accountKeys.forEachIndexed { i, accountKey -> val result = accountKeys.mapIndexed { i, accountKey ->
val noItemsBefore = DataStoreUtils.getActivitiesCount(context, contentUri, accountKey) <= 0 val noItemsBefore = DataStoreUtils.getActivitiesCount(context, contentUri, accountKey) <= 0
val credentials = AccountUtils.getAccountDetails(AccountManager.get(context), accountKey, val credentials = AccountUtils.getAccountDetails(AccountManager.get(context), accountKey,
true) ?: return@forEachIndexed true) ?: return@mapIndexed null
val microBlog = credentials.newMicroBlogInstance(context = context, cls = MicroBlog::class.java)
val paging = Paging() val paging = Paging()
paging.count(loadItemLimit) paging.count(loadItemLimit)
val maxId = param.getMaxId(i) val maxId = param.getMaxId(i)
@ -76,9 +66,9 @@ abstract class GetActivitiesTask(
} }
// We should delete old activities has intersection with new items // We should delete old activities has intersection with new items
try { try {
val activities = getActivities(microBlog, credentials, paging) val activities = getActivities(credentials, paging)
val storeResult = storeActivities(cr, loadItemLimit, credentials, noItemsBefore, val storeResult = storeActivities(credentials, activities, sinceId, maxId,
activities, sinceId, maxId, false) loadItemLimit, noItemsBefore, false)
if (saveReadPosition[i]) { if (saveReadPosition[i]) {
} }
@ -93,17 +83,19 @@ abstract class GetActivitiesTask(
} else if (e.isCausedByNetworkIssue) { } else if (e.isCausedByNetworkIssue) {
errorInfoStore[errorInfoKey, accountKey] = ErrorInfoStore.CODE_NETWORK_ERROR errorInfoStore[errorInfoKey, accountKey] = ErrorInfoStore.CODE_NETWORK_ERROR
} }
return@mapIndexed GetTimelineResult(e)
} catch (e: GetStatusesTask.GetTimelineException) { } catch (e: GetStatusesTask.GetTimelineException) {
result.add(TwitterListResponse(accountKey, e)) return@mapIndexed GetTimelineResult(e)
} }
return@mapIndexed GetTimelineResult(null)
} }
setLocalReadPosition(accountKeys, saveReadPosition) setLocalReadPosition(accountKeys, saveReadPosition)
return result return result
} }
override fun afterExecute(handler: ((Boolean) -> Unit)?, result: List<TwitterListResponse<Activity>>) { override fun afterExecute(handler: ((Boolean) -> Unit)?, result: List<GetTimelineResult?>) {
context.contentResolver.notifyChange(contentUri, null) context.contentResolver.notifyChange(contentUri, null)
val exception = AsyncTwitterWrapper.getException(result) val exception = result.firstOrNull { it?.exception != null }?.exception
bus.post(GetActivitiesTaskEvent(contentUri, false, exception)) bus.post(GetActivitiesTaskEvent(contentUri, false, exception))
handler?.invoke(true) handler?.invoke(true)
} }
@ -114,26 +106,24 @@ abstract class GetActivitiesTask(
} }
@Throws(MicroBlogException::class) @Throws(MicroBlogException::class)
protected abstract fun getActivities(twitter: MicroBlog, details: AccountDetails, paging: Paging): ResponseList<Activity> protected abstract fun getActivities(account: AccountDetails, paging: Paging): List<ParcelableActivity>
protected abstract fun setLocalReadPosition(accountKeys: Array<UserKey>, saveReadPosition: BooleanArray) protected abstract fun setLocalReadPosition(accountKeys: Array<UserKey>, saveReadPosition: BooleanArray)
private fun storeActivities(cr: ContentResolver, loadItemLimit: Int, details: AccountDetails, private fun storeActivities(details: AccountDetails, activities: List<ParcelableActivity>,
noItemsBefore: Boolean, activities: ResponseList<Activity>, sinceId: String?, maxId: String?, loadItemLimit: Int, noItemsBefore: Boolean,
sinceId: String?, maxId: String?, notify: Boolean): Int { notify: Boolean): Int {
val cr = context.contentResolver
val deleteBound = LongArray(2) { -1 } val deleteBound = LongArray(2) { -1 }
val valuesList = ArrayList<ContentValues>() val valuesList = ArrayList<ContentValues>()
var minIdx = -1 var minIdx = -1
var minPositionKey: Long = -1 var minPositionKey: Long = -1
if (!activities.isEmpty()) { if (!activities.isEmpty()) {
val firstSortId = activities.first().createdAt.time val firstSortId = activities.first().timestamp
val lastSortId = activities.last().createdAt.time val lastSortId = activities.last().timestamp
// Get id diff of first and last item // Get id diff of first and last item
val sortDiff = firstSortId - lastSortId val sortDiff = firstSortId - lastSortId
for (i in activities.indices) { activities.forEachIndexed { i, activity ->
val item = activities[i]
val activity = ParcelableActivityUtils.fromActivity(item, details.key, details.type,
false, profileImageSize)
mediaPreloader.preloadActivity(activity) mediaPreloader.preloadActivity(activity)
activity.position_key = GetStatusesTask.getPositionKey(activity.timestamp, activity.position_key = GetStatusesTask.getPositionKey(activity.timestamp,
activity.timestamp, lastSortId, sortDiff, i, activities.size) activity.timestamp, lastSortId, sortDiff, i, activities.size)
@ -147,7 +137,7 @@ abstract class GetActivitiesTask(
} else { } else {
deleteBound[1] = Math.max(deleteBound[1], activity.max_sort_position) deleteBound[1] = Math.max(deleteBound[1], activity.max_sort_position)
} }
if (minIdx == -1 || item < activities[minIdx]) { if (minIdx == -1 || activity < activities[minIdx]) {
minIdx = i minIdx = i
minPositionKey = activity.position_key minPositionKey = activity.position_key
} }

View File

@ -25,6 +25,7 @@ import org.mariotaku.twidere.model.ParcelableStatus
import org.mariotaku.twidere.model.RefreshTaskParam import org.mariotaku.twidere.model.RefreshTaskParam
import org.mariotaku.twidere.model.UserKey import org.mariotaku.twidere.model.UserKey
import org.mariotaku.twidere.model.event.GetStatusesTaskEvent import org.mariotaku.twidere.model.event.GetStatusesTaskEvent
import org.mariotaku.twidere.model.task.GetTimelineResult
import org.mariotaku.twidere.model.util.AccountUtils import org.mariotaku.twidere.model.util.AccountUtils
import org.mariotaku.twidere.model.util.ParcelableStatusUtils import org.mariotaku.twidere.model.util.ParcelableStatusUtils
import org.mariotaku.twidere.provider.TwidereDataStore.AccountSupportColumns import org.mariotaku.twidere.provider.TwidereDataStore.AccountSupportColumns
@ -41,13 +42,13 @@ import org.mariotaku.twidere.util.content.ContentResolverUtils
*/ */
abstract class GetStatusesTask( abstract class GetStatusesTask(
context: Context context: Context
) : BaseAbstractTask<RefreshTaskParam, List<GetStatusesTask.GetStatusesResult?>, (Boolean) -> Unit>(context) { ) : BaseAbstractTask<RefreshTaskParam, List<GetTimelineResult?>, (Boolean) -> Unit>(context) {
protected abstract val contentUri: Uri protected abstract val contentUri: Uri
protected abstract val errorInfoKey: String protected abstract val errorInfoKey: String
override fun doLongOperation(param: RefreshTaskParam): List<GetStatusesResult?> { override fun doLongOperation(param: RefreshTaskParam): List<GetTimelineResult?> {
if (param.shouldAbort) return emptyList() if (param.shouldAbort) return emptyList()
val accountKeys = param.accountKeys val accountKeys = param.accountKeys
val loadItemLimit = preferences[loadItemLimitKey] val loadItemLimit = preferences[loadItemLimitKey]
@ -90,7 +91,7 @@ abstract class GetStatusesTask(
if (storeResult != 0) { if (storeResult != 0) {
throw GetTimelineException(storeResult) throw GetTimelineException(storeResult)
} }
return@mapIndexed GetStatusesResult(null) return@mapIndexed GetTimelineResult(null)
} catch (e: MicroBlogException) { } catch (e: MicroBlogException) {
DebugLog.w(LOGTAG, tr = e) DebugLog.w(LOGTAG, tr = e)
if (e.isCausedByNetworkIssue) { if (e.isCausedByNetworkIssue) {
@ -98,14 +99,14 @@ abstract class GetStatusesTask(
} else if (e.statusCode == 401) { } else if (e.statusCode == 401) {
// Unauthorized // Unauthorized
} }
return@mapIndexed GetStatusesResult(e) return@mapIndexed GetTimelineResult(e)
} catch (e: GetTimelineException) { } catch (e: GetTimelineException) {
return@mapIndexed GetStatusesResult(e) return@mapIndexed GetTimelineResult(e)
} }
} }
} }
override fun afterExecute(handler: ((Boolean) -> Unit)?, result: List<GetStatusesResult?>) { override fun afterExecute(handler: ((Boolean) -> Unit)?, result: List<GetTimelineResult?>) {
context.contentResolver.notifyChange(contentUri, null) context.contentResolver.notifyChange(contentUri, null)
val exception = result.firstOrNull { it?.exception != null }?.exception val exception = result.firstOrNull { it?.exception != null }?.exception
bus.post(GetStatusesTaskEvent(contentUri, false, exception)) bus.post(GetStatusesTaskEvent(contentUri, false, exception))
@ -141,8 +142,7 @@ abstract class GetStatusesTask(
val sortDiff = firstSortId - lastSortId val sortDiff = firstSortId - lastSortId
val creator = ObjectCursor.valuesCreatorFrom(ParcelableStatus::class.java) val creator = ObjectCursor.valuesCreatorFrom(ParcelableStatus::class.java)
for (i in 0 until statuses.size) { statuses.forEachIndexed { i, status ->
val status = statuses[i]
ParcelableStatusUtils.updateExtraInformation(status, account) ParcelableStatusUtils.updateExtraInformation(status, account)
status.position_key = getPositionKey(status.timestamp, status.sort_id, lastSortId, status.position_key = getPositionKey(status.timestamp, status.sort_id, lastSortId,
sortDiff, i, statuses.size) sortDiff, i, statuses.size)
@ -210,8 +210,6 @@ abstract class GetStatusesTask(
} }
} }
data class GetStatusesResult(val exception: Exception?)
companion object { companion object {
const val ERROR_LOAD_GAP = 1 const val ERROR_LOAD_GAP = 1