improved hashtag auto completion
supports double hashtag for fanfou
This commit is contained in:
parent
a3adb9a0aa
commit
0eb13ade81
|
@ -90,7 +90,11 @@ class ComposeAutoCompleteAdapter(context: Context, val requestManager: RequestMa
|
||||||
|
|
||||||
icon.clearColorFilter()
|
icon.clearColorFilter()
|
||||||
} else {
|
} else {
|
||||||
text1.spannable = "#${cursor.getString(indices.title)}"
|
text1.spannable = if (account?.type == AccountType.FANFOU) {
|
||||||
|
"#${cursor.getString(indices.title)}#"
|
||||||
|
} else {
|
||||||
|
"#${cursor.getString(indices.title)}"
|
||||||
|
}
|
||||||
text2.setText(R.string.hashtag)
|
text2.setText(R.string.hashtag)
|
||||||
|
|
||||||
icon.setImageResource(R.drawable.ic_action_hashtag)
|
icon.setImageResource(R.drawable.ic_action_hashtag)
|
||||||
|
@ -111,6 +115,9 @@ class ComposeAutoCompleteAdapter(context: Context, val requestManager: RequestMa
|
||||||
val indices = this.indices!!
|
val indices = this.indices!!
|
||||||
when (cursor.getString(indices.type)) {
|
when (cursor.getString(indices.type)) {
|
||||||
Suggestions.AutoComplete.TYPE_HASHTAGS -> {
|
Suggestions.AutoComplete.TYPE_HASHTAGS -> {
|
||||||
|
if (account?.type == AccountType.FANFOU) {
|
||||||
|
return "#${cursor.getString(indices.value)}#"
|
||||||
|
}
|
||||||
return "#${cursor.getString(indices.value)}"
|
return "#${cursor.getString(indices.value)}"
|
||||||
}
|
}
|
||||||
Suggestions.AutoComplete.TYPE_USERS -> {
|
Suggestions.AutoComplete.TYPE_USERS -> {
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
import android.net.Uri
|
||||||
|
import okhttp3.HttpUrl
|
||||||
|
|
||||||
|
fun HttpUrl.toUri() : Uri {
|
||||||
|
return Uri.parse(toString())
|
||||||
|
}
|
|
@ -2,6 +2,7 @@ package org.mariotaku.twidere.extension.model
|
||||||
|
|
||||||
import org.mariotaku.ktextension.addAllTo
|
import org.mariotaku.ktextension.addAllTo
|
||||||
import org.mariotaku.twidere.model.*
|
import org.mariotaku.twidere.model.*
|
||||||
|
import org.mariotaku.twidere.util.UriUtils
|
||||||
|
|
||||||
|
|
||||||
val ParcelableStatus.originalId: String
|
val ParcelableStatus.originalId: String
|
||||||
|
@ -70,6 +71,23 @@ fun ParcelableStatus.toSummaryLine(): ParcelableActivity.SummaryLine {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun ParcelableStatus.extractFanfouHashtags(): List<String> {
|
||||||
|
return spans?.filter { span ->
|
||||||
|
var link = span.link
|
||||||
|
if (link.startsWith("/")) {
|
||||||
|
link = "http://fanfou.com$link"
|
||||||
|
}
|
||||||
|
if (UriUtils.getAuthority(link) != "fanfou.com") {
|
||||||
|
return@filter false
|
||||||
|
}
|
||||||
|
if (span.start <= 0 || span.end > text_unescaped.lastIndex) return@filter false
|
||||||
|
if (text_unescaped[span.start - 1] == '#' && text_unescaped[span.end] == '#') {
|
||||||
|
return@filter true
|
||||||
|
}
|
||||||
|
return@filter false
|
||||||
|
}?.map { text_unescaped.substring(it.start, it.end) }.orEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
private fun parcelableUserMention(key: UserKey, name: String, screenName: String) = ParcelableUserMention().also {
|
private fun parcelableUserMention(key: UserKey, name: String, screenName: String) = ParcelableUserMention().also {
|
||||||
it.key = key
|
it.key = key
|
||||||
it.name = name
|
it.name = name
|
||||||
|
|
|
@ -23,4 +23,4 @@ import org.mariotaku.twidere.model.AccountDetails
|
||||||
import org.mariotaku.twidere.model.ParcelableUser
|
import org.mariotaku.twidere.model.ParcelableUser
|
||||||
|
|
||||||
data class GetTimelineResult<out T>(val account: AccountDetails, val data: List<T>,
|
data class GetTimelineResult<out T>(val account: AccountDetails, val data: List<T>,
|
||||||
val users: Collection<ParcelableUser>)
|
val users: Collection<ParcelableUser>, val hashtags: Collection<String>)
|
|
@ -3,6 +3,8 @@ package org.mariotaku.twidere.task.cache
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.support.v4.util.ArraySet
|
import android.support.v4.util.ArraySet
|
||||||
|
import org.mariotaku.ktextension.ContentValues
|
||||||
|
import org.mariotaku.ktextension.set
|
||||||
import org.mariotaku.sqliteqb.library.Expression
|
import org.mariotaku.sqliteqb.library.Expression
|
||||||
import org.mariotaku.twidere.extension.bulkInsert
|
import org.mariotaku.twidere.extension.bulkInsert
|
||||||
import org.mariotaku.twidere.extension.model.applyTo
|
import org.mariotaku.twidere.extension.model.applyTo
|
||||||
|
@ -10,26 +12,30 @@ import org.mariotaku.twidere.extension.model.relationship
|
||||||
import org.mariotaku.twidere.extension.queryAll
|
import org.mariotaku.twidere.extension.queryAll
|
||||||
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.task.GetTimelineResult
|
||||||
import org.mariotaku.twidere.provider.TwidereDataStore.CachedRelationships
|
import org.mariotaku.twidere.provider.TwidereDataStore.*
|
||||||
import org.mariotaku.twidere.provider.TwidereDataStore.CachedUsers
|
|
||||||
import org.mariotaku.twidere.task.BaseAbstractTask
|
import org.mariotaku.twidere.task.BaseAbstractTask
|
||||||
|
import org.mariotaku.twidere.util.content.ContentResolverUtils
|
||||||
|
|
||||||
class CacheUserRelationshipTask(
|
class CacheTimelineResultTask(
|
||||||
context: Context,
|
context: Context,
|
||||||
val accountKey: UserKey,
|
val result: GetTimelineResult<*>,
|
||||||
val accountType: String,
|
|
||||||
val users: Collection<ParcelableUser>,
|
|
||||||
val cacheRelationship: Boolean
|
val cacheRelationship: Boolean
|
||||||
) : BaseAbstractTask<Any?, Unit, Any?>(context) {
|
) : BaseAbstractTask<Any?, Unit, Any?>(context) {
|
||||||
|
|
||||||
override fun doLongOperation(param: Any?) {
|
override fun doLongOperation(param: Any?) {
|
||||||
val cr = context.contentResolver
|
val cr = context.contentResolver
|
||||||
cr.bulkInsert(CachedUsers.CONTENT_URI, users, ParcelableUser::class.java)
|
val account = result.account
|
||||||
|
val users = result.users
|
||||||
|
val hashtags = result.hashtags
|
||||||
|
|
||||||
|
cr.bulkInsert(CachedUsers.CONTENT_URI, users, ParcelableUser::class.java)
|
||||||
|
ContentResolverUtils.bulkInsert(cr, CachedHashtags.CONTENT_URI, hashtags.map {
|
||||||
|
ContentValues { this[CachedHashtags.NAME] = it.substringAfter("#") }
|
||||||
|
})
|
||||||
|
|
||||||
if (cacheRelationship) {
|
if (cacheRelationship) {
|
||||||
val selectionArgsList = users.mapTo(mutableListOf(accountKey.toString())) {
|
val selectionArgsList = users.mapTo(mutableListOf(account.key.toString())) {
|
||||||
it.key.toString()
|
it.key.toString()
|
||||||
}
|
}
|
||||||
@SuppressLint("Recycle")
|
@SuppressLint("Recycle")
|
|
@ -34,6 +34,7 @@ import org.mariotaku.twidere.annotation.ReadPositionTag
|
||||||
import org.mariotaku.twidere.extension.api.batchGetRelationships
|
import org.mariotaku.twidere.extension.api.batchGetRelationships
|
||||||
import org.mariotaku.twidere.extension.model.api.mastodon.toParcelable
|
import org.mariotaku.twidere.extension.model.api.mastodon.toParcelable
|
||||||
import org.mariotaku.twidere.extension.model.api.microblog.toParcelable
|
import org.mariotaku.twidere.extension.model.api.microblog.toParcelable
|
||||||
|
import org.mariotaku.twidere.extension.model.extractFanfouHashtags
|
||||||
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.extension.model.newMicroBlogInstance
|
||||||
import org.mariotaku.twidere.fragment.InteractionsTimelineFragment
|
import org.mariotaku.twidere.fragment.InteractionsTimelineFragment
|
||||||
|
@ -84,17 +85,29 @@ class GetActivitiesAboutMeTask(context: Context) : GetActivitiesTask(context) {
|
||||||
}
|
}
|
||||||
return GetTimelineResult(account, activities, activities.flatMap {
|
return GetTimelineResult(account, activities, activities.flatMap {
|
||||||
it.sources?.toList().orEmpty()
|
it.sources?.toList().orEmpty()
|
||||||
|
}, notifications.flatMapTo(HashSet()) { notification ->
|
||||||
|
notification.status?.tags?.map { it.name }.orEmpty()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
AccountType.TWITTER -> {
|
AccountType.TWITTER -> {
|
||||||
val microBlog = account.newMicroBlogInstance(context, MicroBlog::class.java)
|
val microBlog = account.newMicroBlogInstance(context, MicroBlog::class.java)
|
||||||
if (account.isOfficial(context)) {
|
if (account.isOfficial(context)) {
|
||||||
val activities = microBlog.getActivitiesAboutMe(paging).map {
|
val timeline = microBlog.getActivitiesAboutMe(paging)
|
||||||
|
val activities = timeline.map {
|
||||||
it.toParcelable(account, profileImageSize = profileImageSize)
|
it.toParcelable(account, profileImageSize = profileImageSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
return GetTimelineResult(account, activities, activities.flatMap {
|
return GetTimelineResult(account, activities, activities.flatMap {
|
||||||
it.sources?.toList().orEmpty()
|
it.sources?.toList().orEmpty()
|
||||||
|
}, timeline.flatMapTo(HashSet()) { activity ->
|
||||||
|
val mapResult = mutableSetOf<String>()
|
||||||
|
activity.targetStatuses?.flatMapTo(mapResult) { status ->
|
||||||
|
status.entities?.hashtags?.map { it.text }.orEmpty()
|
||||||
|
}
|
||||||
|
activity.targetObjectStatuses?.flatMapTo(mapResult) { status ->
|
||||||
|
status.entities?.hashtags?.map { it.text }.orEmpty()
|
||||||
|
}
|
||||||
|
return@flatMapTo mapResult
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -106,16 +119,19 @@ class GetActivitiesAboutMeTask(context: Context) : GetActivitiesTask(context) {
|
||||||
}
|
}
|
||||||
return GetTimelineResult(account, activities, activities.flatMap {
|
return GetTimelineResult(account, activities, activities.flatMap {
|
||||||
it.sources?.toList().orEmpty()
|
it.sources?.toList().orEmpty()
|
||||||
})
|
}, activities.flatMap { it.extractFanfouHashtags() })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val microBlog = account.newMicroBlogInstance(context, MicroBlog::class.java)
|
val microBlog = account.newMicroBlogInstance(context, MicroBlog::class.java)
|
||||||
val activities = microBlog.getMentionsTimeline(paging).map {
|
val timeline = microBlog.getMentionsTimeline(paging)
|
||||||
|
val activities = timeline.map {
|
||||||
InternalActivityCreator.status(it, account.key.id).toParcelable(account,
|
InternalActivityCreator.status(it, account.key.id).toParcelable(account,
|
||||||
profileImageSize = profileImageSize)
|
profileImageSize = profileImageSize)
|
||||||
}
|
}
|
||||||
return GetTimelineResult(account, activities, activities.flatMap {
|
return GetTimelineResult(account, activities, activities.flatMap {
|
||||||
it.sources?.toList().orEmpty()
|
it.sources?.toList().orEmpty()
|
||||||
|
}, timeline.flatMap {
|
||||||
|
it.entities?.hashtags?.map { it.text }.orEmpty()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -104,7 +104,7 @@ abstract class GetActivitiesTask(
|
||||||
context.contentResolver.notifyChange(contentUri, null)
|
context.contentResolver.notifyChange(contentUri, null)
|
||||||
val exception = results.firstOrNull { it.second != null }?.second
|
val exception = results.firstOrNull { it.second != null }?.second
|
||||||
bus.post(GetActivitiesTaskEvent(contentUri, false, exception))
|
bus.post(GetActivitiesTaskEvent(contentUri, false, exception))
|
||||||
GetStatusesTask.cacheUserRelationship(context, results)
|
GetStatusesTask.cacheItems(context, results)
|
||||||
handler?.invoke(true)
|
handler?.invoke(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,7 @@ 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.mastodon.toParcelable
|
||||||
import org.mariotaku.twidere.extension.model.api.toParcelable
|
import org.mariotaku.twidere.extension.model.api.toParcelable
|
||||||
|
import org.mariotaku.twidere.extension.model.extractFanfouHashtags
|
||||||
import org.mariotaku.twidere.extension.model.newMicroBlogInstance
|
import org.mariotaku.twidere.extension.model.newMicroBlogInstance
|
||||||
import org.mariotaku.twidere.fragment.HomeTimelineFragment
|
import org.mariotaku.twidere.fragment.HomeTimelineFragment
|
||||||
import org.mariotaku.twidere.model.AccountDetails
|
import org.mariotaku.twidere.model.AccountDetails
|
||||||
|
@ -66,14 +67,22 @@ class GetHomeTimelineTask(context: Context) : GetStatusesTask(context) {
|
||||||
val mapResult = mutableListOf(status.account.toParcelable(account))
|
val mapResult = mutableListOf(status.account.toParcelable(account))
|
||||||
status.reblog?.account?.toParcelable(account)?.addTo(mapResult)
|
status.reblog?.account?.toParcelable(account)?.addTo(mapResult)
|
||||||
return@flatMap mapResult
|
return@flatMap mapResult
|
||||||
|
}, timeline.flatMap { status ->
|
||||||
|
status.tags?.map { it.name }.orEmpty()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
val microBlog = account.newMicroBlogInstance(context, MicroBlog::class.java)
|
val microBlog = account.newMicroBlogInstance(context, MicroBlog::class.java)
|
||||||
val timeline = microBlog.getHomeTimeline(paging)
|
val timeline = microBlog.getHomeTimeline(paging)
|
||||||
return GetTimelineResult(account, timeline.map {
|
val statuses = timeline.map {
|
||||||
it.toParcelable(account, profileImageSize)
|
it.toParcelable(account, profileImageSize)
|
||||||
}, timeline.flatMap { status ->
|
}
|
||||||
|
val hashtags = if (account.type == AccountType.FANFOU) statuses.flatMap { status ->
|
||||||
|
return@flatMap status.extractFanfouHashtags()
|
||||||
|
} else timeline.flatMap { status ->
|
||||||
|
status.entities?.hashtags?.map { it.text }.orEmpty()
|
||||||
|
}
|
||||||
|
return GetTimelineResult(account, statuses, timeline.flatMap { status ->
|
||||||
val mapResult = mutableListOf(status.user.toParcelable(account,
|
val mapResult = mutableListOf(status.user.toParcelable(account,
|
||||||
profileImageSize = profileImageSize))
|
profileImageSize = profileImageSize))
|
||||||
status.retweetedStatus?.user?.toParcelable(account,
|
status.retweetedStatus?.user?.toParcelable(account,
|
||||||
|
@ -81,7 +90,7 @@ class GetHomeTimelineTask(context: Context) : GetStatusesTask(context) {
|
||||||
status.quotedStatus?.user?.toParcelable(account,
|
status.quotedStatus?.user?.toParcelable(account,
|
||||||
profileImageSize = profileImageSize)?.addTo(mapResult)
|
profileImageSize = profileImageSize)?.addTo(mapResult)
|
||||||
return@flatMap mapResult
|
return@flatMap mapResult
|
||||||
})
|
}, hashtags)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,7 @@ import org.mariotaku.twidere.model.util.AccountUtils
|
||||||
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.cache.CacheUserRelationshipTask
|
import org.mariotaku.twidere.task.cache.CacheTimelineResultTask
|
||||||
import org.mariotaku.twidere.util.DataStoreUtils
|
import org.mariotaku.twidere.util.DataStoreUtils
|
||||||
import org.mariotaku.twidere.util.DebugLog
|
import org.mariotaku.twidere.util.DebugLog
|
||||||
import org.mariotaku.twidere.util.ErrorInfoStore
|
import org.mariotaku.twidere.util.ErrorInfoStore
|
||||||
|
@ -115,7 +115,7 @@ abstract class GetStatusesTask(
|
||||||
context.contentResolver.notifyChange(contentUri, null)
|
context.contentResolver.notifyChange(contentUri, null)
|
||||||
val exception = results.firstOrNull { it.second != null }?.second
|
val exception = results.firstOrNull { it.second != null }?.second
|
||||||
bus.post(GetStatusesTaskEvent(contentUri, false, exception))
|
bus.post(GetStatusesTaskEvent(contentUri, false, exception))
|
||||||
cacheUserRelationship(context, results)
|
cacheItems(context, results)
|
||||||
handler?.invoke(true)
|
handler?.invoke(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -234,11 +234,11 @@ abstract class GetStatusesTask(
|
||||||
return timestamp + (sortId - lastSortId) * (499 - count) / sortDiff + extraValue.toLong()
|
return timestamp + (sortId - lastSortId) * (499 - count) / sortDiff + extraValue.toLong()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun cacheUserRelationship(context: Context, results: List<Pair<GetTimelineResult<*>?, Exception?>>) {
|
fun cacheItems(context: Context, results: List<Pair<GetTimelineResult<*>?, Exception?>>) {
|
||||||
results.forEach { (result, _) ->
|
results.forEach { (result, _) ->
|
||||||
if (result == null) return@forEach
|
if (result == null) return@forEach
|
||||||
val account = result.account
|
val account = result.account
|
||||||
val task = CacheUserRelationshipTask(context, account.key, account.type, result.users,
|
val task = CacheTimelineResultTask(context, result,
|
||||||
account.type == AccountType.STATUSNET || account.isOfficial(context))
|
account.type == AccountType.STATUSNET || account.isOfficial(context))
|
||||||
TaskStarter.execute(task)
|
TaskStarter.execute(task)
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ import org.mariotaku.kpreferences.get
|
||||||
import org.mariotaku.twidere.constant.IntentConstants.EXTRA_URI
|
import org.mariotaku.twidere.constant.IntentConstants.EXTRA_URI
|
||||||
import org.mariotaku.twidere.constant.phishingLinksWaringKey
|
import org.mariotaku.twidere.constant.phishingLinksWaringKey
|
||||||
import org.mariotaku.twidere.fragment.PhishingLinkWarningDialogFragment
|
import org.mariotaku.twidere.fragment.PhishingLinkWarningDialogFragment
|
||||||
|
import org.mariotaku.twidere.model.UserKey
|
||||||
|
|
||||||
class DirectMessageOnLinkClickHandler(
|
class DirectMessageOnLinkClickHandler(
|
||||||
context: Context,
|
context: Context,
|
||||||
|
@ -38,10 +39,10 @@ class DirectMessageOnLinkClickHandler(
|
||||||
override val isPrivateData: Boolean
|
override val isPrivateData: Boolean
|
||||||
get() = true
|
get() = true
|
||||||
|
|
||||||
override fun openLink(link: String) {
|
override fun openLink(accountKey: UserKey?, link: String) {
|
||||||
if (manager != null && manager.isActive) return
|
if (manager != null && manager.isActive) return
|
||||||
if (!hasShortenedLinks(link)) {
|
if (!hasShortenedLinks(link)) {
|
||||||
super.openLink(link)
|
super.openLink(accountKey, link)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (context is FragmentActivity && preferences[phishingLinksWaringKey]) {
|
if (context is FragmentActivity && preferences[phishingLinksWaringKey]) {
|
||||||
|
@ -52,7 +53,7 @@ class DirectMessageOnLinkClickHandler(
|
||||||
fragment.arguments = args
|
fragment.arguments = args
|
||||||
fragment.show(fm, "phishing_link_warning")
|
fragment.show(fm, "phishing_link_warning")
|
||||||
} else {
|
} else {
|
||||||
super.openLink(link)
|
super.openLink(accountKey, link)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,6 @@ import android.text.SpannableStringBuilder
|
||||||
import android.text.Spanned
|
import android.text.Spanned
|
||||||
import android.text.style.StyleSpan
|
import android.text.style.StyleSpan
|
||||||
import android.text.style.URLSpan
|
import android.text.style.URLSpan
|
||||||
import okhttp3.HttpUrl
|
|
||||||
import org.attoparser.ParseException
|
import org.attoparser.ParseException
|
||||||
import org.attoparser.config.ParseConfiguration
|
import org.attoparser.config.ParseConfiguration
|
||||||
import org.attoparser.simple.AbstractSimpleMarkupHandler
|
import org.attoparser.simple.AbstractSimpleMarkupHandler
|
||||||
|
@ -76,10 +75,7 @@ object HtmlSpanBuilder {
|
||||||
private fun createSpan(info: TagInfo): Any? {
|
private fun createSpan(info: TagInfo): Any? {
|
||||||
when (info.nameLower) {
|
when (info.nameLower) {
|
||||||
"a" -> {
|
"a" -> {
|
||||||
var href = info.getAttribute("href") ?: return null
|
val href = info.getAttribute("href") ?: return null
|
||||||
if (HttpUrl.parse(href)?.scheme() == null) {
|
|
||||||
href = "https://" + href
|
|
||||||
}
|
|
||||||
return URLSpan(href)
|
return URLSpan(href)
|
||||||
}
|
}
|
||||||
"b", "strong" -> {
|
"b", "strong" -> {
|
||||||
|
|
|
@ -26,14 +26,18 @@ import android.content.SharedPreferences
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.BadParcelableException
|
import android.os.BadParcelableException
|
||||||
import android.support.v4.content.ContextCompat
|
import android.support.v4.content.ContextCompat
|
||||||
|
import okhttp3.HttpUrl
|
||||||
import org.mariotaku.kpreferences.get
|
import org.mariotaku.kpreferences.get
|
||||||
|
import org.mariotaku.twidere.TwidereConstants.USER_TYPE_TWITTER_COM
|
||||||
import org.mariotaku.twidere.activity.WebLinkHandlerActivity
|
import org.mariotaku.twidere.activity.WebLinkHandlerActivity
|
||||||
import org.mariotaku.twidere.annotation.Referral
|
import org.mariotaku.twidere.annotation.Referral
|
||||||
import org.mariotaku.twidere.app.TwidereApplication
|
import org.mariotaku.twidere.app.TwidereApplication
|
||||||
|
import org.mariotaku.twidere.constant.IntentConstants.EXTRA_ACCOUNT_HOST
|
||||||
import org.mariotaku.twidere.constant.IntentConstants.EXTRA_ACCOUNT_KEY
|
import org.mariotaku.twidere.constant.IntentConstants.EXTRA_ACCOUNT_KEY
|
||||||
import org.mariotaku.twidere.constant.displaySensitiveContentsKey
|
import org.mariotaku.twidere.constant.displaySensitiveContentsKey
|
||||||
import org.mariotaku.twidere.constant.newDocumentApiKey
|
import org.mariotaku.twidere.constant.newDocumentApiKey
|
||||||
import org.mariotaku.twidere.extension.model.AcctPlaceholderUserKey
|
import org.mariotaku.twidere.extension.model.AcctPlaceholderUserKey
|
||||||
|
import org.mariotaku.twidere.extension.toUri
|
||||||
import org.mariotaku.twidere.model.UserKey
|
import org.mariotaku.twidere.model.UserKey
|
||||||
import org.mariotaku.twidere.model.util.ParcelableMediaUtils
|
import org.mariotaku.twidere.model.util.ParcelableMediaUtils
|
||||||
import org.mariotaku.twidere.util.TwidereLinkify.OnLinkClickListener
|
import org.mariotaku.twidere.util.TwidereLinkify.OnLinkClickListener
|
||||||
|
@ -65,7 +69,7 @@ open class OnLinkClickHandler(
|
||||||
if (accountKey != null && isMedia(link, extraId)) {
|
if (accountKey != null && isMedia(link, extraId)) {
|
||||||
openMedia(accountKey, extraId, sensitive, link, start, end)
|
openMedia(accountKey, extraId, sensitive, link, start, end)
|
||||||
} else {
|
} else {
|
||||||
openLink(link)
|
openLink(accountKey, link)
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -74,20 +78,15 @@ open class OnLinkClickHandler(
|
||||||
openMedia(accountKey, extraId, sensitive, link, start, end)
|
openMedia(accountKey, extraId, sensitive, link, start, end)
|
||||||
} else {
|
} else {
|
||||||
val authority = UriUtils.getAuthority(link)
|
val authority = UriUtils.getAuthority(link)
|
||||||
if (authority == null) {
|
if (authority == "fanfou.com") {
|
||||||
openLink(link)
|
if (accountKey != null && handleFanfouLink(link, orig, accountKey)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
} else if (IntentUtils.isWebLinkHandled(context, Uri.parse(link))) {
|
||||||
|
openTwitterLink(accountKey, link)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
when (authority) {
|
openLink(accountKey, link)
|
||||||
"fanfou.com" -> if (accountKey != null && handleFanfouLink(link, orig, accountKey)) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
else -> if (IntentUtils.isWebLinkHandled(context, Uri.parse(link))) {
|
|
||||||
openTwitterLink(link, accountKey!!)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
openLink(link)
|
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -132,17 +131,27 @@ open class OnLinkClickHandler(
|
||||||
preferences[displaySensitiveContentsKey])
|
preferences[displaySensitiveContentsKey])
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun openLink(link: String) {
|
protected open fun openLink(accountKey: UserKey?, link: String) {
|
||||||
if (manager != null && manager.isActive) return
|
if (manager != null && manager.isActive) return
|
||||||
openLink(context, preferences, Uri.parse(link))
|
val uri = Uri.parse(link)
|
||||||
|
if (uri.isRelative && accountKey != null && accountKey.host != null) {
|
||||||
|
val absUri = HttpUrl.parse("http://${accountKey.host}/").resolve(link).toUri()
|
||||||
|
openLink(context, preferences, absUri)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
openLink(context, preferences, uri)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun openTwitterLink(link: String, accountKey: UserKey) {
|
protected fun openTwitterLink(accountKey: UserKey?, link: String) {
|
||||||
if (manager != null && manager.isActive) return
|
if (manager != null && manager.isActive) return
|
||||||
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(link))
|
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(link))
|
||||||
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||||
intent.setClass(context, WebLinkHandlerActivity::class.java)
|
intent.setClass(context, WebLinkHandlerActivity::class.java)
|
||||||
intent.putExtra(EXTRA_ACCOUNT_KEY, accountKey)
|
if (accountKey != null) {
|
||||||
|
intent.putExtra(EXTRA_ACCOUNT_KEY, accountKey)
|
||||||
|
} else {
|
||||||
|
intent.putExtra(EXTRA_ACCOUNT_HOST, USER_TYPE_TWITTER_COM)
|
||||||
|
}
|
||||||
intent.setExtrasClassLoader(TwidereApplication::class.java.classLoader)
|
intent.setExtrasClassLoader(TwidereApplication::class.java.classLoader)
|
||||||
if (intent.resolveActivity(context.packageManager) != null) {
|
if (intent.resolveActivity(context.packageManager) != null) {
|
||||||
try {
|
try {
|
||||||
|
@ -199,3 +208,4 @@ open class OnLinkClickHandler(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,7 +45,7 @@ class StatusAdapterLinkClickHandler<D>(context: Context, preferences: SharedPref
|
||||||
val media = ParcelableMediaUtils.getAllMedia(status)
|
val media = ParcelableMediaUtils.getAllMedia(status)
|
||||||
val current = StatusLinkClickHandler.findByLink(media, link)
|
val current = StatusLinkClickHandler.findByLink(media, link)
|
||||||
if (current != null && current.open_browser) {
|
if (current != null && current.open_browser) {
|
||||||
openLink(link)
|
openLink(accountKey, link)
|
||||||
} else {
|
} else {
|
||||||
IntentUtils.openMedia(context, status, current, preferences[newDocumentApiKey],
|
IntentUtils.openMedia(context, status, current, preferences[newDocumentApiKey],
|
||||||
preferences[displaySensitiveContentsKey])
|
preferences[displaySensitiveContentsKey])
|
||||||
|
|
|
@ -45,7 +45,7 @@ open class StatusLinkClickHandler(
|
||||||
val status = status
|
val status = status
|
||||||
val current = findByLink(status!!.media, link)
|
val current = findByLink(status!!.media, link)
|
||||||
if (current == null || current.open_browser) {
|
if (current == null || current.open_browser) {
|
||||||
openLink(link)
|
openLink(accountKey, link)
|
||||||
} else {
|
} else {
|
||||||
IntentUtils.openMedia(context, status, current, preferences[newDocumentApiKey],
|
IntentUtils.openMedia(context, status, current, preferences[newDocumentApiKey],
|
||||||
preferences[displaySensitiveContentsKey])
|
preferences[displaySensitiveContentsKey])
|
||||||
|
|
Loading…
Reference in New Issue