mirror of
https://github.com/TwidereProject/Twidere-Android
synced 2025-02-09 08:18:44 +01:00
improved twitter replies #809
This commit is contained in:
parent
fa6d658394
commit
31086e7718
@ -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() {
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.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;
|
||||
}
|
||||
}
|
@ -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<Pair<String, String>> get();
|
||||
List<Pair<String, String>> get(MultiValueMap<String> headers);
|
||||
}
|
||||
}
|
||||
|
@ -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<UserKey>? = null
|
||||
private val gapLoadingIds: MutableSet<ObjectId> = HashSet()
|
||||
private val reuseActivity = ParcelableActivity()
|
||||
private val filterIdCache = LongSparseArray<Array<UserKey>?>()
|
||||
private val filterIdCache = SparseArray<Array<UserKey>?>()
|
||||
|
||||
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<UserKey>? {
|
||||
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<ParcelableActivity>? {
|
||||
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<UserKey>? = getSourceKeys(position, raw)): Array<UserKey>? {
|
||||
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 <T> getFieldValue(position: Int,
|
||||
readCursorValueAction: (cursor: Cursor, indices: ObjectCursor.CursorIndices<ParcelableActivity>) -> T,
|
||||
|
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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.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<Element> {
|
||||
return filterTo(ArrayList(), match)
|
||||
}
|
||||
|
||||
fun <C : MutableCollection<Element>> 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
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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.restfu
|
||||
|
||||
import org.mariotaku.restfu.http.MultiValueMap
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 2017/4/29.
|
||||
*/
|
||||
|
||||
operator fun <T> MultiValueMap<T>.contains(key: String): Boolean {
|
||||
return getFirst(key) != null
|
||||
}
|
@ -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<SingleResponse<Parcelable
|
||||
}
|
||||
adapter.loadMoreSupportedPosition = supportedPositions
|
||||
setConversation(data)
|
||||
val canLoadAllReplies = loader.canLoadAllReplies()
|
||||
val canLoadAllReplies = loader.canLoadAllReplies
|
||||
if (canLoadAllReplies) {
|
||||
adapter.setReplyError(null)
|
||||
} else {
|
||||
@ -884,8 +885,8 @@ class StatusFragment : BaseFragment(), LoaderCallbacks<SingleResponse<Parcelable
|
||||
nameView.updateText(formatter)
|
||||
|
||||
adapter.requestManager.loadProfileImage(context, status, adapter.profileImageStyle,
|
||||
itemView.profileImage.cornerRadius, itemView.profileImage.cornerRadiusRatio)
|
||||
.into(itemView.profileImage)
|
||||
itemView.profileImage.cornerRadius, itemView.profileImage.cornerRadiusRatio,
|
||||
size = ProfileImageSize.ORIGINAL).into(itemView.profileImage)
|
||||
|
||||
val typeIconRes = Utils.getUserTypeIconRes(status.user_is_verified, status.user_is_protected)
|
||||
val typeDescriptionRes = Utils.getUserTypeDescriptionRes(status.user_is_verified, status.user_is_protected)
|
||||
|
@ -22,16 +22,21 @@ package org.mariotaku.twidere.loader.statuses
|
||||
import android.content.Context
|
||||
import android.database.sqlite.SQLiteDatabase
|
||||
import android.support.annotation.WorkerThread
|
||||
import org.attoparser.config.ParseConfiguration
|
||||
import org.attoparser.dom.DOMMarkupParser
|
||||
import org.mariotaku.commons.parcel.ParcelUtils
|
||||
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.TwitterWeb
|
||||
import org.mariotaku.microblog.library.twitter.model.Paging
|
||||
import org.mariotaku.microblog.library.twitter.model.SearchQuery
|
||||
import org.mariotaku.microblog.library.twitter.model.Status
|
||||
import org.mariotaku.twidere.alias.MastodonStatus
|
||||
import org.mariotaku.twidere.annotation.AccountType
|
||||
import org.mariotaku.twidere.exception.APINotSupportedException
|
||||
import org.mariotaku.twidere.extension.atto.filter
|
||||
import org.mariotaku.twidere.extension.atto.firstElementOrNull
|
||||
import org.mariotaku.twidere.extension.model.api.mastodon.toParcelable
|
||||
import org.mariotaku.twidere.extension.model.api.toParcelable
|
||||
import org.mariotaku.twidere.extension.model.isOfficial
|
||||
@ -40,9 +45,11 @@ import org.mariotaku.twidere.model.AccountDetails
|
||||
import org.mariotaku.twidere.model.ParcelableStatus
|
||||
import org.mariotaku.twidere.model.pagination.PaginatedArrayList
|
||||
import org.mariotaku.twidere.model.pagination.PaginatedList
|
||||
import org.mariotaku.twidere.model.pagination.Pagination
|
||||
import org.mariotaku.twidere.model.pagination.SinceMaxPagination
|
||||
import org.mariotaku.twidere.model.util.ParcelableStatusUtils
|
||||
import org.mariotaku.twidere.util.InternalTwitterContentUtils
|
||||
import java.text.ParseException
|
||||
import java.util.*
|
||||
|
||||
class ConversationLoader(
|
||||
@ -54,7 +61,8 @@ class ConversationLoader(
|
||||
) : AbsRequestStatusesLoader(context, status.account_key, adapterData, null, -1, fromUser, loadingMore) {
|
||||
|
||||
private val status = ParcelUtils.clone(status)
|
||||
private var canLoadAllReplies: Boolean = false
|
||||
var canLoadAllReplies: Boolean = false
|
||||
private set
|
||||
|
||||
init {
|
||||
ParcelableStatusUtils.makeOriginalStatus(this.status)
|
||||
@ -66,9 +74,7 @@ class ConversationLoader(
|
||||
AccountType.MASTODON -> 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<Status> {
|
||||
private fun getMicroBlogStatuses(account: AccountDetails, paging: Paging): PaginatedList<ParcelableStatus> {
|
||||
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> {
|
||||
status: ParcelableStatus, loadReplies: Boolean): PaginatedList<ParcelableStatus> {
|
||||
val statuses = ArrayList<Status>()
|
||||
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<Status> {
|
||||
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<String>()
|
||||
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -45,15 +45,12 @@ object ParcelableActivityUtils {
|
||||
}
|
||||
|
||||
fun getAfterFilteredSources(activity: ParcelableActivity): Array<ParcelableLiteUser> {
|
||||
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()
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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<Pair<String, String>> {
|
||||
override fun get(headers: MultiValueMap<String>): List<Pair<String, String>> {
|
||||
val result = ArrayList<Pair<String, String>>()
|
||||
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))
|
||||
|
@ -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<Pair<String, String>> {
|
||||
override fun get(headers: MultiValueMap<String>): List<Pair<String, String>> {
|
||||
val result = ArrayList<Pair<String, String>>()
|
||||
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))
|
||||
|
@ -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<Pair<String, String>> {
|
||||
if (userAgent == null) return emptyList()
|
||||
override fun get(headers: MultiValueMap<String>): List<Pair<String, String>> {
|
||||
if (userAgent == null || "User-Agent" in headers) return emptyList()
|
||||
return listOf(Pair("User-Agent", userAgent))
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user