mirror of
https://github.com/TwidereProject/Twidere-Android
synced 2025-02-02 17:56:56 +01:00
implementing relationship caching
This commit is contained in:
parent
e9205dfb23
commit
0f86fc75a4
1
.gitignore
vendored
1
.gitignore
vendored
@ -29,6 +29,7 @@ Thumbs.db
|
|||||||
|
|
||||||
# Local files
|
# Local files
|
||||||
/captures
|
/captures
|
||||||
|
/reports
|
||||||
|
|
||||||
# JRE error dumps
|
# JRE error dumps
|
||||||
hs_err_*.log
|
hs_err_*.log
|
@ -1,3 +1,3 @@
|
|||||||
org.gradle.daemon=true
|
org.gradle.daemon=true
|
||||||
org.gradle.jvmargs=-Xms2048m -Xmx3072m
|
org.gradle.jvmargs=-Xms2048m -Xmx3072m
|
||||||
systemProp.kotlin.daemon.verbose=true
|
kotlin.incremental=true
|
@ -73,6 +73,25 @@ public class ParcelableRelationship implements Parcelable {
|
|||||||
|
|
||||||
public boolean filtering;
|
public boolean filtering;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
|
||||||
|
ParcelableRelationship that = (ParcelableRelationship) o;
|
||||||
|
|
||||||
|
if (!account_key.equals(that.account_key)) return false;
|
||||||
|
return user_key.equals(that.user_key);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int result = account_key.hashCode();
|
||||||
|
result = 31 * result + user_key.hashCode();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int describeContents() {
|
public int describeContents() {
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -34,7 +34,7 @@ android {
|
|||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "org.mariotaku.twidere"
|
applicationId "org.mariotaku.twidere"
|
||||||
minSdkVersion 14
|
minSdkVersion project.properties['overrideMinSdkVersion'] ?: 14
|
||||||
targetSdkVersion 25
|
targetSdkVersion 25
|
||||||
versionCode 301
|
versionCode 301
|
||||||
versionName '3.4.39'
|
versionName '3.4.39'
|
||||||
|
@ -39,4 +39,8 @@ inline fun <reified T> List<T>.subArray(range: IntRange): Array<T> {
|
|||||||
return Array(range.count()) {
|
return Array(range.count()) {
|
||||||
this[range.start + it]
|
this[range.start + it]
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T> T.addTo(collection: MutableCollection<T>): Boolean {
|
||||||
|
return collection.add(this)
|
||||||
}
|
}
|
@ -19,16 +19,22 @@
|
|||||||
|
|
||||||
package org.mariotaku.twidere.model.util
|
package org.mariotaku.twidere.model.util
|
||||||
|
|
||||||
|
import android.content.ContentResolver
|
||||||
|
import android.support.v4.util.ArraySet
|
||||||
|
import org.mariotaku.library.objectcursor.ObjectCursor
|
||||||
import org.mariotaku.microblog.library.twitter.model.Relationship
|
import org.mariotaku.microblog.library.twitter.model.Relationship
|
||||||
import org.mariotaku.microblog.library.twitter.model.User
|
import org.mariotaku.microblog.library.twitter.model.User
|
||||||
|
import org.mariotaku.sqliteqb.library.Expression
|
||||||
import org.mariotaku.twidere.model.ParcelableRelationship
|
import org.mariotaku.twidere.model.ParcelableRelationship
|
||||||
import org.mariotaku.twidere.model.ParcelableUser
|
import org.mariotaku.twidere.model.ParcelableUser
|
||||||
import org.mariotaku.twidere.model.UserKey
|
import org.mariotaku.twidere.model.UserKey
|
||||||
|
import org.mariotaku.twidere.provider.TwidereDataStore.*
|
||||||
|
import org.mariotaku.twidere.util.content.ContentResolverUtils
|
||||||
|
|
||||||
object ParcelableRelationshipUtils {
|
object ParcelableRelationshipUtils {
|
||||||
|
|
||||||
fun create(accountKey: UserKey, userKey: UserKey, relationship: Relationship?,
|
fun create(accountKey: UserKey, userKey: UserKey, relationship: Relationship?,
|
||||||
filtering: Boolean): ParcelableRelationship {
|
filtering: Boolean): ParcelableRelationship {
|
||||||
val obj = ParcelableRelationship()
|
val obj = ParcelableRelationship()
|
||||||
obj.account_key = accountKey
|
obj.account_key = accountKey
|
||||||
obj.user_key = userKey
|
obj.user_key = userKey
|
||||||
@ -63,7 +69,7 @@ object ParcelableRelationshipUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun create(accountKey: UserKey, userKey: UserKey, user: User,
|
fun create(accountKey: UserKey, userKey: UserKey, user: User,
|
||||||
filtering: Boolean): ParcelableRelationship {
|
filtering: Boolean = false): ParcelableRelationship {
|
||||||
val obj = ParcelableRelationship()
|
val obj = ParcelableRelationship()
|
||||||
obj.account_key = accountKey
|
obj.account_key = accountKey
|
||||||
obj.user_key = userKey
|
obj.user_key = userKey
|
||||||
@ -76,4 +82,25 @@ object ParcelableRelationshipUtils {
|
|||||||
obj.notifications_enabled = user.isNotificationsEnabled == true
|
obj.notifications_enabled = user.isNotificationsEnabled == true
|
||||||
return obj
|
return obj
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param relationships Relationships to update, if an item has _id, then we will call
|
||||||
|
* `ContentResolver.update`, `ContentResolver.bulkInsert` otherwise.
|
||||||
|
*/
|
||||||
|
fun insert(cr: ContentResolver, relationships: Collection<ParcelableRelationship>) {
|
||||||
|
val insertItems = ArraySet<ParcelableRelationship>()
|
||||||
|
val valuesCreator = ObjectCursor.valuesCreatorFrom(ParcelableRelationship::class.java)
|
||||||
|
relationships.forEach {
|
||||||
|
if (it._id > 0) {
|
||||||
|
val values = valuesCreator.create(it)
|
||||||
|
val where = Expression.equalsArgs(CachedRelationships._ID).sql
|
||||||
|
val whereArgs = arrayOf(it._id.toString())
|
||||||
|
cr.update(CachedRelationships.CONTENT_URI, values, where, whereArgs)
|
||||||
|
} else {
|
||||||
|
insertItems.add(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ContentResolverUtils.bulkInsert(cr, CachedRelationships.CONTENT_URI,
|
||||||
|
insertItems.map(valuesCreator::create))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ class DestroyFriendshipTask(context: Context) : AbsFriendshipOperationTask(conte
|
|||||||
val where = Expression.and(Expression.equalsArgs(Statuses.ACCOUNT_KEY),
|
val where = Expression.and(Expression.equalsArgs(Statuses.ACCOUNT_KEY),
|
||||||
Expression.or(Expression.equalsArgs(Statuses.USER_KEY),
|
Expression.or(Expression.equalsArgs(Statuses.USER_KEY),
|
||||||
Expression.equalsArgs(Statuses.RETWEETED_BY_USER_KEY)))
|
Expression.equalsArgs(Statuses.RETWEETED_BY_USER_KEY)))
|
||||||
val whereArgs = arrayOf(args.userKey.toString(), args.userKey.toString(), args.userKey.toString())
|
val whereArgs = arrayOf(args.accountKey.toString(), args.userKey.toString(), args.userKey.toString())
|
||||||
val resolver = context.contentResolver
|
val resolver = context.contentResolver
|
||||||
resolver.delete(Statuses.CONTENT_URI, where.sql, whereArgs)
|
resolver.delete(Statuses.CONTENT_URI, where.sql, whereArgs)
|
||||||
}
|
}
|
||||||
|
68
twidere/src/main/kotlin/org/mariotaku/twidere/task/cache/CacheUserRelationshipTask.kt
vendored
Normal file
68
twidere/src/main/kotlin/org/mariotaku/twidere/task/cache/CacheUserRelationshipTask.kt
vendored
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
package org.mariotaku.twidere.task.cache
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.content.ContentResolver
|
||||||
|
import android.content.Context
|
||||||
|
import android.support.v4.util.ArraySet
|
||||||
|
import org.mariotaku.abstask.library.AbstractTask
|
||||||
|
import org.mariotaku.ktextension.map
|
||||||
|
import org.mariotaku.ktextension.useCursor
|
||||||
|
import org.mariotaku.library.objectcursor.ObjectCursor
|
||||||
|
import org.mariotaku.microblog.library.twitter.model.User
|
||||||
|
import org.mariotaku.sqliteqb.library.Expression
|
||||||
|
import org.mariotaku.twidere.model.ParcelableRelationship
|
||||||
|
import org.mariotaku.twidere.model.ParcelableUser
|
||||||
|
import org.mariotaku.twidere.model.UserKey
|
||||||
|
import org.mariotaku.twidere.model.util.ParcelableRelationshipUtils
|
||||||
|
import org.mariotaku.twidere.model.util.ParcelableUserUtils
|
||||||
|
import org.mariotaku.twidere.model.util.UserKeyUtils
|
||||||
|
import org.mariotaku.twidere.provider.TwidereDataStore.CachedUsers
|
||||||
|
import org.mariotaku.twidere.provider.TwidereDataStore.CachedRelationships
|
||||||
|
import org.mariotaku.twidere.task.BaseAbstractTask
|
||||||
|
import org.mariotaku.twidere.util.content.ContentResolverUtils
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Mariotaku on 2017/3/17.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class CacheUserRelationshipTask(context: Context, val accountKey: UserKey, val users: Collection<User>) : BaseAbstractTask<Any?, Unit, Any?>(context) {
|
||||||
|
override fun doLongOperation(param: Any?) {
|
||||||
|
cacheUserRelationships(context.contentResolver, accountKey, users)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun cacheUserRelationships(cr: ContentResolver, accountKey: UserKey, users: Collection<User>) {
|
||||||
|
|
||||||
|
val parcelableUsers = users.map { ParcelableUserUtils.fromUser(it, accountKey) }
|
||||||
|
|
||||||
|
val userValuesCreator = ObjectCursor.valuesCreatorFrom(ParcelableUser::class.java)
|
||||||
|
ContentResolverUtils.bulkInsert(cr, CachedUsers.CONTENT_URI, parcelableUsers.map(userValuesCreator::create))
|
||||||
|
|
||||||
|
val selectionArgsList = parcelableUsers.mapTo(mutableListOf(accountKey.toString())) {
|
||||||
|
it.key.toString()
|
||||||
|
}
|
||||||
|
@SuppressLint("Recycle")
|
||||||
|
val localRelationships = cr.query(CachedRelationships.CONTENT_URI, CachedRelationships.COLUMNS,
|
||||||
|
Expression.and(Expression.equalsArgs(CachedRelationships.ACCOUNT_KEY),
|
||||||
|
Expression.inArgs(CachedRelationships.USER_KEY, users.size)).sql,
|
||||||
|
selectionArgsList.toTypedArray(), null).useCursor { cur ->
|
||||||
|
return@useCursor cur.map(ObjectCursor.indicesFrom(cur, ParcelableRelationship::class.java))
|
||||||
|
}
|
||||||
|
val relationships = users.mapTo(ArraySet<ParcelableRelationship>()) { user ->
|
||||||
|
val userKey = UserKeyUtils.fromUser(user)
|
||||||
|
return@mapTo localRelationships.find {
|
||||||
|
it.user_key == userKey
|
||||||
|
}?.apply {
|
||||||
|
user.isFollowing?.let { this.following = it }
|
||||||
|
user.isFollowedBy?.let { this.followed_by = it }
|
||||||
|
user.isBlocking?.let { this.blocking = it }
|
||||||
|
user.isBlockedBy?.let { this.blocked_by = it }
|
||||||
|
user.isMuting?.let { this.muting = it }
|
||||||
|
user.isNotificationsEnabled?.let { this.notifications_enabled = it }
|
||||||
|
} ?: ParcelableRelationshipUtils.create(accountKey, userKey, user)
|
||||||
|
}
|
||||||
|
ParcelableRelationshipUtils.insert(cr, relationships)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -17,14 +17,17 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.mariotaku.twidere.task
|
package org.mariotaku.twidere.task.cache
|
||||||
|
|
||||||
import android.content.ContentValues
|
import android.content.ContentValues
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import com.twitter.Extractor
|
import com.twitter.Extractor
|
||||||
import org.mariotaku.abstask.library.AbstractTask
|
import org.mariotaku.abstask.library.AbstractTask
|
||||||
|
import org.mariotaku.ktextension.addTo
|
||||||
import org.mariotaku.microblog.library.twitter.model.Status
|
import org.mariotaku.microblog.library.twitter.model.Status
|
||||||
|
import org.mariotaku.microblog.library.twitter.model.User
|
||||||
import org.mariotaku.twidere.R
|
import org.mariotaku.twidere.R
|
||||||
|
import org.mariotaku.twidere.model.UserKey
|
||||||
import org.mariotaku.twidere.provider.TwidereDataStore.*
|
import org.mariotaku.twidere.provider.TwidereDataStore.*
|
||||||
import org.mariotaku.twidere.util.ContentValuesCreator
|
import org.mariotaku.twidere.util.ContentValuesCreator
|
||||||
import org.mariotaku.twidere.util.InternalTwitterContentUtils
|
import org.mariotaku.twidere.util.InternalTwitterContentUtils
|
||||||
@ -33,28 +36,28 @@ import org.mariotaku.twidere.util.content.ContentResolverUtils
|
|||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class CacheUsersStatusesTask(
|
class CacheUsersStatusesTask(
|
||||||
private val context: Context
|
private val context: Context,
|
||||||
) : AbstractTask<TwitterListResponse<Status>, Unit?, Unit?>() {
|
private val accountKey: UserKey,
|
||||||
|
private val statuses: List<Status>
|
||||||
|
) : AbstractTask<Any?, Unit?, Unit?>() {
|
||||||
|
|
||||||
private val profileImageSize = context.getString(R.string.profile_image_size)
|
private val profileImageSize = context.getString(R.string.profile_image_size)
|
||||||
|
|
||||||
override fun doLongOperation(params: TwitterListResponse<Status>) {
|
override fun doLongOperation(params: Any?) {
|
||||||
val resolver = context.contentResolver
|
val resolver = context.contentResolver
|
||||||
val extractor = Extractor()
|
val extractor = Extractor()
|
||||||
val list = params.data ?: return
|
|
||||||
var bulkIdx = 0
|
var bulkIdx = 0
|
||||||
val totalSize = list.size
|
val totalSize = statuses.size
|
||||||
while (bulkIdx < totalSize) {
|
while (bulkIdx < totalSize) {
|
||||||
var idx = bulkIdx
|
var idx = bulkIdx
|
||||||
val end = Math.min(totalSize, bulkIdx + ContentResolverUtils.MAX_BULK_COUNT)
|
val end = Math.min(totalSize, bulkIdx + ContentResolverUtils.MAX_BULK_COUNT)
|
||||||
while (idx < end) {
|
while (idx < end) {
|
||||||
val status = list[idx]
|
val status = statuses[idx]
|
||||||
|
|
||||||
val usersValues = HashSet<ContentValues>()
|
val users = HashSet<User>()
|
||||||
val statusesValues = HashSet<ContentValues>()
|
val statusesValues = HashSet<ContentValues>()
|
||||||
val hashTagValues = HashSet<ContentValues>()
|
val hashTagValues = HashSet<ContentValues>()
|
||||||
|
|
||||||
val accountKey = params.accountKey
|
|
||||||
statusesValues.add(ContentValuesCreator.createStatus(status, accountKey, profileImageSize))
|
statusesValues.add(ContentValuesCreator.createStatus(status, accountKey, profileImageSize))
|
||||||
val text = InternalTwitterContentUtils.unescapeTwitterStatusText(status.extendedText)
|
val text = InternalTwitterContentUtils.unescapeTwitterStatusText(status.extendedText)
|
||||||
for (hashtag in extractor.extractHashtags(text)) {
|
for (hashtag in extractor.extractHashtags(text)) {
|
||||||
@ -62,19 +65,14 @@ class CacheUsersStatusesTask(
|
|||||||
values.put(CachedHashtags.NAME, hashtag)
|
values.put(CachedHashtags.NAME, hashtag)
|
||||||
hashTagValues.add(values)
|
hashTagValues.add(values)
|
||||||
}
|
}
|
||||||
val cachedUser = ContentValuesCreator.createCachedUser(status.user)
|
|
||||||
cachedUser.put(CachedUsers.LAST_SEEN, System.currentTimeMillis())
|
status.user?.addTo(users)
|
||||||
usersValues.add(cachedUser)
|
status.retweetedStatus?.user?.addTo(users)
|
||||||
if (status.isRetweet) {
|
status.quotedStatus?.user?.addTo(users)
|
||||||
val cachedRetweetedUser = ContentValuesCreator.createCachedUser(status.retweetedStatus.user,
|
|
||||||
profileImageSize)
|
|
||||||
cachedRetweetedUser.put(CachedUsers.LAST_SEEN, System.currentTimeMillis())
|
|
||||||
usersValues.add(cachedRetweetedUser)
|
|
||||||
}
|
|
||||||
|
|
||||||
ContentResolverUtils.bulkInsert(resolver, CachedStatuses.CONTENT_URI, statusesValues)
|
ContentResolverUtils.bulkInsert(resolver, CachedStatuses.CONTENT_URI, statusesValues)
|
||||||
ContentResolverUtils.bulkInsert(resolver, CachedHashtags.CONTENT_URI, hashTagValues)
|
ContentResolverUtils.bulkInsert(resolver, CachedHashtags.CONTENT_URI, hashTagValues)
|
||||||
ContentResolverUtils.bulkInsert(resolver, CachedUsers.CONTENT_URI, usersValues)
|
CacheUserRelationshipTask.cacheUserRelationships(resolver, accountKey, users)
|
||||||
idx++
|
idx++
|
||||||
}
|
}
|
||||||
bulkIdx += 100
|
bulkIdx += 100
|
@ -33,7 +33,7 @@ import org.mariotaku.twidere.model.util.ParcelableStatusUtils
|
|||||||
import org.mariotaku.twidere.provider.TwidereDataStore.AccountSupportColumns
|
import org.mariotaku.twidere.provider.TwidereDataStore.AccountSupportColumns
|
||||||
import org.mariotaku.twidere.provider.TwidereDataStore.Statuses
|
import org.mariotaku.twidere.provider.TwidereDataStore.Statuses
|
||||||
import org.mariotaku.twidere.task.BaseAbstractTask
|
import org.mariotaku.twidere.task.BaseAbstractTask
|
||||||
import org.mariotaku.twidere.task.CacheUsersStatusesTask
|
import org.mariotaku.twidere.task.cache.CacheUsersStatusesTask
|
||||||
import org.mariotaku.twidere.util.*
|
import org.mariotaku.twidere.util.*
|
||||||
import org.mariotaku.twidere.util.content.ContentResolverUtils
|
import org.mariotaku.twidere.util.content.ContentResolverUtils
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@ -109,12 +109,10 @@ abstract class GetStatusesTask(
|
|||||||
val storeResult = storeStatus(accountKey, details, statuses, sinceId, maxId, sinceSortId,
|
val storeResult = storeStatus(accountKey, details, statuses, sinceId, maxId, sinceSortId,
|
||||||
maxSortId, loadItemLimit, false)
|
maxSortId, loadItemLimit, false)
|
||||||
// TODO cache related data and preload
|
// TODO cache related data and preload
|
||||||
val cacheTask = CacheUsersStatusesTask(context)
|
val cacheTask = CacheUsersStatusesTask(context, accountKey, statuses)
|
||||||
val response = TwitterWrapper.StatusListResponse(accountKey, statuses)
|
|
||||||
cacheTask.params = response
|
|
||||||
TaskStarter.execute(cacheTask)
|
TaskStarter.execute(cacheTask)
|
||||||
errorInfoStore.remove(errorInfoKey, accountKey.id)
|
errorInfoStore.remove(errorInfoKey, accountKey.id)
|
||||||
result.add(response)
|
result.add(TwitterWrapper.StatusListResponse(accountKey, statuses))
|
||||||
if (storeResult != 0) {
|
if (storeResult != 0) {
|
||||||
throw GetTimelineException(storeResult)
|
throw GetTimelineException(storeResult)
|
||||||
}
|
}
|
||||||
|
@ -34,10 +34,8 @@ import org.mariotaku.twidere.provider.TwidereDataStore.SavedSearches
|
|||||||
object ContentValuesCreator {
|
object ContentValuesCreator {
|
||||||
|
|
||||||
fun createCachedUser(user: User, profileImageSize: String = "normal"): ContentValues {
|
fun createCachedUser(user: User, profileImageSize: String = "normal"): ContentValues {
|
||||||
val values = ContentValues()
|
return ObjectCursor.valuesCreatorFrom(ParcelableUser::class.java)
|
||||||
ObjectCursor.valuesCreatorFrom(ParcelableUser::class.java).writeTo(ParcelableUserUtils.fromUser(user, null,
|
.create(ParcelableUserUtils.fromUser(user, null, profileImageSize = profileImageSize))
|
||||||
profileImageSize = profileImageSize), values)
|
|
||||||
return values
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createFilteredUser(status: ParcelableStatus): ContentValues {
|
fun createFilteredUser(status: ParcelableStatus): ContentValues {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user