From 31086e7718b5a640afb26c86cf6ba3ba71d123a2 Mon Sep 17 00:00:00 2001 From: Mariotaku Lee Date: Sat, 29 Apr 2017 11:19:31 +0800 Subject: [PATCH] improved twitter replies #809 --- .../twidere/model/ParcelableActivity.java | 2 - .../microblog/library/twitter/TwitterWeb.java | 13 +++ .../library/twitter/model/StatusPage.java | 36 ++++++ .../twidere/util/MicroBlogAPIFactory.java | 3 +- .../adapter/ParcelableActivitiesAdapter.kt | 39 +++++-- .../twidere/extension/atto/DOMExtensions.kt | 58 ++++++++++ .../restfu/MultiValueMapExtensions.kt | 30 +++++ .../twidere/fragment/StatusFragment.kt | 7 +- .../loader/statuses/ConversationLoader.kt | 104 +++++++++++++----- .../model/util/ParcelableActivityUtils.kt | 15 +-- .../mariotaku/twidere/util/DataStoreUtils.kt | 18 ++- .../util/api/TwidereHttpRequestFactory.kt | 4 +- .../util/api/TwitterAndroidExtraHeaders.kt | 8 +- .../util/api/TwitterMacExtraHeaders.kt | 8 +- .../twidere/util/api/UserAgentExtraHeaders.kt | 6 +- 15 files changed, 291 insertions(+), 60 deletions(-) create mode 100644 twidere/src/main/java/org/mariotaku/microblog/library/twitter/model/StatusPage.java create mode 100644 twidere/src/main/kotlin/org/mariotaku/twidere/extension/atto/DOMExtensions.kt create mode 100644 twidere/src/main/kotlin/org/mariotaku/twidere/extension/restfu/MultiValueMapExtensions.kt diff --git a/twidere.component.common/src/main/java/org/mariotaku/twidere/model/ParcelableActivity.java b/twidere.component.common/src/main/java/org/mariotaku/twidere/model/ParcelableActivity.java index f873e2a43..7733804fc 100644 --- a/twidere.component.common/src/main/java/org/mariotaku/twidere/model/ParcelableActivity.java +++ b/twidere.component.common/src/main/java/org/mariotaku/twidere/model/ParcelableActivity.java @@ -102,8 +102,6 @@ public class ParcelableActivity extends ParcelableStatus implements Parcelable { public boolean has_following_source = true; public transient UserKey[] after_filtered_source_keys; - public transient ParcelableLiteUser[] after_filtered_sources; - public ParcelableActivity() { } diff --git a/twidere/src/main/java/org/mariotaku/microblog/library/twitter/TwitterWeb.java b/twidere/src/main/java/org/mariotaku/microblog/library/twitter/TwitterWeb.java index 7695d9796..b3d447eae 100644 --- a/twidere/src/main/java/org/mariotaku/microblog/library/twitter/TwitterWeb.java +++ b/twidere/src/main/java/org/mariotaku/microblog/library/twitter/TwitterWeb.java @@ -21,7 +21,11 @@ package org.mariotaku.microblog.library.twitter; import org.mariotaku.microblog.library.MicroBlogException; import org.mariotaku.microblog.library.twitter.model.FavoritedPopup; +import org.mariotaku.microblog.library.twitter.model.StatusPage; import org.mariotaku.restfu.annotation.method.GET; +import org.mariotaku.restfu.annotation.param.Headers; +import org.mariotaku.restfu.annotation.param.KeyValue; +import org.mariotaku.restfu.annotation.param.Path; import org.mariotaku.restfu.annotation.param.Query; /** @@ -30,5 +34,14 @@ import org.mariotaku.restfu.annotation.param.Query; public interface TwitterWeb { @GET("/i/activity/favorited_popup") + @Headers(value = {@KeyValue(key = "Accept", value = "application/json")}) FavoritedPopup getFavoritedPopup(@Query("id") String statusId) throws MicroBlogException; + + @GET("/{screen_name}/status/{id}") + @Headers({@KeyValue(key = "Accept", value = "application/json, text/javascript, */*; q=0.01"), + @KeyValue(key = "X-Overlay-Request", value = "true"), + @KeyValue(key = "X-Requested-With", value = "XMLHttpRequest"), + @KeyValue(key = "User-Agent", value = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36")}) + StatusPage getStatusPage(@Path("screen_name") String screenName, @Path("id") String statusId) + throws MicroBlogException; } diff --git a/twidere/src/main/java/org/mariotaku/microblog/library/twitter/model/StatusPage.java b/twidere/src/main/java/org/mariotaku/microblog/library/twitter/model/StatusPage.java new file mode 100644 index 000000000..ca7170199 --- /dev/null +++ b/twidere/src/main/java/org/mariotaku/microblog/library/twitter/model/StatusPage.java @@ -0,0 +1,36 @@ +/* + * Twidere - Twitter client for Android + * + * Copyright (C) 2012-2017 Mariotaku Lee + * + * 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 . + */ + +package org.mariotaku.microblog.library.twitter.model; + +import com.bluelinelabs.logansquare.annotation.JsonField; +import com.bluelinelabs.logansquare.annotation.JsonObject; + +/** + * Created by mariotaku on 2017/4/1. + */ +@JsonObject +public class StatusPage { + @JsonField(name = "page") + String page; + + public String getPage() { + return page; + } +} diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/MicroBlogAPIFactory.java b/twidere/src/main/java/org/mariotaku/twidere/util/MicroBlogAPIFactory.java index 3fd4b577e..8405f5b6c 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/util/MicroBlogAPIFactory.java +++ b/twidere/src/main/java/org/mariotaku/twidere/util/MicroBlogAPIFactory.java @@ -14,6 +14,7 @@ import android.webkit.URLUtil; import org.mariotaku.microblog.library.MicroBlog; import org.mariotaku.restfu.http.Endpoint; +import org.mariotaku.restfu.http.MultiValueMap; import org.mariotaku.restfu.http.SimpleValueMap; import org.mariotaku.restfu.oauth.OAuthEndpoint; import org.mariotaku.restfu.oauth.OAuthToken; @@ -252,6 +253,6 @@ public class MicroBlogAPIFactory implements TwidereConstants { public interface ExtraHeaders { @NonNull - List> get(); + List> get(MultiValueMap headers); } } diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/adapter/ParcelableActivitiesAdapter.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/adapter/ParcelableActivitiesAdapter.kt index 642f7e3f0..8146fbcaa 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/adapter/ParcelableActivitiesAdapter.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/adapter/ParcelableActivitiesAdapter.kt @@ -23,9 +23,9 @@ import android.annotation.SuppressLint import android.content.Context import android.database.Cursor import android.database.CursorIndexOutOfBoundsException -import android.support.v4.util.LongSparseArray import android.support.v4.widget.Space import android.support.v7.widget.RecyclerView +import android.util.SparseArray import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -133,7 +133,7 @@ class ParcelableActivitiesAdapter( private var filteredUserKeys: Array? = null private val gapLoadingIds: MutableSet = HashSet() private val reuseActivity = ParcelableActivity() - private val filterIdCache = LongSparseArray?>() + private val filterIdCache = SparseArray?>() init { eventListener = EventListener(this) @@ -241,6 +241,8 @@ class ParcelableActivitiesAdapter( } ITEM_VIEW_TYPE_TITLE_SUMMARY -> { val activity = getActivityInternal(position, raw = false, reuse = true) + activity.after_filtered_source_keys = getAfterFilteredSourceKeys(position, false, + activity.source_keys) (holder as ActivityTitleSummaryViewHolder).displayActivity(activity) } ITEM_VIEW_TYPE_STUB -> { @@ -274,12 +276,10 @@ class ParcelableActivitiesAdapter( Activity.Action.FAVORITED_MEDIA_TAGGED, Activity.Action.JOINED_TWITTER -> { if (mentionsOnly) return ITEM_VIEW_TYPE_EMPTY filteredUserKeys?.let { - // ParcelableActivityUtils.initAfterFilteredSourceIds(activity, it, followingOnly) - // filterIdCache[activity._id] = activity.after_filtered_source_ids - // val sourceIds = activity.after_filtered_source_keys - // if (sourceIds != null && sourceIds.isEmpty()) { - // return ITEM_VIEW_TYPE_EMPTY - // } + val afterFiltered = getAfterFilteredSourceKeys(position, false) + if (afterFiltered != null && afterFiltered.isEmpty()) { + return ITEM_VIEW_TYPE_EMPTY + } } return ITEM_VIEW_TYPE_TITLE_SUMMARY } @@ -347,6 +347,15 @@ class ParcelableActivitiesAdapter( }, defValue = -1L, raw = raw) } + + fun getSourceKeys(adapterPosition: Int, raw: Boolean = false): Array? { + return getFieldValue(adapterPosition, readCursorValueAction = { cursor, indices -> + cursor.getString(indices[Activities.SOURCE_KEYS])?.let(UserKey::arrayOf) + }, readStatusValueAction = { activity -> + activity.source_keys + }, defValue = null, raw = raw) + } + fun getData(): List? { return data } @@ -356,7 +365,6 @@ class ParcelableActivitiesAdapter( itemCounts[1] = if (ILoadMoreSupportAdapter.END in loadMoreIndicatorPosition) 1 else 0 } - private fun getActivityInternal(position: Int, raw: Boolean, reuse: Boolean): ParcelableActivity { val dataPosition = position - activityStartIndex val activityCount = getActivityCount(raw) @@ -375,6 +383,19 @@ class ParcelableActivitiesAdapter( } } + private fun getAfterFilteredSourceKeys(position: Int, raw: Boolean, + sourceKeys: Array? = getSourceKeys(position, raw)): Array? { + return filterIdCache[position] ?: run { + val allFiltered = filteredUserKeys + val keys = if (allFiltered != null) { + sourceKeys?.filterNot { it in allFiltered }?.toTypedArray() + } else { + sourceKeys + } + filterIdCache.put(position, keys) + return@run keys + } + } private inline fun getFieldValue(position: Int, readCursorValueAction: (cursor: Cursor, indices: ObjectCursor.CursorIndices) -> T, diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/extension/atto/DOMExtensions.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/extension/atto/DOMExtensions.kt new file mode 100644 index 000000000..99585002e --- /dev/null +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/extension/atto/DOMExtensions.kt @@ -0,0 +1,58 @@ +/* + * Twidere - Twitter client for Android + * + * Copyright (C) 2012-2017 Mariotaku Lee + * + * 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 . + */ + +package org.mariotaku.twidere.extension.atto + +import org.attoparser.dom.Document +import org.attoparser.dom.Element + +/** + * Created by mariotaku on 2017/4/29. + */ + +fun Document.firstElementOrNull(match: (Element) -> Boolean): Element? { + getChildrenOfType(Element::class.java).forEach { child -> + val element = child.firstElementOrNull(match) + if (element != null) return element + } + return null +} + +fun Element.firstElementOrNull(match: (Element) -> Boolean): Element? { + if (match(this)) return this + getChildrenOfType(Element::class.java).forEach { child -> + val element = child.firstElementOrNull(match) + if (element != null) return element + } + return null +} + +fun Element.filter(match: (Element) -> Boolean): List { + return filterTo(ArrayList(), match) +} + +fun > Element.filterTo(to: C, match: (Element) -> Boolean): C { + if (match(this)) { + to.add(this) + } + getChildrenOfType(Element::class.java).forEach { child -> + child.filterTo(to, match) + } + return to +} \ No newline at end of file diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/extension/restfu/MultiValueMapExtensions.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/extension/restfu/MultiValueMapExtensions.kt new file mode 100644 index 000000000..88c50349c --- /dev/null +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/extension/restfu/MultiValueMapExtensions.kt @@ -0,0 +1,30 @@ +/* + * Twidere - Twitter client for Android + * + * Copyright (C) 2012-2017 Mariotaku Lee + * + * 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 . + */ + +package org.mariotaku.twidere.extension.restfu + +import org.mariotaku.restfu.http.MultiValueMap + +/** + * Created by mariotaku on 2017/4/29. + */ + +operator fun MultiValueMap.contains(key: String): Boolean { + return getFirst(key) != null +} diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/StatusFragment.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/StatusFragment.kt index 3169e270a..59ce3ce1c 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/StatusFragment.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/StatusFragment.kt @@ -84,6 +84,7 @@ import org.mariotaku.twidere.adapter.iface.ILoadMoreSupportAdapter import org.mariotaku.twidere.adapter.iface.ILoadMoreSupportAdapter.IndicatorPosition import org.mariotaku.twidere.adapter.iface.IStatusesAdapter import org.mariotaku.twidere.annotation.AccountType +import org.mariotaku.twidere.annotation.ProfileImageSize import org.mariotaku.twidere.annotation.Referral import org.mariotaku.twidere.constant.* import org.mariotaku.twidere.constant.KeyboardShortcutConstants.* @@ -187,7 +188,7 @@ class StatusFragment : BaseFragment(), LoaderCallbacks return getMastodonStatuses(account, paging).mapTo(PaginatedArrayList()) { it.toParcelable(account) } - else -> return getMicroBlogStatuses(account, paging).mapMicroBlogToPaginated { - it.toParcelable(account, profileImageSize) - } + else -> return getMicroBlogStatuses(account, paging) } } @@ -80,27 +86,33 @@ class ConversationLoader( } @Throws(MicroBlogException::class) - private fun getMicroBlogStatuses(account: AccountDetails, paging: Paging): List { + private fun getMicroBlogStatuses(account: AccountDetails, paging: Paging): PaginatedList { val microBlog = account.newMicroBlogInstance(context, MicroBlog::class.java) canLoadAllReplies = false when (account.type) { AccountType.TWITTER -> { val isOfficial = account.isOfficial(context) canLoadAllReplies = isOfficial - if (isOfficial) { - return microBlog.showConversation(status.id, paging) - } +// if (isOfficial) { +// return microBlog.showConversation(status.id, paging).mapMicroBlogToPaginated { +// it.toParcelable(account, profileImageSize) +// } +// } return showConversationCompat(microBlog, account, status, true) } AccountType.STATUSNET -> { canLoadAllReplies = true status.extras?.statusnet_conversation_id?.let { - return microBlog.getStatusNetConversation(it, paging) + return microBlog.getStatusNetConversation(it, paging).mapMicroBlogToPaginated { + it.toParcelable(account, profileImageSize) + } } } AccountType.FANFOU -> { canLoadAllReplies = true - return microBlog.getContextTimeline(status.id, paging) + return microBlog.getContextTimeline(status.id, paging).mapMicroBlogToPaginated { + it.toParcelable(account, profileImageSize) + } } else -> { throw APINotSupportedException(account.type) @@ -110,9 +122,14 @@ class ConversationLoader( return showConversationCompat(microBlog, account, status, true) } + @WorkerThread + override fun shouldFilterStatus(database: SQLiteDatabase, status: ParcelableStatus): Boolean { + return InternalTwitterContentUtils.isFiltered(database, status, false) + } + @Throws(MicroBlogException::class) private fun showConversationCompat(twitter: MicroBlog, details: AccountDetails, - status: ParcelableStatus, loadReplies: Boolean): List { + status: ParcelableStatus, loadReplies: Boolean): PaginatedList { val statuses = ArrayList() val pagination = this.pagination as? SinceMaxPagination val maxId = pagination?.maxId @@ -120,6 +137,9 @@ class ConversationLoader( val maxSortId = pagination?.maxSortId ?: -1 val sinceSortId = pagination?.sinceSortId ?: -1 val noSinceMaxId = maxId == null && sinceId == null + + var nextPagination: Pagination? = null + // Load conversations if (maxId != null && maxSortId < status.sort_id || noSinceMaxId) { var inReplyToId: String? = maxId ?: status.in_reply_to_status_id @@ -131,35 +151,69 @@ class ConversationLoader( count++ } } - if (loadReplies) { + if (loadReplies || noSinceMaxId || sinceId != null && sinceSortId > status.sort_id) { // Load replies - if (sinceId != null && sinceSortId > status.sort_id || noSinceMaxId) { - val query = SearchQuery() + var repliesLoaded = false + try { if (details.type == AccountType.TWITTER) { - query.query("to:${status.user_screen_name}") + if (noSinceMaxId) { + statuses.addAll(loadTwitterWebReplies(details, twitter)) + } + repliesLoaded = true + } + } catch (e: MicroBlogException) { + // Ignore + } + if (!repliesLoaded) { + val query = SearchQuery() + query.count(100) + if (details.type == AccountType.TWITTER) { + query.query("to:${status.user_screen_name} since_id:${status.id}") } else { query.query("@${status.user_screen_name}") } query.sinceId(sinceId ?: status.id) try { - twitter.search(query).filterTo(statuses) { it.inReplyToStatusId == status.id } + val queryResult = twitter.search(query) + val firstId = queryResult.firstOrNull()?.id + if (firstId != null) { + nextPagination = SinceMaxPagination.sinceId(firstId, 0) + } + queryResult.filterTo(statuses) { it.inReplyToStatusId == status.id } } catch (e: MicroBlogException) { // Ignore for now } - } } - return statuses + return statuses.mapTo(PaginatedArrayList()) { + it.toParcelable(details, profileImageSize) + }.apply { + this.nextPage = nextPagination + } } - fun canLoadAllReplies(): Boolean { - return canLoadAllReplies - } + private fun loadTwitterWebReplies(details: AccountDetails, twitter: MicroBlog): List { + val web = details.newMicroBlogInstance(context, TwitterWeb::class.java) + val page = web.getStatusPage(status.user_screen_name, status.id).page - @WorkerThread - override fun shouldFilterStatus(database: SQLiteDatabase, status: ParcelableStatus): Boolean { - return InternalTwitterContentUtils.isFiltered(database, status, false) - } + val parser = DOMMarkupParser(ParseConfiguration.htmlConfiguration()) + val statusIds = ArrayList() + try { + val document = parser.parse(page) + val repliesElement = document.firstElementOrNull { element -> + element.getAttributeValue("data-component-context") == "replies" + } ?: throw MicroBlogException("No replies data found") + repliesElement.filter { + it.getAttributeValue("data-item-type") == "tweet" && it.hasAttribute("data-item-id") + }.mapTo(statusIds) { it.getAttributeValue("data-item-id") } + } catch (e: ParseException) { + throw MicroBlogException(e) + } + if (statusIds.isEmpty()) { + throw MicroBlogException("Invalid response") + } + return twitter.lookupStatuses(statusIds.distinct().toTypedArray()) + } } diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/model/util/ParcelableActivityUtils.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/model/util/ParcelableActivityUtils.kt index b86026d1a..f3806a0b5 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/model/util/ParcelableActivityUtils.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/model/util/ParcelableActivityUtils.kt @@ -45,15 +45,12 @@ object ParcelableActivityUtils { } fun getAfterFilteredSources(activity: ParcelableActivity): Array { - if (activity.after_filtered_sources != null) return activity.after_filtered_sources - if (activity.after_filtered_source_keys == null || activity.sources.size == activity.after_filtered_source_keys.size) { - return activity.sources_lite - } - val result = Array(activity.after_filtered_source_keys.size) { idx -> - return@Array activity.sources_lite.find { it.key == activity.after_filtered_source_keys[idx] }!! - } - activity.after_filtered_sources = result - return result + val sources = activity.sources_lite ?: return emptyArray() + val afterFilteredKeys = activity.after_filtered_source_keys?.takeIf { + it.size != sources.size + } ?: return sources + return sources.filter { it.key in afterFilteredKeys }.toTypedArray() + } diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/util/DataStoreUtils.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/util/DataStoreUtils.kt index 8fe9c47fa..2c59507a6 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/util/DataStoreUtils.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/util/DataStoreUtils.kt @@ -35,14 +35,18 @@ import org.mariotaku.ktextension.useCursor import org.mariotaku.library.objectcursor.ObjectCursor import org.mariotaku.microblog.library.MicroBlog import org.mariotaku.microblog.library.MicroBlogException +import org.mariotaku.microblog.library.mastodon.Mastodon import org.mariotaku.microblog.library.twitter.model.Activity import org.mariotaku.sqliteqb.library.* import org.mariotaku.sqliteqb.library.Columns.Column import org.mariotaku.sqliteqb.library.query.SQLSelectQuery +import org.mariotaku.twidere.R import org.mariotaku.twidere.TwidereConstants.* +import org.mariotaku.twidere.annotation.AccountType import org.mariotaku.twidere.constant.IntentConstants import org.mariotaku.twidere.constant.databaseItemLimitKey import org.mariotaku.twidere.extension.model.* +import org.mariotaku.twidere.extension.model.api.mastodon.toParcelable import org.mariotaku.twidere.extension.model.api.toParcelable import org.mariotaku.twidere.extension.rawQuery import org.mariotaku.twidere.model.* @@ -919,16 +923,24 @@ object DataStoreUtils { @Throws(MicroBlogException::class) fun findStatus(context: Context, accountKey: UserKey, statusId: String): ParcelableStatus { val cached = findStatusInDatabases(context, accountKey, statusId) + val profileImageSize = context.getString(R.string.profile_image_size) if (cached != null) return cached val details = AccountUtils.getAccountDetails(AccountManager.get(context), accountKey, true) ?: throw MicroBlogException("No account") - val microBlog = details.newMicroBlogInstance(context, MicroBlog::class.java) - val result = microBlog.showStatus(statusId) + val status = when (details.type) { + AccountType.MASTODON -> { + val mastodon = details.newMicroBlogInstance(context, Mastodon::class.java) + mastodon.fetchStatus(statusId).toParcelable(details) + } + else -> { + val microBlog = details.newMicroBlogInstance(context, MicroBlog::class.java) + microBlog.showStatus(statusId).toParcelable(details, profileImageSize) + } + } val where = Expression.and(Expression.equalsArgs(Statuses.ACCOUNT_KEY), Expression.equalsArgs(Statuses.ID)).sql val whereArgs = arrayOf(accountKey.toString(), statusId) val resolver = context.contentResolver - val status = result.toParcelable(details) resolver.delete(CachedStatuses.CONTENT_URI, where, whereArgs) resolver.insert(CachedStatuses.CONTENT_URI, ObjectCursor.valuesCreatorFrom(ParcelableStatus::class.java).create(status)) return status diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/util/api/TwidereHttpRequestFactory.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/util/api/TwidereHttpRequestFactory.kt index ccd9aa6ab..a6584e5a4 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/util/api/TwidereHttpRequestFactory.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/util/api/TwidereHttpRequestFactory.kt @@ -49,8 +49,8 @@ class TwidereHttpRequestFactory( headers.add("Authorization", RestFuUtils.sanitizeHeader(authorization.getHeader(endpoint, info))) } if (extraHeaders != null) { - for (pair in extraHeaders.get()) { - headers.add(pair.first, RestFuUtils.sanitizeHeader(pair.second)) + for ((first, second) in extraHeaders.get(info.headers)) { + headers.add(first, RestFuUtils.sanitizeHeader(second)) } } return HttpRequest(restMethod, url, headers, info.getBody(converterFactory), null) diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/util/api/TwitterAndroidExtraHeaders.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/util/api/TwitterAndroidExtraHeaders.kt index c55979187..f37d7508c 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/util/api/TwitterAndroidExtraHeaders.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/util/api/TwitterAndroidExtraHeaders.kt @@ -21,6 +21,8 @@ package org.mariotaku.twidere.util.api import android.os.Build import org.mariotaku.ktextension.bcp47Tag +import org.mariotaku.restfu.http.MultiValueMap +import org.mariotaku.twidere.extension.restfu.contains import org.mariotaku.twidere.util.MicroBlogAPIFactory.ExtraHeaders import java.util.* @@ -34,10 +36,12 @@ object TwitterAndroidExtraHeaders : ExtraHeaders { const val apiVersion = "5" const val internalVersionName = "7160062-r-930" - override fun get(): List> { + override fun get(headers: MultiValueMap): List> { val result = ArrayList>() val language = Locale.getDefault().bcp47Tag - result.add(Pair("User-Agent", userAgent)) + if ("User-Agent" !in headers) { + result.add(Pair("User-Agent", userAgent)) + } result.add(Pair("Accept-Language", language)) result.add(Pair("X-Twitter-Client", clientName)) result.add(Pair("X-Twitter-Client-Language", language)) diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/util/api/TwitterMacExtraHeaders.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/util/api/TwitterMacExtraHeaders.kt index 2ada1ff31..941ec285b 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/util/api/TwitterMacExtraHeaders.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/util/api/TwitterMacExtraHeaders.kt @@ -21,6 +21,8 @@ package org.mariotaku.twidere.util.api import android.os.Build import org.mariotaku.ktextension.bcp47Tag +import org.mariotaku.restfu.http.MultiValueMap +import org.mariotaku.twidere.extension.restfu.contains import org.mariotaku.twidere.util.MicroBlogAPIFactory.ExtraHeaders import java.util.* @@ -36,10 +38,12 @@ object TwitterMacExtraHeaders : ExtraHeaders { const val platformArchitecture = "x86_64" const val internalVersionName = "5002734" - override fun get(): List> { + override fun get(headers: MultiValueMap): List> { val result = ArrayList>() val language = Locale.getDefault().bcp47Tag - result.add(Pair("User-Agent", userAgent)) + if ("User-Agent" !in headers) { + result.add(Pair("User-Agent", userAgent)) + } result.add(Pair("Accept-Language", language)) result.add(Pair("X-Twitter-Client", clientName)) result.add(Pair("X-Twitter-Client-Version", versionName)) diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/util/api/UserAgentExtraHeaders.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/util/api/UserAgentExtraHeaders.kt index 66a47ef2b..b13d1146d 100644 --- a/twidere/src/main/kotlin/org/mariotaku/twidere/util/api/UserAgentExtraHeaders.kt +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/util/api/UserAgentExtraHeaders.kt @@ -19,6 +19,8 @@ package org.mariotaku.twidere.util.api +import org.mariotaku.restfu.http.MultiValueMap +import org.mariotaku.twidere.extension.restfu.contains import org.mariotaku.twidere.util.MicroBlogAPIFactory /** @@ -26,8 +28,8 @@ import org.mariotaku.twidere.util.MicroBlogAPIFactory */ class UserAgentExtraHeaders(val userAgent: String?) : MicroBlogAPIFactory.ExtraHeaders { - override fun get(): List> { - if (userAgent == null) return emptyList() + override fun get(headers: MultiValueMap): List> { + if (userAgent == null || "User-Agent" in headers) return emptyList() return listOf(Pair("User-Agent", userAgent)) } }