280 character backport

This commit is contained in:
Mariotaku Lee 2017-11-13 12:53:40 +08:00
parent 641f27da54
commit 518d573d32
No known key found for this signature in database
GPG Key ID: 15C10F89D7C33535
19 changed files with 315 additions and 75 deletions

View File

@ -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
}

View File

@ -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]) }
}

View File

@ -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)
}

View File

@ -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)

View File

@ -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)
}
}

View File

@ -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

View File

@ -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() {

View File

@ -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

View File

@ -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

View File

@ -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))
}
}

View File

@ -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

View File

@ -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

View File

@ -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()

View File

@ -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 {

View File

@ -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

View File

@ -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)
}
}

View File

@ -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]}"
}
}

View File

@ -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
}
}

View File

@ -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
}
}
}