Misskeyタンスの画像を内蔵メディアビューアで開けないバグの修正。Misskeyの空欄CWがCWにならなかった問題の修正。
This commit is contained in:
parent
1748c5f37d
commit
ad620c95a6
|
@ -87,7 +87,6 @@ class ActMain : AppCompatActivity()
|
|||
internal val reUrlHashTag =
|
||||
Pattern.compile("""\Ahttps://([^/]+)/tags/([^?#・\s\-+.,:;/]+)(?:\z|[?#])""")
|
||||
|
||||
|
||||
var boostButtonSize = 0
|
||||
var timeline_font : Typeface = Typeface.DEFAULT
|
||||
var timeline_font_bold : Typeface = Typeface.DEFAULT_BOLD
|
||||
|
@ -829,7 +828,7 @@ class ActMain : AppCompatActivity()
|
|||
posted_redraft_id = EntityId.from(data, ActPost.EXTRA_POSTED_REDRAFT_ID)
|
||||
|
||||
}
|
||||
|
||||
|
||||
REQUEST_CODE_COLUMN_COLOR -> if(data != null) {
|
||||
app_state.saveColumnList()
|
||||
val idx = data.getIntExtra(ActColumnCustomize.EXTRA_COLUMN_INDEX, 0)
|
||||
|
@ -1340,7 +1339,6 @@ class ActMain : AppCompatActivity()
|
|||
}
|
||||
val column_w_min = (0.5f + column_w_min_dp * density).toInt()
|
||||
|
||||
|
||||
val sw = dm.widthPixels
|
||||
|
||||
if(Pref.bpDisableTabletMode(pref) || sw < column_w_min * 2) {
|
||||
|
@ -1497,7 +1495,8 @@ class ActMain : AppCompatActivity()
|
|||
var slide_ratio = 0f
|
||||
if(vr.first <= vr.last) {
|
||||
val child = env.tablet_layout_manager.findViewByPosition(vr.first)
|
||||
slide_ratio = clipRange(0f,1f,abs((child?.left ?: 0) / nColumnWidth.toFloat()))
|
||||
slide_ratio =
|
||||
clipRange(0f, 1f, abs((child?.left ?: 0) / nColumnWidth.toFloat()))
|
||||
}
|
||||
|
||||
llColumnStrip.setVisibleRange(vr.first, vr.last, slide_ratio)
|
||||
|
@ -1646,7 +1645,7 @@ class ActMain : AppCompatActivity()
|
|||
}
|
||||
|
||||
// queryIntentActivities に渡すURLは実在しないホストのものにする
|
||||
val intent = Intent(Intent.ACTION_VIEW, "https://dummy.subwaytooter.club/".toUri() )
|
||||
val intent = Intent(Intent.ACTION_VIEW, "https://dummy.subwaytooter.club/".toUri())
|
||||
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
val resolveInfoList = packageManager.queryIntentActivities(intent, query_flag)
|
||||
if(resolveInfoList.isEmpty()) {
|
||||
|
@ -2504,7 +2503,7 @@ class ActMain : AppCompatActivity()
|
|||
var column_w_min = (0.5f + column_w_min_dp * density).toInt()
|
||||
if(column_w_min < 1) column_w_min = 1
|
||||
|
||||
var column_w :Int
|
||||
var column_w : Int
|
||||
|
||||
if(screen_width < column_w_min * 2) {
|
||||
// 最小幅で2つ表示できないのなら1カラム表示
|
||||
|
@ -2533,13 +2532,13 @@ class ActMain : AppCompatActivity()
|
|||
}
|
||||
|
||||
nColumnWidth = column_w // dividerの幅を含む
|
||||
|
||||
|
||||
val divider_width = (0.5f + 1f * density).toInt()
|
||||
column_w -= divider_width
|
||||
env.tablet_pager_adapter.columnWidth = column_w // dividerの幅を含まない
|
||||
// env.tablet_snap_helper.columnWidth = column_w //使われていない
|
||||
|
||||
resizeAutoCW(column_w)// dividerの幅を含まない
|
||||
|
||||
resizeAutoCW(column_w) // dividerの幅を含まない
|
||||
|
||||
// 並べ直す
|
||||
env.tablet_pager_adapter.notifyDataSetChanged()
|
||||
|
@ -2803,9 +2802,8 @@ class ActMain : AppCompatActivity()
|
|||
auto_cw.originalLineCount = l.lineCount
|
||||
val line_count = auto_cw.originalLineCount
|
||||
|
||||
if(nAutoCwLines > 0
|
||||
&& line_count > nAutoCwLines
|
||||
&& status.spoiler_text?.isEmpty() != false
|
||||
if( (nAutoCwLines > 0 && line_count > nAutoCwLines)
|
||||
&& status.spoiler_text.isEmpty()
|
||||
&& (status.mentions?.size ?: 0) <= nAutoCwLines
|
||||
) {
|
||||
val sb = SpannableStringBuilder()
|
||||
|
|
|
@ -42,6 +42,7 @@ import jp.juggler.subwaytooter.dialog.ActionsDialog
|
|||
import jp.juggler.subwaytooter.util.*
|
||||
import jp.juggler.subwaytooter.view.PinchBitmapView
|
||||
import okhttp3.Request
|
||||
import org.json.JSONObject
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.IOException
|
||||
import java.util.*
|
||||
|
@ -66,7 +67,12 @@ class ActMediaViewer : AppCompatActivity(), View.OnClickListener {
|
|||
list?.encodeJson()?.toString() ?: "[]"
|
||||
|
||||
internal fun decodeMediaList(serviceType : ServiceType, src : String?) =
|
||||
parseList(::TootAttachment, serviceType, src?.toJsonArray())
|
||||
ArrayList<TootAttachment>().apply {
|
||||
src?.toJsonArray()?.forEach {
|
||||
if(it !is JSONObject) return@forEach
|
||||
add(TootAttachment.decodeJson(it))
|
||||
}
|
||||
}
|
||||
|
||||
fun open(
|
||||
activity : ActMain,
|
||||
|
|
|
@ -9,7 +9,6 @@ import android.content.ContentValues
|
|||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.content.pm.PackageManager
|
||||
import android.database.Cursor
|
||||
import android.graphics.Bitmap
|
||||
import android.net.Uri
|
||||
import android.os.AsyncTask
|
||||
|
@ -309,7 +308,7 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
|
|||
|
||||
private val link_click_listener : MyClickableSpanClickCallback = { _, span ->
|
||||
// ブラウザで開く
|
||||
span.url.mayUri()?.let{
|
||||
span.url.mayUri()?.let {
|
||||
try {
|
||||
val intent = Intent(Intent.ACTION_VIEW, it)
|
||||
startActivity(intent)
|
||||
|
@ -424,7 +423,7 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
|
|||
mushroom_end = savedInstanceState.getInt(STATE_MUSHROOM_END, 0)
|
||||
redraft_status_id = EntityId.from(savedInstanceState, STATE_REDRAFT_STATUS_ID)
|
||||
|
||||
savedInstanceState.getString(STATE_URI_CAMERA_IMAGE).mayUri()?.let{
|
||||
savedInstanceState.getString(STATE_URI_CAMERA_IMAGE).mayUri()?.let {
|
||||
uriCameraImage = it
|
||||
}
|
||||
|
||||
|
@ -516,13 +515,13 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
|
|||
Intent.ACTION_VIEW -> {
|
||||
val uri = sent_intent.data
|
||||
val type = sent_intent.type
|
||||
if(uri != null) addAttachment(uri,type)
|
||||
if(uri != null) addAttachment(uri, type)
|
||||
}
|
||||
|
||||
Intent.ACTION_SEND -> {
|
||||
val uri = sent_intent.getParcelableExtra<Uri>(Intent.EXTRA_STREAM)
|
||||
val type = sent_intent.type
|
||||
if(uri != null) addAttachment(uri,type)
|
||||
if(uri != null) addAttachment(uri, type)
|
||||
}
|
||||
|
||||
Intent.ACTION_SEND_MULTIPLE -> {
|
||||
|
@ -558,7 +557,7 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
|
|||
} else {
|
||||
|
||||
// CW をリプライ元に合わせる
|
||||
if(reply_status.spoiler_text?.isNotEmpty() == true) {
|
||||
if(reply_status.spoiler_text.isNotEmpty()) {
|
||||
cbContentWarning.isChecked = true
|
||||
etContentWarning.setText(reply_status.spoiler_text)
|
||||
}
|
||||
|
@ -1235,7 +1234,7 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
|
|||
llAttachment.visibility = View.GONE
|
||||
} else {
|
||||
llAttachment.visibility = View.VISIBLE
|
||||
ivMedia.forEachIndexed { i,v ->showAttachment_sub(v, i) }
|
||||
ivMedia.forEachIndexed { i, v -> showAttachment_sub(v, i) }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1599,25 +1598,23 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
|
|||
}
|
||||
}
|
||||
|
||||
class AttachmentRequest(
|
||||
private class AttachmentRequest(
|
||||
val account : SavedAccount,
|
||||
val pa : PostAttachment,
|
||||
val uri : Uri,
|
||||
val mimeType : String,
|
||||
|
||||
val onUploadEnd : () -> Unit
|
||||
)
|
||||
|
||||
val attachment_queue = ConcurrentLinkedQueue<AttachmentRequest>()
|
||||
private var attachment_worker: AttachmentWorker? = null
|
||||
var lastAttachmentAdd : Long = 0L
|
||||
var lastAttachmentComplete : Long = 0L
|
||||
private val attachment_queue = ConcurrentLinkedQueue<AttachmentRequest>()
|
||||
private var attachment_worker : AttachmentWorker? = null
|
||||
private var lastAttachmentAdd : Long = 0L
|
||||
private var lastAttachmentComplete : Long = 0L
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
private fun addAttachment(
|
||||
uri : Uri,
|
||||
mimeTypeArg : String? = null,
|
||||
time :Long =0,
|
||||
onUploadEnd : () -> Unit = {}
|
||||
) {
|
||||
|
||||
|
@ -1645,10 +1642,10 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
|
|||
|
||||
app_state.attachment_list = this.attachment_list
|
||||
|
||||
val pa = PostAttachment( this)
|
||||
val pa = PostAttachment(this)
|
||||
attachment_list.add(pa)
|
||||
showMediaAttachment()
|
||||
|
||||
|
||||
// アップロード開始トースト(連発しない)
|
||||
val now = System.currentTimeMillis()
|
||||
if(now - lastAttachmentAdd >= 5000L) {
|
||||
|
@ -1659,18 +1656,18 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
|
|||
// マストドンは添付メディアをID順に表示するため
|
||||
// 画像が複数ある場合は一つずつ処理する必要がある
|
||||
// 投稿画面ごとに1スレッドだけ作成してバックグラウンド処理を行う
|
||||
attachment_queue.add(AttachmentRequest(account,pa,uri,mime_type,onUploadEnd))
|
||||
attachment_queue.add(AttachmentRequest(account, pa, uri, mime_type, onUploadEnd))
|
||||
val oldWorker = attachment_worker
|
||||
if( oldWorker == null || !oldWorker.isAlive || oldWorker.isInterrupted ){
|
||||
if(oldWorker == null || ! oldWorker.isAlive || oldWorker.isInterrupted) {
|
||||
oldWorker?.cancel()
|
||||
attachment_worker = AttachmentWorker().apply{ start() }
|
||||
}else{
|
||||
attachment_worker = AttachmentWorker().apply { start() }
|
||||
} else {
|
||||
oldWorker.notifyEx()
|
||||
}
|
||||
}
|
||||
|
||||
inner class AttachmentWorker : WorkerBase(){
|
||||
|
||||
inner class AttachmentWorker : WorkerBase() {
|
||||
|
||||
private val isCancelled = AtomicBoolean(false)
|
||||
override fun cancel() {
|
||||
isCancelled.set(true)
|
||||
|
@ -1690,24 +1687,24 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
|
|||
item.handleResult(result)
|
||||
}
|
||||
}
|
||||
}catch(ex:Throwable){
|
||||
} catch(ex : Throwable) {
|
||||
log.trace(ex)
|
||||
log.e(ex,"AttachmentWorker")
|
||||
log.e(ex, "AttachmentWorker")
|
||||
}
|
||||
}
|
||||
|
||||
private fun AttachmentRequest.upload():TootApiResult?{
|
||||
private fun AttachmentRequest.upload() : TootApiResult? {
|
||||
|
||||
if(mimeType.isEmpty()) {
|
||||
return TootApiResult("mime_type is empty.")
|
||||
}
|
||||
|
||||
try {
|
||||
val client = TootApiClient(this@ActPost,callback = object:TootApiCallback{
|
||||
val client = TootApiClient(this@ActPost, callback = object : TootApiCallback {
|
||||
override val isApiCancelled : Boolean
|
||||
get() = isCancelled.get()
|
||||
})
|
||||
|
||||
|
||||
client.account = account
|
||||
|
||||
val opener = createOpener(uri, mimeType)
|
||||
|
@ -1840,8 +1837,8 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
fun AttachmentRequest.handleResult(result : TootApiResult?) {
|
||||
|
||||
private fun AttachmentRequest.handleResult(result : TootApiResult?) {
|
||||
|
||||
if(pa.attachment == null) {
|
||||
pa.status = PostAttachment.STATUS_UPLOAD_FAILED
|
||||
|
@ -1874,7 +1871,7 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
|
|||
val a = pa.attachment
|
||||
if(a != null) {
|
||||
// アップロード完了
|
||||
|
||||
|
||||
val now = System.currentTimeMillis()
|
||||
if(now - lastAttachmentComplete >= 5000L) {
|
||||
showToast(this@ActPost, false, R.string.attachment_uploaded)
|
||||
|
|
|
@ -188,7 +188,10 @@ class TootAttachment : TootAttachmentLike {
|
|||
|
||||
}
|
||||
|
||||
constructor(src : JSONObject, decode : Boolean) {
|
||||
constructor(
|
||||
src : JSONObject,
|
||||
decode : Boolean // dummy parameter to use this ctor.
|
||||
) {
|
||||
|
||||
id = if( src.optBoolean(KEY_IS_STRING_ID) ) {
|
||||
EntityId.mayDefault(src.parseString(KEY_ID))
|
||||
|
|
|
@ -85,7 +85,12 @@ class TootStatus(parser : TootParser, src : JSONObject) : TimelineItem() {
|
|||
private val language : String?
|
||||
|
||||
//If not empty, warning text that should be displayed before the actual content
|
||||
var spoiler_text : String?
|
||||
// アプリ内部では空文字列はCWなしとして扱う
|
||||
// マストドンは「null:CWなし」「空じゃない文字列:CWあり」の2種類
|
||||
// Pleromaは「空文字列:CWなし」「空じゃない文字列:CWあり」の2種類
|
||||
// Misskeyは「CWなし」「空欄CW」「CWあり」の3通り。空欄CWはパース時に書き換えてしまう
|
||||
// Misskeyで投稿が削除された時に変更されるため、val変数にできない
|
||||
var spoiler_text : String =""
|
||||
var decoded_spoiler_text : Spannable
|
||||
|
||||
// Body of the status; this will contain HTML (remote HTML already sanitized)
|
||||
|
@ -114,7 +119,7 @@ class TootStatus(parser : TootParser, src : JSONObject) : TimelineItem() {
|
|||
//One of: public, unlisted, private, direct
|
||||
val visibility : TootVisibility
|
||||
|
||||
val misskeyVisibleIds : ArrayList<String>?
|
||||
private val misskeyVisibleIds : ArrayList<String>?
|
||||
|
||||
// An array of Attachments
|
||||
val media_attachments : ArrayList<TootAttachmentLike>?
|
||||
|
@ -144,10 +149,10 @@ class TootStatus(parser : TootParser, src : JSONObject) : TimelineItem() {
|
|||
|
||||
val serviceType : ServiceType
|
||||
|
||||
val deletedAt : String?
|
||||
private val deletedAt : String?
|
||||
val time_deleted_at : Long
|
||||
|
||||
var localOnly : Boolean = false
|
||||
private var localOnly : Boolean = false
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
// 以下はentityから取得したデータではなく、アプリ内部で使う
|
||||
|
@ -202,7 +207,8 @@ class TootStatus(parser : TootParser, src : JSONObject) : TimelineItem() {
|
|||
// お気に入りカラムなどではパース直後に変更することがある
|
||||
|
||||
// 絵文字マップはすぐ後で使うので、最初の方で読んでおく
|
||||
this.custom_emojis = parseMapOrNull(CustomEmoji.decodeMisskey, src.optJSONArray("emojis"), log)
|
||||
this.custom_emojis =
|
||||
parseMapOrNull(CustomEmoji.decodeMisskey, src.optJSONArray("emojis"), log)
|
||||
this.profile_emojis = null
|
||||
|
||||
val who = parser.account(src.optJSONObject("user"))
|
||||
|
@ -218,8 +224,10 @@ class TootStatus(parser : TootParser, src : JSONObject) : TimelineItem() {
|
|||
this.favourited = src.optBoolean("isFavorited")
|
||||
|
||||
this.localOnly = src.optBoolean("localOnly")
|
||||
this.visibility = TootVisibility.parseMisskey(src.parseString("visibility"),localOnly) ?:
|
||||
TootVisibility.Public
|
||||
this.visibility = TootVisibility.parseMisskey(
|
||||
src.parseString("visibility"),
|
||||
localOnly
|
||||
) ?: TootVisibility.Public
|
||||
|
||||
this.misskeyVisibleIds = parseStringArray(src.optJSONArray("visibleUserIds"))
|
||||
|
||||
|
@ -281,12 +289,13 @@ class TootStatus(parser : TootParser, src : JSONObject) : TimelineItem() {
|
|||
this
|
||||
) ?: EMPTY_SPANNABLE
|
||||
|
||||
// spoiler_text
|
||||
this.spoiler_text = reWhitespace
|
||||
.matcher(src.parseString("cw") ?: "")
|
||||
.replaceAll(" ")
|
||||
.sanitizeBDI()
|
||||
|
||||
val sv = src.parseString("cw")?.cleanCW()
|
||||
this.spoiler_text = when{
|
||||
sv == null -> "" // CWなし
|
||||
sv.isBlank() -> parser.context.getString(R.string.blank_cw)
|
||||
else-> sv
|
||||
}
|
||||
|
||||
options = DecodeOptions(
|
||||
parser.context,
|
||||
emojiMapCustom = custom_emojis,
|
||||
|
@ -316,14 +325,14 @@ class TootStatus(parser : TootParser, src : JSONObject) : TimelineItem() {
|
|||
this.deletedAt = src.parseString("deletedAt")
|
||||
this.time_deleted_at = parseTime(deletedAt)
|
||||
|
||||
if( card == null) {
|
||||
if(card == null) {
|
||||
|
||||
if(reblog != null && hasAnyContent() ) {
|
||||
if(reblog != null && hasAnyContent()) {
|
||||
// 引用Renoteにプレビューカードをでっちあげる
|
||||
card = TootCard(parser, reblog)
|
||||
} else if(reply != null ) {
|
||||
} else if(reply != null) {
|
||||
// 返信にプレビューカードをでっちあげる
|
||||
card = TootCard(parser, reply!! )
|
||||
card = TootCard(parser, reply !!)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -446,11 +455,7 @@ class TootStatus(parser : TootParser, src : JSONObject) : TimelineItem() {
|
|||
this.highlight_sound = options.highlight_sound
|
||||
}
|
||||
|
||||
// spoiler_text
|
||||
this.spoiler_text = reWhitespace
|
||||
.matcher(src.parseString("spoiler_text") ?: "")
|
||||
.replaceAll(" ")
|
||||
.sanitizeBDI()
|
||||
this.spoiler_text = (src.parseString("spoiler_text") ?: "").cleanCW()
|
||||
|
||||
options = DecodeOptions(
|
||||
parser.context,
|
||||
|
@ -576,16 +581,16 @@ class TootStatus(parser : TootParser, src : JSONObject) : TimelineItem() {
|
|||
reblog == null -> true // reblog以外はオリジナルコンテンツがあると見なす
|
||||
serviceType != ServiceType.MISSKEY -> false // misskey以外のreblogはコンテンツがないと見なす
|
||||
content?.isNotEmpty() == true
|
||||
|| spoiler_text?.isNotEmpty() == true
|
||||
|| spoiler_text.isNotEmpty()
|
||||
|| media_attachments?.isNotEmpty() == true
|
||||
|| enquete != null -> true
|
||||
else -> false
|
||||
}
|
||||
|
||||
// return true if updated
|
||||
fun increaseReaction(reaction : String?, byMe : Boolean,caller:String) : Boolean {
|
||||
reaction ?: return false
|
||||
|
||||
// return true if updated
|
||||
fun increaseReaction(reaction : String?, byMe : Boolean, caller : String) : Boolean {
|
||||
reaction ?: return false
|
||||
|
||||
MisskeyReaction.shortcodeMap[reaction] ?: return false
|
||||
|
||||
synchronized(this) {
|
||||
|
@ -638,8 +643,6 @@ class TootStatus(parser : TootParser, src : JSONObject) : TimelineItem() {
|
|||
@Volatile
|
||||
internal var muted_word : WordTrieTree? = null
|
||||
|
||||
private val reWhitespace = Pattern.compile("[\\s\\t\\x0d\\x0a]+")
|
||||
|
||||
val EMPTY_SPANNABLE = SpannableString("")
|
||||
|
||||
// OStatus
|
||||
|
@ -660,12 +663,12 @@ class TootStatus(parser : TootParser, src : JSONObject) : TimelineItem() {
|
|||
|
||||
// 公開ステータスページのURL マストドン
|
||||
@Suppress("HasPlatformType")
|
||||
val reStatusPage = Pattern.compile("""\Ahttps://([^/]+)/@([A-Za-z0-9_]+)/(\d+)(?:\z|[?#])""")
|
||||
val reStatusPage =
|
||||
Pattern.compile("""\Ahttps://([^/]+)/@([A-Za-z0-9_]+)/(\d+)(?:\z|[?#])""")
|
||||
|
||||
// 公開ステータスページのURL Misskey
|
||||
@Suppress("HasPlatformType")
|
||||
val reStatusPageMisskey = Pattern.compile("""\Ahttps://([^/]+)/notes/([0-9a-f]{24})\b""")
|
||||
|
||||
val reStatusPageMisskey = Pattern.compile("""\Ahttps://([^/]+)/notes/([0-9a-f]{24})\b""", Pattern.CASE_INSENSITIVE)
|
||||
|
||||
const val INVALID_ID = - 1L
|
||||
|
||||
|
@ -857,14 +860,11 @@ class TootStatus(parser : TootParser, src : JSONObject) : TimelineItem() {
|
|||
return if(host != null && host.isNotEmpty() && host != "?") host else null
|
||||
}
|
||||
|
||||
private val reMisskeyNoteUrl =
|
||||
Pattern.compile("""https://([^/]+)/notes/([0-9A-F]+)""", Pattern.CASE_INSENSITIVE)
|
||||
|
||||
fun readMisskeyNoteId(url : String) : EntityId? {
|
||||
private fun readMisskeyNoteId(url : String) : EntityId? {
|
||||
// https://misskey.xyz/notes/5b802367744b650030a13640
|
||||
val m = reMisskeyNoteUrl.matcher(url)
|
||||
if(m.find()) return EntityIdString(m.group(2))
|
||||
return null
|
||||
val m = reStatusPageMisskey.matcher(url)
|
||||
return if(!m.find()) null else EntityIdString(m.group(2))
|
||||
}
|
||||
|
||||
fun validStatusId(src : EntityId?) : EntityId? {
|
||||
|
@ -875,6 +875,10 @@ class TootStatus(parser : TootParser, src : JSONObject) : TimelineItem() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun String.cleanCW() =
|
||||
CharacterGroup.reWhitespace.matcher(this).replaceAll(" ").sanitizeBDI()
|
||||
/* 空欄かどうかがCW判定条件に影響するので、trimしてはいけない */
|
||||
|
||||
// 投稿元タンスでのステータスIDを調べる
|
||||
fun findStatusIdFromUri(
|
||||
uri : String?,
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package jp.juggler.subwaytooter.util
|
||||
|
||||
import android.util.SparseBooleanArray
|
||||
import android.util.SparseIntArray
|
||||
import java.util.regex.Pattern
|
||||
|
||||
class CharacterGroup {
|
||||
|
||||
|
@ -20,31 +22,44 @@ class CharacterGroup {
|
|||
return String(tmp, 0, 1)
|
||||
}
|
||||
|
||||
// 空白とみなす文字なら真
|
||||
fun isWhitespace(cp : Int) : Boolean {
|
||||
when(cp) {
|
||||
private val mapWhitespace = SparseBooleanArray().apply {
|
||||
intArrayOf(
|
||||
0x0009 // HORIZONTAL TABULATION
|
||||
, 0x000A // LINE FEED
|
||||
, 0x000B // VERTICAL TABULATION
|
||||
, 0x000C // FORM FEED
|
||||
, 0x000D // CARRIAGE RETURN
|
||||
, 0x001C // FILE SEPARATOR
|
||||
, 0x001D // GROUP SEPARATOR
|
||||
, 0x001E // RECORD SEPARATOR
|
||||
, 0x001F // UNIT SEPARATOR
|
||||
, 0x0020, 0x0085 // next line (latin-1)
|
||||
, 0x00A0 //非区切りスペース
|
||||
, 0x1680, 0x180E, 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006, 0x2007 //非区切りスペース
|
||||
, 0x2008, 0x2009, 0x200A, 0x200B, 0x200C, 0x200D, 0x2028 // line separator
|
||||
, 0x2029 // paragraph separator
|
||||
,
|
||||
|
||||
0x202F //非区切りスペース
|
||||
, 0x205F, 0x2060, 0x3000, 0x3164, 0xFEFF -> return true
|
||||
else -> return false // Character.isWhitespace( cp ); は不要っぽい
|
||||
, 0x000A // LINE FEED
|
||||
, 0x000B // VERTICAL TABULATION
|
||||
, 0x000C // FORM FEED
|
||||
, 0x000D // CARRIAGE RETURN
|
||||
, 0x001C // FILE SEPARATOR
|
||||
, 0x001D // GROUP SEPARATOR
|
||||
, 0x001E // RECORD SEPARATOR
|
||||
, 0x001F // UNIT SEPARATOR
|
||||
, 0x0020, 0x0085 // next line (latin-1)
|
||||
, 0x00A0 //非区切りスペース
|
||||
, 0x1680, 0x180E, 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006, 0x2007 //非区切りスペース
|
||||
, 0x2008, 0x2009, 0x200A, 0x200B, 0x200C, 0x200D, 0x2028 // line separator
|
||||
, 0x2029 // paragraph separator
|
||||
, 0x202F //非区切りスペース
|
||||
, 0x205F, 0x2060, 0x3000, 0x3164, 0xFEFF
|
||||
).forEach {
|
||||
put(it,true)
|
||||
}
|
||||
}
|
||||
|
||||
// 空白とみなす文字なら真
|
||||
fun isWhitespace(cp : Int) : Boolean = mapWhitespace.get(cp,false)
|
||||
|
||||
internal val reWhitespace :Pattern by lazy {
|
||||
val sb = StringBuilder()
|
||||
sb.append("[\\s\\t\\x0d\\x0a")
|
||||
for(i in 0 until mapWhitespace.size()){
|
||||
val k = mapWhitespace.keyAt(i)
|
||||
if( k > 0x20 ) sb.append(k.toChar())
|
||||
}
|
||||
sb.append("]+")
|
||||
Pattern.compile(sb.toString())
|
||||
}
|
||||
|
||||
|
||||
// 文字列のリストからグループIDを決定する
|
||||
private fun findGroupId(list : Array<String>) : Int {
|
||||
// グループのIDは、グループ中の文字(長さ1)のunicode値の最小
|
||||
|
|
|
@ -718,11 +718,11 @@ inline fun JSONArray.downForEachIndexed(block : (i : Int, v : Any?) -> Unit) {
|
|||
}
|
||||
}
|
||||
|
||||
fun JSONArray.toAnyList() : ArrayList<Any> {
|
||||
val dst_list = ArrayList<Any>(length())
|
||||
forEach { if(it != null) dst_list.add(it) }
|
||||
return dst_list
|
||||
}
|
||||
//fun JSONArray.toAnyList() : ArrayList<Any> {
|
||||
// val dst_list = ArrayList<Any>(length())
|
||||
// forEach { if(it != null) dst_list.add(it) }
|
||||
// return dst_list
|
||||
//}
|
||||
|
||||
fun JSONArray.toObjectList() : ArrayList<JSONObject> {
|
||||
val dst_list = ArrayList<JSONObject>(length())
|
||||
|
|
|
@ -811,5 +811,6 @@
|
|||
<string name="dont_show_preview_card">プレビューカードを表示しない</string>
|
||||
<string name="instance_does_not_support_push_api_pleroma">このインスタンスはプッシュ購読APIに対応していません。多分Pleromaです。</string>
|
||||
<string name="quote_renote">引用リノート… (Misskey)</string>
|
||||
<string name="blank_cw">(CW空欄)</string>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -829,5 +829,6 @@
|
|||
<string name="dont_show_preview_card">Don\'t show preview card</string>
|
||||
<string name="instance_does_not_support_push_api_pleroma">This instance does not support push subscription API. maybe it is Pleroma</string>
|
||||
<string name="quote_renote">Quoted renote… (Misskey)</string>
|
||||
<string name="blank_cw">(blank CW)</string>
|
||||
|
||||
</resources>
|
||||
|
|
Loading…
Reference in New Issue