- update translators list in ActAbout
- Android gradle plugin 4.2.0 - Gradle 6.9 - kotlin 1.5.0 - kotlinx.coroutines 1.4.3 - junit 4.13.2 - firebase-messaging:21.1.0 - androidx.annotation:1.2.0 - androidx.recyclerview:1.2.0 - exoplayer:2.13.3
This commit is contained in:
parent
5a3dcd8704
commit
e8222fa2d8
|
@ -3,6 +3,7 @@
|
||||||
package jp.juggler.apng
|
package jp.juggler.apng
|
||||||
|
|
||||||
import jp.juggler.apng.util.getUInt8
|
import jp.juggler.apng.util.getUInt8
|
||||||
|
import kotlin.math.min
|
||||||
|
|
||||||
class ApngPalette(
|
class ApngPalette(
|
||||||
src : ByteArray // repeat of R,G,B
|
src : ByteArray // repeat of R,G,B
|
||||||
|
@ -35,7 +36,7 @@ class ApngPalette(
|
||||||
// update alpha value from tRNS chunk data
|
// update alpha value from tRNS chunk data
|
||||||
fun parseTRNS(ba : ByteArray) {
|
fun parseTRNS(ba : ByteArray) {
|
||||||
hasAlpha = true
|
hasAlpha = true
|
||||||
for(i in 0 until Math.min(list.size, ba.size)) {
|
for(i in 0 until min(list.size, ba.size)) {
|
||||||
list[i] = (list[i] and 0xffffff) or (ba.getUInt8(i) shl 24)
|
list[i] = (list[i] and 0xffffff) or (ba.getUInt8(i) shl 24)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package jp.juggler.apng
|
package jp.juggler.apng
|
||||||
|
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
|
import java.lang.StringBuilder
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
|
|
||||||
// https://raw.githubusercontent.com/rtyley/animated-gif-lib-for-java/master/src/main/java/com/madgag/gif/fmsware/GifDecoder.java
|
// https://raw.githubusercontent.com/rtyley/animated-gif-lib-for-java/master/src/main/java/com/madgag/gif/fmsware/GifDecoder.java
|
||||||
|
@ -44,9 +45,11 @@ class GifDecoder(val callback : GifDecoderCallback) {
|
||||||
|
|
||||||
// Reads specified bytes and compose it to ascii string
|
// Reads specified bytes and compose it to ascii string
|
||||||
fun string(n : Int) : String {
|
fun string(n : Int) : String {
|
||||||
val ba = ByteArray(n)
|
return StringBuilder(n).apply{
|
||||||
array(ba)
|
ByteArray(n)
|
||||||
return ba.map { it.toChar() }.joinToString(separator = "")
|
.also{ array(it)}
|
||||||
|
.forEach { append( Char( it.toInt() and 255)) }
|
||||||
|
}.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reads next variable length block
|
// Reads next variable length block
|
||||||
|
@ -478,11 +481,11 @@ class GifDecoder(val callback : GifDecoderCallback) {
|
||||||
// application extension
|
// application extension
|
||||||
0xff -> {
|
0xff -> {
|
||||||
val block = reader.block()
|
val block = reader.block()
|
||||||
var app = ""
|
val app = StringBuilder(12)
|
||||||
for(i in 0 until 11) {
|
for(i in 0 until 11) {
|
||||||
app += block[i].toChar()
|
app.append( Char( block[i].toInt() and 255 ))
|
||||||
}
|
}
|
||||||
if(app == "NETSCAPE2.0") {
|
if(app.toString() == "NETSCAPE2.0") {
|
||||||
readNetscapeExt(reader)
|
readNetscapeExt(reader)
|
||||||
} else {
|
} else {
|
||||||
reader.skipBlock() // don't care
|
reader.skipBlock() // don't care
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,13 +1,14 @@
|
||||||
package jp.juggler.apng.util
|
package jp.juggler.apng.util
|
||||||
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import kotlin.math.min
|
||||||
|
|
||||||
internal class ByteSequenceQueue(private val bufferRecycler : (ByteSequence) -> Unit) {
|
internal class ByteSequenceQueue(private val bufferRecycler : (ByteSequence) -> Unit) {
|
||||||
|
|
||||||
private val list = LinkedList<ByteSequence>()
|
private val list = LinkedList<ByteSequence>()
|
||||||
|
|
||||||
val remain : Int
|
val remain : Int
|
||||||
get() = list.sumBy { it.length }
|
get() = list.sumOf { it.length }
|
||||||
|
|
||||||
fun add(range : ByteSequence) =list.add(range)
|
fun add(range : ByteSequence) =list.add(range)
|
||||||
|
|
||||||
|
@ -22,7 +23,7 @@ internal class ByteSequenceQueue(private val bufferRecycler : (ByteSequence) ->
|
||||||
bufferRecycler(item)
|
bufferRecycler(item)
|
||||||
list.removeFirst()
|
list.removeFirst()
|
||||||
} else {
|
} else {
|
||||||
val delta = Math.min(item.length, dstRemain)
|
val delta = min(item.length, dstRemain)
|
||||||
System.arraycopy(item.array, item.offset, dst, dstOffset, delta)
|
System.arraycopy(item.array, item.offset, dst, dstOffset, delta)
|
||||||
dstOffset += delta
|
dstOffset += delta
|
||||||
dstRemain -= delta
|
dstRemain -= delta
|
||||||
|
|
|
@ -11,6 +11,7 @@ import android.util.Log
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.util.ArrayList
|
import java.util.ArrayList
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
|
import kotlin.math.min
|
||||||
|
|
||||||
class ApngFrames private constructor(
|
class ApngFrames private constructor(
|
||||||
private val pixelSizeMax : Int = 0,
|
private val pixelSizeMax : Int = 0,
|
||||||
|
@ -126,7 +127,16 @@ class ApngFrames private constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("unused")
|
private val apngHeadKey = byteArrayOf(0x89.toByte(),0x50)
|
||||||
|
private val gifHeadKey = "GIF".toByteArray(Charsets.UTF_8)
|
||||||
|
|
||||||
|
private fun matchBytes(ba1:ByteArray,ba2:ByteArray,length:Int=min(ba1.size,ba2.size)):Boolean{
|
||||||
|
for( i in 0 until length){
|
||||||
|
if( ba1[i] != ba2[i] ) return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
fun parse(
|
fun parse(
|
||||||
pixelSizeMax : Int,
|
pixelSizeMax : Int,
|
||||||
debug : Boolean = false,
|
debug : Boolean = false,
|
||||||
|
@ -136,18 +146,11 @@ class ApngFrames private constructor(
|
||||||
val buf = ByteArray(8) { 0.toByte() }
|
val buf = ByteArray(8) { 0.toByte() }
|
||||||
opener()?.use { it.read(buf, 0, buf.size) }
|
opener()?.use { it.read(buf, 0, buf.size) }
|
||||||
|
|
||||||
if(buf.size >= 8
|
if(buf.size >= 8 && matchBytes(buf, apngHeadKey) ) {
|
||||||
&& (buf[0].toInt() and 0xff) == 0x89
|
|
||||||
&& (buf[1].toInt() and 0xff) == 0x50
|
|
||||||
) {
|
|
||||||
return opener()?.use { parseApng(it, pixelSizeMax, debug) }
|
return opener()?.use { parseApng(it, pixelSizeMax, debug) }
|
||||||
}
|
}
|
||||||
|
|
||||||
if(buf.size >= 6
|
if(buf.size >= 6 && matchBytes(buf, gifHeadKey) ) {
|
||||||
&& buf[0].toChar() == 'G'
|
|
||||||
&& buf[1].toChar() == 'I'
|
|
||||||
&& buf[2].toChar() == 'F'
|
|
||||||
) {
|
|
||||||
return opener()?.use { parseGif(it, pixelSizeMax, debug) }
|
return opener()?.use { parseGif(it, pixelSizeMax, debug) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -97,6 +97,9 @@ dependencies {
|
||||||
|
|
||||||
|
|
||||||
implementation "androidx.appcompat:appcompat:$appcompat_version"
|
implementation "androidx.appcompat:appcompat:$appcompat_version"
|
||||||
|
//noinspection KtxExtensionAvailable
|
||||||
|
implementation "androidx.lifecycle:lifecycle-viewmodel:$lifecycle_version"
|
||||||
|
|
||||||
|
|
||||||
// DrawerLayout
|
// DrawerLayout
|
||||||
implementation "androidx.drawerlayout:drawerlayout:1.1.1"
|
implementation "androidx.drawerlayout:drawerlayout:1.1.1"
|
||||||
|
@ -113,12 +116,12 @@ dependencies {
|
||||||
implementation "androidx.browser:browser:1.3.0"
|
implementation "androidx.browser:browser:1.3.0"
|
||||||
|
|
||||||
// Recyclerview
|
// Recyclerview
|
||||||
implementation "androidx.recyclerview:recyclerview:1.1.0"
|
implementation "androidx.recyclerview:recyclerview:1.2.0"
|
||||||
|
|
||||||
kapt 'androidx.annotation:annotation:1.1.0'
|
kapt 'androidx.annotation:annotation:1.2.0'
|
||||||
|
|
||||||
// https://firebase.google.com/support/release-notes/android
|
// https://firebase.google.com/support/release-notes/android
|
||||||
implementation "com.google.firebase:firebase-messaging:21.0.1"
|
implementation "com.google.firebase:firebase-messaging:21.1.0"
|
||||||
|
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||||
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
|
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
|
||||||
|
@ -174,7 +177,7 @@ dependencies {
|
||||||
|
|
||||||
implementation 'com.astuetz:pagerslidingtabstrip:1.0.1'
|
implementation 'com.astuetz:pagerslidingtabstrip:1.0.1'
|
||||||
|
|
||||||
implementation 'com.google.android.exoplayer:exoplayer:2.12.0'
|
implementation 'com.google.android.exoplayer:exoplayer:2.13.3'
|
||||||
/*
|
/*
|
||||||
WARNING: [Processor] Library '…\exoplayer-ui-2.12.0.aar' contains references to both AndroidX and old support library. This seems like the library is partially migrated. Jetifier will try to rewrite the library anyway.
|
WARNING: [Processor] Library '…\exoplayer-ui-2.12.0.aar' contains references to both AndroidX and old support library. This seems like the library is partially migrated. Jetifier will try to rewrite the library anyway.
|
||||||
Example of androidX reference: 'androidx/core/app/NotificationCompat$Builder'
|
Example of androidX reference: 'androidx/core/app/NotificationCompat$Builder'
|
||||||
|
|
|
@ -48,15 +48,15 @@ class WordTrieTreeTest {
|
||||||
val tokenizer = CharacterGroup.Tokenizer().reset(strTest, 0, strTest.length)
|
val tokenizer = CharacterGroup.Tokenizer().reset(strTest, 0, strTest.length)
|
||||||
//
|
//
|
||||||
id = tokenizer.next()
|
id = tokenizer.next()
|
||||||
assertEquals('A'.toInt(), id)
|
assertEquals('A'.code, id)
|
||||||
assertEquals((whitespace_len + 1), tokenizer.offset) // offset は Aの次の位置になる
|
assertEquals((whitespace_len + 1), tokenizer.offset) // offset は Aの次の位置になる
|
||||||
//
|
//
|
||||||
id = tokenizer.next()
|
id = tokenizer.next()
|
||||||
assertEquals('B'.toInt(), id)
|
assertEquals('B'.code, id)
|
||||||
assertEquals((whitespace_len + 2).toLong(), tokenizer.offset.toLong())
|
assertEquals((whitespace_len + 2).toLong(), tokenizer.offset.toLong())
|
||||||
//
|
//
|
||||||
id = tokenizer.next()
|
id = tokenizer.next()
|
||||||
assertEquals('C'.toInt(), id)
|
assertEquals('C'.code, id)
|
||||||
assertEquals((whitespace_len + 3).toLong(), tokenizer.offset.toLong())
|
assertEquals((whitespace_len + 3).toLong(), tokenizer.offset.toLong())
|
||||||
//
|
//
|
||||||
id = tokenizer.next()
|
id = tokenizer.next()
|
||||||
|
@ -71,15 +71,15 @@ class WordTrieTreeTest {
|
||||||
//
|
//
|
||||||
id = tokenizer.next()
|
id = tokenizer.next()
|
||||||
assertEquals((whitespace_len + 1).toLong(), tokenizer.offset.toLong())
|
assertEquals((whitespace_len + 1).toLong(), tokenizer.offset.toLong())
|
||||||
assertEquals('A'.toInt(), id)
|
assertEquals('A'.code, id)
|
||||||
//
|
//
|
||||||
id = tokenizer.next()
|
id = tokenizer.next()
|
||||||
assertEquals((whitespace_len + 2).toLong(), tokenizer.offset.toLong())
|
assertEquals((whitespace_len + 2).toLong(), tokenizer.offset.toLong())
|
||||||
assertEquals('B'.toInt(), id)
|
assertEquals('B'.code, id)
|
||||||
//
|
//
|
||||||
id = tokenizer.next()
|
id = tokenizer.next()
|
||||||
assertEquals((whitespace_len + 3).toLong(), tokenizer.offset.toLong())
|
assertEquals((whitespace_len + 3).toLong(), tokenizer.offset.toLong())
|
||||||
assertEquals('C'.toInt(), id)
|
assertEquals('C'.code, id)
|
||||||
//
|
//
|
||||||
id = tokenizer.next()
|
id = tokenizer.next()
|
||||||
assertEquals((whitespace_len + 3).toLong(), tokenizer.offset.toLong())
|
assertEquals((whitespace_len + 3).toLong(), tokenizer.offset.toLong())
|
||||||
|
@ -93,15 +93,15 @@ class WordTrieTreeTest {
|
||||||
//
|
//
|
||||||
id = tokenizer.next()
|
id = tokenizer.next()
|
||||||
assertEquals((whitespace_len + 1).toLong(), tokenizer.offset.toLong())
|
assertEquals((whitespace_len + 1).toLong(), tokenizer.offset.toLong())
|
||||||
assertEquals('A'.toInt(), id)
|
assertEquals('A'.code, id)
|
||||||
//
|
//
|
||||||
id = tokenizer.next()
|
id = tokenizer.next()
|
||||||
assertEquals((whitespace_len + 2).toLong(), tokenizer.offset.toLong())
|
assertEquals((whitespace_len + 2).toLong(), tokenizer.offset.toLong())
|
||||||
assertEquals('B'.toInt(), id)
|
assertEquals('B'.code, id)
|
||||||
//
|
//
|
||||||
id = tokenizer.next()
|
id = tokenizer.next()
|
||||||
assertEquals((whitespace_len + 3).toLong(), tokenizer.offset.toLong())
|
assertEquals((whitespace_len + 3).toLong(), tokenizer.offset.toLong())
|
||||||
assertEquals('C'.toInt(), id)
|
assertEquals('C'.code, id)
|
||||||
//
|
//
|
||||||
id = tokenizer.next()
|
id = tokenizer.next()
|
||||||
assertEquals((whitespace_len + 3).toLong(), tokenizer.offset.toLong())
|
assertEquals((whitespace_len + 3).toLong(), tokenizer.offset.toLong())
|
||||||
|
@ -115,15 +115,15 @@ class WordTrieTreeTest {
|
||||||
//
|
//
|
||||||
id = tokenizer.next()
|
id = tokenizer.next()
|
||||||
assertEquals((whitespace_len + 1).toLong(), tokenizer.offset.toLong())
|
assertEquals((whitespace_len + 1).toLong(), tokenizer.offset.toLong())
|
||||||
assertEquals('A'.toInt(), id)
|
assertEquals('A'.code, id)
|
||||||
//
|
//
|
||||||
id = tokenizer.next()
|
id = tokenizer.next()
|
||||||
assertEquals((whitespace_len + 2).toLong(), tokenizer.offset.toLong())
|
assertEquals((whitespace_len + 2).toLong(), tokenizer.offset.toLong())
|
||||||
assertEquals('B'.toInt(), id)
|
assertEquals('B'.code, id)
|
||||||
//
|
//
|
||||||
id = tokenizer.next()
|
id = tokenizer.next()
|
||||||
assertEquals((whitespace_len + 3).toLong(), tokenizer.offset.toLong())
|
assertEquals((whitespace_len + 3).toLong(), tokenizer.offset.toLong())
|
||||||
assertEquals('C'.toInt(), id)
|
assertEquals('C'.code, id)
|
||||||
//
|
//
|
||||||
id = tokenizer.next()
|
id = tokenizer.next()
|
||||||
assertEquals((whitespace_len + 3).toLong(), tokenizer.offset.toLong())
|
assertEquals((whitespace_len + 3).toLong(), tokenizer.offset.toLong())
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
<!-- CAMERAパーミッションをつけるとPlayストアにプライバシーポリシーを記載する必要がある -->
|
<!-- CAMERAパーミッションをつけるとPlayストアにプライバシーポリシーを記載する必要がある -->
|
||||||
|
|
|
@ -14,125 +14,142 @@ import jp.juggler.util.LogCategory
|
||||||
|
|
||||||
class ActAbout : AppCompatActivity() {
|
class ActAbout : AppCompatActivity() {
|
||||||
|
|
||||||
class Translators(
|
class Translators(
|
||||||
val name : String,
|
val name: String,
|
||||||
val acct : String?,
|
val acct: String?,
|
||||||
val lang : String
|
val lang: String
|
||||||
)
|
)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
val log = LogCategory("ActAbout")
|
val log = LogCategory("ActAbout")
|
||||||
|
|
||||||
const val EXTRA_SEARCH = "search"
|
const val EXTRA_SEARCH = "search"
|
||||||
|
|
||||||
const val developer_acct = "tateisu@mastodon.juggler.jp"
|
const val developer_acct = "tateisu@mastodon.juggler.jp"
|
||||||
const val official_acct = "SubwayTooter@mastodon.juggler.jp"
|
const val official_acct = "SubwayTooter@mastodon.juggler.jp"
|
||||||
|
|
||||||
const val url_release = "https://github.com/tateisu/SubwayTooter/releases"
|
const val url_release = "https://github.com/tateisu/SubwayTooter/releases"
|
||||||
|
|
||||||
const val url_weblate = "https://hosted.weblate.org/projects/subway-tooter/"
|
const val url_weblate = "https://hosted.weblate.org/projects/subway-tooter/"
|
||||||
|
|
||||||
// git log --pretty=format:"%an %s" |grep "Translated using Weblate"|sort|uniq
|
// git log --pretty=format:"%an %s" |grep "Translated using Weblate"|sort|uniq
|
||||||
val translators = arrayOf(
|
val translators = arrayOf(
|
||||||
Translators("Allan Nordhøy", null, "English & Norwegian Bokmål"),
|
Translators("Allan Nordhøy", null, "English, Norwegian Bokmål"),
|
||||||
Translators("ButterflyOfFire", "@ButterflyOfFire@mstdn.fr", "Arabic & French"),
|
Translators("ayiniho", null, "French"),
|
||||||
Translators("Ch", null, "Korean"),
|
Translators("ButterflyOfFire", "@ButterflyOfFire@mstdn.fr", "Arabic, French, Kabyle"),
|
||||||
Translators("Elizabeth Sherrock", null, "Chinese (Simplified)"),
|
Translators("Ch", null, "Korean"),
|
||||||
Translators("Gennady Archangorodsky", null, "Hebrew"),
|
Translators("chinnux", "@chinnux@neko.ci", "Chinese (Simplified)"),
|
||||||
Translators("inqbs Siina", null, "Korean"),
|
Translators("Dyxang", null, "Chinese (Simplified)"),
|
||||||
Translators("Jeong Arm", "@jarm@qdon.space", "Korean"),
|
Translators("Elizabeth Sherrock", null, "Chinese (Simplified)"),
|
||||||
Translators("Joan Pujolar", "@jpujolar@mastodont.cat", "Catalan"),
|
Translators("Gennady Archangorodsky", null, "Hebrew"),
|
||||||
Translators("Kai Zhang", "@bearzk@mastodon.social", "Chinese (Simplified)"),
|
Translators("inqbs Siina", null, "Korean"),
|
||||||
Translators("lptprjh", null, "Korean"),
|
Translators("J. Lavoie", null, "French, German"),
|
||||||
Translators("mynameismonkey", null, "Welsh"),
|
Translators("Jeong Arm", "@jarm@qdon.space", "Korean"),
|
||||||
Translators("Nathan", null, "French"),
|
Translators("Joan Pujolar", "@jpujolar@mastodont.cat", "Catalan"),
|
||||||
Translators("Owain Rhys Lewis", null, "Welsh"),
|
Translators("Kai Zhang", "@bearzk@mastodon.social", "Chinese (Simplified)"),
|
||||||
Translators("Swann Martinet", null, "French"),
|
Translators("koyu", null, "German"),
|
||||||
Translators("takubunn", null, "Chinese (Simplified)"),
|
Translators("Liaizon Wakest", null, "English"),
|
||||||
Translators("배태길", null, "Korea")
|
Translators("lingcas", null, "Chinese (Traditional)"),
|
||||||
)
|
Translators("Love Xu", null, "Chinese (Simplified)"),
|
||||||
}
|
Translators("lptprjh", null, "Korean"),
|
||||||
|
Translators("mv87", null, "German"),
|
||||||
override fun onCreate(savedInstanceState : Bundle?) {
|
Translators("mynameismonkey", null, "Welsh"),
|
||||||
super.onCreate(savedInstanceState)
|
Translators("Nathan", null, "French"),
|
||||||
App1.setActivityTheme(this)
|
Translators("Niek Visser", null, "Dutch"),
|
||||||
setContentView(R.layout.act_about)
|
Translators("Owain Rhys Lewis", null, "Welsh"),
|
||||||
App1.initEdgeToEdge(this)
|
Translators("Remi Rampin", null, "French"),
|
||||||
|
Translators("Sachin", null, "Kannada"),
|
||||||
Styler.fixHorizontalPadding(findViewById(R.id.svContent))
|
Translators("Swann Martinet", null, "French"),
|
||||||
|
Translators("takubunn", null, "Chinese (Simplified)"),
|
||||||
|
Translators("Whod", null, "Bulgarian"),
|
||||||
|
Translators("yucj", null, "Chinese (Traditional)"),
|
||||||
|
Translators("邓志诚", null, "Chinese (Simplified)"),
|
||||||
|
Translators("배태길", null, "Korea"),
|
||||||
|
|
||||||
|
|
||||||
try {
|
)
|
||||||
val pInfo = packageManager.getPackageInfo(packageName, 0)
|
}
|
||||||
val tv = findViewById<TextView>(R.id.tvVersion)
|
|
||||||
tv.text = getString(R.string.version_is, pInfo.versionName)
|
|
||||||
} catch(ex : PackageManager.NameNotFoundException) {
|
|
||||||
log.trace(ex, "getPackageInfo failed.")
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setButton(btnId : Int, caption : String, onClick : () -> Unit) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
val b : Button = findViewById(btnId)
|
super.onCreate(savedInstanceState)
|
||||||
b.text = caption
|
App1.setActivityTheme(this)
|
||||||
b.setOnClickListener { onClick() }
|
setContentView(R.layout.act_about)
|
||||||
}
|
App1.initEdgeToEdge(this)
|
||||||
|
|
||||||
fun searchAcct(acct : String) {
|
Styler.fixHorizontalPadding(findViewById(R.id.svContent))
|
||||||
setResult(Activity.RESULT_OK, Intent().apply { putExtra(EXTRA_SEARCH, acct) })
|
|
||||||
finish()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
setButton(
|
try {
|
||||||
R.id.btnDeveloper,
|
val pInfo = packageManager.getPackageInfo(packageName, 0)
|
||||||
getString(R.string.search_for, developer_acct)
|
val tv = findViewById<TextView>(R.id.tvVersion)
|
||||||
) { searchAcct(developer_acct) }
|
tv.text = getString(R.string.version_is, pInfo.versionName)
|
||||||
|
} catch (ex: PackageManager.NameNotFoundException) {
|
||||||
|
log.trace(ex, "getPackageInfo failed.")
|
||||||
|
}
|
||||||
|
|
||||||
setButton(
|
fun setButton(btnId: Int, caption: String, onClick: () -> Unit) {
|
||||||
R.id.btnOfficialAccount,
|
val b: Button = findViewById(btnId)
|
||||||
getString(R.string.search_for, official_acct)
|
b.text = caption
|
||||||
) { searchAcct(official_acct) }
|
b.setOnClickListener { onClick() }
|
||||||
|
}
|
||||||
|
|
||||||
setButton(R.id.btnReleaseNote, url_release)
|
fun searchAcct(acct: String) {
|
||||||
{ openBrowser(url_release) }
|
setResult(Activity.RESULT_OK, Intent().apply { putExtra(EXTRA_SEARCH, acct) })
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
|
||||||
// setButton(R.id.btnIconDesign, url_futaba)
|
|
||||||
// { openUrl(url_futaba) }
|
|
||||||
|
|
||||||
setButton(R.id.btnWeblate, getString(R.string.please_help_translation))
|
setButton(
|
||||||
{ openBrowser(url_weblate) }
|
R.id.btnDeveloper,
|
||||||
|
getString(R.string.search_for, developer_acct)
|
||||||
|
) { searchAcct(developer_acct) }
|
||||||
|
|
||||||
val ll = findViewById<LinearLayout>(R.id.llContributors)
|
setButton(
|
||||||
val density = resources.displayMetrics.density
|
R.id.btnOfficialAccount,
|
||||||
val margin_top = (0.5f + density * 8).toInt()
|
getString(R.string.search_for, official_acct)
|
||||||
val padding = (0.5f + density * 8).toInt()
|
) { searchAcct(official_acct) }
|
||||||
|
|
||||||
for(who in translators) {
|
setButton(R.id.btnReleaseNote, url_release)
|
||||||
ll.addView(Button(this).apply {
|
{ openBrowser(url_release) }
|
||||||
//
|
|
||||||
layoutParams = LinearLayout.LayoutParams(
|
|
||||||
LinearLayout.LayoutParams.WRAP_CONTENT,
|
|
||||||
LinearLayout.LayoutParams.WRAP_CONTENT
|
|
||||||
).apply {
|
|
||||||
if(ll.childCount != 0) topMargin = margin_top
|
|
||||||
}
|
|
||||||
//
|
|
||||||
setBackgroundResource(R.drawable.btn_bg_transparent_round6dp)
|
|
||||||
setPadding(padding, padding, padding, padding)
|
|
||||||
isAllCaps = false
|
|
||||||
|
|
||||||
//
|
// setButton(R.id.btnIconDesign, url_futaba)
|
||||||
val acct = who.acct ?: "@?@?"
|
// { openUrl(url_futaba) }
|
||||||
text = "${who.name}\n$acct\n${getString(R.string.thanks_for, who.lang)}"
|
|
||||||
gravity = Gravity.START or Gravity.CENTER_VERTICAL
|
|
||||||
|
|
||||||
setOnClickListener {
|
setButton(R.id.btnWeblate, getString(R.string.please_help_translation))
|
||||||
val data = Intent()
|
{ openBrowser(url_weblate) }
|
||||||
data.putExtra(EXTRA_SEARCH, who.acct ?: who.name)
|
|
||||||
setResult(Activity.RESULT_OK, data)
|
val ll = findViewById<LinearLayout>(R.id.llContributors)
|
||||||
finish()
|
val density = resources.displayMetrics.density
|
||||||
}
|
val margin_top = (0.5f + density * 8).toInt()
|
||||||
})
|
val padding = (0.5f + density * 8).toInt()
|
||||||
}
|
|
||||||
}
|
for (who in translators) {
|
||||||
|
ll.addView(Button(this).apply {
|
||||||
|
//
|
||||||
|
layoutParams = LinearLayout.LayoutParams(
|
||||||
|
LinearLayout.LayoutParams.WRAP_CONTENT,
|
||||||
|
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||||
|
).apply {
|
||||||
|
if (ll.childCount != 0) topMargin = margin_top
|
||||||
|
}
|
||||||
|
//
|
||||||
|
setBackgroundResource(R.drawable.btn_bg_transparent_round6dp)
|
||||||
|
setPadding(padding, padding, padding, padding)
|
||||||
|
isAllCaps = false
|
||||||
|
|
||||||
|
//
|
||||||
|
val acct = who.acct ?: "@?@?"
|
||||||
|
text = "${who.name}\n$acct\n${getString(R.string.thanks_for, who.lang)}"
|
||||||
|
gravity = Gravity.START or Gravity.CENTER_VERTICAL
|
||||||
|
|
||||||
|
setOnClickListener {
|
||||||
|
val data = Intent()
|
||||||
|
data.putExtra(EXTRA_SEARCH, who.acct ?: who.name)
|
||||||
|
setResult(Activity.RESULT_OK, data)
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -338,7 +338,7 @@ class ActAccountSetting : AsyncActivity(), View.OnClickListener,
|
||||||
spResizeImage = findViewById(R.id.spResizeImage)
|
spResizeImage = findViewById(R.id.spResizeImage)
|
||||||
|
|
||||||
imageResizeItems = SavedAccount.resizeConfigList.map {
|
imageResizeItems = SavedAccount.resizeConfigList.map {
|
||||||
var caption = when (it.type) {
|
val caption = when (it.type) {
|
||||||
ResizeType.None -> getString(R.string.dont_resize)
|
ResizeType.None -> getString(R.string.dont_resize)
|
||||||
ResizeType.LongSide -> getString(R.string.long_side_pixel, it.size)
|
ResizeType.LongSide -> getString(R.string.long_side_pixel, it.size)
|
||||||
ResizeType.SquarePixel -> if (it.extraStringId != 0) {
|
ResizeType.SquarePixel -> if (it.extraStringId != 0) {
|
||||||
|
@ -436,7 +436,7 @@ class ActAccountSetting : AsyncActivity(), View.OnClickListener,
|
||||||
btnNotificationStyleEditReply.setOnClickListener(this)
|
btnNotificationStyleEditReply.setOnClickListener(this)
|
||||||
|
|
||||||
|
|
||||||
spResizeImage.setOnItemSelectedListener(this)
|
spResizeImage.onItemSelectedListener = this
|
||||||
|
|
||||||
|
|
||||||
btnNotificationStyleEditReply.vg(Pref.bpSeparateReplyNotificationGroup(pref))
|
btnNotificationStyleEditReply.vg(Pref.bpSeparateReplyNotificationGroup(pref))
|
||||||
|
|
|
@ -1125,7 +1125,7 @@ class ActPost : AsyncActivity(),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (editable.isNotEmpty()
|
if (editable.isNotEmpty()
|
||||||
&& !CharacterGroup.isWhitespace(editable[editable.length - 1].toInt())
|
&& !CharacterGroup.isWhitespace(editable[editable.length - 1].code)
|
||||||
) {
|
) {
|
||||||
editable.append(' ')
|
editable.append(' ')
|
||||||
}
|
}
|
||||||
|
@ -2571,7 +2571,7 @@ class ActPost : AsyncActivity(),
|
||||||
val e = etContent.editableText
|
val e = etContent.editableText
|
||||||
val len = e.length
|
val len = e.length
|
||||||
val last_char = if (len <= 0) ' ' else e[len - 1]
|
val last_char = if (len <= 0) ' ' else e[len - 1]
|
||||||
if (!CharacterGroup.isWhitespace(last_char.toInt())) {
|
if (!CharacterGroup.isWhitespace(last_char.code)) {
|
||||||
e.append(" ").append(a.text_url)
|
e.append(" ").append(a.text_url)
|
||||||
} else {
|
} else {
|
||||||
e.append(a.text_url)
|
e.append(a.text_url)
|
||||||
|
|
|
@ -3,7 +3,6 @@ package jp.juggler.subwaytooter.action
|
||||||
import android.app.AlertDialog
|
import android.app.AlertDialog
|
||||||
import jp.juggler.subwaytooter.ActColumnList
|
import jp.juggler.subwaytooter.ActColumnList
|
||||||
import jp.juggler.subwaytooter.ActMain
|
import jp.juggler.subwaytooter.ActMain
|
||||||
import jp.juggler.subwaytooter.App1
|
|
||||||
import jp.juggler.subwaytooter.R
|
import jp.juggler.subwaytooter.R
|
||||||
import jp.juggler.subwaytooter.api.entity.TootApplication
|
import jp.juggler.subwaytooter.api.entity.TootApplication
|
||||||
import jp.juggler.subwaytooter.table.MutedApp
|
import jp.juggler.subwaytooter.table.MutedApp
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package jp.juggler.subwaytooter.action
|
package jp.juggler.subwaytooter.action
|
||||||
|
|
||||||
import jp.juggler.subwaytooter.ActMain
|
import jp.juggler.subwaytooter.ActMain
|
||||||
import jp.juggler.subwaytooter.App1
|
|
||||||
import jp.juggler.subwaytooter.R
|
import jp.juggler.subwaytooter.R
|
||||||
import jp.juggler.subwaytooter.api.TootApiClient
|
import jp.juggler.subwaytooter.api.TootApiClient
|
||||||
import jp.juggler.subwaytooter.api.TootApiResult
|
import jp.juggler.subwaytooter.api.TootApiResult
|
||||||
|
|
|
@ -2,7 +2,6 @@ package jp.juggler.subwaytooter.action
|
||||||
|
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import jp.juggler.subwaytooter.ActMain
|
import jp.juggler.subwaytooter.ActMain
|
||||||
import jp.juggler.subwaytooter.App1
|
|
||||||
import jp.juggler.subwaytooter.ColumnType
|
import jp.juggler.subwaytooter.ColumnType
|
||||||
import jp.juggler.subwaytooter.R
|
import jp.juggler.subwaytooter.R
|
||||||
import jp.juggler.subwaytooter.api.*
|
import jp.juggler.subwaytooter.api.*
|
||||||
|
|
|
@ -2,7 +2,6 @@ package jp.juggler.subwaytooter.action
|
||||||
|
|
||||||
import android.app.AlertDialog
|
import android.app.AlertDialog
|
||||||
import jp.juggler.subwaytooter.ActMain
|
import jp.juggler.subwaytooter.ActMain
|
||||||
import jp.juggler.subwaytooter.App1
|
|
||||||
import jp.juggler.subwaytooter.R
|
import jp.juggler.subwaytooter.R
|
||||||
import jp.juggler.subwaytooter.api.*
|
import jp.juggler.subwaytooter.api.*
|
||||||
import jp.juggler.subwaytooter.api.entity.*
|
import jp.juggler.subwaytooter.api.entity.*
|
||||||
|
|
|
@ -47,7 +47,7 @@ class Host private constructor(
|
||||||
val cached = hostSet[srcArg]
|
val cached = hostSet[srcArg]
|
||||||
if(cached != null) return cached
|
if(cached != null) return cached
|
||||||
val src = srcArg.removeUrlSchema()
|
val src = srcArg.removeUrlSchema()
|
||||||
val ascii = IDN.toASCII(src, IDN.ALLOW_UNASSIGNED).toLowerCase(Locale.JAPAN)
|
val ascii = IDN.toASCII(src, IDN.ALLOW_UNASSIGNED).lowercase()
|
||||||
val pretty = IDN.toUnicode(src, IDN.ALLOW_UNASSIGNED)
|
val pretty = IDN.toUnicode(src, IDN.ALLOW_UNASSIGNED)
|
||||||
val host = if(ascii == pretty) Host(ascii) else Host(ascii, pretty)
|
val host = if(ascii == pretty) Host(ascii) else Host(ascii, pretty)
|
||||||
hostSet[src] = host
|
hostSet[src] = host
|
||||||
|
|
|
@ -427,7 +427,7 @@ class TootInstance(parser: TootParser, src: JsonObject) {
|
||||||
|
|
||||||
private fun Host.getCacheEntry(): CacheEntry =
|
private fun Host.getCacheEntry(): CacheEntry =
|
||||||
synchronized(_hostCache) {
|
synchronized(_hostCache) {
|
||||||
val hostLower = ascii.toLowerCase(Locale.JAPAN)
|
val hostLower = ascii.lowercase()
|
||||||
var item = _hostCache[hostLower]
|
var item = _hostCache[hostLower]
|
||||||
if (item == null) {
|
if (item == null) {
|
||||||
item = CacheEntry()
|
item = CacheEntry()
|
||||||
|
|
|
@ -240,7 +240,7 @@ class TootPolls (
|
||||||
TootStatus.parseTime(src.string("endTime")).notZero() ?: Long.MAX_VALUE
|
TootStatus.parseTime(src.string("endTime")).notZero() ?: Long.MAX_VALUE
|
||||||
this.expired = expired_at >= System.currentTimeMillis()
|
this.expired = expired_at >= System.currentTimeMillis()
|
||||||
this.multiple = src.containsKey("anyOf")
|
this.multiple = src.containsKey("anyOf")
|
||||||
this.votes_count = items?.sumBy{ it.votes?: 0 }?.notZero()
|
this.votes_count = items?.sumOf{ it.votes ?: 0 }?.notZero()
|
||||||
|
|
||||||
this.ownVoted = false
|
this.ownVoted = false
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ open class TootTag constructor(
|
||||||
|
|
||||||
init {
|
init {
|
||||||
countDaily = history?.first()?.uses ?: 0
|
countDaily = history?.first()?.uses ?: 0
|
||||||
countWeekly = history?.sumBy { it.uses } ?: 0
|
countWeekly = history?.sumOf{ it.uses } ?: 0
|
||||||
|
|
||||||
accountDaily = history?.first()?.accounts ?: 0
|
accountDaily = history?.first()?.accounts ?: 0
|
||||||
accountWeekly = history?.map { it.accounts }?.maxOrNull() ?: accountDaily
|
accountWeekly = history?.map { it.accounts }?.maxOrNull() ?: accountDaily
|
||||||
|
|
|
@ -25,157 +25,155 @@ import java.util.concurrent.atomic.AtomicBoolean
|
||||||
|
|
||||||
object AccountPicker {
|
object AccountPicker {
|
||||||
|
|
||||||
@SuppressLint("InflateParams")
|
@SuppressLint("InflateParams")
|
||||||
fun pick(
|
fun pick(
|
||||||
activity : AppCompatActivity,
|
activity: AppCompatActivity,
|
||||||
bAllowPseudo : Boolean = false,
|
bAllowPseudo: Boolean = false,
|
||||||
bAllowMisskey : Boolean = true,
|
bAllowMisskey: Boolean = true,
|
||||||
bAllowMastodon : Boolean = true,
|
bAllowMastodon: Boolean = true,
|
||||||
bAuto : Boolean = false,
|
bAuto: Boolean = false,
|
||||||
message : String? = null,
|
message: String? = null,
|
||||||
accountListArg : ArrayList<SavedAccount>? = null,
|
accountListArg: ArrayList<SavedAccount>? = null,
|
||||||
dismiss_callback : DialogInterfaceCallback? = null,
|
dismiss_callback: DialogInterfaceCallback? = null,
|
||||||
extra_callback : (LinearLayout, Int, Int) -> Unit = { _, _, _ -> },
|
extra_callback: (LinearLayout, Int, Int) -> Unit = { _, _, _ -> },
|
||||||
callback : SavedAccountCallback
|
callback: SavedAccountCallback
|
||||||
) {
|
) {
|
||||||
var removedMisskey = 0
|
|
||||||
var removedPseudo = 0
|
|
||||||
var removeMastodon = 0
|
var removeMastodon = 0
|
||||||
val account_list : MutableList<SavedAccount> = accountListArg ?: {
|
var removedMisskey = 0
|
||||||
val l = SavedAccount.loadAccountList(activity).filter { a ->
|
var removedPseudo = 0
|
||||||
var bOk = true
|
|
||||||
|
|
||||||
if(! bAllowMastodon && ! a.isMisskey) {
|
fun SavedAccount.checkMastodon() = when {
|
||||||
++ removeMastodon
|
!bAllowMastodon && !isMisskey -> ++removeMastodon
|
||||||
bOk = false
|
else -> 0
|
||||||
}
|
}
|
||||||
|
|
||||||
if(! bAllowMisskey && a.isMisskey) {
|
fun SavedAccount.checkMisskey() = when {
|
||||||
++ removedMisskey
|
!bAllowMisskey && isMisskey -> ++removedMisskey
|
||||||
bOk = false
|
else -> 0
|
||||||
}
|
}
|
||||||
|
|
||||||
if(! bAllowPseudo && a.isPseudo) {
|
fun SavedAccount.checkPseudo() = when {
|
||||||
++ removedPseudo
|
!bAllowPseudo && isPseudo -> ++removedPseudo
|
||||||
bOk = false
|
else -> 0
|
||||||
}
|
}
|
||||||
|
|
||||||
bOk
|
|
||||||
}.toMutableList()
|
|
||||||
SavedAccount.sort(l)
|
|
||||||
l
|
|
||||||
}()
|
|
||||||
|
|
||||||
if(account_list.isEmpty()) {
|
val account_list: MutableList<SavedAccount> = accountListArg
|
||||||
|
?: SavedAccount.loadAccountList(activity)
|
||||||
|
.filter { 0 == it.checkMastodon() + it.checkMisskey() + it.checkPseudo() }
|
||||||
|
.toMutableList()
|
||||||
|
.also { SavedAccount.sort(it) }
|
||||||
|
|
||||||
val sb = StringBuilder()
|
if (account_list.isEmpty()) {
|
||||||
|
|
||||||
if(removedPseudo > 0) {
|
val sb = StringBuilder()
|
||||||
sb.append(activity.getString(R.string.not_available_for_pseudo_account))
|
|
||||||
}
|
|
||||||
|
|
||||||
if(removedMisskey > 0) {
|
if (removedPseudo > 0) {
|
||||||
if(sb.isNotEmpty()) sb.append('\n')
|
sb.append(activity.getString(R.string.not_available_for_pseudo_account))
|
||||||
sb.append(activity.getString(R.string.not_available_for_misskey_account))
|
}
|
||||||
}
|
|
||||||
if(removeMastodon > 0) {
|
|
||||||
if(sb.isNotEmpty()) sb.append('\n')
|
|
||||||
sb.append(activity.getString(R.string.not_available_for_mastodon_account))
|
|
||||||
}
|
|
||||||
|
|
||||||
if(sb.isEmpty()) {
|
if (removedMisskey > 0) {
|
||||||
sb.append(activity.getString(R.string.account_empty))
|
if (sb.isNotEmpty()) sb.append('\n')
|
||||||
}
|
sb.append(activity.getString(R.string.not_available_for_misskey_account))
|
||||||
|
}
|
||||||
|
if (removeMastodon > 0) {
|
||||||
|
if (sb.isNotEmpty()) sb.append('\n')
|
||||||
|
sb.append(activity.getString(R.string.not_available_for_mastodon_account))
|
||||||
|
}
|
||||||
|
|
||||||
activity.showToast(false, sb.toString())
|
if (sb.isEmpty()) {
|
||||||
return
|
sb.append(activity.getString(R.string.account_empty))
|
||||||
}
|
}
|
||||||
|
|
||||||
if(bAuto && account_list.size == 1) {
|
activity.showToast(false, sb.toString())
|
||||||
callback(account_list[0])
|
return
|
||||||
return
|
}
|
||||||
}
|
|
||||||
|
|
||||||
val viewRoot = activity.layoutInflater.inflate(R.layout.dlg_account_picker, null, false)
|
if (bAuto && account_list.size == 1) {
|
||||||
|
callback(account_list[0])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
val dialog = Dialog(activity)
|
val viewRoot = activity.layoutInflater.inflate(R.layout.dlg_account_picker, null, false)
|
||||||
val isDialogClosed = AtomicBoolean(false)
|
|
||||||
|
|
||||||
dialog.setOnDismissListener {
|
val dialog = Dialog(activity)
|
||||||
if(dismiss_callback != null) dismiss_callback(it)
|
val isDialogClosed = AtomicBoolean(false)
|
||||||
}
|
|
||||||
|
|
||||||
dialog.setContentView(viewRoot)
|
dialog.setOnDismissListener {
|
||||||
if(message != null && message.isNotEmpty()) {
|
if (dismiss_callback != null) dismiss_callback(it)
|
||||||
val tv = viewRoot.findViewById<TextView>(R.id.tvMessage)
|
}
|
||||||
tv.visibility = View.VISIBLE
|
|
||||||
tv.text = message
|
|
||||||
}
|
|
||||||
viewRoot.findViewById<View>(R.id.btnCancel).setOnClickListener {
|
|
||||||
isDialogClosed.set(true)
|
|
||||||
dialog.cancel()
|
|
||||||
}
|
|
||||||
dialog.setCancelable(true)
|
|
||||||
dialog.setCanceledOnTouchOutside(true)
|
|
||||||
dialog.setOnCancelListener { isDialogClosed.set(true) }
|
|
||||||
|
|
||||||
val density = activity.resources.displayMetrics.density
|
dialog.setContentView(viewRoot)
|
||||||
|
if (message != null && message.isNotEmpty()) {
|
||||||
|
val tv = viewRoot.findViewById<TextView>(R.id.tvMessage)
|
||||||
|
tv.visibility = View.VISIBLE
|
||||||
|
tv.text = message
|
||||||
|
}
|
||||||
|
viewRoot.findViewById<View>(R.id.btnCancel).setOnClickListener {
|
||||||
|
isDialogClosed.set(true)
|
||||||
|
dialog.cancel()
|
||||||
|
}
|
||||||
|
dialog.setCancelable(true)
|
||||||
|
dialog.setCanceledOnTouchOutside(true)
|
||||||
|
dialog.setOnCancelListener { isDialogClosed.set(true) }
|
||||||
|
|
||||||
val llAccounts : LinearLayout = viewRoot.findViewById(R.id.llAccounts)
|
val density = activity.resources.displayMetrics.density
|
||||||
val pad_se = (0.5f + 12f * density).toInt()
|
|
||||||
val pad_tb = (0.5f + 6f * density).toInt()
|
|
||||||
|
|
||||||
for(a in account_list) {
|
val llAccounts: LinearLayout = viewRoot.findViewById(R.id.llAccounts)
|
||||||
val lp = LinearLayout.LayoutParams(
|
val pad_se = (0.5f + 12f * density).toInt()
|
||||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
val pad_tb = (0.5f + 6f * density).toInt()
|
||||||
LinearLayout.LayoutParams.WRAP_CONTENT
|
|
||||||
)
|
|
||||||
|
|
||||||
val ac = AcctColor.load(a)
|
for (a in account_list) {
|
||||||
|
val lp = LinearLayout.LayoutParams(
|
||||||
|
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||||
|
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||||
|
)
|
||||||
|
|
||||||
val b = Button(activity)
|
val ac = AcctColor.load(a)
|
||||||
|
|
||||||
if(AcctColor.hasColorBackground(ac)) {
|
val b = Button(activity)
|
||||||
b.background = getAdaptiveRippleDrawableRound(activity, ac.color_bg, ac.color_fg)
|
|
||||||
} else {
|
|
||||||
b.setBackgroundResource(R.drawable.btn_bg_transparent_round6dp)
|
|
||||||
}
|
|
||||||
if(AcctColor.hasColorForeground(ac)) {
|
|
||||||
b.textColor = ac.color_fg
|
|
||||||
}
|
|
||||||
|
|
||||||
b.setPaddingRelative(pad_se, pad_tb, pad_se, pad_tb)
|
if (AcctColor.hasColorBackground(ac)) {
|
||||||
b.gravity = Gravity.START or Gravity.CENTER_VERTICAL
|
b.background = getAdaptiveRippleDrawableRound(activity, ac.color_bg, ac.color_fg)
|
||||||
b.isAllCaps = false
|
} else {
|
||||||
b.layoutParams = lp
|
b.setBackgroundResource(R.drawable.btn_bg_transparent_round6dp)
|
||||||
b.minHeight = (0.5f + 32f * density).toInt()
|
}
|
||||||
|
if (AcctColor.hasColorForeground(ac)) {
|
||||||
|
b.textColor = ac.color_fg
|
||||||
|
}
|
||||||
|
|
||||||
val sb = SpannableStringBuilder(ac.nickname)
|
b.setPaddingRelative(pad_se, pad_tb, pad_se, pad_tb)
|
||||||
if(a.last_notification_error?.isNotEmpty() == true) {
|
b.gravity = Gravity.START or Gravity.CENTER_VERTICAL
|
||||||
sb.append("\n")
|
b.isAllCaps = false
|
||||||
val start = sb.length
|
b.layoutParams = lp
|
||||||
sb.append(a.last_notification_error)
|
b.minHeight = (0.5f + 32f * density).toInt()
|
||||||
val end = sb.length
|
|
||||||
sb.setSpan(RelativeSizeSpan(0.7f), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
|
||||||
} else if(a.last_subscription_error?.isNotEmpty() == true) {
|
|
||||||
sb.append("\n")
|
|
||||||
val start = sb.length
|
|
||||||
sb.append(a.last_subscription_error)
|
|
||||||
val end = sb.length
|
|
||||||
sb.setSpan(RelativeSizeSpan(0.7f), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
|
||||||
}
|
|
||||||
b.text = sb
|
|
||||||
|
|
||||||
b.setOnClickListener {
|
val sb = SpannableStringBuilder(ac.nickname)
|
||||||
isDialogClosed.set(true)
|
if (a.last_notification_error?.isNotEmpty() == true) {
|
||||||
callback(a)
|
sb.append("\n")
|
||||||
dialog.dismissSafe()
|
val start = sb.length
|
||||||
}
|
sb.append(a.last_notification_error)
|
||||||
llAccounts.addView(b)
|
val end = sb.length
|
||||||
}
|
sb.setSpan(RelativeSizeSpan(0.7f), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||||
|
} else if (a.last_subscription_error?.isNotEmpty() == true) {
|
||||||
|
sb.append("\n")
|
||||||
|
val start = sb.length
|
||||||
|
sb.append(a.last_subscription_error)
|
||||||
|
val end = sb.length
|
||||||
|
sb.setSpan(RelativeSizeSpan(0.7f), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||||
|
}
|
||||||
|
b.text = sb
|
||||||
|
|
||||||
extra_callback(llAccounts, pad_se, pad_tb)
|
b.setOnClickListener {
|
||||||
|
isDialogClosed.set(true)
|
||||||
|
callback(a)
|
||||||
|
dialog.dismissSafe()
|
||||||
|
}
|
||||||
|
llAccounts.addView(b)
|
||||||
|
}
|
||||||
|
|
||||||
dialog.show()
|
extra_callback(llAccounts, pad_se, pad_tb)
|
||||||
}
|
|
||||||
|
dialog.show()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -267,7 +267,7 @@ class EmojiPicker(
|
||||||
val entries = newList.entries
|
val entries = newList.entries
|
||||||
custom_list.clear()
|
custom_list.clear()
|
||||||
custom_categories.clear()
|
custom_categories.clear()
|
||||||
custom_list.ensureCapacity(entries.sumBy { it.value.size })
|
custom_list.ensureCapacity(entries.sumOf { it.value.size })
|
||||||
custom_categories.ensureCapacity(entries.size)
|
custom_categories.ensureCapacity(entries.size)
|
||||||
entries.forEach {
|
entries.forEach {
|
||||||
val rangeStart = custom_list.size
|
val rangeStart = custom_list.size
|
||||||
|
|
|
@ -131,7 +131,7 @@ object LoginForm {
|
||||||
val br = BufferedReader(InputStreamReader(inStream, "UTF-8"))
|
val br = BufferedReader(InputStreamReader(inStream, "UTF-8"))
|
||||||
while(true) {
|
while(true) {
|
||||||
val s : String =
|
val s : String =
|
||||||
br.readLine()?.trim { it <= ' ' }?.toLowerCase(Locale.JAPAN) ?: break
|
br.readLine()?.trim { it <= ' ' }?.lowercase() ?: break
|
||||||
if(s.isEmpty()) continue
|
if(s.isEmpty()) continue
|
||||||
add(s)
|
add(s)
|
||||||
add(IDN.toASCII(s, IDN.ALLOW_UNASSIGNED))
|
add(IDN.toASCII(s, IDN.ALLOW_UNASSIGNED))
|
||||||
|
@ -155,7 +155,7 @@ object LoginForm {
|
||||||
override fun performFiltering(constraint : CharSequence?) : FilterResults =
|
override fun performFiltering(constraint : CharSequence?) : FilterResults =
|
||||||
FilterResults().also { result ->
|
FilterResults().also { result ->
|
||||||
if(constraint?.isNotEmpty() == true) {
|
if(constraint?.isNotEmpty() == true) {
|
||||||
val key = constraint.toString().toLowerCase(Locale.JAPAN)
|
val key = constraint.toString().lowercase()
|
||||||
// suggestions リストは毎回生成する必要がある。publishResultsと同時にアクセスされる場合がある
|
// suggestions リストは毎回生成する必要がある。publishResultsと同時にアクセスされる場合がある
|
||||||
val suggestions = StringArray()
|
val suggestions = StringArray()
|
||||||
for(s in instance_list) {
|
for(s in instance_list) {
|
||||||
|
|
|
@ -59,7 +59,7 @@ class PollingForegrounder : IntentService("PollingForegrounder") {
|
||||||
intent_click.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
intent_click.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
val pi_click = PendingIntent.getActivity(
|
val pi_click = PendingIntent.getActivity(
|
||||||
context, 2, intent_click,
|
context, 2, intent_click,
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
PendingIntent.FLAG_UPDATE_CURRENT or (if(Build.VERSION.SDK_INT>=23) PendingIntent.FLAG_IMMUTABLE else 0)
|
||||||
)
|
)
|
||||||
|
|
||||||
val builder = if (Build.VERSION.SDK_INT >= 26) {
|
val builder = if (Build.VERSION.SDK_INT >= 26) {
|
||||||
|
|
|
@ -692,7 +692,7 @@ class TaskRunner(
|
||||||
// FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY を付与してはいけない
|
// FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY を付与してはいけない
|
||||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
},
|
},
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
PendingIntent.FLAG_UPDATE_CURRENT or (if(Build.VERSION.SDK_INT>=23) PendingIntent.FLAG_IMMUTABLE else 0)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -705,7 +705,7 @@ class TaskRunner(
|
||||||
data =
|
data =
|
||||||
"subwaytooter://notification_delete/?$params".toUri()
|
"subwaytooter://notification_delete/?$params".toUri()
|
||||||
},
|
},
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
PendingIntent.FLAG_UPDATE_CURRENT or (if(Build.VERSION.SDK_INT>=23) PendingIntent.FLAG_IMMUTABLE else 0)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -748,7 +748,7 @@ class TaskRunner(
|
||||||
context,
|
context,
|
||||||
3,
|
3,
|
||||||
intent_click,
|
intent_click,
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
PendingIntent.FLAG_UPDATE_CURRENT or (if(Build.VERSION.SDK_INT>=23) PendingIntent.FLAG_IMMUTABLE else 0)
|
||||||
)
|
)
|
||||||
|
|
||||||
val builder = if (Build.VERSION.SDK_INT >= 26) {
|
val builder = if (Build.VERSION.SDK_INT >= 26) {
|
||||||
|
|
|
@ -50,7 +50,7 @@ class AcctColor {
|
||||||
|
|
||||||
fun save(now : Long) {
|
fun save(now : Long) {
|
||||||
|
|
||||||
val key = acctAscii.toLowerCase(Locale.ENGLISH)
|
val key = acctAscii.lowercase()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
val cv = ContentValues()
|
val cv = ContentValues()
|
||||||
|
@ -137,7 +137,7 @@ class AcctColor {
|
||||||
fun load(acct:Acct) =load(acct.ascii,acct.pretty)
|
fun load(acct:Acct) =load(acct.ascii,acct.pretty)
|
||||||
|
|
||||||
fun load(acctAscii: String,acctPretty : String) : AcctColor {
|
fun load(acctAscii: String,acctPretty : String) : AcctColor {
|
||||||
val key = acctAscii.toLowerCase(Locale.ENGLISH)
|
val key = acctAscii.lowercase()
|
||||||
val cached : AcctColor? = mMemoryCache.get(key)
|
val cached : AcctColor? = mMemoryCache.get(key)
|
||||||
if(cached != null) return cached
|
if(cached != null) return cached
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package jp.juggler.subwaytooter.table
|
package jp.juggler.subwaytooter.table
|
||||||
|
|
||||||
import android.content.ContentValues
|
import android.content.ContentValues
|
||||||
import android.content.Context
|
|
||||||
import android.database.sqlite.SQLiteDatabase
|
import android.database.sqlite.SQLiteDatabase
|
||||||
import android.provider.BaseColumns
|
import android.provider.BaseColumns
|
||||||
import jp.juggler.subwaytooter.App1
|
import jp.juggler.subwaytooter.App1
|
||||||
|
|
|
@ -28,9 +28,9 @@ object EmojiDecoder {
|
||||||
|
|
||||||
// private val log = LogCategory("EmojiDecoder")
|
// private val log = LogCategory("EmojiDecoder")
|
||||||
|
|
||||||
private const val cpColon = ':'.toInt()
|
private const val cpColon = ':'.code
|
||||||
|
|
||||||
private const val cpZwsp = '\u200B'.toInt()
|
private const val cpZwsp = '\u200B'.code
|
||||||
|
|
||||||
var handleUnicodeEmoji = true
|
var handleUnicodeEmoji = true
|
||||||
|
|
||||||
|
@ -223,7 +223,7 @@ object EmojiDecoder {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
val nextChar = if (result.endPos >= end) null else s[result.endPos].toInt()
|
val nextChar = if (result.endPos >= end) null else s[result.endPos].code
|
||||||
|
|
||||||
// 絵文字バリエーション・シーケンス(EVS)のU+FE0E(VS-15)が直後にある場合
|
// 絵文字バリエーション・シーケンス(EVS)のU+FE0E(VS-15)が直後にある場合
|
||||||
// その文字を絵文字化しない
|
// その文字を絵文字化しない
|
||||||
|
@ -232,7 +232,7 @@ object EmojiDecoder {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
val emoji = if (nextChar == 0xFE0F && s[result.endPos - 1].toInt() != 0xFE0F) {
|
val emoji = if (nextChar == 0xFE0F && s[result.endPos - 1].code != 0xFE0F) {
|
||||||
// 絵文字の最後が 0xFE0F でない
|
// 絵文字の最後が 0xFE0F でない
|
||||||
// 直後にU+0xFE0F (絵文字バリエーション・シーケンスEVSのVS-16)がある
|
// 直後にU+0xFE0F (絵文字バリエーション・シーケンスEVSのVS-16)がある
|
||||||
// 直後のそれまで含めて絵文字として表示する
|
// 直後のそれまで含めて絵文字として表示する
|
||||||
|
@ -246,16 +246,16 @@ object EmojiDecoder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private const val codepointColon = ':'.toInt()
|
private const val codepointColon = ':'.code
|
||||||
// private const val codepointAtmark = '@'.toInt()
|
// private const val codepointAtmark = '@'.toInt()
|
||||||
|
|
||||||
private val shortCodeCharacterSet =
|
private val shortCodeCharacterSet =
|
||||||
SparseBooleanArray().apply {
|
SparseBooleanArray().apply {
|
||||||
for (c in 'A'..'Z') put(c.toInt(), true)
|
for (c in 'A'..'Z') put(c.code, true)
|
||||||
for (c in 'a'..'z') put(c.toInt(), true)
|
for (c in 'a'..'z') put(c.code, true)
|
||||||
for (c in '0'..'9') put(c.toInt(), true)
|
for (c in '0'..'9') put(c.code, true)
|
||||||
for (c in "+-_@:") put(c.toInt(), true)
|
for (c in "+-_@:") put(c.code, true)
|
||||||
for (c in ".") put(c.toInt(), true)
|
for (c in ".") put(c.code, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
private interface ShortCodeSplitterCallback {
|
private interface ShortCodeSplitterCallback {
|
||||||
|
@ -392,7 +392,7 @@ object EmojiDecoder {
|
||||||
else -> {
|
else -> {
|
||||||
// EmojiOneのショートコード
|
// EmojiOneのショートコード
|
||||||
val emoji = if (useEmojioneShortcode) {
|
val emoji = if (useEmojioneShortcode) {
|
||||||
EmojiMap.shortNameMap[name.toLowerCase(Locale.JAPAN).replace('-', '_')]
|
EmojiMap.shortNameMap[name.lowercase().replace('-', '_')]
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
@ -437,7 +437,7 @@ object EmojiDecoder {
|
||||||
|
|
||||||
// カスタム絵文字ではなく通常の絵文字のショートコードなら絵文字に変換する
|
// カスタム絵文字ではなく通常の絵文字のショートコードなら絵文字に変換する
|
||||||
val emoji = if (decodeEmojioneShortcode) {
|
val emoji = if (decodeEmojioneShortcode) {
|
||||||
EmojiMap.shortNameMap[name.toLowerCase(Locale.JAPAN).replace('-', '_')]
|
EmojiMap.shortNameMap[name.lowercase().replace('-', '_')]
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
|
@ -258,7 +258,7 @@ object HTMLDecoder {
|
||||||
val m = reTag.matcher(text)
|
val m = reTag.matcher(text)
|
||||||
if (m.find()) {
|
if (m.find()) {
|
||||||
val is_close = m.groupEx(1)!!.isNotEmpty()
|
val is_close = m.groupEx(1)!!.isNotEmpty()
|
||||||
tag = m.groupEx(2)!!.toLowerCase(Locale.JAPAN)
|
tag = m.groupEx(2)!!.lowercase()
|
||||||
|
|
||||||
val m2 = reTagEnd.matcher(text)
|
val m2 = reTagEnd.matcher(text)
|
||||||
val is_openclose = when {
|
val is_openclose = when {
|
||||||
|
@ -369,15 +369,15 @@ object HTMLDecoder {
|
||||||
ArrayList<SpannableStringBuilder>().also { dst ->
|
ArrayList<SpannableStringBuilder>().also { dst ->
|
||||||
// 入力の末尾のtrim
|
// 入力の末尾のtrim
|
||||||
var end = this.length
|
var end = this.length
|
||||||
while (end > 0 && CharacterGroup.isWhitespace(this[end - 1].toInt())) --end
|
while (end > 0 && CharacterGroup.isWhitespace(this[end - 1].code)) --end
|
||||||
|
|
||||||
// 入力の最初の非空白文字の位置を調べておく
|
// 入力の最初の非空白文字の位置を調べておく
|
||||||
var firstNonSpace = 0
|
var firstNonSpace = 0
|
||||||
while (firstNonSpace <end && CharacterGroup.isWhitespace(this[firstNonSpace].toInt())) ++firstNonSpace
|
while (firstNonSpace <end && CharacterGroup.isWhitespace(this[firstNonSpace].code)) ++firstNonSpace
|
||||||
|
|
||||||
var i = 0
|
var i = 0
|
||||||
while (i < end) {
|
while (i < end) {
|
||||||
var lineStart = i
|
val lineStart = i
|
||||||
while (i < end && this[i] != '\n') ++i
|
while (i < end && this[i] != '\n') ++i
|
||||||
val lineEnd = if (i >= end) end else i + 1
|
val lineEnd = if (i >= end) end else i + 1
|
||||||
++i
|
++i
|
||||||
|
@ -758,7 +758,7 @@ object HTMLDecoder {
|
||||||
val dst = HashMap<String, String>()
|
val dst = HashMap<String, String>()
|
||||||
val m = reAttribute.matcher(text)
|
val m = reAttribute.matcher(text)
|
||||||
while (m.find()) {
|
while (m.find()) {
|
||||||
val name = m.groupEx(1)!!.toLowerCase(Locale.JAPAN)
|
val name = m.groupEx(1)!!.lowercase()
|
||||||
val value = decodeEntity(m.groupEx(3))
|
val value = decodeEntity(m.groupEx(3))
|
||||||
dst[name] = value
|
dst[name] = value
|
||||||
}
|
}
|
||||||
|
|
|
@ -367,10 +367,10 @@ object MisskeySyntaxHighlighter {
|
||||||
addAll(_keywords)
|
addAll(_keywords)
|
||||||
|
|
||||||
// UPPER
|
// UPPER
|
||||||
addAll(_keywords.map { k -> k.toUpperCase(Locale.JAPAN) })
|
addAll(_keywords.map { it.uppercase() })
|
||||||
|
|
||||||
// Snake
|
// Snake
|
||||||
addAll(_keywords.map { k -> k[0].toUpperCase() + k.substring(1) })
|
addAll(_keywords.map { k -> k[0].uppercase() + k.substring(1) })
|
||||||
|
|
||||||
add("NaN")
|
add("NaN")
|
||||||
|
|
||||||
|
@ -378,12 +378,12 @@ object MisskeySyntaxHighlighter {
|
||||||
}
|
}
|
||||||
|
|
||||||
private val symbolMap = SparseBooleanArray().apply {
|
private val symbolMap = SparseBooleanArray().apply {
|
||||||
"=+-*/%~^&|><!?".forEach { put(it.toInt(), true) }
|
"=+-*/%~^&|><!?".forEach { put(it.code, true) }
|
||||||
}
|
}
|
||||||
|
|
||||||
// 文字列リテラルの開始文字のマップ
|
// 文字列リテラルの開始文字のマップ
|
||||||
private val stringStart = SparseBooleanArray().apply {
|
private val stringStart = SparseBooleanArray().apply {
|
||||||
"\"'`".forEach { put(it.toInt(), true) }
|
"\"'`".forEach { put(it.code, true) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private class Token(
|
private class Token(
|
||||||
|
@ -536,7 +536,7 @@ object MisskeySyntaxHighlighter {
|
||||||
// string
|
// string
|
||||||
{
|
{
|
||||||
val beginChar = source[pos]
|
val beginChar = source[pos]
|
||||||
if (!stringStart[beginChar.toInt()]) return@arrayOf null
|
if (!stringStart[beginChar.code]) return@arrayOf null
|
||||||
var i = pos + 1
|
var i = pos + 1
|
||||||
while (i < end) {
|
while (i < end) {
|
||||||
val char = source[i++]
|
val char = source[i++]
|
||||||
|
@ -653,7 +653,7 @@ object MisskeySyntaxHighlighter {
|
||||||
{
|
{
|
||||||
val c = source[pos]
|
val c = source[pos]
|
||||||
when {
|
when {
|
||||||
symbolMap.get(c.toInt(), false) ->
|
symbolMap.get(c.code, false) ->
|
||||||
Token(length = 1, color = 0x42b983)
|
Token(length = 1, color = 0x42b983)
|
||||||
c == '-' ->
|
c == '-' ->
|
||||||
Token(length = 1, color = 0x42b983)
|
Token(length = 1, color = 0x42b983)
|
||||||
|
@ -1348,7 +1348,7 @@ object MisskeyMarkdownDecoder {
|
||||||
var i = lastEnd //スキャン中の位置
|
var i = lastEnd //スキャン中の位置
|
||||||
while (i < end) {
|
while (i < end) {
|
||||||
// 注目位置の文字に関連するパーサー
|
// 注目位置の文字に関連するパーサー
|
||||||
val lastParsers = nodeParserMap[text[i].toInt()]
|
val lastParsers = nodeParserMap[text[i].code]
|
||||||
if (lastParsers == null) {
|
if (lastParsers == null) {
|
||||||
++i
|
++i
|
||||||
continue
|
continue
|
||||||
|
@ -1538,8 +1538,11 @@ object MisskeyMarkdownDecoder {
|
||||||
}
|
}
|
||||||
|
|
||||||
// \} \]はムダなエスケープに見えるが、androidでは必要なので削ってはいけない
|
// \} \]はムダなエスケープに見えるが、androidでは必要なので削ってはいけない
|
||||||
|
@Suppress("RegExpRedundantEscape")
|
||||||
private val reLatexRemove = """\\(?:quad|Huge|atop|sf|scriptsize|bf|small|tiny|underline|large|(?:color)\{[^}]*\})""".toRegex()
|
private val reLatexRemove = """\\(?:quad|Huge|atop|sf|scriptsize|bf|small|tiny|underline|large|(?:color)\{[^}]*\})""".toRegex()
|
||||||
|
@Suppress("RegExpRedundantEscape")
|
||||||
private val reLatex1 = """\\(?:(?:url)|(?:textcolor|colorbox)\{[^}]*\}|(?:fcolorbox|raisebox)\{[^}]*\}\{[^}]*\}|includegraphics\[[^]]*\])\{([^}]*)\}""".toRegex()
|
private val reLatex1 = """\\(?:(?:url)|(?:textcolor|colorbox)\{[^}]*\}|(?:fcolorbox|raisebox)\{[^}]*\}\{[^}]*\}|includegraphics\[[^]]*\])\{([^}]*)\}""".toRegex()
|
||||||
|
@Suppress("RegExpRedundantEscape")
|
||||||
private val reLatex2reversed = """\\(?:overset|href)\{([^}]+)\}\{([^}]+)\}""".toRegex()
|
private val reLatex2reversed = """\\(?:overset|href)\{([^}]+)\}\{([^}]+)\}""".toRegex()
|
||||||
|
|
||||||
private fun String.removeLatex(): String {
|
private fun String.removeLatex(): String {
|
||||||
|
@ -1586,7 +1589,7 @@ object MisskeyMarkdownDecoder {
|
||||||
vararg nodeParsers: NodeParseEnv.() -> NodeDetected?
|
vararg nodeParsers: NodeParseEnv.() -> NodeDetected?
|
||||||
) {
|
) {
|
||||||
for (s in firstChars) {
|
for (s in firstChars) {
|
||||||
put(s.toInt(), nodeParsers)
|
put(s.code, nodeParsers)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1808,15 +1811,15 @@ object MisskeyMarkdownDecoder {
|
||||||
// メールアドレスの@の手前に使える文字なら真
|
// メールアドレスの@の手前に使える文字なら真
|
||||||
val mailChars = SparseBooleanArray().apply {
|
val mailChars = SparseBooleanArray().apply {
|
||||||
for (it in '0'..'9') {
|
for (it in '0'..'9') {
|
||||||
put(it.toInt(), true)
|
put(it.code, true)
|
||||||
}
|
}
|
||||||
for (it in 'A'..'Z') {
|
for (it in 'A'..'Z') {
|
||||||
put(it.toInt(), true)
|
put(it.code, true)
|
||||||
}
|
}
|
||||||
for (it in 'a'..'z') {
|
for (it in 'a'..'z') {
|
||||||
put(it.toInt(), true)
|
put(it.code, true)
|
||||||
}
|
}
|
||||||
"""${'$'}!#%&'`"*+-/=?^_{|}~""".forEach { put(it.toInt(), true) }
|
"""${'$'}!#%&'`"*+-/=?^_{|}~""".forEach { put(it.code, true) }
|
||||||
}
|
}
|
||||||
|
|
||||||
addParser("@", {
|
addParser("@", {
|
||||||
|
|
|
@ -760,7 +760,7 @@ class PostHelper(
|
||||||
val cp = src.codePointBefore(i)
|
val cp = src.codePointBefore(i)
|
||||||
i -= Character.charCount(cp)
|
i -= Character.charCount(cp)
|
||||||
|
|
||||||
if (cp == '@'.toInt()) {
|
if (cp == '@'.code) {
|
||||||
start = i
|
start = i
|
||||||
if (++count_atMark >= 2) break else continue
|
if (++count_atMark >= 2) break else continue
|
||||||
} else if (count_atMark == 1) {
|
} else if (count_atMark == 1) {
|
||||||
|
@ -865,7 +865,7 @@ class PostHelper(
|
||||||
val remain = limit - code_list.size
|
val remain = limit - code_list.size
|
||||||
if (remain > 0) {
|
if (remain > 0) {
|
||||||
val s =
|
val s =
|
||||||
src.substring(last_colon + 1, end).toLowerCase(Locale.JAPAN).replace('-', '_')
|
src.substring(last_colon + 1, end).lowercase().replace('-', '_')
|
||||||
val matches = EmojiDecoder.searchShortCode(activity, s, remain)
|
val matches = EmojiDecoder.searchShortCode(activity, s, remain)
|
||||||
log.d("checkEmoji: search for %s, result=%d", s, matches.size)
|
log.d("checkEmoji: search for %s, result=%d", s, matches.size)
|
||||||
code_list.addAll(matches)
|
code_list.addAll(matches)
|
||||||
|
|
|
@ -18,7 +18,7 @@ class Blurhash(blurhash : String, punch : Float = 1f) {
|
||||||
private val base83Map = SparseIntArray().apply {
|
private val base83Map = SparseIntArray().apply {
|
||||||
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz#$%*+,-.:;=?@[]^_{|}~"
|
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz#$%*+,-.:;=?@[]^_{|}~"
|
||||||
.forEachIndexed { index, c ->
|
.forEachIndexed { index, c ->
|
||||||
put(c.toInt(), index)
|
put(c.code, index)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,11 +26,9 @@ class Blurhash(blurhash : String, punch : Float = 1f) {
|
||||||
private fun String.decodeBase83(start : Int, length : Int) : Int {
|
private fun String.decodeBase83(start : Int, length : Int) : Int {
|
||||||
var v = 0
|
var v = 0
|
||||||
for(i in start until start + length) {
|
for(i in start until start + length) {
|
||||||
val ci = this[i].toInt()
|
val ci = this[i].code
|
||||||
val idx = base83Map.get(ci, - 1)
|
val idx = base83Map.get(ci, - 1)
|
||||||
if(idx == - 1) {
|
if(idx == - 1) error("decodeBase83: incorrect char code $ci")
|
||||||
error("decodeBase83: incorrect char code $ci")
|
|
||||||
}
|
|
||||||
v = v * 83 + idx
|
v = v * 83 + idx
|
||||||
}
|
}
|
||||||
return v
|
return v
|
||||||
|
|
|
@ -109,7 +109,7 @@ object CharacterGroup {
|
||||||
var id = Integer.MAX_VALUE
|
var id = Integer.MAX_VALUE
|
||||||
for(s in list) {
|
for(s in list) {
|
||||||
if(s.length == 1) {
|
if(s.length == 1) {
|
||||||
val c = s[0].toInt()
|
val c = s[0].code
|
||||||
if(c < id) id = c
|
if(c < id) id = c
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -130,7 +130,7 @@ object CharacterGroup {
|
||||||
// ユニコード文字を正規化する。
|
// ユニコード文字を正規化する。
|
||||||
// 簡易版なので全ての文字には対応していない
|
// 簡易版なので全ての文字には対応していない
|
||||||
fun getUnifiedCharacter(c : Char) : Char {
|
fun getUnifiedCharacter(c : Char) : Char {
|
||||||
val v1 = map1[c.toInt()]
|
val v1 = map1[c.code]
|
||||||
return if(v1 != 0) v1.toChar() else c
|
return if(v1 != 0) v1.toChar() else c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,13 +145,13 @@ object CharacterGroup {
|
||||||
val map : SparseIntArray
|
val map : SparseIntArray
|
||||||
val key : Int
|
val key : Int
|
||||||
|
|
||||||
val v1 = s[0].toInt()
|
val v1 = s[0].code
|
||||||
if(s.length == 1) {
|
if(s.length == 1) {
|
||||||
map = map1
|
map = map1
|
||||||
key = v1
|
key = v1
|
||||||
} else {
|
} else {
|
||||||
map = map2
|
map = map2
|
||||||
val v2 = s[1].toInt()
|
val v2 = s[1].code
|
||||||
key = v1 or (v2 shl 16)
|
key = v1 or (v2 shl 16)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -183,7 +183,7 @@ object CharacterGroup {
|
||||||
var pos = offset
|
var pos = offset
|
||||||
|
|
||||||
// 空白を読み飛ばす
|
// 空白を読み飛ばす
|
||||||
while(pos < end && isWhitespace(text[pos].toInt())) ++ pos
|
while(pos < end && isWhitespace(text[pos].code)) ++ pos
|
||||||
|
|
||||||
// 終端までの文字数
|
// 終端までの文字数
|
||||||
val remain = end - pos
|
val remain = end - pos
|
||||||
|
@ -193,7 +193,7 @@ object CharacterGroup {
|
||||||
return END
|
return END
|
||||||
}
|
}
|
||||||
|
|
||||||
val v1 = text[pos].toInt()
|
val v1 = text[pos].code
|
||||||
|
|
||||||
// グループに登録された文字を長い順にチェック
|
// グループに登録された文字を長い順にチェック
|
||||||
var check_len = if(remain > 2) 2 else remain
|
var check_len = if(remain > 2) 2 else remain
|
||||||
|
@ -201,7 +201,7 @@ object CharacterGroup {
|
||||||
val group_id = if(check_len == 1)
|
val group_id = if(check_len == 1)
|
||||||
map1.get(v1)
|
map1.get(v1)
|
||||||
else
|
else
|
||||||
map2.get(v1 or (text[pos + 1].toInt() shl 16))
|
map2.get(v1 or (text[pos + 1].code shl 16))
|
||||||
if(group_id != 0) {
|
if(group_id != 0) {
|
||||||
this.offset = pos + check_len
|
this.offset = pos + check_len
|
||||||
return group_id
|
return group_id
|
||||||
|
|
|
@ -853,7 +853,7 @@ private fun Writer.writeQuote(string: String): Writer {
|
||||||
in '\u0080' until '\u00a0',
|
in '\u0080' until '\u00a0',
|
||||||
in '\u2000' until '\u2100' -> {
|
in '\u2000' until '\u2100' -> {
|
||||||
write("\\u")
|
write("\\u")
|
||||||
val hexCode: String = Integer.toHexString(c.toInt())
|
val hexCode: String = Integer.toHexString(c.code)
|
||||||
write("0000", 0, 4 - hexCode.length)
|
write("0000", 0, 4 - hexCode.length)
|
||||||
write(hexCode)
|
write(hexCode)
|
||||||
}
|
}
|
||||||
|
@ -1046,7 +1046,7 @@ fun Writer.writeJsonValue(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
value is Char -> writeJsonValue(indentFactor, indent, value.toInt(), sort = sort)
|
value is Char -> writeJsonValue(indentFactor, indent, value.code, sort = sort)
|
||||||
|
|
||||||
value is String -> writeQuote(value)
|
value is String -> writeQuote(value)
|
||||||
value is Enum<*> -> writeQuote(value.name)
|
value is Enum<*> -> writeQuote(value.name)
|
||||||
|
|
|
@ -182,7 +182,7 @@ private val mimeTypeExMap : HashMap<String, String> by lazy {
|
||||||
fun getMimeType(log : LogCategory?, src : String) : String {
|
fun getMimeType(log : LogCategory?, src : String) : String {
|
||||||
var ext = MimeTypeMap.getFileExtensionFromUrl(src)
|
var ext = MimeTypeMap.getFileExtensionFromUrl(src)
|
||||||
if(ext != null && ext.isNotEmpty()) {
|
if(ext != null && ext.isNotEmpty()) {
|
||||||
ext = ext.toLowerCase(Locale.US)
|
ext = ext.lowercase()
|
||||||
|
|
||||||
//
|
//
|
||||||
var mime_type : String? = MimeTypeMap.getSingleton().getMimeTypeFromExtension(ext)
|
var mime_type : String? = MimeTypeMap.getSingleton().getMimeTypeFromExtension(ext)
|
||||||
|
|
|
@ -81,7 +81,7 @@ fun IntArray.toByteArray(): ByteArray {
|
||||||
fun CharArray.toLowerByteArray(): ByteArray {
|
fun CharArray.toLowerByteArray(): ByteArray {
|
||||||
val dst = ByteArray(this.size)
|
val dst = ByteArray(this.size)
|
||||||
for (i in this.indices) {
|
for (i in this.indices) {
|
||||||
dst[i] = this[i].toByte()
|
dst[i] = this[i].code.toByte()
|
||||||
}
|
}
|
||||||
return dst
|
return dst
|
||||||
}
|
}
|
||||||
|
@ -124,7 +124,7 @@ fun CharSequence.codePointBefore(index: Int): Int {
|
||||||
val c1 = this[index - 2]
|
val c1 = this[index - 2]
|
||||||
if (Character.isHighSurrogate(c1)) return Character.toCodePoint(c1, c2)
|
if (Character.isHighSurrogate(c1)) return Character.toCodePoint(c1, c2)
|
||||||
}
|
}
|
||||||
return c2.toInt()
|
return c2.code
|
||||||
} else {
|
} else {
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
|
|
|
@ -152,7 +152,7 @@ class WordTrieTree {
|
||||||
val t = CharacterGroup.Tokenizer()
|
val t = CharacterGroup.Tokenizer()
|
||||||
|
|
||||||
for(i in start until end) {
|
for(i in start until end) {
|
||||||
if(! CharacterGroup.isWhitespace(src[i].toInt())) {
|
if(! CharacterGroup.isWhitespace(src[i].code)) {
|
||||||
val item = match(true, t.reset(src, i, end))
|
val item = match(true, t.reset(src, i, end))
|
||||||
if(item != null) return item
|
if(item != null) return item
|
||||||
}
|
}
|
||||||
|
@ -174,7 +174,7 @@ class WordTrieTree {
|
||||||
|
|
||||||
var i = start
|
var i = start
|
||||||
while(i < end) {
|
while(i < end) {
|
||||||
if(! CharacterGroup.isWhitespace(src[i].toInt())) {
|
if(! CharacterGroup.isWhitespace(src[i].code)) {
|
||||||
val item = match(false, t.reset(src, i, end))
|
val item = match(false, t.reset(src, i, end))
|
||||||
if(item != null) {
|
if(item != null) {
|
||||||
if(dst == null) dst = ArrayList()
|
if(dst == null) dst = ArrayList()
|
||||||
|
|
|
@ -131,11 +131,9 @@ class TestKotlinFeature {
|
||||||
// 型が分からないようにするとビルドできるが、intの10とlongの10は異なると判断される
|
// 型が分からないようにするとビルドできるが、intの10とlongの10は異なると判断される
|
||||||
// Int.equals も同じ結果
|
// Int.equals も同じ結果
|
||||||
assertEquals(false, int10 as Any == long10 as Any)
|
assertEquals(false, int10 as Any == long10 as Any)
|
||||||
assertEquals(false, int10.equals(long10))
|
|
||||||
|
|
||||||
// Long.equals でも同じ結果
|
// Long.equals でも同じ結果
|
||||||
assertEquals(false, long10 as Any == int10 as Any)
|
assertEquals(false, long10 as Any == int10 as Any)
|
||||||
assertEquals(false, long10.equals(int10))
|
|
||||||
|
|
||||||
// 同じ型に変換すると数値が同じだと判定できる
|
// 同じ型に変換すると数値が同じだと判定できる
|
||||||
assertEquals(true, int10.toLong() == long10)
|
assertEquals(true, int10.toLong() == long10)
|
||||||
|
|
11
build.gradle
11
build.gradle
|
@ -3,13 +3,15 @@ buildscript {
|
||||||
ext.min_sdk_version = 21
|
ext.min_sdk_version = 21
|
||||||
ext.target_sdk_version = 30
|
ext.target_sdk_version = 30
|
||||||
ext.compile_sdk_version = 30
|
ext.compile_sdk_version = 30
|
||||||
ext.appcompat_version='1.2.0'
|
|
||||||
|
|
||||||
ext.kotlin_version = '1.4.32'
|
ext.appcompat_version='1.2.0'
|
||||||
ext.kotlinx_coroutines_version = '1.4.2'
|
ext.lifecycle_version='2.3.1'
|
||||||
|
|
||||||
|
ext.kotlin_version = '1.5.0'
|
||||||
|
ext.kotlinx_coroutines_version = '1.4.3'
|
||||||
ext.anko_version='0.10.8'
|
ext.anko_version='0.10.8'
|
||||||
|
|
||||||
ext.junit_version='4.13.1'
|
ext.junit_version='4.13.2'
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
|
@ -20,6 +22,7 @@ buildscript {
|
||||||
classpath 'com.android.tools.build:gradle:4.2.0'
|
classpath 'com.android.tools.build:gradle:4.2.0'
|
||||||
classpath 'com.google.gms:google-services:4.3.5'
|
classpath 'com.google.gms:google-services:4.3.5'
|
||||||
|
|
||||||
|
//noinspection DifferentKotlinGradleVersion
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
|
|
||||||
classpath "com.github.bjoernq:unmockplugin:0.7.6"
|
classpath "com.github.bjoernq:unmockplugin:0.7.6"
|
||||||
|
|
|
@ -34,7 +34,7 @@ object EmojiMap {
|
||||||
|
|
||||||
// 素の数字とcopyright,registered, trademark は絵文字にしない
|
// 素の数字とcopyright,registered, trademark は絵文字にしない
|
||||||
fun isIgnored(code: String): Boolean {
|
fun isIgnored(code: String): Boolean {
|
||||||
val c = code[0].toInt()
|
val c = code[0].code
|
||||||
return code.length == 1 && c <= 0xae
|
return code.length == 1 && c <= 0xae
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,17 +15,17 @@ class EmojiTrie<T> {
|
||||||
this.data = data
|
this.data = data
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
val c = src[offset].toInt()
|
val c = src[offset].code
|
||||||
val next = map[c] ?: EmojiTrie<T>().also { map.put(c, it) }
|
val next = map[c] ?: EmojiTrie<T>().also { map.put(c, it) }
|
||||||
next.append(src, offset + 1, data)
|
next.append(src, offset + 1, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun hasNext(c: Char) = map.containsKey(c.toInt())
|
fun hasNext(c: Char) = map.containsKey(c.code)
|
||||||
|
|
||||||
fun get(src: String, offset: Int, end: Int): Result<T>? {
|
fun get(src: String, offset: Int, end: Int): Result<T>? {
|
||||||
// 長い方を優先するので、先に子を調べる
|
// 長い方を優先するので、先に子を調べる
|
||||||
if (offset < end)
|
if (offset < end)
|
||||||
map[src[offset].toInt()]?.get(src, offset + 1, end)
|
map[src[offset].code]?.get(src, offset + 1, end)
|
||||||
?.let { return it }
|
?.let { return it }
|
||||||
return this.data?.let { Result(it, offset) }
|
return this.data?.let { Result(it, offset) }
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#Wed May 05 20:20:56 JST 2021
|
#Wed May 05 20:20:56 JST 2021
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-6.9-bin.zip
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
|
|
@ -40,6 +40,9 @@ dependencies {
|
||||||
implementation project(':apng_android')
|
implementation project(':apng_android')
|
||||||
|
|
||||||
implementation "androidx.appcompat:appcompat:$appcompat_version"
|
implementation "androidx.appcompat:appcompat:$appcompat_version"
|
||||||
|
//noinspection KtxExtensionAvailable
|
||||||
|
implementation "androidx.lifecycle:lifecycle-viewmodel:$lifecycle_version"
|
||||||
|
|
||||||
testImplementation "junit:junit:$junit_version"
|
testImplementation "junit:junit:$junit_version"
|
||||||
androidTestImplementation 'androidx.test:runner:1.3.0'
|
androidTestImplementation 'androidx.test:runner:1.3.0'
|
||||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
|
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
|
||||||
|
|
|
@ -5,9 +5,6 @@ import android.content.pm.PackageManager
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.SystemClock
|
import android.os.SystemClock
|
||||||
import androidx.core.app.ActivityCompat
|
|
||||||
import androidx.core.content.ContextCompat
|
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
@ -15,206 +12,209 @@ import android.widget.AdapterView
|
||||||
import android.widget.BaseAdapter
|
import android.widget.BaseAdapter
|
||||||
import android.widget.ListView
|
import android.widget.ListView
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.core.app.ActivityCompat
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
import jp.juggler.apng.ApngFrames
|
import jp.juggler.apng.ApngFrames
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import kotlin.coroutines.CoroutineContext
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
|
||||||
class ActList : AppCompatActivity(), CoroutineScope {
|
class ActList : AppCompatActivity(), CoroutineScope {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val TAG = "ActList"
|
const val TAG = "ActList"
|
||||||
|
|
||||||
const val PERMISSION_REQUEST_CODE_STORAGE = 1
|
const val PERMISSION_REQUEST_CODE_STORAGE = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
class ListItem(val id : Int, val caption : String)
|
class ListItem(val id: Int, val caption: String)
|
||||||
|
|
||||||
private lateinit var listView : ListView
|
private lateinit var listView: ListView
|
||||||
private lateinit var listAdapter : MyAdapter
|
private lateinit var listAdapter: MyAdapter
|
||||||
private var timeAnimationStart : Long = 0L
|
private var timeAnimationStart: Long = 0L
|
||||||
|
|
||||||
private lateinit var activityJob : Job
|
private lateinit var activityJob: Job
|
||||||
|
|
||||||
override val coroutineContext : CoroutineContext
|
override val coroutineContext: CoroutineContext
|
||||||
get() = Dispatchers.Main + activityJob
|
get() = Dispatchers.Main + activityJob
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState : Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
|
||||||
activityJob = Job()
|
activityJob = Job()
|
||||||
|
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(R.layout.act_list)
|
setContentView(R.layout.act_list)
|
||||||
this.listView = findViewById(R.id.listView)
|
this.listView = findViewById(R.id.listView)
|
||||||
listAdapter = MyAdapter()
|
listAdapter = MyAdapter()
|
||||||
listView.adapter = listAdapter
|
listView.adapter = listAdapter
|
||||||
listView.onItemClickListener = listAdapter
|
listView.onItemClickListener = listAdapter
|
||||||
timeAnimationStart = SystemClock.elapsedRealtime()
|
timeAnimationStart = SystemClock.elapsedRealtime()
|
||||||
|
|
||||||
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
// Assume thisActivity is the current activity
|
// Assume thisActivity is the current activity
|
||||||
if(PackageManager.PERMISSION_GRANTED != ContextCompat.checkSelfPermission(
|
if (PackageManager.PERMISSION_GRANTED != ContextCompat.checkSelfPermission(
|
||||||
this,
|
this,
|
||||||
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||||
)) {
|
)
|
||||||
ActivityCompat.requestPermissions(
|
) {
|
||||||
this,
|
ActivityCompat.requestPermissions(
|
||||||
arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),
|
this,
|
||||||
|
arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),
|
||||||
|
|
||||||
PERMISSION_REQUEST_CODE_STORAGE
|
PERMISSION_REQUEST_CODE_STORAGE
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
load()
|
load()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
activityJob.cancel()
|
activityJob.cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onRequestPermissionsResult(
|
override fun onRequestPermissionsResult(
|
||||||
requestCode : Int,
|
requestCode: Int,
|
||||||
permissions : Array<String>,
|
permissions: Array<String>,
|
||||||
grantResults : IntArray
|
grantResults: IntArray
|
||||||
) {
|
) {
|
||||||
when(requestCode) {
|
when (requestCode) {
|
||||||
PERMISSION_REQUEST_CODE_STORAGE -> {
|
PERMISSION_REQUEST_CODE_STORAGE -> {
|
||||||
// If request is cancelled, the result arrays are empty.
|
// If request is cancelled, the result arrays are empty.
|
||||||
if(grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||||
// permission was granted, yay! Do the
|
// permission was granted, yay! Do the
|
||||||
// contacts-related task you need to do.
|
// contacts-related task you need to do.
|
||||||
} else {
|
} else {
|
||||||
// permission denied, boo! Disable the
|
// permission denied, boo! Disable the
|
||||||
// functionality that depends on this permission.
|
// functionality that depends on this permission.
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// other 'case' lines to check for other
|
// other 'case' lines to check for other
|
||||||
// permissions this app might request
|
// permissions this app might request
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun load() = launch {
|
private fun load() = launch {
|
||||||
val list = async(Dispatchers.IO) {
|
val list = withContext(Dispatchers.IO) {
|
||||||
// RawリソースのIDと名前の一覧
|
// RawリソースのIDと名前の一覧
|
||||||
R.raw::class.java.fields
|
R.raw::class.java.fields
|
||||||
.mapNotNull { it.get(null) as? Int }
|
.mapNotNull { it.get(null) as? Int }
|
||||||
.map { id ->
|
.map { id ->
|
||||||
ListItem(
|
ListItem(
|
||||||
id,
|
id,
|
||||||
resources.getResourceName(id)
|
resources.getResourceName(id)
|
||||||
.replaceFirst(""".+/""".toRegex(), "")
|
.replaceFirst(""".+/""".toRegex(), "")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.toMutableList()
|
.toMutableList()
|
||||||
.apply { sortBy { it.caption } }
|
.apply { sortBy { it.caption } }
|
||||||
|
}
|
||||||
|
|
||||||
}.await()
|
listAdapter.list.addAll(list)
|
||||||
|
listAdapter.notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
|
||||||
listAdapter.list.addAll(list)
|
inner class MyAdapter : BaseAdapter(), AdapterView.OnItemClickListener {
|
||||||
listAdapter.notifyDataSetChanged()
|
|
||||||
}
|
|
||||||
|
|
||||||
inner class MyAdapter : BaseAdapter(), AdapterView.OnItemClickListener {
|
val list = ArrayList<ListItem>()
|
||||||
|
|
||||||
val list = ArrayList<ListItem>()
|
override fun getCount(): Int {
|
||||||
|
return list.size
|
||||||
|
}
|
||||||
|
|
||||||
override fun getCount() : Int {
|
override fun getItem(position: Int): Any {
|
||||||
return list.size
|
return list[position]
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getItem(position : Int) : Any {
|
override fun getItemId(position: Int): Long {
|
||||||
return list[position]
|
return list[position].id.toLong()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getItemId(position : Int) : Long {
|
override fun getView(
|
||||||
return list[position].id.toLong()
|
position: Int,
|
||||||
}
|
viewArg: View?,
|
||||||
|
parent: ViewGroup?
|
||||||
|
): View {
|
||||||
|
val view: View
|
||||||
|
val holder: MyViewHolder
|
||||||
|
if (viewArg == null) {
|
||||||
|
view = layoutInflater.inflate(R.layout.lv_item, parent, false)
|
||||||
|
holder = MyViewHolder(view, this@ActList)
|
||||||
|
view.tag = holder
|
||||||
|
} else {
|
||||||
|
view = viewArg
|
||||||
|
holder = view.tag as MyViewHolder
|
||||||
|
}
|
||||||
|
holder.bind(list[position])
|
||||||
|
return view
|
||||||
|
}
|
||||||
|
|
||||||
override fun getView(
|
override fun onItemClick(
|
||||||
position : Int,
|
parent: AdapterView<*>?,
|
||||||
viewArg : View?,
|
view: View?,
|
||||||
parent : ViewGroup?
|
position: Int,
|
||||||
) : View {
|
id: Long
|
||||||
val view : View
|
) {
|
||||||
val holder : MyViewHolder
|
val item = list[position]
|
||||||
if(viewArg == null) {
|
ActViewer.open(this@ActList, item.id, item.caption)
|
||||||
view = layoutInflater.inflate(R.layout.lv_item, parent, false)
|
}
|
||||||
holder = MyViewHolder(view, this@ActList)
|
|
||||||
view.tag = holder
|
|
||||||
} else {
|
|
||||||
view = viewArg
|
|
||||||
holder = view.tag as MyViewHolder
|
|
||||||
}
|
|
||||||
holder.bind(list[position])
|
|
||||||
return view
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onItemClick(
|
}
|
||||||
parent : AdapterView<*>?,
|
|
||||||
view : View?,
|
|
||||||
position : Int,
|
|
||||||
id : Long
|
|
||||||
) {
|
|
||||||
val item = list[position]
|
|
||||||
ActViewer.open(this@ActList, item.id, item.caption)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
inner class MyViewHolder(
|
||||||
|
viewRoot: View,
|
||||||
|
_activity: ActList
|
||||||
|
) {
|
||||||
|
|
||||||
inner class MyViewHolder(
|
private val tvCaption: TextView = viewRoot.findViewById(R.id.tvCaption)
|
||||||
viewRoot : View,
|
private val apngView: ApngView = viewRoot.findViewById(R.id.apngView)
|
||||||
_activity : ActList
|
|
||||||
) {
|
|
||||||
|
|
||||||
private val tvCaption : TextView = viewRoot.findViewById(R.id.tvCaption)
|
init {
|
||||||
private val apngView : ApngView = viewRoot.findViewById(R.id.apngView)
|
apngView.timeAnimationStart = _activity.timeAnimationStart
|
||||||
|
}
|
||||||
|
|
||||||
init {
|
private var lastId: Int = 0
|
||||||
apngView.timeAnimationStart = _activity.timeAnimationStart
|
private var lastJob: Job? = null
|
||||||
}
|
|
||||||
|
|
||||||
private var lastId : Int = 0
|
fun bind(listItem: ListItem) {
|
||||||
private var lastJob : Job? = null
|
tvCaption.text = listItem.caption
|
||||||
|
|
||||||
fun bind(listItem : ListItem) {
|
val resId = listItem.id
|
||||||
tvCaption.text = listItem.caption
|
if (lastId != resId) {
|
||||||
|
lastId = resId
|
||||||
|
apngView.apngFrames?.dispose()
|
||||||
|
apngView.apngFrames = null
|
||||||
|
launch {
|
||||||
|
var apngFrames: ApngFrames? = null
|
||||||
|
try {
|
||||||
|
lastJob?.cancelAndJoin()
|
||||||
|
|
||||||
val resId = listItem.id
|
val job = async(Dispatchers.IO) {
|
||||||
if(lastId != resId) {
|
try {
|
||||||
lastId = resId
|
ApngFrames.parse(128) { resources?.openRawResource(resId) }
|
||||||
apngView.apngFrames?.dispose()
|
} catch (ex: Throwable) {
|
||||||
apngView.apngFrames = null
|
ex.printStackTrace()
|
||||||
launch {
|
null
|
||||||
var apngFrames : ApngFrames? = null
|
}
|
||||||
try {
|
}
|
||||||
lastJob?.cancelAndJoin()
|
|
||||||
|
|
||||||
val job = async(Dispatchers.IO) {
|
lastJob = job
|
||||||
try {
|
apngFrames = job.await()
|
||||||
ApngFrames.parse(128){resources?.openRawResource(resId)}
|
|
||||||
} catch(ex : Throwable) {
|
|
||||||
ex.printStackTrace()
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
lastJob = job
|
if (apngFrames != null && lastId == resId) {
|
||||||
apngFrames = job.await()
|
apngView.apngFrames = apngFrames
|
||||||
|
apngFrames = null
|
||||||
|
}
|
||||||
|
|
||||||
if(apngFrames != null && lastId == resId) {
|
} catch (ex: Throwable) {
|
||||||
apngView.apngFrames = apngFrames
|
ex.printStackTrace()
|
||||||
apngFrames = null
|
Log.e(TAG, "load error: ${ex.javaClass.simpleName} ${ex.message}")
|
||||||
}
|
} finally {
|
||||||
|
apngFrames?.dispose()
|
||||||
} catch(ex : Throwable) {
|
}
|
||||||
ex.printStackTrace()
|
}
|
||||||
Log.e(TAG, "load error: ${ex.javaClass.simpleName} ${ex.message}")
|
}
|
||||||
} finally {
|
}
|
||||||
apngFrames?.dispose()
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import android.os.SystemClock
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import jp.juggler.apng.ApngFrames
|
import jp.juggler.apng.ApngFrames
|
||||||
|
import kotlin.math.max
|
||||||
|
|
||||||
class ApngView : View{
|
class ApngView : View{
|
||||||
|
|
||||||
|
@ -64,8 +65,8 @@ class ApngView : View{
|
||||||
override fun onSizeChanged(w : Int, h : Int, oldw : Int, oldh : Int) {
|
override fun onSizeChanged(w : Int, h : Int, oldw : Int, oldh : Int) {
|
||||||
super.onSizeChanged(w, h, oldw, oldh)
|
super.onSizeChanged(w, h, oldw, oldh)
|
||||||
|
|
||||||
wView = Math.max(1, w).toFloat()
|
wView = max(1, w).toFloat()
|
||||||
hView = Math.max(1, h).toFloat()
|
hView = max(1, h).toFloat()
|
||||||
aspectView = wView / hView
|
aspectView = wView / hView
|
||||||
|
|
||||||
initializeScale()
|
initializeScale()
|
||||||
|
@ -74,8 +75,8 @@ class ApngView : View{
|
||||||
private fun initializeScale(){
|
private fun initializeScale(){
|
||||||
val apngFrames = this.apngFrames
|
val apngFrames = this.apngFrames
|
||||||
if( apngFrames != null) {
|
if( apngFrames != null) {
|
||||||
wImage =Math.max(1, apngFrames.width).toFloat()
|
wImage = max(1, apngFrames.width).toFloat()
|
||||||
hImage =Math.max(1, apngFrames.height).toFloat()
|
hImage = max(1, apngFrames.height).toFloat()
|
||||||
aspectImage = wImage / hImage
|
aspectImage = wImage / hImage
|
||||||
|
|
||||||
currentScale = if(aspectView > aspectImage) {
|
currentScale = if(aspectView > aspectImage) {
|
||||||
|
@ -118,7 +119,7 @@ class ApngView : View{
|
||||||
canvas.drawBitmap(bitmap, drawMatrix, paint)
|
canvas.drawBitmap(bitmap, drawMatrix, paint)
|
||||||
|
|
||||||
if( delay != Long.MAX_VALUE){
|
if( delay != Long.MAX_VALUE){
|
||||||
postInvalidateDelayed(Math.max(1L,delay))
|
postInvalidateDelayed(max(1L,delay))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
>
|
>
|
||||||
<ListView
|
<ListView
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:id="@+id/listView"
|
android:id="@+id/listView"
|
||||||
|
|
Loading…
Reference in New Issue