280 character backport
This commit is contained in:
parent
641f27da54
commit
518d573d32
|
@ -6,7 +6,7 @@ buildscript {
|
||||||
maven { url 'https://maven.google.com' }
|
maven { url 'https://maven.google.com' }
|
||||||
}
|
}
|
||||||
dependencies {
|
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
|
// NOTE: Do not place your application dependencies here; they belong
|
||||||
// in the individual module build.gradle files
|
// 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]) }
|
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> {
|
inline fun <T, reified R> Array<T>.mapIndexedToArray(transform: (Int, T) -> R): Array<R> {
|
||||||
return Array(size) { transform(it, this[it]) }
|
return Array(size) { transform(it, this[it]) }
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,14 @@
|
||||||
|
|
||||||
package org.mariotaku.ktextension
|
package org.mariotaku.ktextension
|
||||||
|
|
||||||
|
import java.text.Normalizer
|
||||||
|
|
||||||
fun CharSequence.appendTo(sb: StringBuilder) {
|
fun CharSequence.appendTo(sb: StringBuilder) {
|
||||||
sb.append(this)
|
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
|
text = value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline val TextView.textIfVisible: CharSequence?
|
||||||
|
get() = if (visibility == View.VISIBLE) text else null
|
||||||
|
|
||||||
fun TextView.applyFontFamily(lightFont: Boolean) {
|
fun TextView.applyFontFamily(lightFont: Boolean) {
|
||||||
if (lightFont) {
|
if (lightFont) {
|
||||||
typeface = Typeface.create("sans-serif-light", typeface?.style ?: Typeface.NORMAL)
|
typeface = Typeface.create("sans-serif-light", typeface?.style ?: Typeface.NORMAL)
|
||||||
|
|
|
@ -57,7 +57,6 @@ import android.widget.ImageView
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import com.twitter.Extractor
|
import com.twitter.Extractor
|
||||||
import com.twitter.Validator
|
|
||||||
import kotlinx.android.synthetic.main.activity_compose.*
|
import kotlinx.android.synthetic.main.activity_compose.*
|
||||||
import nl.komponents.kovenant.task
|
import nl.komponents.kovenant.task
|
||||||
import org.mariotaku.abstask.library.AbstractTask
|
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.EditTextEnterHandler.EnterListener
|
||||||
import org.mariotaku.twidere.util.dagger.GeneralComponent
|
import org.mariotaku.twidere.util.dagger.GeneralComponent
|
||||||
import org.mariotaku.twidere.util.premium.ExtraFeaturesService
|
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.SimpleTextWatcher
|
||||||
import org.mariotaku.twidere.util.view.ViewAnimator
|
import org.mariotaku.twidere.util.view.ViewAnimator
|
||||||
import org.mariotaku.twidere.util.view.ViewProperties
|
import org.mariotaku.twidere.util.view.ViewProperties
|
||||||
|
@ -123,8 +123,6 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var extractor: Extractor
|
lateinit var extractor: Extractor
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var validator: Validator
|
|
||||||
@Inject
|
|
||||||
lateinit var locationManager: LocationManager
|
lateinit var locationManager: LocationManager
|
||||||
|
|
||||||
private lateinit var itemTouchHelper: ItemTouchHelper
|
private lateinit var itemTouchHelper: ItemTouchHelper
|
||||||
|
@ -1539,20 +1537,12 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getStatusUpdate(checkLength: Boolean): ParcelableStatusUpdate {
|
private fun getStatusUpdate(checkLength: Boolean): ParcelableStatusUpdate {
|
||||||
val accountKeys = accountsAdapter.selectedAccountKeys
|
val accounts = accountsAdapter.selectedAccounts
|
||||||
if (accountKeys.isEmpty()) throw NoAccountException()
|
if (accounts.isEmpty()) throw NoAccountException()
|
||||||
val update = ParcelableStatusUpdate()
|
val update = ParcelableStatusUpdate()
|
||||||
val media = this.media
|
val media = this.media
|
||||||
val text = editText.string?.let { Normalizer.normalize(it, Normalizer.Form.NFC) }.orEmpty()
|
val text = editText.text?.normalized(Normalizer.Form.NFC).orEmpty()
|
||||||
var summary: String? = null
|
val summary = editSummary.textIfVisible?.normalized(Normalizer.Form.NFC)
|
||||||
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 maxLength = statusTextCount.maxLength
|
val maxLength = statusTextCount.maxLength
|
||||||
val inReplyTo = inReplyToStatus
|
val inReplyTo = inReplyToStatus
|
||||||
val replyTextAndMentions = getTwitterReplyTextAndMentions(text, accounts)
|
val replyTextAndMentions = getTwitterReplyTextAndMentions(text, accounts)
|
||||||
|
@ -1560,10 +1550,10 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
|
||||||
val (replyStartIndex, replyText, _, excludedMentions, replyToOriginalUser) =
|
val (replyStartIndex, replyText, _, excludedMentions, replyToOriginalUser) =
|
||||||
replyTextAndMentions
|
replyTextAndMentions
|
||||||
if (replyText.isEmpty() && media.isEmpty()) throw NoContentException()
|
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) {
|
if (checkLength && !statusShortenerUsed && maxLength > 0 && totalLength > maxLength) {
|
||||||
throw StatusTooLongException(replyStartIndex +
|
throw StatusTooLongException(replyStartIndex +
|
||||||
replyText.offsetByCodePoints(0, maxLength - summaryLength))
|
replyText.offsetByCodePoints(0, maxLength))
|
||||||
}
|
}
|
||||||
update.text = replyText
|
update.text = replyText
|
||||||
update.extended_reply_mode = true
|
update.extended_reply_mode = true
|
||||||
|
@ -1575,9 +1565,9 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (text.isEmpty() && media.isEmpty()) throw NoContentException()
|
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) {
|
if (checkLength && !statusShortenerUsed && maxLength > 0 && totalLength > maxLength) {
|
||||||
throw StatusTooLongException(text.offsetByCodePoints(0, maxLength - summaryLength))
|
throw StatusTooLongException(text.offsetByCodePoints(0, maxLength))
|
||||||
}
|
}
|
||||||
update.text = text
|
update.text = text
|
||||||
update.extended_reply_mode = false
|
update.extended_reply_mode = false
|
||||||
|
@ -1604,27 +1594,28 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
|
||||||
|
|
||||||
private fun updateTextCount() {
|
private fun updateTextCount() {
|
||||||
val editable = editText.editableText ?: return
|
val editable = editText.editableText ?: return
|
||||||
var summaryLength = 0
|
val summary = editSummary.textIfVisible?.toString()
|
||||||
if (editSummary.visibility == View.VISIBLE) {
|
val accounts = accountsAdapter.selectedAccounts
|
||||||
summaryLength = validator.getTweetLength(editSummary.string.orEmpty())
|
|
||||||
}
|
|
||||||
val text = editable.toString()
|
val text = editable.toString()
|
||||||
val textAndMentions = getTwitterReplyTextAndMentions(text)
|
val textAndMentions = getTwitterReplyTextAndMentions(text)
|
||||||
if (textAndMentions == null) {
|
if (textAndMentions == null) {
|
||||||
hintLabel.visibility = View.GONE
|
hintLabel.visibility = View.GONE
|
||||||
editable.clearSpans(MentionColorSpan::class.java)
|
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) {
|
} else if (textAndMentions.replyToOriginalUser || replyToSelf) {
|
||||||
hintLabel.visibility = View.GONE
|
hintLabel.visibility = View.GONE
|
||||||
val mentionColor = ThemeUtils.getTextColorSecondary(this)
|
val mentionColor = ThemeUtils.getTextColorSecondary(this)
|
||||||
editable.clearSpans(MentionColorSpan::class.java)
|
editable.clearSpans(MentionColorSpan::class.java)
|
||||||
editable.setSpan(MentionColorSpan(mentionColor), 0, textAndMentions.replyStartIndex,
|
editable.setSpan(MentionColorSpan(mentionColor), 0, textAndMentions.replyStartIndex,
|
||||||
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||||
statusTextCount.textCount = summaryLength + validator.getTweetLength(textAndMentions.replyText)
|
statusTextCount.textCount = StatusTextValidator.calculateLength(accounts, summary,
|
||||||
|
textAndMentions.replyText, false, null)
|
||||||
} else {
|
} else {
|
||||||
hintLabel.visibility = View.VISIBLE
|
hintLabel.visibility = View.VISIBLE
|
||||||
editable.clearSpans(MentionColorSpan::class.java)
|
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
|
package org.mariotaku.twidere.extension.model
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import com.twitter.Validator
|
|
||||||
import org.mariotaku.microblog.library.twitter.annotation.MediaCategory
|
import org.mariotaku.microblog.library.twitter.annotation.MediaCategory
|
||||||
import org.mariotaku.twidere.annotation.AccountType
|
import org.mariotaku.twidere.annotation.AccountType
|
||||||
import org.mariotaku.twidere.model.AccountDetails
|
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.model.account.cred.OAuthCredentials
|
||||||
import org.mariotaku.twidere.task.twitter.UpdateStatusTask
|
import org.mariotaku.twidere.task.twitter.UpdateStatusTask
|
||||||
import org.mariotaku.twidere.util.InternalTwitterContentUtils
|
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 {
|
fun AccountDetails.isOfficial(context: Context): Boolean {
|
||||||
val extra = this.extras
|
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
|
* Text limit when composing a status, 0 for no limit
|
||||||
*/
|
*/
|
||||||
val AccountDetails.textLimit: Int get() {
|
val AccountDetails.textLimit: Int
|
||||||
if (type == null) {
|
get() = when (type) {
|
||||||
return Validator.MAX_TWEET_LENGTH
|
|
||||||
}
|
|
||||||
when (type) {
|
|
||||||
AccountType.STATUSNET -> {
|
AccountType.STATUSNET -> {
|
||||||
val extras = this.extras as? StatusNetAccountExtras
|
(this.extras as? StatusNetAccountExtras)?.textLimit ?: 140
|
||||||
if (extras != null) {
|
|
||||||
return extras.textLimit
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
AccountType.MASTODON -> {
|
AccountType.MASTODON -> {
|
||||||
val extras = this.extras as? MastodonAccountExtras
|
(this.extras as? MastodonAccountExtras)?.statusTextLimit ?: MastodonValidator.textLimit
|
||||||
if (extras != null) {
|
|
||||||
return extras.statusTextLimit
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
AccountType.FANFOU -> {
|
||||||
|
FanfouValidator.textLimit
|
||||||
|
}
|
||||||
|
AccountType.TWITTER -> {
|
||||||
|
TwitterValidator.maxWeightedTweetLength
|
||||||
|
}
|
||||||
|
else -> 140
|
||||||
}
|
}
|
||||||
return Validator.MAX_TWEET_LENGTH
|
|
||||||
}
|
|
||||||
|
|
||||||
val Array<AccountDetails>.textLimit: Int
|
val Array<AccountDetails>.textLimit: Int
|
||||||
get() {
|
get() {
|
||||||
|
|
|
@ -26,7 +26,6 @@ import android.support.v4.app.DialogFragment
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import com.bumptech.glide.RequestManager
|
import com.bumptech.glide.RequestManager
|
||||||
import com.squareup.otto.Bus
|
import com.squareup.otto.Bus
|
||||||
import com.twitter.Validator
|
|
||||||
import okhttp3.Dns
|
import okhttp3.Dns
|
||||||
import org.mariotaku.kpreferences.KPreferences
|
import org.mariotaku.kpreferences.KPreferences
|
||||||
import org.mariotaku.restfu.http.RestHttpClient
|
import org.mariotaku.restfu.http.RestHttpClient
|
||||||
|
@ -49,8 +48,6 @@ open class BaseDialogFragment : DialogFragment() {
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var kPreferences: KPreferences
|
lateinit var kPreferences: KPreferences
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var validator: Validator
|
|
||||||
@Inject
|
|
||||||
lateinit var keyboardShortcutsHandler: KeyboardShortcutsHandler
|
lateinit var keyboardShortcutsHandler: KeyboardShortcutsHandler
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var bus: Bus
|
lateinit var bus: Bus
|
||||||
|
|
|
@ -27,7 +27,6 @@ import android.support.v4.text.BidiFormatter
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import com.bumptech.glide.RequestManager
|
import com.bumptech.glide.RequestManager
|
||||||
import com.squareup.otto.Bus
|
import com.squareup.otto.Bus
|
||||||
import com.twitter.Validator
|
|
||||||
import nl.komponents.kovenant.Promise
|
import nl.komponents.kovenant.Promise
|
||||||
import okhttp3.Dns
|
import okhttp3.Dns
|
||||||
import org.mariotaku.restfu.http.RestHttpClient
|
import org.mariotaku.restfu.http.RestHttpClient
|
||||||
|
@ -65,8 +64,6 @@ open class BaseFragment : Fragment(), IBaseFragment<BaseFragment> {
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var errorInfoStore: ErrorInfoStore
|
lateinit var errorInfoStore: ErrorInfoStore
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var validator: Validator
|
|
||||||
@Inject
|
|
||||||
lateinit var extraFeaturesService: ExtraFeaturesService
|
lateinit var extraFeaturesService: ExtraFeaturesService
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var permissionsManager: PermissionsManager
|
lateinit var permissionsManager: PermissionsManager
|
||||||
|
|
|
@ -31,7 +31,6 @@ import android.view.View
|
||||||
import android.widget.CheckBox
|
import android.widget.CheckBox
|
||||||
import android.widget.RelativeLayout
|
import android.widget.RelativeLayout
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import com.twitter.Validator
|
|
||||||
import org.mariotaku.kpreferences.get
|
import org.mariotaku.kpreferences.get
|
||||||
import org.mariotaku.ktextension.*
|
import org.mariotaku.ktextension.*
|
||||||
import org.mariotaku.library.objectcursor.ObjectCursor
|
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.service.LengthyOperationsService
|
||||||
import org.mariotaku.twidere.util.EditTextEnterHandler
|
import org.mariotaku.twidere.util.EditTextEnterHandler
|
||||||
import org.mariotaku.twidere.util.LinkCreator
|
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.util.view.SimpleTextWatcher
|
||||||
import org.mariotaku.twidere.view.ComposeEditText
|
import org.mariotaku.twidere.view.ComposeEditText
|
||||||
import org.mariotaku.twidere.view.StatusTextCountView
|
import org.mariotaku.twidere.view.StatusTextCountView
|
||||||
|
@ -184,7 +185,8 @@ class RetweetQuoteDialogFragment : AbsStatusDialogFragment() {
|
||||||
positiveButton.setText(R.string.action_retweet)
|
positiveButton.setText(R.string.action_retweet)
|
||||||
positiveButton.isEnabled = status.can_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 {
|
private fun DialogInterface.shouldQuoteRetweet(account: AccountDetails): Boolean {
|
||||||
|
@ -224,8 +226,8 @@ class RetweetQuoteDialogFragment : AbsStatusDialogFragment() {
|
||||||
status.quoted_user_screen_name, status.quoted_text_plain)
|
status.quoted_user_screen_name, status.quoted_text_plain)
|
||||||
update.repost_status_id = status.quoted_id
|
update.repost_status_id = status.quoted_id
|
||||||
}
|
}
|
||||||
if (commentText.length > Validator.MAX_TWEET_LENGTH) {
|
if (FanfouValidator.calculateLength(commentText) > FanfouValidator.textLimit) {
|
||||||
commentText = commentText.substring(0, Math.max(Validator.MAX_TWEET_LENGTH,
|
commentText = commentText.substring(0, Math.max(FanfouValidator.textLimit,
|
||||||
editingComment.length))
|
editingComment.length))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@ package org.mariotaku.twidere.service
|
||||||
import android.app.IntentService
|
import android.app.IntentService
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import com.twitter.Extractor
|
import com.twitter.Extractor
|
||||||
import com.twitter.Validator
|
|
||||||
import org.mariotaku.twidere.util.AsyncTwitterWrapper
|
import org.mariotaku.twidere.util.AsyncTwitterWrapper
|
||||||
import org.mariotaku.twidere.util.NotificationManagerWrapper
|
import org.mariotaku.twidere.util.NotificationManagerWrapper
|
||||||
import org.mariotaku.twidere.util.UserColorNameManager
|
import org.mariotaku.twidere.util.UserColorNameManager
|
||||||
|
@ -19,8 +18,6 @@ abstract class BaseIntentService(tag: String) : IntentService(tag) {
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var notificationManager: NotificationManagerWrapper
|
lateinit var notificationManager: NotificationManagerWrapper
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var validator: Validator
|
|
||||||
@Inject
|
|
||||||
lateinit var extractor: Extractor
|
lateinit var extractor: Extractor
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var userColorNameManager: UserColorNameManager
|
lateinit var userColorNameManager: UserColorNameManager
|
||||||
|
|
|
@ -23,7 +23,6 @@ import android.app.Service
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import android.net.ConnectivityManager
|
import android.net.ConnectivityManager
|
||||||
import com.twitter.Extractor
|
import com.twitter.Extractor
|
||||||
import com.twitter.Validator
|
|
||||||
import org.mariotaku.twidere.util.*
|
import org.mariotaku.twidere.util.*
|
||||||
import org.mariotaku.twidere.util.dagger.GeneralComponent
|
import org.mariotaku.twidere.util.dagger.GeneralComponent
|
||||||
import org.mariotaku.twidere.util.notification.ContentNotificationManager
|
import org.mariotaku.twidere.util.notification.ContentNotificationManager
|
||||||
|
@ -38,8 +37,6 @@ abstract class BaseService : Service() {
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var notificationManager: NotificationManagerWrapper
|
lateinit var notificationManager: NotificationManagerWrapper
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var validator: Validator
|
|
||||||
@Inject
|
|
||||||
lateinit var extractor: Extractor
|
lateinit var extractor: Extractor
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var userColorNameManager: UserColorNameManager
|
lateinit var userColorNameManager: UserColorNameManager
|
||||||
|
|
|
@ -14,7 +14,6 @@ import android.support.annotation.WorkerThread
|
||||||
import android.support.media.ExifInterface
|
import android.support.media.ExifInterface
|
||||||
import android.text.TextUtils
|
import android.text.TextUtils
|
||||||
import android.webkit.MimeTypeMap
|
import android.webkit.MimeTypeMap
|
||||||
import com.twitter.Validator
|
|
||||||
import net.ypresto.androidtranscoder.MediaTranscoder
|
import net.ypresto.androidtranscoder.MediaTranscoder
|
||||||
import net.ypresto.androidtranscoder.format.MediaFormatStrategyPresets
|
import net.ypresto.androidtranscoder.format.MediaFormatStrategyPresets
|
||||||
import org.mariotaku.ktextension.*
|
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.*
|
||||||
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.text.twitter.getTweetLength
|
|
||||||
import org.mariotaku.twidere.model.*
|
import org.mariotaku.twidere.model.*
|
||||||
import org.mariotaku.twidere.model.account.AccountExtras
|
import org.mariotaku.twidere.model.account.AccountExtras
|
||||||
import org.mariotaku.twidere.model.analyzer.UpdateStatus
|
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.*
|
||||||
import org.mariotaku.twidere.util.io.ContentLengthInputStream
|
import org.mariotaku.twidere.util.io.ContentLengthInputStream
|
||||||
import org.mariotaku.twidere.util.premium.ExtraFeaturesService
|
import org.mariotaku.twidere.util.premium.ExtraFeaturesService
|
||||||
|
import org.mariotaku.twidere.util.text.StatusTextValidator
|
||||||
import java.io.Closeable
|
import java.io.Closeable
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileNotFoundException
|
import java.io.FileNotFoundException
|
||||||
|
@ -222,7 +221,6 @@ class UpdateStatusTask(
|
||||||
update: ParcelableStatusUpdate,
|
update: ParcelableStatusUpdate,
|
||||||
pending: PendingStatusUpdate) {
|
pending: PendingStatusUpdate) {
|
||||||
if (shortener == null) return
|
if (shortener == null) return
|
||||||
val validator = Validator()
|
|
||||||
stateCallback.onShorteningStatus()
|
stateCallback.onShorteningStatus()
|
||||||
val sharedShortened = HashMap<UserKey, StatusShortenResult>()
|
val sharedShortened = HashMap<UserKey, StatusShortenResult>()
|
||||||
for (i in 0 until pending.length) {
|
for (i in 0 until pending.length) {
|
||||||
|
@ -230,8 +228,8 @@ class UpdateStatusTask(
|
||||||
val text = pending.overrideTexts[i]
|
val text = pending.overrideTexts[i]
|
||||||
val textLimit = account.textLimit
|
val textLimit = account.textLimit
|
||||||
val ignoreMentions = account.type == AccountType.TWITTER
|
val ignoreMentions = account.type == AccountType.TWITTER
|
||||||
if (textLimit >= 0 && validator.getTweetLength(text, ignoreMentions,
|
if (textLimit >= 0 && StatusTextValidator.calculateLength(account.type, account.key,
|
||||||
update.in_reply_to_status, account.key) <= textLimit) {
|
update.summary, text, ignoreMentions, update.in_reply_to_status) <= textLimit) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
shortener.waitForService()
|
shortener.waitForService()
|
||||||
|
|
|
@ -34,7 +34,6 @@ import com.google.android.exoplayer2.upstream.DataSource
|
||||||
import com.squareup.otto.Bus
|
import com.squareup.otto.Bus
|
||||||
import com.squareup.otto.ThreadEnforcer
|
import com.squareup.otto.ThreadEnforcer
|
||||||
import com.twitter.Extractor
|
import com.twitter.Extractor
|
||||||
import com.twitter.Validator
|
|
||||||
import dagger.Module
|
import dagger.Module
|
||||||
import dagger.Provides
|
import dagger.Provides
|
||||||
import okhttp3.Cache
|
import okhttp3.Cache
|
||||||
|
@ -199,12 +198,6 @@ class ApplicationModule(private val context: Context) {
|
||||||
return TwidereMediaDownloader(context, client, thumbor)
|
return TwidereMediaDownloader(context, client, thumbor)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Singleton
|
|
||||||
fun validator(): Validator {
|
|
||||||
return Validator()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
fun extractor(): Extractor {
|
fun extractor(): Extractor {
|
||||||
|
|
|
@ -21,7 +21,6 @@ package org.mariotaku.twidere.util.dagger
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import com.twitter.Validator
|
|
||||||
import okhttp3.Cache
|
import okhttp3.Cache
|
||||||
import okhttp3.ConnectionPool
|
import okhttp3.ConnectionPool
|
||||||
import okhttp3.Dns
|
import okhttp3.Dns
|
||||||
|
@ -55,9 +54,6 @@ class DependencyHolder internal constructor(context: Context) {
|
||||||
lateinit var dns: Dns
|
lateinit var dns: Dns
|
||||||
internal set
|
internal set
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var validator: Validator
|
|
||||||
internal set
|
|
||||||
@Inject
|
|
||||||
lateinit var preferences: SharedPreferences
|
lateinit var preferences: SharedPreferences
|
||||||
internal set
|
internal set
|
||||||
@Inject
|
@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