280 character backport
This commit is contained in:
parent
641f27da54
commit
518d573d32
|
@ -6,7 +6,7 @@ buildscript {
|
|||
maven { url 'https://maven.google.com' }
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.0.0-beta7'
|
||||
classpath 'com.android.tools.build:gradle:3.0.0'
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
}
|
||||
|
|
|
@ -26,6 +26,10 @@ inline fun <T, reified R> Array<T>.mapToArray(transform: (T) -> R): Array<R> {
|
|||
return Array(size) { transform(this[it]) }
|
||||
}
|
||||
|
||||
inline fun <T> Array<T>.mapToIntArray(transform: (T) -> Int): IntArray {
|
||||
return IntArray(size) { transform(this[it]) }
|
||||
}
|
||||
|
||||
inline fun <T, reified R> Array<T>.mapIndexedToArray(transform: (Int, T) -> R): Array<R> {
|
||||
return Array(size) { transform(it, this[it]) }
|
||||
}
|
||||
|
|
|
@ -19,6 +19,14 @@
|
|||
|
||||
package org.mariotaku.ktextension
|
||||
|
||||
import java.text.Normalizer
|
||||
|
||||
fun CharSequence.appendTo(sb: StringBuilder) {
|
||||
sb.append(this)
|
||||
}
|
||||
|
||||
operator fun CharSequence.times(n: Int): String = repeat(n)
|
||||
|
||||
fun CharSequence.normalized(form: Normalizer.Form): String {
|
||||
return Normalizer.normalize(this, form)
|
||||
}
|
|
@ -25,6 +25,9 @@ inline var TextView.charSequence: CharSequence?
|
|||
text = value
|
||||
}
|
||||
|
||||
inline val TextView.textIfVisible: CharSequence?
|
||||
get() = if (visibility == View.VISIBLE) text else null
|
||||
|
||||
fun TextView.applyFontFamily(lightFont: Boolean) {
|
||||
if (lightFont) {
|
||||
typeface = Typeface.create("sans-serif-light", typeface?.style ?: Typeface.NORMAL)
|
||||
|
|
|
@ -57,7 +57,6 @@ import android.widget.ImageView
|
|||
import android.widget.Toast
|
||||
import com.bumptech.glide.Glide
|
||||
import com.twitter.Extractor
|
||||
import com.twitter.Validator
|
||||
import kotlinx.android.synthetic.main.activity_compose.*
|
||||
import nl.komponents.kovenant.task
|
||||
import org.mariotaku.abstask.library.AbstractTask
|
||||
|
@ -99,6 +98,7 @@ import org.mariotaku.twidere.util.*
|
|||
import org.mariotaku.twidere.util.EditTextEnterHandler.EnterListener
|
||||
import org.mariotaku.twidere.util.dagger.GeneralComponent
|
||||
import org.mariotaku.twidere.util.premium.ExtraFeaturesService
|
||||
import org.mariotaku.twidere.util.text.StatusTextValidator
|
||||
import org.mariotaku.twidere.util.view.SimpleTextWatcher
|
||||
import org.mariotaku.twidere.util.view.ViewAnimator
|
||||
import org.mariotaku.twidere.util.view.ViewProperties
|
||||
|
@ -123,8 +123,6 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
|
|||
@Inject
|
||||
lateinit var extractor: Extractor
|
||||
@Inject
|
||||
lateinit var validator: Validator
|
||||
@Inject
|
||||
lateinit var locationManager: LocationManager
|
||||
|
||||
private lateinit var itemTouchHelper: ItemTouchHelper
|
||||
|
@ -1539,20 +1537,12 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
|
|||
}
|
||||
|
||||
private fun getStatusUpdate(checkLength: Boolean): ParcelableStatusUpdate {
|
||||
val accountKeys = accountsAdapter.selectedAccountKeys
|
||||
if (accountKeys.isEmpty()) throw NoAccountException()
|
||||
val accounts = accountsAdapter.selectedAccounts
|
||||
if (accounts.isEmpty()) throw NoAccountException()
|
||||
val update = ParcelableStatusUpdate()
|
||||
val media = this.media
|
||||
val text = editText.string?.let { Normalizer.normalize(it, Normalizer.Form.NFC) }.orEmpty()
|
||||
var summary: String? = null
|
||||
var summaryLength = 0
|
||||
if (editSummary.visibility == View.VISIBLE) {
|
||||
summary = editSummary.string?.takeIf(String::isNotEmpty)?.let {
|
||||
Normalizer.normalize(it, Normalizer.Form.NFC)
|
||||
}
|
||||
summaryLength = summary?.let { validator.getTweetLength(it) } ?: 0
|
||||
}
|
||||
val accounts = AccountUtils.getAllAccountDetails(AccountManager.get(this), accountKeys, true)
|
||||
val text = editText.text?.normalized(Normalizer.Form.NFC).orEmpty()
|
||||
val summary = editSummary.textIfVisible?.normalized(Normalizer.Form.NFC)
|
||||
val maxLength = statusTextCount.maxLength
|
||||
val inReplyTo = inReplyToStatus
|
||||
val replyTextAndMentions = getTwitterReplyTextAndMentions(text, accounts)
|
||||
|
@ -1560,10 +1550,10 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
|
|||
val (replyStartIndex, replyText, _, excludedMentions, replyToOriginalUser) =
|
||||
replyTextAndMentions
|
||||
if (replyText.isEmpty() && media.isEmpty()) throw NoContentException()
|
||||
val totalLength = summaryLength + validator.getTweetLength(replyText)
|
||||
val totalLength = StatusTextValidator.calculateLength(accounts, summary, text)
|
||||
if (checkLength && !statusShortenerUsed && maxLength > 0 && totalLength > maxLength) {
|
||||
throw StatusTooLongException(replyStartIndex +
|
||||
replyText.offsetByCodePoints(0, maxLength - summaryLength))
|
||||
replyText.offsetByCodePoints(0, maxLength))
|
||||
}
|
||||
update.text = replyText
|
||||
update.extended_reply_mode = true
|
||||
|
@ -1575,9 +1565,9 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
|
|||
}
|
||||
} else {
|
||||
if (text.isEmpty() && media.isEmpty()) throw NoContentException()
|
||||
val totalLength = summaryLength + validator.getTweetLength(text)
|
||||
val totalLength = StatusTextValidator.calculateLength(accounts, summary, text)
|
||||
if (checkLength && !statusShortenerUsed && maxLength > 0 && totalLength > maxLength) {
|
||||
throw StatusTooLongException(text.offsetByCodePoints(0, maxLength - summaryLength))
|
||||
throw StatusTooLongException(text.offsetByCodePoints(0, maxLength))
|
||||
}
|
||||
update.text = text
|
||||
update.extended_reply_mode = false
|
||||
|
@ -1604,27 +1594,28 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
|
|||
|
||||
private fun updateTextCount() {
|
||||
val editable = editText.editableText ?: return
|
||||
var summaryLength = 0
|
||||
if (editSummary.visibility == View.VISIBLE) {
|
||||
summaryLength = validator.getTweetLength(editSummary.string.orEmpty())
|
||||
}
|
||||
val summary = editSummary.textIfVisible?.toString()
|
||||
val accounts = accountsAdapter.selectedAccounts
|
||||
val text = editable.toString()
|
||||
val textAndMentions = getTwitterReplyTextAndMentions(text)
|
||||
if (textAndMentions == null) {
|
||||
hintLabel.visibility = View.GONE
|
||||
editable.clearSpans(MentionColorSpan::class.java)
|
||||
statusTextCount.textCount = summaryLength + validator.getTweetLength(text)
|
||||
statusTextCount.textCount = StatusTextValidator.calculateLength(accounts, summary, text,
|
||||
false, null)
|
||||
} else if (textAndMentions.replyToOriginalUser || replyToSelf) {
|
||||
hintLabel.visibility = View.GONE
|
||||
val mentionColor = ThemeUtils.getTextColorSecondary(this)
|
||||
editable.clearSpans(MentionColorSpan::class.java)
|
||||
editable.setSpan(MentionColorSpan(mentionColor), 0, textAndMentions.replyStartIndex,
|
||||
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
statusTextCount.textCount = summaryLength + validator.getTweetLength(textAndMentions.replyText)
|
||||
statusTextCount.textCount = StatusTextValidator.calculateLength(accounts, summary,
|
||||
textAndMentions.replyText, false, null)
|
||||
} else {
|
||||
hintLabel.visibility = View.VISIBLE
|
||||
editable.clearSpans(MentionColorSpan::class.java)
|
||||
statusTextCount.textCount = summaryLength + validator.getTweetLength(textAndMentions.replyText)
|
||||
statusTextCount.textCount = StatusTextValidator.calculateLength(accounts, summary,
|
||||
textAndMentions.replyText, false, null)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* Twidere - Twitter client for Android
|
||||
*
|
||||
* Copyright (C) 2012-2017 Mariotaku Lee <mariotaku.lee@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.mariotaku.twidere.alias
|
||||
|
||||
typealias TwitterRegex = com.twitter.Regex
|
|
@ -1,7 +1,6 @@
|
|||
package org.mariotaku.twidere.extension.model
|
||||
|
||||
import android.content.Context
|
||||
import com.twitter.Validator
|
||||
import org.mariotaku.microblog.library.twitter.annotation.MediaCategory
|
||||
import org.mariotaku.twidere.annotation.AccountType
|
||||
import org.mariotaku.twidere.model.AccountDetails
|
||||
|
@ -13,6 +12,9 @@ import org.mariotaku.twidere.model.account.cred.Credentials
|
|||
import org.mariotaku.twidere.model.account.cred.OAuthCredentials
|
||||
import org.mariotaku.twidere.task.twitter.UpdateStatusTask
|
||||
import org.mariotaku.twidere.util.InternalTwitterContentUtils
|
||||
import org.mariotaku.twidere.util.text.FanfouValidator
|
||||
import org.mariotaku.twidere.util.text.MastodonValidator
|
||||
import org.mariotaku.twidere.util.text.TwitterValidator
|
||||
|
||||
fun AccountDetails.isOfficial(context: Context): Boolean {
|
||||
val extra = this.extras
|
||||
|
@ -73,26 +75,23 @@ fun AccountDetails.getMediaSizeLimit(@MediaCategory mediaCategory: String? = nul
|
|||
/**
|
||||
* Text limit when composing a status, 0 for no limit
|
||||
*/
|
||||
val AccountDetails.textLimit: Int get() {
|
||||
if (type == null) {
|
||||
return Validator.MAX_TWEET_LENGTH
|
||||
}
|
||||
when (type) {
|
||||
val AccountDetails.textLimit: Int
|
||||
get() = when (type) {
|
||||
AccountType.STATUSNET -> {
|
||||
val extras = this.extras as? StatusNetAccountExtras
|
||||
if (extras != null) {
|
||||
return extras.textLimit
|
||||
}
|
||||
(this.extras as? StatusNetAccountExtras)?.textLimit ?: 140
|
||||
}
|
||||
AccountType.MASTODON -> {
|
||||
val extras = this.extras as? MastodonAccountExtras
|
||||
if (extras != null) {
|
||||
return extras.statusTextLimit
|
||||
}
|
||||
(this.extras as? MastodonAccountExtras)?.statusTextLimit ?: MastodonValidator.textLimit
|
||||
}
|
||||
AccountType.FANFOU -> {
|
||||
FanfouValidator.textLimit
|
||||
}
|
||||
AccountType.TWITTER -> {
|
||||
TwitterValidator.maxWeightedTweetLength
|
||||
}
|
||||
else -> 140
|
||||
}
|
||||
return Validator.MAX_TWEET_LENGTH
|
||||
}
|
||||
|
||||
|
||||
val Array<AccountDetails>.textLimit: Int
|
||||
get() {
|
||||
|
|
|
@ -26,7 +26,6 @@ import android.support.v4.app.DialogFragment
|
|||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.RequestManager
|
||||
import com.squareup.otto.Bus
|
||||
import com.twitter.Validator
|
||||
import okhttp3.Dns
|
||||
import org.mariotaku.kpreferences.KPreferences
|
||||
import org.mariotaku.restfu.http.RestHttpClient
|
||||
|
@ -49,8 +48,6 @@ open class BaseDialogFragment : DialogFragment() {
|
|||
@Inject
|
||||
lateinit var kPreferences: KPreferences
|
||||
@Inject
|
||||
lateinit var validator: Validator
|
||||
@Inject
|
||||
lateinit var keyboardShortcutsHandler: KeyboardShortcutsHandler
|
||||
@Inject
|
||||
lateinit var bus: Bus
|
||||
|
|
|
@ -27,7 +27,6 @@ import android.support.v4.text.BidiFormatter
|
|||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.RequestManager
|
||||
import com.squareup.otto.Bus
|
||||
import com.twitter.Validator
|
||||
import nl.komponents.kovenant.Promise
|
||||
import okhttp3.Dns
|
||||
import org.mariotaku.restfu.http.RestHttpClient
|
||||
|
@ -65,8 +64,6 @@ open class BaseFragment : Fragment(), IBaseFragment<BaseFragment> {
|
|||
@Inject
|
||||
lateinit var errorInfoStore: ErrorInfoStore
|
||||
@Inject
|
||||
lateinit var validator: Validator
|
||||
@Inject
|
||||
lateinit var extraFeaturesService: ExtraFeaturesService
|
||||
@Inject
|
||||
lateinit var permissionsManager: PermissionsManager
|
||||
|
|
|
@ -31,7 +31,6 @@ import android.view.View
|
|||
import android.widget.CheckBox
|
||||
import android.widget.RelativeLayout
|
||||
import android.widget.Toast
|
||||
import com.twitter.Validator
|
||||
import org.mariotaku.kpreferences.get
|
||||
import org.mariotaku.ktextension.*
|
||||
import org.mariotaku.library.objectcursor.ObjectCursor
|
||||
|
@ -53,6 +52,8 @@ import org.mariotaku.twidere.provider.TwidereDataStore.Drafts
|
|||
import org.mariotaku.twidere.service.LengthyOperationsService
|
||||
import org.mariotaku.twidere.util.EditTextEnterHandler
|
||||
import org.mariotaku.twidere.util.LinkCreator
|
||||
import org.mariotaku.twidere.util.text.FanfouValidator
|
||||
import org.mariotaku.twidere.util.text.StatusTextValidator
|
||||
import org.mariotaku.twidere.util.view.SimpleTextWatcher
|
||||
import org.mariotaku.twidere.view.ComposeEditText
|
||||
import org.mariotaku.twidere.view.StatusTextCountView
|
||||
|
@ -184,7 +185,8 @@ class RetweetQuoteDialogFragment : AbsStatusDialogFragment() {
|
|||
positiveButton.setText(R.string.action_retweet)
|
||||
positiveButton.isEnabled = status.can_retweet
|
||||
}
|
||||
textCountView.textCount = validator.getTweetLength(s.toString())
|
||||
textCountView.textCount = StatusTextValidator.calculateLength(account.type, account.key,
|
||||
null, s.toString())
|
||||
}
|
||||
|
||||
private fun DialogInterface.shouldQuoteRetweet(account: AccountDetails): Boolean {
|
||||
|
@ -224,8 +226,8 @@ class RetweetQuoteDialogFragment : AbsStatusDialogFragment() {
|
|||
status.quoted_user_screen_name, status.quoted_text_plain)
|
||||
update.repost_status_id = status.quoted_id
|
||||
}
|
||||
if (commentText.length > Validator.MAX_TWEET_LENGTH) {
|
||||
commentText = commentText.substring(0, Math.max(Validator.MAX_TWEET_LENGTH,
|
||||
if (FanfouValidator.calculateLength(commentText) > FanfouValidator.textLimit) {
|
||||
commentText = commentText.substring(0, Math.max(FanfouValidator.textLimit,
|
||||
editingComment.length))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ package org.mariotaku.twidere.service
|
|||
import android.app.IntentService
|
||||
import android.content.SharedPreferences
|
||||
import com.twitter.Extractor
|
||||
import com.twitter.Validator
|
||||
import org.mariotaku.twidere.util.AsyncTwitterWrapper
|
||||
import org.mariotaku.twidere.util.NotificationManagerWrapper
|
||||
import org.mariotaku.twidere.util.UserColorNameManager
|
||||
|
@ -19,8 +18,6 @@ abstract class BaseIntentService(tag: String) : IntentService(tag) {
|
|||
@Inject
|
||||
lateinit var notificationManager: NotificationManagerWrapper
|
||||
@Inject
|
||||
lateinit var validator: Validator
|
||||
@Inject
|
||||
lateinit var extractor: Extractor
|
||||
@Inject
|
||||
lateinit var userColorNameManager: UserColorNameManager
|
||||
|
|
|
@ -23,7 +23,6 @@ import android.app.Service
|
|||
import android.content.SharedPreferences
|
||||
import android.net.ConnectivityManager
|
||||
import com.twitter.Extractor
|
||||
import com.twitter.Validator
|
||||
import org.mariotaku.twidere.util.*
|
||||
import org.mariotaku.twidere.util.dagger.GeneralComponent
|
||||
import org.mariotaku.twidere.util.notification.ContentNotificationManager
|
||||
|
@ -38,8 +37,6 @@ abstract class BaseService : Service() {
|
|||
@Inject
|
||||
lateinit var notificationManager: NotificationManagerWrapper
|
||||
@Inject
|
||||
lateinit var validator: Validator
|
||||
@Inject
|
||||
lateinit var extractor: Extractor
|
||||
@Inject
|
||||
lateinit var userColorNameManager: UserColorNameManager
|
||||
|
|
|
@ -14,7 +14,6 @@ import android.support.annotation.WorkerThread
|
|||
import android.support.media.ExifInterface
|
||||
import android.text.TextUtils
|
||||
import android.webkit.MimeTypeMap
|
||||
import com.twitter.Validator
|
||||
import net.ypresto.androidtranscoder.MediaTranscoder
|
||||
import net.ypresto.androidtranscoder.format.MediaFormatStrategyPresets
|
||||
import org.mariotaku.ktextension.*
|
||||
|
@ -43,7 +42,6 @@ import org.mariotaku.twidere.extension.calculateInSampleSize
|
|||
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.text.twitter.getTweetLength
|
||||
import org.mariotaku.twidere.model.*
|
||||
import org.mariotaku.twidere.model.account.AccountExtras
|
||||
import org.mariotaku.twidere.model.analyzer.UpdateStatus
|
||||
|
@ -55,6 +53,7 @@ import org.mariotaku.twidere.task.BaseAbstractTask
|
|||
import org.mariotaku.twidere.util.*
|
||||
import org.mariotaku.twidere.util.io.ContentLengthInputStream
|
||||
import org.mariotaku.twidere.util.premium.ExtraFeaturesService
|
||||
import org.mariotaku.twidere.util.text.StatusTextValidator
|
||||
import java.io.Closeable
|
||||
import java.io.File
|
||||
import java.io.FileNotFoundException
|
||||
|
@ -222,7 +221,6 @@ class UpdateStatusTask(
|
|||
update: ParcelableStatusUpdate,
|
||||
pending: PendingStatusUpdate) {
|
||||
if (shortener == null) return
|
||||
val validator = Validator()
|
||||
stateCallback.onShorteningStatus()
|
||||
val sharedShortened = HashMap<UserKey, StatusShortenResult>()
|
||||
for (i in 0 until pending.length) {
|
||||
|
@ -230,8 +228,8 @@ class UpdateStatusTask(
|
|||
val text = pending.overrideTexts[i]
|
||||
val textLimit = account.textLimit
|
||||
val ignoreMentions = account.type == AccountType.TWITTER
|
||||
if (textLimit >= 0 && validator.getTweetLength(text, ignoreMentions,
|
||||
update.in_reply_to_status, account.key) <= textLimit) {
|
||||
if (textLimit >= 0 && StatusTextValidator.calculateLength(account.type, account.key,
|
||||
update.summary, text, ignoreMentions, update.in_reply_to_status) <= textLimit) {
|
||||
continue
|
||||
}
|
||||
shortener.waitForService()
|
||||
|
|
|
@ -34,7 +34,6 @@ import com.google.android.exoplayer2.upstream.DataSource
|
|||
import com.squareup.otto.Bus
|
||||
import com.squareup.otto.ThreadEnforcer
|
||||
import com.twitter.Extractor
|
||||
import com.twitter.Validator
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import okhttp3.Cache
|
||||
|
@ -199,12 +198,6 @@ class ApplicationModule(private val context: Context) {
|
|||
return TwidereMediaDownloader(context, client, thumbor)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun validator(): Validator {
|
||||
return Validator()
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun extractor(): Extractor {
|
||||
|
|
|
@ -21,7 +21,6 @@ package org.mariotaku.twidere.util.dagger
|
|||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import com.twitter.Validator
|
||||
import okhttp3.Cache
|
||||
import okhttp3.ConnectionPool
|
||||
import okhttp3.Dns
|
||||
|
@ -55,9 +54,6 @@ class DependencyHolder internal constructor(context: Context) {
|
|||
lateinit var dns: Dns
|
||||
internal set
|
||||
@Inject
|
||||
lateinit var validator: Validator
|
||||
internal set
|
||||
@Inject
|
||||
lateinit var preferences: SharedPreferences
|
||||
internal set
|
||||
@Inject
|
||||
|
|
|
@ -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.util.text
|
||||
|
||||
object FanfouValidator {
|
||||
|
||||
const val textLimit = 140
|
||||
|
||||
fun calculateLength(text: String): Int {
|
||||
return text.codePointCount(0, text.length)
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* 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.util.text
|
||||
|
||||
import org.mariotaku.ktextension.times
|
||||
import org.mariotaku.twidere.alias.TwitterRegex
|
||||
|
||||
|
||||
object MastodonValidator {
|
||||
|
||||
const val textLimit: Int = 500
|
||||
|
||||
private val mentionRegex = Regex("(?<=^|[^/[\\w]])@(([a-z0-9_]+)(?:@[a-z0-9.\\-]+[a-z0-9]+)?)", RegexOption.IGNORE_CASE)
|
||||
|
||||
private val urlRegex = TwitterRegex.VALID_URL.toRegex()
|
||||
|
||||
fun getCountableLength(summary: String?, text: String): Int {
|
||||
var length = 0
|
||||
if (summary != null) {
|
||||
length += summary.codePointCount
|
||||
}
|
||||
length += text.countableText.codePointCount
|
||||
return length
|
||||
}
|
||||
|
||||
private inline val String.codePointCount: Int
|
||||
get() = codePointCount(0, length)
|
||||
|
||||
private inline val String.countableText: String
|
||||
get() = replace(urlRegex) {
|
||||
it.groupValues[TwitterRegex.VALID_URL_GROUP_BEFORE] + "x" * 23
|
||||
}.replace(mentionRegex) { mr ->
|
||||
"@${mr.groupValues[2]}"
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* 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.util.text
|
||||
|
||||
import org.mariotaku.ktextension.mapToIntArray
|
||||
import org.mariotaku.twidere.annotation.AccountType
|
||||
import org.mariotaku.twidere.extension.text.twitter.getTweetLength
|
||||
import org.mariotaku.twidere.model.AccountDetails
|
||||
import org.mariotaku.twidere.model.ParcelableStatus
|
||||
import org.mariotaku.twidere.model.UserKey
|
||||
|
||||
object StatusTextValidator {
|
||||
|
||||
fun calculateLength(@AccountType accountType: String, accountKey: UserKey?, summary: String?,
|
||||
text: String, ignoreMentions: Boolean = false, inReplyTo: ParcelableStatus? = null): Int {
|
||||
when (accountType) {
|
||||
AccountType.TWITTER -> {
|
||||
return TwitterValidator.getTweetLength(text, ignoreMentions, inReplyTo, accountKey)
|
||||
}
|
||||
AccountType.MASTODON -> {
|
||||
return MastodonValidator.getCountableLength(summary, text)
|
||||
}
|
||||
AccountType.FANFOU -> {
|
||||
return FanfouValidator.calculateLength(text)
|
||||
}
|
||||
AccountType.STATUSNET -> {
|
||||
return text.codePointCount(0, text.length)
|
||||
}
|
||||
else -> {
|
||||
return text.codePointCount(0, text.length)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun calculateLengths(accounts: Array<AccountDetails>, summary: String?, text: String,
|
||||
ignoreMentions: Boolean = false, inReplyTo: ParcelableStatus? = null): IntArray {
|
||||
return accounts.mapToIntArray {
|
||||
calculateLength(it.type, it.key, summary, text, ignoreMentions, inReplyTo)
|
||||
}
|
||||
}
|
||||
|
||||
fun calculateLength(accounts: Array<AccountDetails>, summary: String?, text: String,
|
||||
ignoreMentions: Boolean = false, inReplyTo: ParcelableStatus? = null): Int {
|
||||
return calculateLengths(accounts, summary, text, ignoreMentions, inReplyTo).max() ?: 0
|
||||
}
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* 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.util.text
|
||||
|
||||
import com.twitter.Extractor
|
||||
import com.twitter.Validator
|
||||
import java.text.Normalizer
|
||||
|
||||
object TwitterValidator : Validator() {
|
||||
|
||||
const val maxWeightedTweetLength: Int = 280
|
||||
|
||||
const val defaultWeight: Int = 200
|
||||
|
||||
var ranges: Array<WeightRange> = arrayOf(
|
||||
WeightRange(0, 4351, 100),
|
||||
WeightRange(8192, 8205, 100),
|
||||
WeightRange(8208, 8223, 100),
|
||||
WeightRange(8242, 8247, 100)
|
||||
)
|
||||
|
||||
private val extractor = Extractor()
|
||||
|
||||
override fun getTweetLength(text: String): Int {
|
||||
val normalized = Normalizer.normalize(text, Normalizer.Form.NFC)
|
||||
var weightedLength = 0
|
||||
val inputLength = normalized.length
|
||||
|
||||
var charOffset = 0
|
||||
while (charOffset < inputLength) {
|
||||
val codePoint = Character.codePointAt(normalized, charOffset)
|
||||
weightedLength += weightForCodePoint(codePoint)
|
||||
charOffset += Character.charCount(codePoint)
|
||||
}
|
||||
|
||||
var length = weightedLength / 100
|
||||
|
||||
for (urlEntity in extractor.extractURLsWithIndices(normalized)) {
|
||||
length += urlEntity.start - urlEntity.end
|
||||
length += if (urlEntity.value.toLowerCase().startsWith("https://")) shortUrlLengthHttps else shortUrlLength
|
||||
}
|
||||
|
||||
return length
|
||||
}
|
||||
|
||||
override fun isValidTweet(text: String?): Boolean {
|
||||
if (text == null || text.isEmpty()) {
|
||||
return false
|
||||
}
|
||||
|
||||
for (c in text.toCharArray()) {
|
||||
if (c == '\uFFFE' || c == '\uFEFF' || // BOM
|
||||
c == '\uFFFF' || // Special
|
||||
c in '\u202A'..'\u202E') { // Direction change
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return getTweetLength(text) <= maxWeightedTweetLength
|
||||
}
|
||||
|
||||
private fun weightForCodePoint(codePoint: Int): Int {
|
||||
val range = ranges.find { codePoint in it } ?: return defaultWeight
|
||||
return range.weight
|
||||
}
|
||||
|
||||
data class WeightRange(val start: Int, val end: Int, val weight: Int) {
|
||||
operator fun contains(codePoint: Int): Boolean {
|
||||
return codePoint in start..end
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue