クラッシュレポート対応。リファクタ。
This commit is contained in:
parent
ecb7b1bc75
commit
cb34c6c458
|
@ -18,6 +18,7 @@
|
|||
<inspection_tool class="PrivatePropertyName" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="PropertyName" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="TryFinallyCanBeTryWithResources" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="UnnecessaryModuleDependencyInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="UseWithIndex" enabled="false" level="INFO" enabled_by_default="false" />
|
||||
</profile>
|
||||
</component>
|
|
@ -12,8 +12,8 @@ android {
|
|||
minSdkVersion 21
|
||||
targetSdkVersion 27
|
||||
|
||||
versionCode 210
|
||||
versionName "2.1.0"
|
||||
versionCode 211
|
||||
versionName "2.1.1"
|
||||
|
||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||
|
||||
|
@ -119,6 +119,8 @@ dependencies {
|
|||
|
||||
|
||||
// compile 'com.simplecityapps:recyclerview-fastscroll:1.0.16'
|
||||
|
||||
implementation 'me.drakeet.support:toastcompat:1.0.2'
|
||||
}
|
||||
|
||||
repositories {
|
||||
|
|
|
@ -24,7 +24,7 @@ class TestDuplicateMap {
|
|||
)
|
||||
)
|
||||
|
||||
private val generatedItems = ArrayList<Any>()
|
||||
private val generatedItems = ArrayList<TimelineItem>()
|
||||
|
||||
private fun genStatus(
|
||||
parser : TootParser,
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
@file:Suppress("MemberVisibilityCanBePrivate")
|
||||
|
||||
package jp.juggler.subwaytooter.api
|
||||
|
||||
import android.support.test.InstrumentationRegistry
|
||||
|
|
|
@ -4,7 +4,8 @@ import android.support.test.runner.AndroidJUnit4
|
|||
import android.test.mock.MockContext
|
||||
import jp.juggler.subwaytooter.api.TootParser
|
||||
import jp.juggler.subwaytooter.table.SavedAccount
|
||||
import jp.juggler.subwaytooter.util.Utils
|
||||
import jp.juggler.subwaytooter.util.parseLong
|
||||
import jp.juggler.subwaytooter.util.toJsonObject
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
import org.junit.Assert.*
|
||||
|
@ -18,13 +19,13 @@ class TestEntityUtils {
|
|||
class TestEntity(val s : String, val l : Long) : Mappable<String> {
|
||||
constructor(src : JSONObject) : this(
|
||||
s = src.notEmptyOrThrow("s"),
|
||||
l = Utils.optLongX(src, "l")
|
||||
l = src.parseLong("l") ?: -1L
|
||||
)
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
constructor(parser : TootParser, src : JSONObject) : this(
|
||||
s = src.notEmptyOrThrow("s"),
|
||||
l = Utils.optLongX(src, "l")
|
||||
l = src.parseLong("l") ?: -1L
|
||||
)
|
||||
|
||||
override val mapKey : String
|
||||
|
@ -36,38 +37,38 @@ class TestEntityUtils {
|
|||
assertEquals(null, parseItem(::TestEntity, null))
|
||||
|
||||
run {
|
||||
val src = JSONObject("""{"s":null,"l":"100"}""")
|
||||
val src = """{"s":null,"l":"100"}""".toJsonObject()
|
||||
val item = parseItem(::TestEntity, src)
|
||||
assertNull(item)
|
||||
}
|
||||
run {
|
||||
val src = JSONObject("""{"s":"","l":"100"}""")
|
||||
val src = """{"s":"","l":"100"}""".toJsonObject()
|
||||
val item = parseItem(::TestEntity, src)
|
||||
assertNull(item)
|
||||
}
|
||||
run {
|
||||
val src = JSONObject("""{"s":"A","l":null}""")
|
||||
val src = """{"s":"A","l":null}""".toJsonObject()
|
||||
val item = parseItem(::TestEntity, src)
|
||||
assertNotNull(item)
|
||||
assertEquals(src.optString("s"), item?.s)
|
||||
assertEquals(src.optLong("l"), item?.l)
|
||||
}
|
||||
run {
|
||||
val src = JSONObject("""{"s":"A","l":""}""")
|
||||
val src = """{"s":"A","l":""}""".toJsonObject()
|
||||
val item = parseItem(::TestEntity, src)
|
||||
assertNotNull(item)
|
||||
assertEquals(src.optString("s"), item?.s)
|
||||
assertEquals(src.optLong("l"), item?.l)
|
||||
}
|
||||
run {
|
||||
val src = JSONObject("""{"s":"A","l":100}""")
|
||||
val src = """{"s":"A","l":100}""".toJsonObject()
|
||||
val item = parseItem(::TestEntity, src)
|
||||
assertNotNull(item)
|
||||
assertEquals(src.optString("s"), item?.s)
|
||||
assertEquals(src.optLong("l"), item?.l)
|
||||
}
|
||||
run {
|
||||
val src = JSONObject("""{"s":"A","l":"100"}""")
|
||||
val src ="""{"s":"A","l":"100"}""".toJsonObject()
|
||||
val item = parseItem(::TestEntity, src)
|
||||
assertNotNull(item)
|
||||
assertEquals(src.optString("s"), item?.s)
|
||||
|
@ -76,77 +77,77 @@ class TestEntityUtils {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun TestParseList() {
|
||||
fun testParseList() {
|
||||
assertEquals(0, parseList(::TestEntity, null).size)
|
||||
|
||||
val src = JSONArray()
|
||||
assertEquals(0, parseList(::TestEntity, src).size)
|
||||
|
||||
src.put(JSONObject("""{"s":"A","l":"100"}"""))
|
||||
src.put("""{"s":"A","l":"100"}""".toJsonObject())
|
||||
assertEquals(1, parseList(::TestEntity, src).size)
|
||||
|
||||
src.put(JSONObject("""{"s":"A","l":"100"}"""))
|
||||
src.put("""{"s":"A","l":"100"}""".toJsonObject())
|
||||
assertEquals(2, parseList(::TestEntity, src).size)
|
||||
|
||||
// error
|
||||
src.put(JSONObject("""{"s":"","l":"100"}"""))
|
||||
src.put("""{"s":"","l":"100"}""".toJsonObject())
|
||||
assertEquals(2, parseList(::TestEntity, src).size)
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
fun TestParseListOrNull() {
|
||||
fun testParseListOrNull() {
|
||||
assertEquals(null, parseListOrNull(::TestEntity, null))
|
||||
|
||||
val src = JSONArray()
|
||||
assertEquals(null, parseListOrNull(::TestEntity, src))
|
||||
|
||||
src.put(JSONObject("""{"s":"A","l":"100"}"""))
|
||||
src.put("""{"s":"A","l":"100"}""".toJsonObject())
|
||||
assertEquals(1, parseListOrNull(::TestEntity, src)?.size)
|
||||
|
||||
src.put(JSONObject("""{"s":"A","l":"100"}"""))
|
||||
src.put("""{"s":"A","l":"100"}""".toJsonObject())
|
||||
assertEquals(2, parseListOrNull(::TestEntity, src)?.size)
|
||||
|
||||
// error
|
||||
src.put(JSONObject("""{"s":"","l":"100"}"""))
|
||||
src.put("""{"s":"","l":"100"}""".toJsonObject())
|
||||
assertEquals(2, parseListOrNull(::TestEntity, src)?.size)
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
fun TestParseMap() {
|
||||
fun testParseMap() {
|
||||
assertEquals(0, parseMap(::TestEntity, null).size)
|
||||
|
||||
val src = JSONArray()
|
||||
assertEquals(0, parseMap(::TestEntity, src).size)
|
||||
|
||||
src.put(JSONObject("""{"s":"A","l":"100"}"""))
|
||||
src.put("""{"s":"A","l":"100"}""".toJsonObject())
|
||||
assertEquals(1, parseMap(::TestEntity, src).size)
|
||||
|
||||
src.put(JSONObject("""{"s":"B","l":"100"}"""))
|
||||
src.put("""{"s":"B","l":"100"}""".toJsonObject())
|
||||
assertEquals(2, parseMap(::TestEntity, src).size)
|
||||
|
||||
// error
|
||||
src.put(JSONObject("""{"s":"","l":"100"}"""))
|
||||
src.put("""{"s":"","l":"100"}""".toJsonObject())
|
||||
assertEquals(2, parseMap(::TestEntity, src).size)
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
fun TestParseMapOrNull() {
|
||||
fun testParseMapOrNull() {
|
||||
assertEquals(null, parseMapOrNull(::TestEntity, null))
|
||||
|
||||
val src = JSONArray()
|
||||
assertEquals(null, parseMapOrNull(::TestEntity, src))
|
||||
|
||||
src.put(JSONObject("""{"s":"A","l":"100"}"""))
|
||||
src.put("""{"s":"A","l":"100"}""".toJsonObject())
|
||||
assertEquals(1, parseMapOrNull(::TestEntity, src)?.size)
|
||||
|
||||
src.put(JSONObject("""{"s":"B","l":"100"}"""))
|
||||
src.put("""{"s":"B","l":"100"}""".toJsonObject())
|
||||
assertEquals(2, parseMapOrNull(::TestEntity, src)?.size)
|
||||
|
||||
// error
|
||||
src.put(JSONObject("""{"s":"","l":"100"}"""))
|
||||
src.put("""{"s":"","l":"100"}""".toJsonObject())
|
||||
assertEquals(2, parseMapOrNull(::TestEntity, src)?.size)
|
||||
|
||||
}
|
||||
|
@ -159,38 +160,38 @@ class TestEntityUtils {
|
|||
assertEquals(null, parseItem(::TestEntity, parser, null))
|
||||
|
||||
run {
|
||||
val src = JSONObject("""{"s":null,"l":"100"}""")
|
||||
val src ="""{"s":null,"l":"100"}""".toJsonObject()
|
||||
val item = parseItem(::TestEntity, parser, src)
|
||||
assertNull(item)
|
||||
}
|
||||
run {
|
||||
val src = JSONObject("""{"s":"","l":"100"}""")
|
||||
val src = """{"s":"","l":"100"}""".toJsonObject()
|
||||
val item = parseItem(::TestEntity, parser, src)
|
||||
assertNull(item)
|
||||
}
|
||||
run {
|
||||
val src = JSONObject("""{"s":"A","l":null}""")
|
||||
val src = """{"s":"A","l":null}""".toJsonObject()
|
||||
val item = parseItem(::TestEntity, parser, src)
|
||||
assertNotNull(item)
|
||||
assertEquals(src.optString("s"), item?.s)
|
||||
assertEquals(src.optLong("l"), item?.l)
|
||||
}
|
||||
run {
|
||||
val src = JSONObject("""{"s":"A","l":""}""")
|
||||
val src = """{"s":"A","l":""}""".toJsonObject()
|
||||
val item = parseItem(::TestEntity, parser, src)
|
||||
assertNotNull(item)
|
||||
assertEquals(src.optString("s"), item?.s)
|
||||
assertEquals(src.optLong("l"), item?.l)
|
||||
}
|
||||
run {
|
||||
val src = JSONObject("""{"s":"A","l":100}""")
|
||||
val src = """{"s":"A","l":100}""".toJsonObject()
|
||||
val item = parseItem(::TestEntity, parser, src)
|
||||
assertNotNull(item)
|
||||
assertEquals(src.optString("s"), item?.s)
|
||||
assertEquals(src.optLong("l"), item?.l)
|
||||
}
|
||||
run {
|
||||
val src = JSONObject("""{"s":"A","l":"100"}""")
|
||||
val src = """{"s":"A","l":"100"}""".toJsonObject()
|
||||
val item = parseItem(::TestEntity, parser, src)
|
||||
assertNotNull(item)
|
||||
assertEquals(src.optString("s"), item?.s)
|
||||
|
@ -199,70 +200,70 @@ class TestEntityUtils {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun TestParseListWithParser() {
|
||||
fun testParseListWithParser() {
|
||||
assertEquals(0, parseList(::TestEntity, parser, null).size)
|
||||
|
||||
val src = JSONArray()
|
||||
assertEquals(0, parseList(::TestEntity, parser, src).size)
|
||||
|
||||
src.put(JSONObject("""{"s":"A","l":"100"}"""))
|
||||
src.put("""{"s":"A","l":"100"}""".toJsonObject())
|
||||
assertEquals(1, parseList(::TestEntity, parser, src).size)
|
||||
|
||||
src.put(JSONObject("""{"s":"A","l":"100"}"""))
|
||||
src.put("""{"s":"A","l":"100"}""".toJsonObject())
|
||||
assertEquals(2, parseList(::TestEntity, parser, src).size)
|
||||
|
||||
// error
|
||||
src.put(JSONObject("""{"s":"","l":"100"}"""))
|
||||
src.put("""{"s":"","l":"100"}""".toJsonObject())
|
||||
assertEquals(2, parseList(::TestEntity, parser, src).size)
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
fun TestParseListOrNullWithParser() {
|
||||
fun testParseListOrNullWithParser() {
|
||||
assertEquals(null, parseListOrNull(::TestEntity, parser, null))
|
||||
|
||||
val src = JSONArray()
|
||||
assertEquals(null, parseListOrNull(::TestEntity, parser, src))
|
||||
|
||||
src.put(JSONObject("""{"s":"A","l":"100"}"""))
|
||||
src.put("""{"s":"A","l":"100"}""".toJsonObject())
|
||||
assertEquals(1, parseListOrNull(::TestEntity, parser, src)?.size)
|
||||
|
||||
src.put(JSONObject("""{"s":"A","l":"100"}"""))
|
||||
src.put("""{"s":"A","l":"100"}""".toJsonObject())
|
||||
assertEquals(2, parseListOrNull(::TestEntity, parser, src)?.size)
|
||||
|
||||
// error
|
||||
src.put(JSONObject("""{"s":"","l":"100"}"""))
|
||||
src.put("""{"s":"","l":"100"}""".toJsonObject())
|
||||
assertEquals(2, parseListOrNull(::TestEntity, parser, src)?.size)
|
||||
|
||||
}
|
||||
|
||||
@Test(expected = RuntimeException::class)
|
||||
fun TestNotEmptyOrThrow1() {
|
||||
fun testNotEmptyOrThrow1() {
|
||||
println(notEmptyOrThrow("param1", null))
|
||||
}
|
||||
|
||||
@Test(expected = RuntimeException::class)
|
||||
fun TestNotEmptyOrThrow2() {
|
||||
fun testNotEmptyOrThrow2() {
|
||||
println(notEmptyOrThrow("param1", ""))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun TestNotEmptyOrThrow3() {
|
||||
fun testNotEmptyOrThrow3() {
|
||||
assertEquals("A", notEmptyOrThrow("param1", "A"))
|
||||
}
|
||||
|
||||
@Test(expected = RuntimeException::class)
|
||||
fun TestNotEmptyOrThrow4() {
|
||||
println(JSONObject("""{"param1":null}""").notEmptyOrThrow("param1"))
|
||||
fun testNotEmptyOrThrow4() {
|
||||
println("""{"param1":null}""".toJsonObject().notEmptyOrThrow("param1"))
|
||||
}
|
||||
|
||||
@Test(expected = RuntimeException::class)
|
||||
fun TestNotEmptyOrThrow5() {
|
||||
println(JSONObject("""{"param1":""}""").notEmptyOrThrow("param1"))
|
||||
fun testNotEmptyOrThrow5() {
|
||||
println("""{"param1":""}""".toJsonObject().notEmptyOrThrow("param1"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun TestNotEmptyOrThrow6() {
|
||||
assertEquals("A", JSONObject("""{"param1":"A"}""").notEmptyOrThrow("param1"))
|
||||
fun testNotEmptyOrThrow6() {
|
||||
assertEquals("A", """{"param1":"A"}""".toJsonObject().notEmptyOrThrow("param1"))
|
||||
}
|
||||
}
|
|
@ -37,9 +37,10 @@ class MyGifDrawable internal constructor(
|
|||
/**
|
||||
* True if the drawable's resources have been recycled.
|
||||
*/
|
||||
|
||||
// For testing.
|
||||
internal var isRecycled : Boolean = false
|
||||
private set
|
||||
private var isRecycled : Boolean = false
|
||||
|
||||
/**
|
||||
* True if the drawable is currently visible. Default to true because on certain platforms (at
|
||||
* least 4.1.1), setVisible is not called on [Drawables][android.graphics.drawable.Drawable]
|
||||
|
@ -74,7 +75,7 @@ class MyGifDrawable internal constructor(
|
|||
val buffer : ByteBuffer
|
||||
get() = state.frameLoader.buffer
|
||||
|
||||
val frameCount : Int
|
||||
private val frameCount : Int
|
||||
get() = state.frameLoader.frameCount
|
||||
|
||||
/**
|
||||
|
@ -82,7 +83,7 @@ class MyGifDrawable internal constructor(
|
|||
* is displayed.
|
||||
*/
|
||||
// Public API.
|
||||
val frameIndex : Int
|
||||
private val frameIndex : Int
|
||||
get() = state.frameLoader.currentIndex
|
||||
|
||||
/**
|
||||
|
|
|
@ -6,7 +6,6 @@ import android.content.pm.PackageManager
|
|||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.support.v7.app.AppCompatActivity
|
||||
import android.view.View
|
||||
import android.widget.Button
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
|
@ -18,13 +17,13 @@ class ActAbout : AppCompatActivity() {
|
|||
companion object {
|
||||
val log = LogCategory("ActAbout")
|
||||
|
||||
val EXTRA_SEARCH = "search"
|
||||
const val EXTRA_SEARCH = "search"
|
||||
|
||||
val url_store = "https://play.google.com/store/apps/details?id=jp.juggler.subwaytooter"
|
||||
const val url_store = "https://play.google.com/store/apps/details?id=jp.juggler.subwaytooter"
|
||||
|
||||
val developer_acct = "tateisu@mastodon.juggler.jp"
|
||||
const val developer_acct = "tateisu@mastodon.juggler.jp"
|
||||
|
||||
val url_futaba = "https://www.instagram.com/hinomoto_hutaba/"
|
||||
const val url_futaba = "https://www.instagram.com/hinomoto_hutaba/"
|
||||
|
||||
val contributors = arrayOf("@Balor@freeradical.zone", "update english language", "@Luattic@oc.todon.fr", "update french language")
|
||||
}
|
||||
|
@ -39,7 +38,8 @@ class ActAbout : AppCompatActivity() {
|
|||
|
||||
try {
|
||||
val pInfo = packageManager.getPackageInfo(packageName, 0)
|
||||
findViewById<TextView>(R.id.tvVersion) .text = getString(R.string.version_is, pInfo.versionName)
|
||||
val tv = findViewById<TextView>(R.id.tvVersion)
|
||||
tv.text = getString(R.string.version_is, pInfo.versionName)
|
||||
} catch(ex : PackageManager.NameNotFoundException) {
|
||||
log.trace(ex)
|
||||
}
|
||||
|
|
|
@ -49,20 +49,50 @@ import jp.juggler.subwaytooter.api.entity.TootStatus
|
|||
import jp.juggler.subwaytooter.dialog.ActionsDialog
|
||||
import jp.juggler.subwaytooter.table.AcctColor
|
||||
import jp.juggler.subwaytooter.table.SavedAccount
|
||||
import jp.juggler.subwaytooter.util.DecodeOptions
|
||||
import jp.juggler.subwaytooter.util.EmojiDecoder
|
||||
import jp.juggler.subwaytooter.util.LogCategory
|
||||
import jp.juggler.subwaytooter.util.NetworkEmojiInvalidator
|
||||
import jp.juggler.subwaytooter.util.NotificationHelper
|
||||
import jp.juggler.subwaytooter.util.Utils
|
||||
import jp.juggler.subwaytooter.util.*
|
||||
import jp.juggler.subwaytooter.view.MyNetworkImageView
|
||||
import okhttp3.Request
|
||||
import okhttp3.RequestBody
|
||||
|
||||
class ActAccountSetting : AppCompatActivity(), View.OnClickListener, CompoundButton.OnCheckedChangeListener {
|
||||
class ActAccountSetting
|
||||
: AppCompatActivity(), View.OnClickListener, CompoundButton.OnCheckedChangeListener {
|
||||
|
||||
lateinit internal var account : SavedAccount
|
||||
lateinit internal var pref : SharedPreferences
|
||||
|
||||
companion object {
|
||||
|
||||
internal val log = LogCategory("ActAccountSetting")
|
||||
|
||||
internal const val KEY_ACCOUNT_DB_ID = "account_db_id"
|
||||
|
||||
internal const val REQUEST_CODE_ACCT_CUSTOMIZE = 1
|
||||
internal const val REQUEST_CODE_NOTIFICATION_SOUND = 2
|
||||
private const val REQUEST_CODE_AVATAR_ATTACHMENT = 3
|
||||
private const val REQUEST_CODE_HEADER_ATTACHMENT = 4
|
||||
private const val REQUEST_CODE_AVATAR_CAMERA = 5
|
||||
private const val REQUEST_CODE_HEADER_CAMERA = 6
|
||||
|
||||
internal const val RESULT_INPUT_ACCESS_TOKEN = Activity.RESULT_FIRST_USER + 10
|
||||
internal const val EXTRA_DB_ID = "db_id"
|
||||
|
||||
internal const val max_length_display_name = 30
|
||||
internal const val max_length_note = 160
|
||||
|
||||
private const val PERMISSION_REQUEST_AVATAR = 1
|
||||
private const val PERMISSION_REQUEST_HEADER = 2
|
||||
|
||||
internal const val MIME_TYPE_JPEG = "image/jpeg"
|
||||
internal const val MIME_TYPE_PNG = "image/png"
|
||||
|
||||
fun open(activity : Activity, ai : SavedAccount, requestCode : Int) {
|
||||
val intent = Intent(activity, ActAccountSetting::class.java)
|
||||
intent.putExtra(KEY_ACCOUNT_DB_ID, ai.db_id)
|
||||
activity.startActivityForResult(intent, requestCode)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal lateinit var account : SavedAccount
|
||||
internal lateinit var pref : SharedPreferences
|
||||
|
||||
private lateinit var tvInstance : TextView
|
||||
private lateinit var tvUser : TextView
|
||||
|
@ -104,7 +134,7 @@ class ActAccountSetting : AppCompatActivity(), View.OnClickListener, CompoundBut
|
|||
private lateinit var btnNote : View
|
||||
private lateinit var name_invalidator : NetworkEmojiInvalidator
|
||||
private lateinit var note_invalidator : NetworkEmojiInvalidator
|
||||
lateinit internal var handler : Handler
|
||||
internal lateinit var handler : Handler
|
||||
|
||||
internal var loading = false
|
||||
|
||||
|
@ -123,7 +153,7 @@ class ActAccountSetting : AppCompatActivity(), View.OnClickListener, CompoundBut
|
|||
initUI()
|
||||
|
||||
val a = SavedAccount.loadAccount(this, intent.getLongExtra(KEY_ACCOUNT_DB_ID, - 1L))
|
||||
if(a == null){
|
||||
if(a == null) {
|
||||
finish()
|
||||
return
|
||||
}
|
||||
|
@ -152,7 +182,7 @@ class ActAccountSetting : AppCompatActivity(), View.OnClickListener, CompoundBut
|
|||
REQUEST_CODE_NOTIFICATION_SOUND -> {
|
||||
if(resultCode == Activity.RESULT_OK) {
|
||||
// RINGTONE_PICKERからの選択されたデータを取得する
|
||||
val uri = Utils.getExtraObject(data, RingtoneManager.EXTRA_RINGTONE_PICKED_URI)
|
||||
val uri = data?.extras?.get(RingtoneManager.EXTRA_RINGTONE_PICKED_URI)
|
||||
if(uri is Uri) {
|
||||
notification_sound_uri = uri.toString()
|
||||
saveUIToData()
|
||||
|
@ -174,7 +204,11 @@ class ActAccountSetting : AppCompatActivity(), View.OnClickListener, CompoundBut
|
|||
if(uri1 != null) {
|
||||
// 単一選択
|
||||
val type = data.type
|
||||
addAttachment(requestCode, uri1, if(type?.isNotEmpty() == true) type else contentResolver.getType(uri1))
|
||||
addAttachment(
|
||||
requestCode,
|
||||
uri1,
|
||||
if(type?.isNotEmpty() == true) type else contentResolver.getType(uri1)
|
||||
)
|
||||
} else {
|
||||
// 複数選択
|
||||
data.clipData?.let { clipData ->
|
||||
|
@ -195,7 +229,7 @@ class ActAccountSetting : AppCompatActivity(), View.OnClickListener, CompoundBut
|
|||
// 失敗したら DBからデータを削除
|
||||
val uriCameraImage = this@ActAccountSetting.uriCameraImage
|
||||
if(uriCameraImage != null) {
|
||||
contentResolver.delete(uriCameraImage , null, null)
|
||||
contentResolver.delete(uriCameraImage, null, null)
|
||||
this@ActAccountSetting.uriCameraImage = null
|
||||
}
|
||||
} else {
|
||||
|
@ -212,7 +246,7 @@ class ActAccountSetting : AppCompatActivity(), View.OnClickListener, CompoundBut
|
|||
}
|
||||
}
|
||||
|
||||
var density: Float = 1f
|
||||
var density : Float = 1f
|
||||
|
||||
private fun initUI() {
|
||||
this.density = resources.displayMetrics.density
|
||||
|
@ -348,7 +382,12 @@ class ActAccountSetting : AppCompatActivity(), View.OnClickListener, CompoundBut
|
|||
val ac = AcctColor.load(full_acct)
|
||||
val nickname = ac.nickname
|
||||
tvUserCustom.text = if(nickname?.isNotEmpty() == true) nickname else full_acct
|
||||
tvUserCustom.setTextColor(if(ac.color_fg != 0) ac.color_fg else Styler.getAttributeColor(this, R.attr.colorTimeSmall))
|
||||
tvUserCustom.setTextColor(
|
||||
if(ac.color_fg != 0) ac.color_fg else Styler.getAttributeColor(
|
||||
this,
|
||||
R.attr.colorTimeSmall
|
||||
)
|
||||
)
|
||||
tvUserCustom.setBackgroundColor(ac.color_bg)
|
||||
}
|
||||
|
||||
|
@ -390,7 +429,12 @@ class ActAccountSetting : AppCompatActivity(), View.OnClickListener, CompoundBut
|
|||
R.id.btnVisibility -> performVisibility()
|
||||
R.id.btnOpenBrowser -> open_browser("https://" + account.host + "/")
|
||||
|
||||
R.id.btnUserCustom -> ActNickname.open(this, full_acct, false, REQUEST_CODE_ACCT_CUSTOMIZE)
|
||||
R.id.btnUserCustom -> ActNickname.open(
|
||||
this,
|
||||
full_acct,
|
||||
false,
|
||||
REQUEST_CODE_ACCT_CUSTOMIZE
|
||||
)
|
||||
|
||||
R.id.btnNotificationSoundEdit -> openNotificationSoundPicker()
|
||||
|
||||
|
@ -432,7 +476,13 @@ class ActAccountSetting : AppCompatActivity(), View.OnClickListener, CompoundBut
|
|||
}
|
||||
|
||||
private fun performVisibility() {
|
||||
val caption_list = arrayOf(Styler.getVisibilityCaption(this, TootStatus.VISIBILITY_WEB_SETTING), Styler.getVisibilityCaption(this, TootStatus.VISIBILITY_PUBLIC), Styler.getVisibilityCaption(this, TootStatus.VISIBILITY_UNLISTED), Styler.getVisibilityCaption(this, TootStatus.VISIBILITY_PRIVATE), Styler.getVisibilityCaption(this, TootStatus.VISIBILITY_DIRECT))
|
||||
val caption_list = arrayOf(
|
||||
Styler.getVisibilityCaption(this, TootStatus.VISIBILITY_WEB_SETTING),
|
||||
Styler.getVisibilityCaption(this, TootStatus.VISIBILITY_PUBLIC),
|
||||
Styler.getVisibilityCaption(this, TootStatus.VISIBILITY_UNLISTED),
|
||||
Styler.getVisibilityCaption(this, TootStatus.VISIBILITY_PRIVATE),
|
||||
Styler.getVisibilityCaption(this, TootStatus.VISIBILITY_DIRECT)
|
||||
)
|
||||
|
||||
AlertDialog.Builder(this)
|
||||
.setTitle(R.string.choose_visibility)
|
||||
|
@ -462,7 +512,7 @@ class ActAccountSetting : AppCompatActivity(), View.OnClickListener, CompoundBut
|
|||
account.delete()
|
||||
|
||||
val pref = Pref.pref(this@ActAccountSetting)
|
||||
if(account.db_id == Pref.lpTabletTootDefaultAccount(pref) ) {
|
||||
if(account.db_id == Pref.lpTabletTootDefaultAccount(pref)) {
|
||||
pref.edit().put(Pref.lpTabletTootDefaultAccount, - 1L).apply()
|
||||
}
|
||||
|
||||
|
@ -474,7 +524,8 @@ class ActAccountSetting : AppCompatActivity(), View.OnClickListener, CompoundBut
|
|||
internal fun unregister() {
|
||||
try {
|
||||
|
||||
val install_id = PrefDevice.prefDevice(this@ActAccountSetting).getString(PrefDevice.KEY_INSTALL_ID, null)
|
||||
val install_id = PrefDevice.prefDevice(this@ActAccountSetting)
|
||||
.getString(PrefDevice.KEY_INSTALL_ID, null)
|
||||
if(install_id?.isEmpty() != false) {
|
||||
log.d("performAccountRemove: missing install_id")
|
||||
return
|
||||
|
@ -486,13 +537,18 @@ class ActAccountSetting : AppCompatActivity(), View.OnClickListener, CompoundBut
|
|||
return
|
||||
}
|
||||
|
||||
val post_data = ("instance_url=" + Uri.encode("https://" + account.host)
|
||||
+ "&app_id=" + Uri.encode(packageName)
|
||||
val post_data = ("instance_url=" + ("https://" + account.host).encodePercent()
|
||||
+ "&app_id=" + packageName.encodePercent()
|
||||
+ "&tag=" + tag)
|
||||
|
||||
val request = Request.Builder()
|
||||
.url(PollingWorker.APP_SERVER + "/unregister")
|
||||
.post(RequestBody.create(TootApiClient.MEDIA_TYPE_FORM_URL_ENCODED, post_data))
|
||||
.post(
|
||||
RequestBody.create(
|
||||
TootApiClient.MEDIA_TYPE_FORM_URL_ENCODED,
|
||||
post_data
|
||||
)
|
||||
)
|
||||
.build()
|
||||
|
||||
val call = App1.ok_http_client.newCall(request)
|
||||
|
@ -549,7 +605,7 @@ class ActAccountSetting : AppCompatActivity(), View.OnClickListener, CompoundBut
|
|||
}
|
||||
// エラーを表示
|
||||
error != null -> {
|
||||
Utils.showToast(this@ActAccountSetting, true, error)
|
||||
showToast(this@ActAccountSetting, true, error)
|
||||
log.e("can't get oauth browser URL. $error")
|
||||
}
|
||||
}
|
||||
|
@ -574,8 +630,11 @@ class ActAccountSetting : AppCompatActivity(), View.OnClickListener, CompoundBut
|
|||
intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, false)
|
||||
try {
|
||||
val notification_sound_uri = this.notification_sound_uri
|
||||
if(notification_sound_uri?.isNotEmpty() == true){
|
||||
intent.putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, Uri.parse(notification_sound_uri))
|
||||
if(notification_sound_uri?.isNotEmpty() == true) {
|
||||
intent.putExtra(
|
||||
RingtoneManager.EXTRA_RINGTONE_EXISTING_URI,
|
||||
Uri.parse(notification_sound_uri)
|
||||
)
|
||||
}
|
||||
} catch(ignored : Throwable) {
|
||||
}
|
||||
|
@ -589,7 +648,12 @@ class ActAccountSetting : AppCompatActivity(), View.OnClickListener, CompoundBut
|
|||
private fun initializeProfile() {
|
||||
// 初期状態
|
||||
ivProfileAvatar.setErrorImageResId(Styler.getAttributeResourceId(this, R.attr.ic_question))
|
||||
ivProfileAvatar.setDefaultImageResId(Styler.getAttributeResourceId(this, R.attr.ic_question))
|
||||
ivProfileAvatar.setDefaultImageResId(
|
||||
Styler.getAttributeResourceId(
|
||||
this,
|
||||
R.attr.ic_question
|
||||
)
|
||||
)
|
||||
etDisplayName.setText("(loading...)")
|
||||
etNote.setText("(loading...)")
|
||||
// 初期状態では編集不可能
|
||||
|
@ -628,7 +692,7 @@ class ActAccountSetting : AppCompatActivity(), View.OnClickListener, CompoundBut
|
|||
if(data != null) {
|
||||
showProfile(data)
|
||||
} else {
|
||||
Utils.showToast(this@ActAccountSetting, true, result.error)
|
||||
showToast(this@ActAccountSetting, true, result.error)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -676,9 +740,11 @@ class ActAccountSetting : AppCompatActivity(), View.OnClickListener, CompoundBut
|
|||
internal var data : TootAccount? = null
|
||||
override fun background(client : TootApiClient) : TootApiResult? {
|
||||
val request_builder = Request.Builder()
|
||||
.patch(RequestBody.create(
|
||||
TootApiClient.MEDIA_TYPE_FORM_URL_ENCODED, form_data
|
||||
))
|
||||
.patch(
|
||||
RequestBody.create(
|
||||
TootApiClient.MEDIA_TYPE_FORM_URL_ENCODED, form_data
|
||||
)
|
||||
)
|
||||
|
||||
val result = client.request("/api/v1/accounts/update_credentials", request_builder)
|
||||
val jsonObject = result?.jsonObject
|
||||
|
@ -696,7 +762,7 @@ class ActAccountSetting : AppCompatActivity(), View.OnClickListener, CompoundBut
|
|||
if(data != null) {
|
||||
showProfile(data)
|
||||
} else {
|
||||
Utils.showToast(this@ActAccountSetting, true, result.error)
|
||||
showToast(this@ActAccountSetting, true, result.error)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -710,8 +776,14 @@ class ActAccountSetting : AppCompatActivity(), View.OnClickListener, CompoundBut
|
|||
val length = sv.codePointCount(0, sv.length)
|
||||
if(length > max_length_display_name) {
|
||||
AlertDialog.Builder(this)
|
||||
.setMessage(getString(R.string.length_warning, getString(R.string.display_name), length, max_length_display_name
|
||||
))
|
||||
.setMessage(
|
||||
getString(
|
||||
R.string.length_warning,
|
||||
getString(R.string.display_name),
|
||||
length,
|
||||
max_length_display_name
|
||||
)
|
||||
)
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.setPositiveButton(R.string.ok) { _, _ -> sendDisplayName(true) }
|
||||
.setCancelable(true)
|
||||
|
@ -719,7 +791,7 @@ class ActAccountSetting : AppCompatActivity(), View.OnClickListener, CompoundBut
|
|||
return
|
||||
}
|
||||
}
|
||||
updateCredential("display_name=" + Uri.encode(EmojiDecoder.decodeShortCode(sv)))
|
||||
updateCredential("display_name=" + EmojiDecoder.decodeShortCode(sv).encodePercent() )
|
||||
}
|
||||
|
||||
private fun sendNote(bConfirmed : Boolean) {
|
||||
|
@ -728,8 +800,14 @@ class ActAccountSetting : AppCompatActivity(), View.OnClickListener, CompoundBut
|
|||
val length = sv.codePointCount(0, sv.length)
|
||||
if(length > max_length_note) {
|
||||
AlertDialog.Builder(this)
|
||||
.setMessage(getString(R.string.length_warning, getString(R.string.note), length, max_length_note
|
||||
))
|
||||
.setMessage(
|
||||
getString(
|
||||
R.string.length_warning,
|
||||
getString(R.string.note),
|
||||
length,
|
||||
max_length_note
|
||||
)
|
||||
)
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.setPositiveButton(R.string.ok) { _, _ -> sendNote(true) }
|
||||
.setCancelable(true)
|
||||
|
@ -737,7 +815,7 @@ class ActAccountSetting : AppCompatActivity(), View.OnClickListener, CompoundBut
|
|||
return
|
||||
}
|
||||
}
|
||||
updateCredential("note=" + Uri.encode(EmojiDecoder.decodeShortCode(sv)))
|
||||
updateCredential("note=" + EmojiDecoder.decodeShortCode(sv).encodePercent())
|
||||
}
|
||||
|
||||
private fun pickAvatarImage() {
|
||||
|
@ -749,7 +827,10 @@ class ActAccountSetting : AppCompatActivity(), View.OnClickListener, CompoundBut
|
|||
}
|
||||
|
||||
private fun openPicker(permission_request_code : Int) {
|
||||
val permissionCheck = ContextCompat.checkSelfPermission(this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
val permissionCheck = ContextCompat.checkSelfPermission(
|
||||
this,
|
||||
android.Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||
)
|
||||
if(permissionCheck != PackageManager.PERMISSION_GRANTED) {
|
||||
preparePermission(permission_request_code)
|
||||
return
|
||||
|
@ -779,11 +860,12 @@ class ActAccountSetting : AppCompatActivity(), View.OnClickListener, CompoundBut
|
|||
if(Build.VERSION.SDK_INT >= 23) {
|
||||
// No explanation needed, we can request the permission.
|
||||
|
||||
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), request_code
|
||||
ActivityCompat.requestPermissions(
|
||||
this, arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), request_code
|
||||
)
|
||||
return
|
||||
}
|
||||
Utils.showToast(this, true, R.string.missing_permission_to_access_media)
|
||||
showToast(this, true, R.string.missing_permission_to_access_media)
|
||||
}
|
||||
|
||||
override fun onRequestPermissionsResult(
|
||||
|
@ -795,7 +877,7 @@ class ActAccountSetting : AppCompatActivity(), View.OnClickListener, CompoundBut
|
|||
if(grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
openPicker(requestCode)
|
||||
} else {
|
||||
Utils.showToast(this, true, R.string.missing_permission_to_access_media)
|
||||
showToast(this, true, R.string.missing_permission_to_access_media)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -811,7 +893,7 @@ class ActAccountSetting : AppCompatActivity(), View.OnClickListener, CompoundBut
|
|||
startActivityForResult(intent, request_code)
|
||||
} catch(ex : Throwable) {
|
||||
log.trace(ex)
|
||||
Utils.showToast(this, ex, "ACTION_OPEN_DOCUMENT failed.")
|
||||
showToast(this, ex, "ACTION_OPEN_DOCUMENT failed.")
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -824,7 +906,8 @@ class ActAccountSetting : AppCompatActivity(), View.OnClickListener, CompoundBut
|
|||
val values = ContentValues()
|
||||
values.put(MediaStore.Images.Media.TITLE, filename)
|
||||
values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg")
|
||||
uriCameraImage = contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
|
||||
uriCameraImage =
|
||||
contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
|
||||
|
||||
val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
|
||||
intent.putExtra(MediaStore.EXTRA_OUTPUT, uriCameraImage)
|
||||
|
@ -832,7 +915,7 @@ class ActAccountSetting : AppCompatActivity(), View.OnClickListener, CompoundBut
|
|||
startActivityForResult(intent, request_code)
|
||||
} catch(ex : Throwable) {
|
||||
log.trace(ex)
|
||||
Utils.showToast(this, ex, "opening camera app failed.")
|
||||
showToast(this, ex, "opening camera app failed.")
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -862,12 +945,12 @@ class ActAccountSetting : AppCompatActivity(), View.OnClickListener, CompoundBut
|
|||
// 設定からリサイズ指定を読む
|
||||
val resize_to = 1280
|
||||
|
||||
val bitmap = Utils.createResizedBitmap(log, this, uri, false, resize_to)
|
||||
val bitmap = createResizedBitmap( this, uri, resize_to )
|
||||
if(bitmap != null) {
|
||||
try {
|
||||
val cache_dir = externalCacheDir
|
||||
if(cache_dir == null) {
|
||||
Utils.showToast(this, false, "getExternalCacheDir returns null.")
|
||||
showToast(this, false, "getExternalCacheDir returns null.")
|
||||
break
|
||||
}
|
||||
|
||||
|
@ -905,7 +988,7 @@ class ActAccountSetting : AppCompatActivity(), View.OnClickListener, CompoundBut
|
|||
|
||||
} catch(ex : Throwable) {
|
||||
log.trace(ex)
|
||||
Utils.showToast(this, ex, "Resizing image failed.")
|
||||
showToast(this, ex, "Resizing image failed.")
|
||||
}
|
||||
|
||||
break
|
||||
|
@ -929,12 +1012,12 @@ class ActAccountSetting : AppCompatActivity(), View.OnClickListener, CompoundBut
|
|||
private fun addAttachment(request_code : Int, uri : Uri, mime_type : String?) {
|
||||
|
||||
if(mime_type == null) {
|
||||
Utils.showToast(this, false, "mime type is not provided.")
|
||||
showToast(this, false, "mime type is not provided.")
|
||||
return
|
||||
}
|
||||
|
||||
if(! mime_type.startsWith("image/")) {
|
||||
Utils.showToast(this, false, "mime type is not image.")
|
||||
showToast(this, false, "mime type is not image.")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -948,22 +1031,27 @@ class ActAccountSetting : AppCompatActivity(), View.OnClickListener, CompoundBut
|
|||
opener.open().use { inData ->
|
||||
val bao = ByteArrayOutputStream()
|
||||
//
|
||||
bao.write(Utils.encodeUTF8("data:" + opener.mimeType + ";base64,"))
|
||||
bao.write("data:${opener.mimeType};base64,".encodeUTF8())
|
||||
//
|
||||
Base64OutputStream(bao, Base64.NO_WRAP).use { base64 -> IOUtils.copy(inData, base64) }
|
||||
val value = Utils.decodeUTF8(bao.toByteArray())
|
||||
Base64OutputStream(bao, Base64.NO_WRAP).use { base64 ->
|
||||
IOUtils.copy(
|
||||
inData,
|
||||
base64
|
||||
)
|
||||
}
|
||||
val value = bao.toByteArray().decodeUTF8()
|
||||
|
||||
return when(request_code) {
|
||||
REQUEST_CODE_HEADER_ATTACHMENT, REQUEST_CODE_HEADER_CAMERA -> "header="
|
||||
else -> "avatar="
|
||||
} + Uri.encode(value)
|
||||
} + value.encodePercent()
|
||||
}
|
||||
} finally {
|
||||
opener.deleteTempFile()
|
||||
}
|
||||
|
||||
} catch(ex : Throwable) {
|
||||
Utils.showToast(this@ActAccountSetting, ex, "image converting failed.")
|
||||
showToast(this@ActAccountSetting, ex, "image converting failed.")
|
||||
}
|
||||
|
||||
return null
|
||||
|
@ -979,37 +1067,5 @@ class ActAccountSetting : AppCompatActivity(), View.OnClickListener, CompoundBut
|
|||
task.executeOnExecutor(App1.task_executor)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
internal val log = LogCategory("ActAccountSetting")
|
||||
|
||||
internal val KEY_ACCOUNT_DB_ID = "account_db_id"
|
||||
|
||||
fun open(activity : Activity, ai : SavedAccount, requestCode : Int) {
|
||||
val intent = Intent(activity, ActAccountSetting::class.java)
|
||||
intent.putExtra(KEY_ACCOUNT_DB_ID, ai.db_id)
|
||||
activity.startActivityForResult(intent, requestCode)
|
||||
}
|
||||
|
||||
internal val REQUEST_CODE_ACCT_CUSTOMIZE = 1
|
||||
internal val REQUEST_CODE_NOTIFICATION_SOUND = 2
|
||||
private val REQUEST_CODE_AVATAR_ATTACHMENT = 3
|
||||
private val REQUEST_CODE_HEADER_ATTACHMENT = 4
|
||||
private val REQUEST_CODE_AVATAR_CAMERA = 5
|
||||
private val REQUEST_CODE_HEADER_CAMERA = 6
|
||||
|
||||
internal val RESULT_INPUT_ACCESS_TOKEN = Activity.RESULT_FIRST_USER + 10
|
||||
internal val EXTRA_DB_ID = "db_id"
|
||||
|
||||
internal val max_length_display_name = 30
|
||||
internal val max_length_note = 160
|
||||
|
||||
private val PERMISSION_REQUEST_AVATAR = 1
|
||||
private val PERMISSION_REQUEST_HEADER = 2
|
||||
|
||||
internal val MIME_TYPE_JPEG = "image/jpeg"
|
||||
internal val MIME_TYPE_PNG = "image/png"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -43,7 +43,7 @@ import java.util.Locale
|
|||
import jp.juggler.subwaytooter.table.AcctColor
|
||||
import jp.juggler.subwaytooter.table.SavedAccount
|
||||
import jp.juggler.subwaytooter.util.LogCategory
|
||||
import jp.juggler.subwaytooter.util.Utils
|
||||
import jp.juggler.subwaytooter.util.showToast
|
||||
|
||||
class ActAppSetting : AppCompatActivity()
|
||||
, CompoundButton.OnCheckedChangeListener
|
||||
|
@ -440,7 +440,7 @@ class ActAppSetting : AppCompatActivity()
|
|||
intent.type = "*/*"
|
||||
startActivityForResult(intent, REQUEST_CODE_TIMELINE_FONT)
|
||||
} catch(ex : Throwable) {
|
||||
Utils.showToast(this, ex, "could not open picker for font")
|
||||
showToast(this, ex, "could not open picker for font")
|
||||
}
|
||||
|
||||
R.id.btnTimelineFontBoldReset -> {
|
||||
|
@ -455,7 +455,7 @@ class ActAppSetting : AppCompatActivity()
|
|||
intent.type = "*/*"
|
||||
startActivityForResult(intent, REQUEST_CODE_TIMELINE_FONT_BOLD)
|
||||
} catch(ex : Throwable) {
|
||||
Utils.showToast(this, ex, "could not open picker for font")
|
||||
showToast(this, ex, "could not open picker for font")
|
||||
}
|
||||
|
||||
R.id.btnSettingExport -> exportAppData()
|
||||
|
@ -472,7 +472,7 @@ class ActAppSetting : AppCompatActivity()
|
|||
.apply()
|
||||
SavedAccount.clearRegistrationCache()
|
||||
PollingWorker.queueUpdateListener(this)
|
||||
Utils.showToast(this, false, getString(R.string.custom_stream_listener_was_reset))
|
||||
showToast(this, false, getString(R.string.custom_stream_listener_was_reset))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -687,7 +687,7 @@ class ActAppSetting : AppCompatActivity()
|
|||
private fun saveTimelineFont(uri : Uri?, file_name : String) : File? {
|
||||
try {
|
||||
if(uri == null) {
|
||||
Utils.showToast(this, false, "missing uri.")
|
||||
showToast(this, false, "missing uri.")
|
||||
return null
|
||||
}
|
||||
|
||||
|
@ -701,7 +701,7 @@ class ActAppSetting : AppCompatActivity()
|
|||
|
||||
val source = contentResolver.openInputStream(uri) // nullable
|
||||
if(source == null) {
|
||||
Utils.showToast(this, false, "openInputStream returns null. uri=%s", uri)
|
||||
showToast(this, false, "openInputStream returns null. uri=%s", uri)
|
||||
return null
|
||||
} else {
|
||||
source.use { inStream ->
|
||||
|
@ -713,20 +713,20 @@ class ActAppSetting : AppCompatActivity()
|
|||
|
||||
val face = Typeface.createFromFile(tmp_file)
|
||||
if(face == null) {
|
||||
Utils.showToast(this, false, "Typeface.createFromFile() failed.")
|
||||
showToast(this, false, "Typeface.createFromFile() failed.")
|
||||
return null
|
||||
}
|
||||
|
||||
val file = File(dir, file_name)
|
||||
if(! tmp_file.renameTo(file)) {
|
||||
Utils.showToast(this, false, "File operation failed.")
|
||||
showToast(this, false, "File operation failed.")
|
||||
return null
|
||||
}
|
||||
|
||||
return file
|
||||
} catch(ex : Throwable) {
|
||||
log.trace(ex)
|
||||
Utils.showToast(this, ex, "saveTimelineFont failed.")
|
||||
showToast(this, ex, "saveTimelineFont failed.")
|
||||
return null
|
||||
}
|
||||
|
||||
|
@ -755,7 +755,7 @@ class ActAppSetting : AppCompatActivity()
|
|||
return file
|
||||
} catch(ex : Throwable) {
|
||||
log.trace(ex)
|
||||
Utils.showToast(this@ActAppSetting, ex, "exportAppData failed.")
|
||||
showToast(this@ActAppSetting, ex, "exportAppData failed.")
|
||||
}
|
||||
|
||||
return null
|
||||
|
@ -784,7 +784,7 @@ class ActAppSetting : AppCompatActivity()
|
|||
startActivityForResult(intent, REQUEST_CODE_APP_DATA_EXPORT)
|
||||
} catch(ex : Throwable) {
|
||||
log.trace(ex)
|
||||
Utils.showToast(this@ActAppSetting, ex, "exportAppData failed.")
|
||||
showToast(this@ActAppSetting, ex, "exportAppData failed.")
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -804,7 +804,7 @@ class ActAppSetting : AppCompatActivity()
|
|||
intent.type = "*/*"
|
||||
startActivityForResult(intent, REQUEST_CODE_APP_DATA_IMPORT)
|
||||
} catch(ex : Throwable) {
|
||||
Utils.showToast(this, ex, "importAppData(1) failed.")
|
||||
showToast(this, ex, "importAppData(1) failed.")
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -14,10 +14,19 @@ import java.util.ArrayList
|
|||
import java.util.concurrent.atomic.AtomicReference
|
||||
|
||||
import jp.juggler.subwaytooter.util.LogCategory
|
||||
import jp.juggler.subwaytooter.util.Utils
|
||||
import jp.juggler.subwaytooter.util.digestSHA256
|
||||
|
||||
class ActCallback : AppCompatActivity() {
|
||||
|
||||
companion object {
|
||||
private val log = LogCategory("ActCallback")
|
||||
|
||||
const val ACTION_NOTIFICATION_CLICK = "notification_click"
|
||||
|
||||
internal val last_uri = AtomicReference<Uri>(null)
|
||||
internal val sent_intent = AtomicReference<Intent>(null)
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState : Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
var intent : Intent? = intent
|
||||
|
@ -145,7 +154,7 @@ class ActCallback : AppCompatActivity() {
|
|||
|
||||
cache_dir.mkdirs()
|
||||
|
||||
val name = "img." + System.currentTimeMillis().toString() + "." + Utils.digestSHA256(uri.toString())
|
||||
val name = "img." + System.currentTimeMillis().toString() + "." + uri.toString().digestSHA256()
|
||||
|
||||
val dst = File(cache_dir, name)
|
||||
|
||||
|
@ -187,12 +196,4 @@ class ActCallback : AppCompatActivity() {
|
|||
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val log = LogCategory("ActCallback")
|
||||
|
||||
val ACTION_NOTIFICATION_CLICK = "notification_click"
|
||||
|
||||
internal val last_uri = AtomicReference<Uri>(null)
|
||||
internal val sent_intent = AtomicReference<Intent>(null)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ import java.text.NumberFormat
|
|||
import java.util.Locale
|
||||
|
||||
import jp.juggler.subwaytooter.util.LogCategory
|
||||
import jp.juggler.subwaytooter.util.Utils
|
||||
import jp.juggler.subwaytooter.util.createResizedBitmap
|
||||
|
||||
class ActColumnCustomize : AppCompatActivity(), View.OnClickListener, ColorPickerDialogListener {
|
||||
|
||||
|
@ -56,12 +56,12 @@ class ActColumnCustomize : AppCompatActivity(), View.OnClickListener, ColorPicke
|
|||
internal var density : Float = 0f
|
||||
|
||||
private lateinit var flColumnBackground : View
|
||||
lateinit internal var ivColumnBackground : ImageView
|
||||
lateinit internal var sbColumnBackgroundAlpha : SeekBar
|
||||
internal lateinit var ivColumnBackground : ImageView
|
||||
internal lateinit var sbColumnBackgroundAlpha : SeekBar
|
||||
private lateinit var llColumnHeader : View
|
||||
private lateinit var ivColumnHeader : ImageView
|
||||
private lateinit var tvColumnName : TextView
|
||||
lateinit internal var etAlpha : EditText
|
||||
internal lateinit var etAlpha : EditText
|
||||
private lateinit var tvSampleAcct : TextView
|
||||
private lateinit var tvSampleContent : TextView
|
||||
|
||||
|
@ -195,18 +195,23 @@ class ActColumnCustomize : AppCompatActivity(), View.OnClickListener, ColorPicke
|
|||
}
|
||||
|
||||
// 0xFF000000 と書きたいがkotlinではこれはlong型定数になってしまう
|
||||
private val colorFF000000 :Int = (0xff shl 24 )
|
||||
private val colorFF000000 : Int = (0xff shl 24)
|
||||
|
||||
override fun onColorSelected(dialogId : Int, @ColorInt colorSelected : Int) {
|
||||
when(dialogId) {
|
||||
COLOR_DIALOG_ID_HEADER_BACKGROUND -> column.header_bg_color = colorFF000000 or colorSelected
|
||||
COLOR_DIALOG_ID_HEADER_FOREGROUND -> column.header_fg_color = colorFF000000 or colorSelected
|
||||
COLOR_DIALOG_ID_COLUMN_BACKGROUND -> column.column_bg_color = colorFF000000 or colorSelected
|
||||
COLOR_DIALOG_ID_HEADER_BACKGROUND -> column.header_bg_color = colorFF000000 or
|
||||
colorSelected
|
||||
COLOR_DIALOG_ID_HEADER_FOREGROUND -> column.header_fg_color = colorFF000000 or
|
||||
colorSelected
|
||||
COLOR_DIALOG_ID_COLUMN_BACKGROUND -> column.column_bg_color = colorFF000000 or
|
||||
colorSelected
|
||||
|
||||
COLOR_DIALOG_ID_ACCT_TEXT -> {
|
||||
column.acct_color = if(colorSelected == 0) 1 else colorSelected
|
||||
}
|
||||
|
||||
COLOR_DIALOG_ID_CONTENT_TEXT -> {
|
||||
column.content_color = if(colorSelected == 0) 1 else colorSelected
|
||||
column.content_color = if(colorSelected == 0) 1 else colorSelected
|
||||
}
|
||||
}
|
||||
show()
|
||||
|
@ -219,7 +224,10 @@ class ActColumnCustomize : AppCompatActivity(), View.OnClickListener, ColorPicke
|
|||
if(data != null) {
|
||||
val uri = data.data
|
||||
if(uri != null) {
|
||||
contentResolver.takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
contentResolver.takePersistableUriPermission(
|
||||
uri,
|
||||
Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||
)
|
||||
column.column_bg_image = uri.toString()
|
||||
show()
|
||||
}
|
||||
|
@ -260,7 +268,8 @@ class ActColumnCustomize : AppCompatActivity(), View.OnClickListener, ColorPicke
|
|||
sbColumnBackgroundAlpha = findViewById(R.id.sbColumnBackgroundAlpha)
|
||||
sbColumnBackgroundAlpha.max = PROGRESS_MAX
|
||||
|
||||
sbColumnBackgroundAlpha.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
|
||||
sbColumnBackgroundAlpha.setOnSeekBarChangeListener(object :
|
||||
SeekBar.OnSeekBarChangeListener {
|
||||
override fun onStartTrackingTouch(seekBar : SeekBar) {}
|
||||
|
||||
override fun onStopTrackingTouch(seekBar : SeekBar) {
|
||||
|
@ -272,14 +281,25 @@ class ActColumnCustomize : AppCompatActivity(), View.OnClickListener, ColorPicke
|
|||
if(! fromUser) return
|
||||
column.column_bg_image_alpha = progress / PROGRESS_MAX.toFloat()
|
||||
ivColumnBackground.alpha = column.column_bg_image_alpha
|
||||
etAlpha.setText(String.format(Locale.getDefault(), "%.4f", column.column_bg_image_alpha))
|
||||
etAlpha.setText(
|
||||
String.format(
|
||||
Locale.getDefault(),
|
||||
"%.4f",
|
||||
column.column_bg_image_alpha
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
etAlpha = findViewById(R.id.etAlpha)
|
||||
etAlpha.addTextChangedListener(object : TextWatcher {
|
||||
override fun beforeTextChanged(s : CharSequence, start : Int, count : Int, after : Int) {
|
||||
override fun beforeTextChanged(
|
||||
s : CharSequence,
|
||||
start : Int,
|
||||
count : Int,
|
||||
after : Int
|
||||
) {
|
||||
|
||||
}
|
||||
|
||||
|
@ -290,7 +310,9 @@ class ActColumnCustomize : AppCompatActivity(), View.OnClickListener, ColorPicke
|
|||
override fun afterTextChanged(s : Editable) {
|
||||
if(loading_busy) return
|
||||
try {
|
||||
var f = NumberFormat.getInstance(Locale.getDefault()).parse(etAlpha.text.toString()).toFloat()
|
||||
var f =
|
||||
NumberFormat.getInstance(Locale.getDefault()).parse(etAlpha.text.toString())
|
||||
.toFloat()
|
||||
if(! f.isNaN()) {
|
||||
if(f < 0f) f = 0f
|
||||
if(f > 1f) f = 1f
|
||||
|
@ -313,22 +335,38 @@ class ActColumnCustomize : AppCompatActivity(), View.OnClickListener, ColorPicke
|
|||
if(c == 0) {
|
||||
llColumnHeader.setBackgroundResource(R.drawable.btn_bg_ddd)
|
||||
} else {
|
||||
ViewCompat.setBackground(llColumnHeader, Styler.getAdaptiveRippleDrawable(
|
||||
c,
|
||||
if(column.header_fg_color != 0)
|
||||
column.header_fg_color
|
||||
else
|
||||
Styler.getAttributeColor(this, R.attr.colorRippleEffect)
|
||||
))
|
||||
ViewCompat.setBackground(
|
||||
llColumnHeader, Styler.getAdaptiveRippleDrawable(
|
||||
c,
|
||||
if(column.header_fg_color != 0)
|
||||
column.header_fg_color
|
||||
else
|
||||
Styler.getAttributeColor(this, R.attr.colorRippleEffect)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
c = column.header_fg_color
|
||||
if(c == 0) {
|
||||
tvColumnName.setTextColor(Styler.getAttributeColor(this, android.R.attr.textColorPrimary))
|
||||
Styler.setIconDefaultColor(this, ivColumnHeader, column.getIconAttrId(column.column_type))
|
||||
tvColumnName.setTextColor(
|
||||
Styler.getAttributeColor(
|
||||
this,
|
||||
android.R.attr.textColorPrimary
|
||||
)
|
||||
)
|
||||
Styler.setIconDefaultColor(
|
||||
this,
|
||||
ivColumnHeader,
|
||||
column.getIconAttrId(column.column_type)
|
||||
)
|
||||
} else {
|
||||
tvColumnName.setTextColor(c)
|
||||
Styler.setIconCustomColor(this, ivColumnHeader, c, column.getIconAttrId(column.column_type))
|
||||
Styler.setIconCustomColor(
|
||||
this,
|
||||
ivColumnHeader,
|
||||
c,
|
||||
column.getIconAttrId(column.column_type)
|
||||
)
|
||||
}
|
||||
|
||||
tvColumnName.text = column.getColumnName(false)
|
||||
|
@ -340,18 +378,27 @@ class ActColumnCustomize : AppCompatActivity(), View.OnClickListener, ColorPicke
|
|||
}
|
||||
|
||||
var alpha = column.column_bg_image_alpha
|
||||
if( alpha.isNaN() ) {
|
||||
if(alpha.isNaN()) {
|
||||
alpha = 1f
|
||||
column.column_bg_image_alpha = alpha
|
||||
}
|
||||
ivColumnBackground.alpha = alpha
|
||||
sbColumnBackgroundAlpha.progress = (0.5f + alpha * PROGRESS_MAX).toInt()
|
||||
|
||||
etAlpha.setText(String.format(Locale.getDefault(), "%.4f", column.column_bg_image_alpha))
|
||||
etAlpha.setText(
|
||||
String.format(
|
||||
Locale.getDefault(),
|
||||
"%.4f",
|
||||
column.column_bg_image_alpha
|
||||
)
|
||||
)
|
||||
|
||||
loadImage(ivColumnBackground, column.column_bg_image)
|
||||
|
||||
c = if(column.acct_color != 0) column.acct_color else Styler.getAttributeColor(this, R.attr.colorTimeSmall)
|
||||
c = if(column.acct_color != 0) column.acct_color else Styler.getAttributeColor(
|
||||
this,
|
||||
R.attr.colorTimeSmall
|
||||
)
|
||||
tvSampleAcct.setTextColor(c)
|
||||
|
||||
c = if(column.content_color != 0) column.content_color else content_color_default
|
||||
|
@ -375,9 +422,9 @@ class ActColumnCustomize : AppCompatActivity(), View.OnClickListener, ColorPicke
|
|||
|
||||
}
|
||||
|
||||
private fun loadImage(ivColumnBackground : ImageView, url : String ) {
|
||||
private fun loadImage(ivColumnBackground : ImageView, url : String) {
|
||||
try {
|
||||
if( url.isEmpty() ) {
|
||||
if(url.isEmpty()) {
|
||||
closeBitmaps()
|
||||
return
|
||||
|
||||
|
@ -392,7 +439,7 @@ class ActColumnCustomize : AppCompatActivity(), View.OnClickListener, ColorPicke
|
|||
// 画像をロードして、成功したら表示してURLを覚える
|
||||
val resize_max = (0.5f + 64f * density).toInt()
|
||||
val uri = Uri.parse(url)
|
||||
last_image_bitmap = Utils.createResizedBitmap(log, this, uri, false, resize_max)
|
||||
last_image_bitmap = createResizedBitmap( this, uri, resize_max )
|
||||
if(last_image_bitmap != null) {
|
||||
ivColumnBackground.setImageBitmap(last_image_bitmap)
|
||||
last_image_uri = url
|
||||
|
@ -404,5 +451,4 @@ class ActColumnCustomize : AppCompatActivity(), View.OnClickListener, ColorPicke
|
|||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -23,10 +23,27 @@ import org.json.JSONObject
|
|||
import java.util.ArrayList
|
||||
|
||||
import jp.juggler.subwaytooter.util.LogCategory
|
||||
import jp.juggler.subwaytooter.util.Utils
|
||||
import jp.juggler.subwaytooter.util.activity
|
||||
import jp.juggler.subwaytooter.util.showToast
|
||||
|
||||
class ActColumnList : AppCompatActivity() {
|
||||
|
||||
companion object {
|
||||
private val log = LogCategory("ActColumnList")
|
||||
internal const val TMP_FILE_COLUMN_LIST = "tmp_column_list"
|
||||
|
||||
const val EXTRA_ORDER = "order"
|
||||
const val EXTRA_SELECTION = "selection"
|
||||
|
||||
fun open(activity : ActMain, currentItem : Int, requestCode : Int) {
|
||||
val array = activity.app_state.encodeColumnList()
|
||||
AppState.saveColumnList(activity, ActColumnList.TMP_FILE_COLUMN_LIST, array)
|
||||
val intent = Intent(activity, ActColumnList::class.java)
|
||||
intent.putExtra(ActColumnList.EXTRA_SELECTION, currentItem)
|
||||
activity.startActivityForResult(intent, requestCode)
|
||||
}
|
||||
}
|
||||
|
||||
private lateinit var listView : DragListView
|
||||
private lateinit var listAdapter : MyListAdapter
|
||||
private var old_selection : Int = 0
|
||||
|
@ -46,9 +63,9 @@ class ActColumnList : AppCompatActivity() {
|
|||
|
||||
override fun onSaveInstanceState(outState : Bundle?) {
|
||||
super.onSaveInstanceState(outState)
|
||||
|
||||
outState?: return
|
||||
|
||||
|
||||
outState ?: return
|
||||
|
||||
outState.putInt(EXTRA_SELECTION, old_selection)
|
||||
|
||||
//
|
||||
|
@ -108,7 +125,10 @@ class ActColumnList : AppCompatActivity() {
|
|||
// mRefreshLayout.setEnabled( false );
|
||||
}
|
||||
|
||||
override fun onItemSwipeEnded(item : ListSwipeItem, swipedDirection : ListSwipeItem.SwipeDirection?) {
|
||||
override fun onItemSwipeEnded(
|
||||
item : ListSwipeItem,
|
||||
swipedDirection : ListSwipeItem.SwipeDirection?
|
||||
) {
|
||||
// 操作完了でリフレッシュ許可
|
||||
// mRefreshLayout.setEnabled( USE_SWIPE_REFRESH );
|
||||
|
||||
|
@ -116,7 +136,11 @@ class ActColumnList : AppCompatActivity() {
|
|||
if(swipedDirection == ListSwipeItem.SwipeDirection.LEFT) {
|
||||
val adapterItem = item.tag as MyItem
|
||||
if(adapterItem.json.optBoolean(Column.KEY_DONT_CLOSE, false)) {
|
||||
Utils.showToast(this@ActColumnList, false, R.string.column_has_dont_close_option)
|
||||
showToast(
|
||||
this@ActColumnList,
|
||||
false,
|
||||
R.string.column_has_dont_close_option
|
||||
)
|
||||
listView.resetSwipedViews(null)
|
||||
return
|
||||
}
|
||||
|
@ -196,6 +220,7 @@ class ActColumnList : AppCompatActivity() {
|
|||
|
||||
// リスト要素のデータ
|
||||
internal class MyItem(val json : JSONObject, val id : Long, context : Context) {
|
||||
|
||||
val name : String = json.optString(Column.KEY_COLUMN_NAME)
|
||||
val acct : String = json.optString(Column.KEY_COLUMN_ACCESS)
|
||||
val old_index = json.optInt(Column.KEY_OLD_INDEX)
|
||||
|
@ -206,7 +231,8 @@ class ActColumnList : AppCompatActivity() {
|
|||
|
||||
init {
|
||||
var c = json.optInt(Column.KEY_COLUMN_ACCESS_COLOR, 0)
|
||||
this.acct_color_fg = if(c != 0) c else Styler.getAttributeColor(context, R.attr.colorColumnListItemText)
|
||||
this.acct_color_fg =
|
||||
if(c != 0) c else Styler.getAttributeColor(context, R.attr.colorColumnListItemText)
|
||||
|
||||
c = json.optInt(Column.KEY_COLUMN_ACCESS_COLOR_BG, 0)
|
||||
this.acct_color_bg = c
|
||||
|
@ -223,10 +249,10 @@ class ActColumnList : AppCompatActivity() {
|
|||
, true // 長押しでドラッグ開始するなら真
|
||||
) {
|
||||
|
||||
private val ivBookmark:View = viewRoot.findViewById(R.id.ivBookmark)
|
||||
private val tvAccess:TextView = viewRoot.findViewById(R.id.tvAccess)
|
||||
private val tvName :TextView= viewRoot.findViewById(R.id.tvName)
|
||||
private val ivColumnIcon:ImageView = viewRoot.findViewById(R.id.ivColumnIcon)
|
||||
private val ivBookmark : View = viewRoot.findViewById(R.id.ivBookmark)
|
||||
private val tvAccess : TextView = viewRoot.findViewById(R.id.tvAccess)
|
||||
private val tvName : TextView = viewRoot.findViewById(R.id.tvName)
|
||||
private val ivColumnIcon : ImageView = viewRoot.findViewById(R.id.ivColumnIcon)
|
||||
private val acct_pad_lr = (0.5f + 4f * viewRoot.resources.displayMetrics.density).toInt()
|
||||
|
||||
init {
|
||||
|
@ -245,9 +271,11 @@ class ActColumnList : AppCompatActivity() {
|
|||
tvAccess.setBackgroundColor(item.acct_color_bg)
|
||||
tvAccess.setPaddingRelative(acct_pad_lr, 0, acct_pad_lr, 0)
|
||||
tvName.text = item.name
|
||||
ivColumnIcon.setImageResource(Styler.getAttributeResourceId(
|
||||
this@ActColumnList, Column.getIconAttrId(item.acct, item.type)
|
||||
))
|
||||
ivColumnIcon.setImageResource(
|
||||
Styler.getAttributeResourceId(
|
||||
this@ActColumnList, Column.getIconAttrId(item.acct, item.type)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// @Override
|
||||
|
@ -257,13 +285,13 @@ class ActColumnList : AppCompatActivity() {
|
|||
|
||||
override fun onItemClicked(view : View?) {
|
||||
val item = itemView.tag as MyItem // itemView は親クラスのメンバ変数
|
||||
val activity = Utils.getActivityFromView(view)
|
||||
(activity as? ActColumnList)?.performItemSelected(item)
|
||||
(view.activity as? ActColumnList)?.performItemSelected(item)
|
||||
}
|
||||
}
|
||||
|
||||
// ドラッグ操作中のデータ
|
||||
private inner class MyDragItem internal constructor(context : Context, layoutId : Int) : DragItem(context, layoutId) {
|
||||
private inner class MyDragItem internal constructor(context : Context, layoutId : Int) :
|
||||
DragItem(context, layoutId) {
|
||||
|
||||
override fun onBindDragView(clickedView : View, dragView : View) {
|
||||
val item = clickedView.tag as MyItem
|
||||
|
@ -277,10 +305,14 @@ class ActColumnList : AppCompatActivity() {
|
|||
tv.text = item.name
|
||||
|
||||
val ivColumnIcon = dragView.findViewById<ImageView>(R.id.ivColumnIcon)
|
||||
ivColumnIcon.setImageResource(Styler.getAttributeResourceId(
|
||||
this@ActColumnList, Column.getIconAttrId(item.acct, item.type)))
|
||||
ivColumnIcon.setImageResource(
|
||||
Styler.getAttributeResourceId(
|
||||
this@ActColumnList, Column.getIconAttrId(item.acct, item.type)
|
||||
)
|
||||
)
|
||||
|
||||
dragView.findViewById<View>(R.id.ivBookmark).visibility = clickedView.findViewById<View>(R.id.ivBookmark).visibility
|
||||
dragView.findViewById<View>(R.id.ivBookmark).visibility =
|
||||
clickedView.findViewById<View>(R.id.ivBookmark).visibility
|
||||
|
||||
dragView.findViewById<View>(R.id.item_layout).setBackgroundColor(
|
||||
Styler.getAttributeColor(this@ActColumnList, R.attr.list_item_bg_pressed_dragged)
|
||||
|
@ -288,7 +320,8 @@ class ActColumnList : AppCompatActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
private inner class MyListAdapter internal constructor() : DragItemAdapter<MyItem, MyViewHolder>() {
|
||||
private inner class MyListAdapter internal constructor() :
|
||||
DragItemAdapter<MyItem, MyViewHolder>() {
|
||||
|
||||
|
||||
init {
|
||||
|
@ -313,19 +346,4 @@ class ActColumnList : AppCompatActivity() {
|
|||
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val log = LogCategory("ActColumnList")
|
||||
internal val TMP_FILE_COLUMN_LIST = "tmp_column_list"
|
||||
|
||||
val EXTRA_ORDER = "order"
|
||||
val EXTRA_SELECTION = "selection"
|
||||
|
||||
fun open(activity : ActMain, currentItem : Int, requestCode : Int) {
|
||||
val array = activity.app_state.encodeColumnList()
|
||||
AppState.saveColumnList(activity, ActColumnList.TMP_FILE_COLUMN_LIST, array)
|
||||
val intent = Intent(activity, ActColumnList::class.java)
|
||||
intent.putExtra(ActColumnList.EXTRA_SELECTION, currentItem)
|
||||
activity.startActivityForResult(intent, requestCode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,12 +19,28 @@ import org.hjson.JsonValue
|
|||
import java.util.regex.Pattern
|
||||
|
||||
import jp.juggler.subwaytooter.table.SavedAccount
|
||||
import jp.juggler.subwaytooter.util.LogCategory
|
||||
import jp.juggler.subwaytooter.util.Utils
|
||||
import jp.juggler.subwaytooter.util.*
|
||||
import okhttp3.Request
|
||||
|
||||
class ActCustomStreamListener : AppCompatActivity(), View.OnClickListener, TextWatcher {
|
||||
|
||||
companion object {
|
||||
|
||||
internal val log = LogCategory("ActCustomStreamListener")
|
||||
|
||||
// internal val EXTRA_ACCT = "acct"
|
||||
|
||||
fun open(activity : Activity) {
|
||||
val intent = Intent(activity, ActCustomStreamListener::class.java)
|
||||
activity.startActivity(intent)
|
||||
}
|
||||
|
||||
internal const val STATE_STREAM_CONFIG_JSON = "stream_config_json"
|
||||
internal val reInstanceURL = Pattern.compile("\\Ahttps://[a-z0-9.-_:]+\\z")
|
||||
internal val reUpperCase = Pattern.compile("[A-Z]")
|
||||
internal val reUrl = Pattern.compile("\\Ahttps?://[\\w\\-?&#%~!$'()*+,/:;=@._\\[\\]]+\\z")
|
||||
}
|
||||
|
||||
private lateinit var etStreamListenerConfigurationUrl : EditText
|
||||
private lateinit var etStreamListenerSecret : EditText
|
||||
private lateinit var tvLog : TextView
|
||||
|
@ -120,17 +136,17 @@ class ActCustomStreamListener : AppCompatActivity(), View.OnClickListener, TextW
|
|||
override fun onClick(v : View) {
|
||||
when(v.id) {
|
||||
R.id.btnDiscard -> {
|
||||
Utils.hideKeyboard(this, etStreamListenerConfigurationUrl)
|
||||
etStreamListenerConfigurationUrl.hideKeyboard()
|
||||
finish()
|
||||
}
|
||||
|
||||
R.id.btnTest -> {
|
||||
Utils.hideKeyboard(this, etStreamListenerConfigurationUrl)
|
||||
etStreamListenerConfigurationUrl.hideKeyboard()
|
||||
startTest()
|
||||
}
|
||||
|
||||
R.id.btnSave -> {
|
||||
Utils.hideKeyboard(this, etStreamListenerConfigurationUrl)
|
||||
etStreamListenerConfigurationUrl.hideKeyboard()
|
||||
if(save()) {
|
||||
SavedAccount.clearRegistrationCache()
|
||||
PollingWorker.queueUpdateListener(this)
|
||||
|
@ -142,20 +158,24 @@ class ActCustomStreamListener : AppCompatActivity(), View.OnClickListener, TextW
|
|||
|
||||
private fun save() : Boolean {
|
||||
if(stream_config_json == null) {
|
||||
Utils.showToast(this, false, "please test before save.")
|
||||
showToast(this, false, "please test before save.")
|
||||
return false
|
||||
}
|
||||
|
||||
Pref.pref(this).edit()
|
||||
.put(Pref.spStreamListenerConfigUrl, etStreamListenerConfigurationUrl.text.toString().trim { it <= ' ' })
|
||||
.put(Pref.spStreamListenerSecret, etStreamListenerSecret.text.toString().trim { it <= ' ' })
|
||||
.put(
|
||||
Pref.spStreamListenerConfigUrl,
|
||||
etStreamListenerConfigurationUrl.text.toString().trim { it <= ' ' })
|
||||
.put(
|
||||
Pref.spStreamListenerSecret,
|
||||
etStreamListenerSecret.text.toString().trim { it <= ' ' })
|
||||
.put(Pref.spStreamListenerConfigData, stream_config_json ?: "")
|
||||
.apply()
|
||||
return true
|
||||
}
|
||||
|
||||
internal fun addLog(line : String) {
|
||||
Utils.runOnMainThread {
|
||||
runOnMainLooper {
|
||||
val old = tvLog.text.toString()
|
||||
tvLog.text = if(old.isEmpty()) line else old + "\n" + line
|
||||
}
|
||||
|
@ -197,7 +217,13 @@ class ActCustomStreamListener : AppCompatActivity(), View.OnClickListener, TextW
|
|||
}
|
||||
|
||||
if(! response.isSuccessful || bodyString?.isEmpty() != false) {
|
||||
addLog(TootApiClient.formatResponse(response, "Can't get configuration from URL.", bodyString))
|
||||
addLog(
|
||||
TootApiClient.formatResponse(
|
||||
response,
|
||||
"Can't get configuration from URL.",
|
||||
bodyString
|
||||
)
|
||||
)
|
||||
break
|
||||
}
|
||||
|
||||
|
@ -205,7 +231,7 @@ class ActCustomStreamListener : AppCompatActivity(), View.OnClickListener, TextW
|
|||
JsonValue.readHjson(bodyString)
|
||||
} catch(ex : Throwable) {
|
||||
log.trace(ex)
|
||||
addLog(Utils.formatError(ex, "Can't parse configuration data."))
|
||||
addLog(ex.withCaption("Can't parse configuration data."))
|
||||
break
|
||||
}
|
||||
|
||||
|
@ -242,7 +268,11 @@ class ActCustomStreamListener : AppCompatActivity(), View.OnClickListener, TextW
|
|||
}
|
||||
val entry = entry_value.asObject()
|
||||
|
||||
val keys = arrayOf("urlStreamingListenerRegister", "urlStreamingListenerUnregister", "appId")
|
||||
val keys = arrayOf(
|
||||
"urlStreamingListenerRegister",
|
||||
"urlStreamingListenerUnregister",
|
||||
"appId"
|
||||
)
|
||||
for(key in keys) {
|
||||
val v = entry.get(key)
|
||||
if(! v.isString) {
|
||||
|
@ -271,7 +301,7 @@ class ActCustomStreamListener : AppCompatActivity(), View.OnClickListener, TextW
|
|||
call.execute()
|
||||
} catch(ex : Throwable) {
|
||||
log.trace(ex)
|
||||
addLog(strInstance + "." + key + " : " + Utils.formatError(ex, "connect failed."))
|
||||
addLog(ex.withCaption("$strInstance.$key : connect failed."))
|
||||
has_error = true
|
||||
}
|
||||
|
||||
|
@ -304,7 +334,7 @@ class ActCustomStreamListener : AppCompatActivity(), View.OnClickListener, TextW
|
|||
|
||||
} catch(ex : Throwable) {
|
||||
log.trace(ex)
|
||||
addLog(Utils.formatError(ex, "Can't read configuration from URL."))
|
||||
addLog(ex.withCaption("Can't read configuration from URL."))
|
||||
}
|
||||
|
||||
return null
|
||||
|
@ -330,20 +360,4 @@ class ActCustomStreamListener : AppCompatActivity(), View.OnClickListener, TextW
|
|||
task.executeOnExecutor(App1.task_executor)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
internal val log = LogCategory("ActCustomStreamListener")
|
||||
|
||||
// internal val EXTRA_ACCT = "acct"
|
||||
|
||||
fun open(activity : Activity) {
|
||||
val intent = Intent(activity, ActCustomStreamListener::class.java)
|
||||
activity.startActivity(intent)
|
||||
}
|
||||
|
||||
internal val STATE_STREAM_CONFIG_JSON = "stream_config_json"
|
||||
internal val reInstanceURL = Pattern.compile("\\Ahttps://[a-z0-9.-_:]+\\z")
|
||||
internal val reUpperCase = Pattern.compile("[A-Z]")
|
||||
internal val reUrl = Pattern.compile("\\Ahttps?://[\\w\\-?&#%~!$'()*+,/:;=@._\\[\\]]+\\z")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,13 +15,16 @@ import com.jrummyapps.android.colorpicker.ColorPickerDialog
|
|||
import com.jrummyapps.android.colorpicker.ColorPickerDialogListener
|
||||
|
||||
import org.json.JSONException
|
||||
import org.json.JSONObject
|
||||
|
||||
import jp.juggler.subwaytooter.table.HighlightWord
|
||||
import jp.juggler.subwaytooter.util.LogCategory
|
||||
import jp.juggler.subwaytooter.util.Utils
|
||||
import jp.juggler.subwaytooter.util.toJsonObject
|
||||
|
||||
class ActHighlightWordEdit : AppCompatActivity(), View.OnClickListener, ColorPickerDialogListener, CompoundButton.OnCheckedChangeListener {
|
||||
class ActHighlightWordEdit
|
||||
: AppCompatActivity(),
|
||||
View.OnClickListener,
|
||||
ColorPickerDialogListener,
|
||||
CompoundButton.OnCheckedChangeListener {
|
||||
|
||||
companion object {
|
||||
internal val log = LogCategory("ActHighlightWordEdit")
|
||||
|
@ -44,10 +47,10 @@ class ActHighlightWordEdit : AppCompatActivity(), View.OnClickListener, ColorPic
|
|||
}
|
||||
}
|
||||
|
||||
lateinit internal var item : HighlightWord
|
||||
internal lateinit var item : HighlightWord
|
||||
|
||||
lateinit private var tvName : TextView
|
||||
lateinit private var swSound : Switch
|
||||
private lateinit var tvName : TextView
|
||||
private lateinit var swSound : Switch
|
||||
|
||||
private var bBusy = false
|
||||
|
||||
|
@ -72,22 +75,23 @@ class ActHighlightWordEdit : AppCompatActivity(), View.OnClickListener, ColorPic
|
|||
App1.setActivityTheme(this, false)
|
||||
initUI()
|
||||
|
||||
item = HighlightWord(JSONObject(
|
||||
if(savedInstanceState != null) savedInstanceState.getString(EXTRA_ITEM)
|
||||
else intent.getStringExtra(EXTRA_ITEM)
|
||||
))
|
||||
item = HighlightWord(
|
||||
(savedInstanceState?.getString(EXTRA_ITEM)
|
||||
?: intent.getStringExtra(EXTRA_ITEM)
|
||||
).toJsonObject()
|
||||
)
|
||||
|
||||
showSampleText()
|
||||
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode : Int, resultCode : Int, data : Intent) {
|
||||
override fun onActivityResult(requestCode : Int, resultCode : Int, data : Intent?) {
|
||||
when(requestCode) {
|
||||
|
||||
REQUEST_CODE_NOTIFICATION_SOUND -> {
|
||||
if(resultCode == Activity.RESULT_OK) {
|
||||
// RINGTONE_PICKERからの選択されたデータを取得する
|
||||
val uri = Utils.getExtraObject(data, RingtoneManager.EXTRA_RINGTONE_PICKED_URI)
|
||||
val uri = data?.extras?.get(RingtoneManager.EXTRA_RINGTONE_PICKED_URI)
|
||||
if(uri is Uri) {
|
||||
item.sound_uri = uri.toString()
|
||||
item.sound_type = HighlightWord.SOUND_TYPE_CUSTOM
|
||||
|
@ -95,6 +99,7 @@ class ActHighlightWordEdit : AppCompatActivity(), View.OnClickListener, ColorPic
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
else -> super.onActivityResult(requestCode, resultCode, data)
|
||||
}
|
||||
}
|
||||
|
@ -151,7 +156,10 @@ class ActHighlightWordEdit : AppCompatActivity(), View.OnClickListener, ColorPic
|
|||
showSampleText()
|
||||
}
|
||||
|
||||
R.id.btnBackgroundColorEdit -> openColorPicker(COLOR_DIALOG_ID_BACKGROUND, item.color_bg)
|
||||
R.id.btnBackgroundColorEdit -> openColorPicker(
|
||||
COLOR_DIALOG_ID_BACKGROUND,
|
||||
item.color_bg
|
||||
)
|
||||
|
||||
R.id.btnBackgroundColorReset -> {
|
||||
item.color_bg = 0
|
||||
|
@ -162,7 +170,8 @@ class ActHighlightWordEdit : AppCompatActivity(), View.OnClickListener, ColorPic
|
|||
|
||||
R.id.btnNotificationSoundReset -> {
|
||||
item.sound_uri = null
|
||||
item.sound_type = if(swSound.isChecked) HighlightWord.SOUND_TYPE_DEFAULT else HighlightWord.SOUND_TYPE_NONE
|
||||
item.sound_type =
|
||||
if(swSound.isChecked) HighlightWord.SOUND_TYPE_DEFAULT else HighlightWord.SOUND_TYPE_NONE
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -175,7 +184,8 @@ class ActHighlightWordEdit : AppCompatActivity(), View.OnClickListener, ColorPic
|
|||
item.sound_type = HighlightWord.SOUND_TYPE_NONE
|
||||
} else {
|
||||
|
||||
item.sound_type = if(item.sound_uri?.isEmpty() != false ) HighlightWord.SOUND_TYPE_DEFAULT else HighlightWord.SOUND_TYPE_CUSTOM
|
||||
item.sound_type =
|
||||
if(item.sound_uri?.isEmpty() != false) HighlightWord.SOUND_TYPE_DEFAULT else HighlightWord.SOUND_TYPE_CUSTOM
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -212,7 +222,7 @@ class ActHighlightWordEdit : AppCompatActivity(), View.OnClickListener, ColorPic
|
|||
intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, false)
|
||||
try {
|
||||
val sound_uri = item.sound_uri
|
||||
val uri = if(sound_uri?.isEmpty()!= false ) null else Uri.parse(sound_uri)
|
||||
val uri = if(sound_uri?.isEmpty() != false) null else Uri.parse(sound_uri)
|
||||
if(uri != null) {
|
||||
intent.putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, uri)
|
||||
}
|
||||
|
@ -223,5 +233,4 @@ class ActHighlightWordEdit : AppCompatActivity(), View.OnClickListener, ColorPic
|
|||
startActivityForResult(chooser, REQUEST_CODE_NOTIFICATION_SOUND)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -19,15 +19,14 @@ import com.woxthebox.draglistview.DragListView
|
|||
import com.woxthebox.draglistview.swipe.ListSwipeHelper
|
||||
import com.woxthebox.draglistview.swipe.ListSwipeItem
|
||||
|
||||
import org.json.JSONObject
|
||||
|
||||
import java.lang.ref.WeakReference
|
||||
import java.util.ArrayList
|
||||
|
||||
import jp.juggler.subwaytooter.dialog.DlgTextInput
|
||||
import jp.juggler.subwaytooter.table.HighlightWord
|
||||
import jp.juggler.subwaytooter.util.LogCategory
|
||||
import jp.juggler.subwaytooter.util.Utils
|
||||
import jp.juggler.subwaytooter.util.showToast
|
||||
import jp.juggler.subwaytooter.util.toJsonObject
|
||||
|
||||
class ActHighlightWordList : AppCompatActivity(), View.OnClickListener {
|
||||
|
||||
|
@ -236,7 +235,7 @@ class ActHighlightWordList : AppCompatActivity(), View.OnClickListener {
|
|||
private fun create() {
|
||||
DlgTextInput.show(this, getString(R.string.new_item), "", object : DlgTextInput.Callback {
|
||||
override fun onEmptyError() {
|
||||
Utils.showToast(this@ActHighlightWordList, true, R.string.word_empty)
|
||||
showToast(this@ActHighlightWordList, true, R.string.word_empty)
|
||||
}
|
||||
|
||||
override fun onOK(dialog : Dialog, text : String) {
|
||||
|
@ -264,7 +263,7 @@ class ActHighlightWordList : AppCompatActivity(), View.OnClickListener {
|
|||
override fun onActivityResult(requestCode : Int, resultCode : Int, data : Intent?) {
|
||||
if(requestCode == REQUEST_CODE_EDIT && resultCode == RESULT_OK && data != null) {
|
||||
try {
|
||||
val item = HighlightWord(JSONObject(data.getStringExtra(ActHighlightWordEdit.EXTRA_ITEM)))
|
||||
val item = HighlightWord(data.getStringExtra(ActHighlightWordEdit.EXTRA_ITEM).toJsonObject())
|
||||
item.save()
|
||||
loadData()
|
||||
return
|
||||
|
|
|
@ -27,7 +27,6 @@ import android.support.design.widget.NavigationView
|
|||
import android.support.v4.view.GravityCompat
|
||||
import android.support.v4.widget.DrawerLayout
|
||||
import android.support.v7.app.AppCompatActivity
|
||||
import android.support.v7.widget.DefaultItemAnimator
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.view.Window
|
||||
|
@ -214,7 +213,7 @@ class ActMain : AppCompatActivity()
|
|||
if(tag is ItemViewHolder) {
|
||||
column = tag.column
|
||||
break
|
||||
}else if(tag is ViewHolderItem ) {
|
||||
} else if(tag is ViewHolderItem) {
|
||||
column = tag.ivh.column
|
||||
break
|
||||
} else if(tag is ViewHolderHeaderBase) {
|
||||
|
@ -267,27 +266,27 @@ class ActMain : AppCompatActivity()
|
|||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
val follow_complete_callback : EmptyCallback = {
|
||||
Utils.showToast(this@ActMain, false, R.string.follow_succeeded)
|
||||
showToast(this@ActMain, false, R.string.follow_succeeded)
|
||||
}
|
||||
|
||||
val unfollow_complete_callback : EmptyCallback = {
|
||||
Utils.showToast(this@ActMain, false, R.string.unfollow_succeeded)
|
||||
showToast(this@ActMain, false, R.string.unfollow_succeeded)
|
||||
}
|
||||
|
||||
val favourite_complete_callback : EmptyCallback = {
|
||||
Utils.showToast(this@ActMain, false, R.string.favourite_succeeded)
|
||||
showToast(this@ActMain, false, R.string.favourite_succeeded)
|
||||
}
|
||||
|
||||
val unfavourite_complete_callback : EmptyCallback = {
|
||||
Utils.showToast(this@ActMain, false, R.string.unfavourite_succeeded)
|
||||
showToast(this@ActMain, false, R.string.unfavourite_succeeded)
|
||||
}
|
||||
|
||||
val boost_complete_callback : EmptyCallback = {
|
||||
Utils.showToast(this@ActMain, false, R.string.boost_succeeded)
|
||||
showToast(this@ActMain, false, R.string.boost_succeeded)
|
||||
}
|
||||
|
||||
val unboost_complete_callback : EmptyCallback = {
|
||||
Utils.showToast(this@ActMain, false, R.string.unboost_succeeded)
|
||||
showToast(this@ActMain, false, R.string.unboost_succeeded)
|
||||
}
|
||||
|
||||
private var nScreenColumn : Int = 0
|
||||
|
@ -454,12 +453,13 @@ class ActMain : AppCompatActivity()
|
|||
// アカウント設定から戻ってきたら、カラムを消す必要があるかもしれない
|
||||
run {
|
||||
val new_order = ArrayList<Int>()
|
||||
for( i in 0 until app_state.column_list.size){
|
||||
for(i in 0 until app_state.column_list.size) {
|
||||
val column = app_state.column_list[i]
|
||||
|
||||
if(! column.access_info.isNA) {
|
||||
val sa = SavedAccount.loadAccount(this@ActMain, column.access_info.db_id)
|
||||
if(sa == null) continue
|
||||
// 存在確認
|
||||
SavedAccount.loadAccount(this@ActMain, column.access_info.db_id)
|
||||
?: continue
|
||||
}
|
||||
new_order.add(i)
|
||||
}
|
||||
|
@ -484,7 +484,7 @@ class ActMain : AppCompatActivity()
|
|||
updateColumnStripSelection(- 1, - 1f)
|
||||
|
||||
for(c in app_state.column_list) {
|
||||
c.fireShowContent(reason="ActMain onStart",reset=true)
|
||||
c.fireShowContent(reason = "ActMain onStart", reset = true)
|
||||
}
|
||||
|
||||
// 相対時刻表示
|
||||
|
@ -634,7 +634,8 @@ class ActMain : AppCompatActivity()
|
|||
post_helper.in_reply_to_id = - 1L
|
||||
post_helper.attachment_list = null
|
||||
|
||||
Utils.hideKeyboard(this, etQuickToot)
|
||||
etQuickToot.hideKeyboard()
|
||||
|
||||
post_helper.post(
|
||||
account
|
||||
, false
|
||||
|
@ -738,7 +739,10 @@ class ActMain : AppCompatActivity()
|
|||
val idx = data.getIntExtra(ActColumnCustomize.EXTRA_COLUMN_INDEX, 0)
|
||||
if(idx >= 0 && idx < app_state.column_list.size) {
|
||||
app_state.column_list[idx].fireColumnColor()
|
||||
app_state.column_list[idx].fireShowContent(reason="ActMain column color changed",reset=true)
|
||||
app_state.column_list[idx].fireShowContent(
|
||||
reason = "ActMain column color changed",
|
||||
reset = true
|
||||
)
|
||||
}
|
||||
updateColumnStrip()
|
||||
}
|
||||
|
@ -858,7 +862,7 @@ class ActMain : AppCompatActivity()
|
|||
if(vs == ve && vs != RecyclerView.NO_POSITION) {
|
||||
app_state.column_list[vs].let(closer)
|
||||
} else {
|
||||
Utils.showToast(
|
||||
showToast(
|
||||
this,
|
||||
false,
|
||||
getString(R.string.cant_close_column_by_back_button_when_multiple_column_shown)
|
||||
|
@ -1246,10 +1250,10 @@ class ActMain : AppCompatActivity()
|
|||
})
|
||||
|
||||
env.tablet_pager.itemAnimator = null
|
||||
// val animator = env.tablet_pager.itemAnimator
|
||||
// if( animator is DefaultItemAnimator){
|
||||
// animator.supportsChangeAnimations = false
|
||||
// }
|
||||
// val animator = env.tablet_pager.itemAnimator
|
||||
// if( animator is DefaultItemAnimator){
|
||||
// animator.supportsChangeAnimations = false
|
||||
// }
|
||||
|
||||
env.tablet_snap_helper = GravitySnapHelper(Gravity.START)
|
||||
env.tablet_snap_helper.attachToRecyclerView(env.tablet_pager)
|
||||
|
@ -1423,7 +1427,7 @@ class ActMain : AppCompatActivity()
|
|||
status_id
|
||||
)
|
||||
} catch(ex : Throwable) {
|
||||
Utils.showToast(this, ex, "can't parse status id.")
|
||||
showToast(this, ex, "can't parse status id.")
|
||||
}
|
||||
|
||||
return
|
||||
|
@ -1575,7 +1579,7 @@ class ActMain : AppCompatActivity()
|
|||
client.account = sa
|
||||
} catch(ex : Throwable) {
|
||||
log.trace(ex)
|
||||
return TootApiResult(Utils.formatError(ex, "invalid state"))
|
||||
return TootApiResult(ex.withCaption("invalid state"))
|
||||
}
|
||||
|
||||
} else if(sv.startsWith("host:")) {
|
||||
|
@ -1622,27 +1626,27 @@ class ActMain : AppCompatActivity()
|
|||
// cancelled.
|
||||
|
||||
} else if(error != null) {
|
||||
Utils.showToast(this@ActMain, true, result.error)
|
||||
showToast(this@ActMain, true, result.error)
|
||||
|
||||
} else if(token_info == null) {
|
||||
Utils.showToast(this@ActMain, true, "can't get access token.")
|
||||
showToast(this@ActMain, true, "can't get access token.")
|
||||
|
||||
} else if(jsonObject == null) {
|
||||
Utils.showToast(this@ActMain, true, "can't parse json response.")
|
||||
showToast(this@ActMain, true, "can't parse json response.")
|
||||
|
||||
} else if(ta == null) {
|
||||
// 自分のユーザネームを取れなかった
|
||||
// …普通はエラーメッセージが設定されてるはずだが
|
||||
Utils.showToast(this@ActMain, true, "can't verify user credential.")
|
||||
showToast(this@ActMain, true, "can't verify user credential.")
|
||||
|
||||
} else if(sa != null) {
|
||||
// アクセストークン更新時
|
||||
|
||||
// インスタンスは同じだと思うが、ユーザ名が異なる可能性がある
|
||||
if(sa.username != ta.username) {
|
||||
Utils.showToast(this@ActMain, true, R.string.user_name_not_match)
|
||||
showToast(this@ActMain, true, R.string.user_name_not_match)
|
||||
} else {
|
||||
Utils.showToast(this@ActMain, false, R.string.access_token_updated_for, sa.acct)
|
||||
showToast(this@ActMain, false, R.string.access_token_updated_for, sa.acct)
|
||||
|
||||
// DBの情報を更新する
|
||||
sa.updateTokenInfo(token_info)
|
||||
|
@ -1690,7 +1694,7 @@ class ActMain : AppCompatActivity()
|
|||
if(bModified) {
|
||||
account.saveSetting()
|
||||
}
|
||||
Utils.showToast(this@ActMain, false, R.string.account_confirmed)
|
||||
showToast(this@ActMain, false, R.string.account_confirmed)
|
||||
|
||||
// 通知の更新が必要かもしれない
|
||||
PollingWorker.queueUpdateNotification(this@ActMain)
|
||||
|
@ -1771,7 +1775,7 @@ class ActMain : AppCompatActivity()
|
|||
}
|
||||
|
||||
override fun onEmptyError() {
|
||||
Utils.showToast(this@ActMain, true, R.string.token_not_specified)
|
||||
showToast(this@ActMain, true, R.string.token_not_specified)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -1802,7 +1806,7 @@ class ActMain : AppCompatActivity()
|
|||
fun closeColumn(bConfirm : Boolean, column : Column) {
|
||||
|
||||
if(column.dont_close) {
|
||||
Utils.showToast(this, false, R.string.column_has_dont_close_option)
|
||||
showToast(this, false, R.string.column_has_dont_close_option)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -1920,7 +1924,7 @@ class ActMain : AppCompatActivity()
|
|||
)
|
||||
}
|
||||
} catch(ex : Throwable) {
|
||||
Utils.showToast(this, ex, "can't parse status id.")
|
||||
showToast(this, ex, "can't parse status id.")
|
||||
}
|
||||
|
||||
return
|
||||
|
@ -2063,8 +2067,6 @@ class ActMain : AppCompatActivity()
|
|||
return false
|
||||
}
|
||||
|
||||
|
||||
|
||||
private fun addColumn(column : Column, indexArg : Int) : Int {
|
||||
var index = indexArg
|
||||
val size = app_state.column_list.size
|
||||
|
@ -2197,13 +2199,13 @@ class ActMain : AppCompatActivity()
|
|||
env.tablet_pager_adapter.notifyDataSetChanged()
|
||||
}
|
||||
|
||||
private fun scrollToColumn(index : Int, smoothScroll:Boolean = true ) {
|
||||
private fun scrollToColumn(index : Int, smoothScroll : Boolean = true) {
|
||||
scrollColumnStrip(index)
|
||||
phoneTab(
|
||||
|
||||
|
||||
// スマホはスムーススクロール基本ありだがたまにしない
|
||||
{ env -> env.pager.setCurrentItem(index, smoothScroll) },
|
||||
|
||||
|
||||
// タブレットでスムーススクロールさせると頻繁にオーバーランするので絶対しない
|
||||
{ env -> env.tablet_pager.scrollToPosition(index) }
|
||||
)
|
||||
|
@ -2238,7 +2240,7 @@ class ActMain : AppCompatActivity()
|
|||
AsyncTask<Void, String, ArrayList<Column>?>() {
|
||||
|
||||
internal fun setProgressMessage(sv : String) {
|
||||
Utils.runOnMainThread { progress.setMessage(sv) }
|
||||
runOnMainLooper { progress.setMessage(sv) }
|
||||
}
|
||||
|
||||
override fun doInBackground(vararg params : Void) : ArrayList<Column>? {
|
||||
|
@ -2256,7 +2258,7 @@ class ActMain : AppCompatActivity()
|
|||
// ローカルファイルにコピーする
|
||||
val source = contentResolver.openInputStream(uri)
|
||||
if(source == null) {
|
||||
Utils.showToast(this@ActMain, true, "openInputStream failed.")
|
||||
showToast(this@ActMain, true, "openInputStream failed.")
|
||||
return null
|
||||
} else {
|
||||
source.use { inStream ->
|
||||
|
@ -2281,7 +2283,7 @@ class ActMain : AppCompatActivity()
|
|||
}
|
||||
} catch(ex : Throwable) {
|
||||
log.trace(ex)
|
||||
Utils.showToast(this@ActMain, ex, "importAppData failed.")
|
||||
showToast(this@ActMain, ex, "importAppData failed.")
|
||||
}
|
||||
|
||||
return null
|
||||
|
@ -2360,7 +2362,7 @@ class ActMain : AppCompatActivity()
|
|||
|
||||
private fun resizeAutoCW(column_w : Int) {
|
||||
val sv = Pref.spAutoCWLines(pref)
|
||||
nAutoCwLines = Utils.parse_int(sv, - 1)
|
||||
nAutoCwLines = sv.optInt() ?: - 1
|
||||
if(nAutoCwLines > 0) {
|
||||
val lv_pad = (0.5f + 12 * density).toInt()
|
||||
val icon_width = avatarIconSize
|
||||
|
|
|
@ -38,8 +38,6 @@ import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter
|
|||
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory
|
||||
import com.google.android.exoplayer2.util.Util
|
||||
|
||||
import org.json.JSONArray
|
||||
|
||||
import java.util.LinkedList
|
||||
|
||||
import jp.juggler.subwaytooter.api.TootApiClient
|
||||
|
@ -48,9 +46,7 @@ import jp.juggler.subwaytooter.api.TootTask
|
|||
import jp.juggler.subwaytooter.api.TootTaskRunner
|
||||
import jp.juggler.subwaytooter.api.entity.*
|
||||
import jp.juggler.subwaytooter.dialog.ActionsDialog
|
||||
import jp.juggler.subwaytooter.util.LogCategory
|
||||
import jp.juggler.subwaytooter.util.ProgressResponseBody
|
||||
import jp.juggler.subwaytooter.util.Utils
|
||||
import jp.juggler.subwaytooter.util.*
|
||||
import jp.juggler.subwaytooter.view.PinchBitmapView
|
||||
|
||||
class ActMediaViewer : AppCompatActivity(), View.OnClickListener {
|
||||
|
@ -68,11 +64,10 @@ class ActMediaViewer : AppCompatActivity(), View.OnClickListener {
|
|||
internal const val EXTRA_IDX = "idx"
|
||||
internal const val EXTRA_DATA = "data"
|
||||
|
||||
internal fun <T: TootAttachmentLike> encodeMediaList(list : ArrayList<T>?)
|
||||
= list?.encodeJson()?.toString() ?: "[]"
|
||||
internal fun <T : TootAttachmentLike> encodeMediaList(list : ArrayList<T>?) =
|
||||
list?.encodeJson()?.toString() ?: "[]"
|
||||
|
||||
internal fun decodeMediaList(src : String?)
|
||||
= parseList(::TootAttachment,JSONArray(src))
|
||||
internal fun decodeMediaList(src : String?) = parseList(::TootAttachment, src?.toJsonArray() )
|
||||
|
||||
fun open(activity : ActMain, list : ArrayList<TootAttachmentLike>, idx : Int) {
|
||||
val intent = Intent(activity, ActMediaViewer::class.java)
|
||||
|
@ -102,7 +97,10 @@ class ActMediaViewer : AppCompatActivity(), View.OnClickListener {
|
|||
log.d("exoPlayer onTimelineChanged")
|
||||
}
|
||||
|
||||
override fun onTracksChanged(trackGroups : TrackGroupArray?, trackSelections : TrackSelectionArray?) {
|
||||
override fun onTracksChanged(
|
||||
trackGroups : TrackGroupArray?,
|
||||
trackSelections : TrackSelectionArray?
|
||||
) {
|
||||
log.d("exoPlayer onTracksChanged")
|
||||
|
||||
}
|
||||
|
@ -119,7 +117,7 @@ class ActMediaViewer : AppCompatActivity(), View.OnClickListener {
|
|||
val now = SystemClock.elapsedRealtime()
|
||||
if(now - buffering_last_shown >= short_limit && exoPlayer.duration >= short_limit) {
|
||||
buffering_last_shown = now
|
||||
Utils.showToast(this@ActMediaViewer, false, R.string.video_buffering)
|
||||
showToast(this@ActMediaViewer, false, R.string.video_buffering)
|
||||
}
|
||||
/*
|
||||
exoPlayer.getDuration() may returns negative value (TIME_UNSET ,same as Long.MIN_VALUE + 1).
|
||||
|
@ -133,7 +131,7 @@ class ActMediaViewer : AppCompatActivity(), View.OnClickListener {
|
|||
|
||||
override fun onPlayerError(error : ExoPlaybackException) {
|
||||
log.d("exoPlayer onPlayerError")
|
||||
Utils.showToast(this@ActMediaViewer, error, "player error.")
|
||||
showToast(this@ActMediaViewer, error, "player error.")
|
||||
}
|
||||
|
||||
override fun onPositionDiscontinuity() {
|
||||
|
@ -148,9 +146,9 @@ class ActMediaViewer : AppCompatActivity(), View.OnClickListener {
|
|||
|
||||
override fun onSaveInstanceState(outState : Bundle?) {
|
||||
super.onSaveInstanceState(outState)
|
||||
|
||||
|
||||
outState ?: return
|
||||
|
||||
|
||||
outState.putInt(EXTRA_IDX, idx)
|
||||
outState.putString(EXTRA_DATA, encodeMediaList(media_list))
|
||||
}
|
||||
|
@ -210,11 +208,22 @@ class ActMediaViewer : AppCompatActivity(), View.OnClickListener {
|
|||
loadDelta(delta)
|
||||
}
|
||||
|
||||
override fun onMove(bitmap_w : Float, bitmap_h : Float, tx : Float, ty : Float, scale : Float) {
|
||||
override fun onMove(
|
||||
bitmap_w : Float,
|
||||
bitmap_h : Float,
|
||||
tx : Float,
|
||||
ty : Float,
|
||||
scale : Float
|
||||
) {
|
||||
App1.getAppState(this@ActMediaViewer).handler.post(Runnable {
|
||||
if(isDestroyed) return@Runnable
|
||||
if(tvStatus.visibility == View.VISIBLE) {
|
||||
tvStatus.text = getString(R.string.zooming_of, bitmap_w.toInt(), bitmap_h.toInt(), scale)
|
||||
tvStatus.text = getString(
|
||||
R.string.zooming_of,
|
||||
bitmap_w.toInt(),
|
||||
bitmap_h.toInt(),
|
||||
scale
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -248,7 +257,7 @@ class ActMediaViewer : AppCompatActivity(), View.OnClickListener {
|
|||
}
|
||||
val ta = media_list[idx]
|
||||
val description = ta.description
|
||||
if( description?.isNotEmpty() == true ){
|
||||
if(description?.isNotEmpty() == true) {
|
||||
svDescription.visibility = View.VISIBLE
|
||||
tvDescription.text = description
|
||||
}
|
||||
|
@ -271,7 +280,8 @@ class ActMediaViewer : AppCompatActivity(), View.OnClickListener {
|
|||
|
||||
}
|
||||
|
||||
@SuppressLint("StaticFieldLeak") private fun loadVideo(ta : TootAttachment) {
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
private fun loadVideo(ta : TootAttachment) {
|
||||
|
||||
val url = ta.getLargeUrl(App1.pref)
|
||||
if(url == null) {
|
||||
|
@ -288,7 +298,12 @@ class ActMediaViewer : AppCompatActivity(), View.OnClickListener {
|
|||
this, Util.getUserAgent(this, getString(R.string.app_name)), defaultBandwidthMeter
|
||||
)
|
||||
|
||||
val mediaSource = ExtractorMediaSource(Uri.parse(url), dataSourceFactory, extractorsFactory, App1.getAppState(this).handler, ExtractorMediaSource.EventListener { error -> showError(Utils.formatError(error, "load error.")) }
|
||||
val mediaSource = ExtractorMediaSource(
|
||||
Uri.parse(url),
|
||||
dataSourceFactory,
|
||||
extractorsFactory,
|
||||
App1.getAppState(this).handler,
|
||||
ExtractorMediaSource.EventListener { showError(it.withCaption("load error.") ) }
|
||||
)
|
||||
exoPlayer.prepare(mediaSource)
|
||||
exoPlayer.playWhenReady = true
|
||||
|
@ -300,9 +315,10 @@ class ActMediaViewer : AppCompatActivity(), View.OnClickListener {
|
|||
}
|
||||
}
|
||||
|
||||
@SuppressLint("StaticFieldLeak") private fun loadBitmap(ta : TootAttachment) {
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
private fun loadBitmap(ta : TootAttachment) {
|
||||
val urlList = ta.getLargeUrlList(App1.pref)
|
||||
if(urlList.isEmpty() ) {
|
||||
if(urlList.isEmpty()) {
|
||||
showError("missing media attachment url.")
|
||||
return
|
||||
}
|
||||
|
@ -346,17 +362,17 @@ class ActMediaViewer : AppCompatActivity(), View.OnClickListener {
|
|||
internal fun getHttpCached(client : TootApiClient, url : String) : TootApiResult? {
|
||||
val result = TootApiResult.makeWithCaption(url)
|
||||
|
||||
if(!client.sendRequest(result){
|
||||
okhttp3.Request.Builder()
|
||||
.url(url)
|
||||
.cacheControl(App1.CACHE_5MIN)
|
||||
.build()
|
||||
}) return result
|
||||
if(! client.sendRequest(result) {
|
||||
okhttp3.Request.Builder()
|
||||
.url(url)
|
||||
.cacheControl(App1.CACHE_5MIN)
|
||||
.build()
|
||||
}) return result
|
||||
|
||||
if( client.isApiCancelled ) return null
|
||||
if(client.isApiCancelled) return null
|
||||
val response = requireNotNull(result.response)
|
||||
if(! response.isSuccessful) {
|
||||
return result.setError( TootApiClient.formatResponse(response,result.caption))
|
||||
return result.setError(TootApiClient.formatResponse(response, result.caption))
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -367,15 +383,15 @@ class ActMediaViewer : AppCompatActivity(), View.OnClickListener {
|
|||
}
|
||||
client.publishApiProgressRatio(bytesRead.toInt(), bytesTotal.toInt())
|
||||
}
|
||||
if( client.isApiCancelled ) return null
|
||||
if(client.isApiCancelled) return null
|
||||
} catch(ex : Throwable) {
|
||||
result.setError( TootApiClient.formatResponse(response,result.caption,"?"))
|
||||
result.setError(TootApiClient.formatResponse(response, result.caption, "?"))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
override fun background(client : TootApiClient) : TootApiResult? {
|
||||
if( urlList.isEmpty()) return TootApiResult("missing url")
|
||||
if(urlList.isEmpty()) return TootApiResult("missing url")
|
||||
var result : TootApiResult? = null
|
||||
for(url in urlList) {
|
||||
result = getHttpCached(client, url)
|
||||
|
@ -383,7 +399,7 @@ class ActMediaViewer : AppCompatActivity(), View.OnClickListener {
|
|||
if(data != null) {
|
||||
client.publishApiProgress("decoding image…")
|
||||
val bitmap = decodeBitmap(data, 2048)
|
||||
if( bitmap != null ) {
|
||||
if(bitmap != null) {
|
||||
this.bitmap = bitmap
|
||||
break
|
||||
}
|
||||
|
@ -394,10 +410,10 @@ class ActMediaViewer : AppCompatActivity(), View.OnClickListener {
|
|||
|
||||
override fun handleResult(result : TootApiResult?) {
|
||||
val bitmap = this.bitmap
|
||||
if(bitmap != null){
|
||||
if(bitmap != null) {
|
||||
pbvImage.setBitmap(bitmap)
|
||||
}else if(result != null){
|
||||
Utils.showToast(this@ActMediaViewer, true, result.error)
|
||||
} else if(result != null) {
|
||||
showToast(this@ActMediaViewer, true, result.error)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -425,7 +441,7 @@ class ActMediaViewer : AppCompatActivity(), View.OnClickListener {
|
|||
R.id.btnMore -> more(media_list[idx])
|
||||
}
|
||||
} catch(ex : Throwable) {
|
||||
Utils.showToast(this, ex, "action failed.")
|
||||
showToast(this, ex, "action failed.")
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -434,7 +450,10 @@ class ActMediaViewer : AppCompatActivity(), View.OnClickListener {
|
|||
|
||||
private fun download(ta : TootAttachmentLike) {
|
||||
|
||||
val permissionCheck = ContextCompat.checkSelfPermission(this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
val permissionCheck = ContextCompat.checkSelfPermission(
|
||||
this,
|
||||
android.Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||
)
|
||||
if(permissionCheck != PackageManager.PERMISSION_GRANTED) {
|
||||
preparePermission()
|
||||
return
|
||||
|
@ -462,7 +481,7 @@ class ActMediaViewer : AppCompatActivity(), View.OnClickListener {
|
|||
it.remove()
|
||||
} else if(url == dh.url) {
|
||||
// 履歴に同じURLがあればエラーとする
|
||||
Utils.showToast(this, false, R.string.dont_repeat_download_to_same_url)
|
||||
showToast(this, false, R.string.dont_repeat_download_to_same_url)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -478,7 +497,7 @@ class ActMediaViewer : AppCompatActivity(), View.OnClickListener {
|
|||
val size = pathSegments.size
|
||||
for(i in size - 1 downTo 0) {
|
||||
val s = pathSegments[i]
|
||||
if( s?.isNotEmpty() == true) {
|
||||
if(s?.isNotEmpty() == true) {
|
||||
fileName = s
|
||||
break
|
||||
}
|
||||
|
@ -506,7 +525,7 @@ class ActMediaViewer : AppCompatActivity(), View.OnClickListener {
|
|||
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
|
||||
|
||||
downLoadManager.enqueue(request)
|
||||
Utils.showToast(this, false, R.string.downloading)
|
||||
showToast(this, false, R.string.downloading)
|
||||
}
|
||||
|
||||
private fun share(action : String, url : String) {
|
||||
|
@ -523,7 +542,7 @@ class ActMediaViewer : AppCompatActivity(), View.OnClickListener {
|
|||
|
||||
startActivity(intent)
|
||||
} catch(ex : Throwable) {
|
||||
Utils.showToast(this, ex, "can't open app.")
|
||||
showToast(this, ex, "can't open app.")
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -546,10 +565,10 @@ class ActMediaViewer : AppCompatActivity(), View.OnClickListener {
|
|||
//クリップボードにデータを格納
|
||||
cm.primaryClip = cd
|
||||
|
||||
Utils.showToast(this, false, R.string.url_is_copied)
|
||||
showToast(this, false, R.string.url_is_copied)
|
||||
|
||||
} catch(ex : Throwable) {
|
||||
Utils.showToast(this, ex, "clipboard access failed.")
|
||||
showToast(this, ex, "clipboard access failed.")
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -579,8 +598,13 @@ class ActMediaViewer : AppCompatActivity(), View.OnClickListener {
|
|||
ad.show(this, null)
|
||||
}
|
||||
|
||||
private fun addMoreMenu(ad : ActionsDialog, caption_prefix : String, url : String?, action : String) {
|
||||
if( url ?.isEmpty() != false ) return
|
||||
private fun addMoreMenu(
|
||||
ad : ActionsDialog,
|
||||
caption_prefix : String,
|
||||
url : String?,
|
||||
action : String
|
||||
) {
|
||||
if(url?.isEmpty() != false) return
|
||||
|
||||
val caption = getString(R.string.open_browser_of, caption_prefix)
|
||||
|
||||
|
@ -590,17 +614,18 @@ class ActMediaViewer : AppCompatActivity(), View.OnClickListener {
|
|||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
startActivity(intent)
|
||||
} catch(ex : Throwable) {
|
||||
Utils.showToast(this@ActMediaViewer, ex, "can't open app.")
|
||||
showToast(this@ActMediaViewer, ex, "can't open app.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun preparePermission() {
|
||||
if(Build.VERSION.SDK_INT >= 23) {
|
||||
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), PERMISSION_REQUEST_CODE
|
||||
ActivityCompat.requestPermissions(
|
||||
this, arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), PERMISSION_REQUEST_CODE
|
||||
)
|
||||
} else {
|
||||
Utils.showToast(this, true, R.string.missing_permission_to_access_media)
|
||||
showToast(this, true, R.string.missing_permission_to_access_media)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -619,7 +644,7 @@ class ActMediaViewer : AppCompatActivity(), View.OnClickListener {
|
|||
++ i
|
||||
}
|
||||
if(bNotGranted) {
|
||||
Utils.showToast(this, true, R.string.missing_permission_to_access_media)
|
||||
showToast(this, true, R.string.missing_permission_to_access_media)
|
||||
} else {
|
||||
download(media_list[idx])
|
||||
}
|
||||
|
|
|
@ -21,13 +21,11 @@ import jp.juggler.subwaytooter.util.LogCategory
|
|||
|
||||
class ActMutedApp : AppCompatActivity() {
|
||||
|
||||
|
||||
companion object {
|
||||
|
||||
private val log = LogCategory("ActMutedApp")
|
||||
}
|
||||
|
||||
lateinit internal var listView : DragListView
|
||||
internal lateinit var listView : DragListView
|
||||
private lateinit var listAdapter : MyListAdapter
|
||||
|
||||
override fun onCreate(savedInstanceState : Bundle?) {
|
||||
|
@ -86,13 +84,16 @@ class ActMutedApp : AppCompatActivity() {
|
|||
// mRefreshLayout.setEnabled( false );
|
||||
}
|
||||
|
||||
override fun onItemSwipeEnded(item : ListSwipeItem?, swipedDirection : ListSwipeItem.SwipeDirection?) {
|
||||
override fun onItemSwipeEnded(
|
||||
item : ListSwipeItem?,
|
||||
swipedDirection : ListSwipeItem.SwipeDirection?
|
||||
) {
|
||||
// 操作完了でリフレッシュ許可
|
||||
// mRefreshLayout.setEnabled( USE_SWIPE_REFRESH );
|
||||
|
||||
// 左にスワイプした(右端に青が見えた) なら要素を削除する
|
||||
if(swipedDirection == ListSwipeItem.SwipeDirection.LEFT) {
|
||||
val o = item ?.tag
|
||||
val o = item?.tag
|
||||
if(o is MyItem) {
|
||||
MutedApp.delete(o.name)
|
||||
listAdapter.removeItem(listAdapter.getPositionForItem(o))
|
||||
|
@ -128,7 +129,8 @@ class ActMutedApp : AppCompatActivity() {
|
|||
internal class MyItem(val id : Long, val name : String)
|
||||
|
||||
// リスト要素のViewHolder
|
||||
internal class MyViewHolder(viewRoot : View) : DragItemAdapter.ViewHolder(viewRoot, R.id.ivDragHandle, false) {
|
||||
internal class MyViewHolder(viewRoot : View) :
|
||||
DragItemAdapter.ViewHolder(viewRoot, R.id.ivDragHandle, false) {
|
||||
|
||||
val tvName : TextView
|
||||
|
||||
|
@ -142,7 +144,7 @@ class ActMutedApp : AppCompatActivity() {
|
|||
viewRoot.supportedSwipeDirection = ListSwipeItem.SwipeDirection.LEFT
|
||||
}
|
||||
|
||||
}// View ID。 ここを押すとドラッグ操作をすぐに開始する
|
||||
} // View ID。 ここを押すとドラッグ操作をすぐに開始する
|
||||
// 長押しでドラッグ開始するなら真
|
||||
|
||||
fun bind(item : MyItem) {
|
||||
|
@ -161,10 +163,12 @@ class ActMutedApp : AppCompatActivity() {
|
|||
}
|
||||
|
||||
// ドラッグ操作中のデータ
|
||||
private inner class MyDragItem internal constructor(context : Context, layoutId : Int) : DragItem(context, layoutId) {
|
||||
private inner class MyDragItem internal constructor(context : Context, layoutId : Int) :
|
||||
DragItem(context, layoutId) {
|
||||
|
||||
override fun onBindDragView(clickedView : View, dragView : View) {
|
||||
dragView.findViewById<TextView>(R.id.tvName).text = clickedView.findViewById<TextView>(R.id.tvName).text
|
||||
dragView.findViewById<TextView>(R.id.tvName).text =
|
||||
clickedView.findViewById<TextView>(R.id.tvName).text
|
||||
|
||||
dragView.findViewById<View>(R.id.item_layout).setBackgroundColor(
|
||||
Styler.getAttributeColor(this@ActMutedApp, R.attr.list_item_bg_pressed_dragged)
|
||||
|
@ -172,7 +176,8 @@ class ActMutedApp : AppCompatActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
private inner class MyListAdapter internal constructor() : DragItemAdapter<MyItem, MyViewHolder>() {
|
||||
private inner class MyListAdapter internal constructor() :
|
||||
DragItemAdapter<MyItem, MyViewHolder>() {
|
||||
|
||||
init {
|
||||
setHasStableIds(true)
|
||||
|
|
|
@ -19,7 +19,7 @@ import com.jrummyapps.android.colorpicker.ColorPickerDialog
|
|||
import com.jrummyapps.android.colorpicker.ColorPickerDialogListener
|
||||
|
||||
import jp.juggler.subwaytooter.table.AcctColor
|
||||
import jp.juggler.subwaytooter.util.Utils
|
||||
import jp.juggler.subwaytooter.util.hideKeyboard
|
||||
|
||||
class ActNickname : AppCompatActivity(), View.OnClickListener, ColorPickerDialogListener {
|
||||
|
||||
|
@ -29,7 +29,12 @@ class ActNickname : AppCompatActivity(), View.OnClickListener, ColorPickerDialog
|
|||
internal const val EXTRA_SHOW_NOTIFICATION_SOUND = "show_notification_sound"
|
||||
internal const val REQUEST_CODE_NOTIFICATION_SOUND = 2
|
||||
|
||||
fun open(activity : Activity, full_acct : String, bShowNotificationSound : Boolean, requestCode : Int) {
|
||||
fun open(
|
||||
activity : Activity,
|
||||
full_acct : String,
|
||||
bShowNotificationSound : Boolean,
|
||||
requestCode : Int
|
||||
) {
|
||||
val intent = Intent(activity, ActNickname::class.java)
|
||||
intent.putExtra(EXTRA_ACCT, full_acct)
|
||||
intent.putExtra(EXTRA_SHOW_NOTIFICATION_SOUND, bShowNotificationSound)
|
||||
|
@ -78,7 +83,12 @@ class ActNickname : AppCompatActivity(), View.OnClickListener, ColorPickerDialog
|
|||
|
||||
private fun initUI() {
|
||||
|
||||
title = getString(if(show_notification_sound) R.string.nickname_and_color_and_notification_sound else R.string.nickname_and_color)
|
||||
title = getString(
|
||||
if(show_notification_sound)
|
||||
R.string.nickname_and_color_and_notification_sound
|
||||
else
|
||||
R.string.nickname_and_color
|
||||
)
|
||||
setContentView(R.layout.act_nickname)
|
||||
|
||||
Styler.fixHorizontalPadding(findViewById(R.id.llContent))
|
||||
|
@ -112,7 +122,12 @@ class ActNickname : AppCompatActivity(), View.OnClickListener, ColorPickerDialog
|
|||
btnNotificationSoundReset.isEnabled = bBefore8
|
||||
|
||||
etNickname.addTextChangedListener(object : TextWatcher {
|
||||
override fun beforeTextChanged(s : CharSequence, start : Int, count : Int, after : Int) {
|
||||
override fun beforeTextChanged(
|
||||
s : CharSequence,
|
||||
start : Int,
|
||||
count : Int,
|
||||
after : Int
|
||||
) {
|
||||
|
||||
}
|
||||
|
||||
|
@ -129,7 +144,8 @@ class ActNickname : AppCompatActivity(), View.OnClickListener, ColorPickerDialog
|
|||
private fun load() {
|
||||
bLoading = true
|
||||
|
||||
findViewById<View>(R.id.llNotificationSound).visibility = if(show_notification_sound) View.VISIBLE else View.GONE
|
||||
findViewById<View>(R.id.llNotificationSound).visibility =
|
||||
if(show_notification_sound) View.VISIBLE else View.GONE
|
||||
|
||||
tvAcct.text = acct
|
||||
|
||||
|
@ -146,13 +162,17 @@ class ActNickname : AppCompatActivity(), View.OnClickListener, ColorPickerDialog
|
|||
private fun save() {
|
||||
if(bLoading) return
|
||||
AcctColor(
|
||||
acct, etNickname.text.toString().trim { it <= ' ' }, color_fg, color_bg, notification_sound_uri
|
||||
acct,
|
||||
etNickname.text.toString().trim { it <= ' ' },
|
||||
color_fg,
|
||||
color_bg,
|
||||
notification_sound_uri
|
||||
).save(System.currentTimeMillis())
|
||||
}
|
||||
|
||||
private fun show() {
|
||||
val s = etNickname.text.toString().trim { it <= ' ' }
|
||||
tvPreview.text = if( s.isNotEmpty() ) s else acct
|
||||
tvPreview.text = if(s.isNotEmpty()) s else acct
|
||||
var c : Int
|
||||
|
||||
c = color_fg
|
||||
|
@ -167,7 +187,7 @@ class ActNickname : AppCompatActivity(), View.OnClickListener, ColorPickerDialog
|
|||
val builder : ColorPickerDialog.Builder
|
||||
when(v.id) {
|
||||
R.id.btnTextColorEdit -> {
|
||||
Utils.hideKeyboard(this, etNickname)
|
||||
etNickname.hideKeyboard()
|
||||
builder = ColorPickerDialog.newBuilder()
|
||||
.setDialogType(ColorPickerDialog.TYPE_CUSTOM)
|
||||
.setAllowPresets(true)
|
||||
|
@ -176,12 +196,14 @@ class ActNickname : AppCompatActivity(), View.OnClickListener, ColorPickerDialog
|
|||
if(color_fg != 0) builder.setColor(color_fg)
|
||||
builder.show(this)
|
||||
}
|
||||
|
||||
R.id.btnTextColorReset -> {
|
||||
color_fg = 0
|
||||
show()
|
||||
}
|
||||
|
||||
R.id.btnBackgroundColorEdit -> {
|
||||
Utils.hideKeyboard(this, etNickname)
|
||||
etNickname.hideKeyboard()
|
||||
builder = ColorPickerDialog.newBuilder()
|
||||
.setDialogType(ColorPickerDialog.TYPE_CUSTOM)
|
||||
.setAllowPresets(true)
|
||||
|
@ -190,15 +212,18 @@ class ActNickname : AppCompatActivity(), View.OnClickListener, ColorPickerDialog
|
|||
if(color_bg != 0) builder.setColor(color_bg)
|
||||
builder.show(this)
|
||||
}
|
||||
|
||||
R.id.btnBackgroundColorReset -> {
|
||||
color_bg = 0
|
||||
show()
|
||||
}
|
||||
|
||||
R.id.btnSave -> {
|
||||
save()
|
||||
setResult(Activity.RESULT_OK)
|
||||
finish()
|
||||
}
|
||||
|
||||
R.id.btnDiscard -> {
|
||||
setResult(Activity.RESULT_CANCELED)
|
||||
finish()
|
||||
|
@ -228,7 +253,9 @@ class ActNickname : AppCompatActivity(), View.OnClickListener, ColorPickerDialog
|
|||
intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, false)
|
||||
try {
|
||||
val notification_sound_uri = this.notification_sound_uri
|
||||
val uri = if(notification_sound_uri?.isEmpty() != false ) null else Uri.parse(notification_sound_uri)
|
||||
val uri = if(notification_sound_uri?.isEmpty() != false) null else Uri.parse(
|
||||
notification_sound_uri
|
||||
)
|
||||
if(uri != null) {
|
||||
intent.putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, uri)
|
||||
}
|
||||
|
@ -239,16 +266,15 @@ class ActNickname : AppCompatActivity(), View.OnClickListener, ColorPickerDialog
|
|||
startActivityForResult(chooser, REQUEST_CODE_NOTIFICATION_SOUND)
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode : Int, resultCode : Int, data : Intent) {
|
||||
override fun onActivityResult(requestCode : Int, resultCode : Int, data : Intent?) {
|
||||
if(resultCode == Activity.RESULT_OK && requestCode == REQUEST_CODE_NOTIFICATION_SOUND) {
|
||||
// RINGTONE_PICKERからの選択されたデータを取得する
|
||||
val uri = Utils.getExtraObject(data, RingtoneManager.EXTRA_RINGTONE_PICKED_URI)
|
||||
if(uri is Uri ) {
|
||||
val uri = data?.extras?.get(RingtoneManager.EXTRA_RINGTONE_PICKED_URI)
|
||||
if(uri is Uri) {
|
||||
notification_sound_uri = uri.toString()
|
||||
}
|
||||
}
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -9,26 +9,25 @@ import org.apache.commons.io.IOUtils
|
|||
import java.io.ByteArrayOutputStream
|
||||
|
||||
import jp.juggler.subwaytooter.util.LogCategory
|
||||
import jp.juggler.subwaytooter.util.Utils
|
||||
import jp.juggler.subwaytooter.util.decodeUTF8
|
||||
|
||||
class ActOSSLicense : AppCompatActivity() {
|
||||
|
||||
companion object {
|
||||
private val log = LogCategory("ActOSSLicense")
|
||||
}
|
||||
|
||||
|
||||
override fun onCreate(savedInstanceState : Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
App1.setActivityTheme(this, true)
|
||||
setContentView(R.layout.act_oss_license)
|
||||
|
||||
try {
|
||||
resources.openRawResource(R.raw.oss_license)?.use{ inData ->
|
||||
resources.openRawResource(R.raw.oss_license)?.use { inData ->
|
||||
ByteArrayOutputStream().use { bao ->
|
||||
IOUtils.copy(inData, bao)
|
||||
val text = Utils.decodeUTF8(bao.toByteArray())
|
||||
val tv = findViewById<TextView>(R.id.tvText)
|
||||
tv.text = text
|
||||
tv.text = bao.toByteArray().decodeUTF8()
|
||||
}
|
||||
}
|
||||
} catch(ex : Throwable) {
|
||||
|
|
|
@ -147,7 +147,12 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
|
|||
private const val STATE_MUSHROOM_START = "mushroom_start"
|
||||
private const val STATE_MUSHROOM_END = "mushroom_end"
|
||||
|
||||
fun open(activity : Activity, request_code : Int, account_db_id : Long, reply_status : TootStatus?) {
|
||||
fun open(
|
||||
activity : Activity,
|
||||
request_code : Int,
|
||||
account_db_id : Long,
|
||||
reply_status : TootStatus?
|
||||
) {
|
||||
val intent = Intent(activity, ActPost::class.java)
|
||||
intent.putExtra(KEY_ACCOUNT_DB_ID, account_db_id)
|
||||
if(reply_status != null) {
|
||||
|
@ -156,7 +161,12 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
|
|||
activity.startActivityForResult(intent, request_code)
|
||||
}
|
||||
|
||||
fun open(activity : Activity, request_code : Int, account_db_id : Long, initial_text : String?) {
|
||||
fun open(
|
||||
activity : Activity,
|
||||
request_code : Int,
|
||||
account_db_id : Long,
|
||||
initial_text : String?
|
||||
) {
|
||||
val intent = Intent(activity, ActPost::class.java)
|
||||
intent.putExtra(KEY_ACCOUNT_DB_ID, account_db_id)
|
||||
if(initial_text != null) {
|
||||
|
@ -165,7 +175,12 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
|
|||
activity.startActivityForResult(intent, request_code)
|
||||
}
|
||||
|
||||
fun open(activity : Activity, request_code : Int, account_db_id : Long, sent_intent : Intent?) {
|
||||
fun open(
|
||||
activity : Activity,
|
||||
request_code : Int,
|
||||
account_db_id : Long,
|
||||
sent_intent : Intent?
|
||||
) {
|
||||
val intent = Intent(activity, ActPost::class.java)
|
||||
intent.putExtra(KEY_ACCOUNT_DB_ID, account_db_id)
|
||||
if(sent_intent != null) {
|
||||
|
@ -175,9 +190,9 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
|
|||
}
|
||||
|
||||
internal fun check_exist(url : String?) : Boolean {
|
||||
if( url?.isEmpty() != false ) return false
|
||||
if(url?.isEmpty() != false) return false
|
||||
try {
|
||||
val request = Request.Builder().url(url ).build()
|
||||
val request = Request.Builder().url(url).build()
|
||||
val call = App1.ok_http_client.newCall(request)
|
||||
val response = call.execute()
|
||||
if(response.isSuccessful) {
|
||||
|
@ -242,7 +257,8 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
|
|||
}
|
||||
}
|
||||
|
||||
private val scroll_listener : ViewTreeObserver.OnScrollChangedListener = ViewTreeObserver.OnScrollChangedListener { post_helper.onScrollChanged() }
|
||||
private val scroll_listener : ViewTreeObserver.OnScrollChangedListener =
|
||||
ViewTreeObserver.OnScrollChangedListener { post_helper.onScrollChanged() }
|
||||
|
||||
//////////////////////////////////////////////////////////
|
||||
// Account
|
||||
|
@ -302,7 +318,7 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
|
|||
if(uri != null) {
|
||||
// 単一選択
|
||||
var type = data.type
|
||||
if(type?.isEmpty() != false ) {
|
||||
if(type?.isEmpty() != false) {
|
||||
type = contentResolver.getType(uri)
|
||||
}
|
||||
addAttachment(uri, type)
|
||||
|
@ -324,15 +340,15 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
|
|||
if(resultCode != Activity.RESULT_OK) {
|
||||
// 失敗したら DBからデータを削除
|
||||
val uriCameraImage = this.uriCameraImage
|
||||
if(uriCameraImage != null){
|
||||
contentResolver.delete(uriCameraImage , null, null)
|
||||
if(uriCameraImage != null) {
|
||||
contentResolver.delete(uriCameraImage, null, null)
|
||||
this@ActPost.uriCameraImage = null
|
||||
}
|
||||
} else {
|
||||
// 画像のURL
|
||||
val uri : Uri? = data?.data ?: uriCameraImage
|
||||
if(uri != null) {
|
||||
val type :String? = contentResolver.getType(uri)
|
||||
val type : String? = contentResolver.getType(uri)
|
||||
addAttachment(uri, type)
|
||||
}
|
||||
}
|
||||
|
@ -345,7 +361,7 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
|
|||
|
||||
} else if(requestCode == REQUEST_CODE_MUSHROOM && resultCode == Activity.RESULT_OK) {
|
||||
val text = data?.getStringExtra("replace_key")
|
||||
if( text != null ){
|
||||
if(text != null) {
|
||||
applyMushroomResult(text)
|
||||
}
|
||||
}
|
||||
|
@ -384,7 +400,7 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
|
|||
initUI()
|
||||
|
||||
if(account_list.isEmpty()) {
|
||||
Utils.showToast(this, true, R.string.please_add_account)
|
||||
showToast(this, true, R.string.please_add_account)
|
||||
finish()
|
||||
return
|
||||
}
|
||||
|
@ -395,7 +411,8 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
|
|||
mushroom_start = savedInstanceState.getInt(STATE_MUSHROOM_START, 0)
|
||||
mushroom_end = savedInstanceState.getInt(STATE_MUSHROOM_END, 0)
|
||||
|
||||
val account_db_id = savedInstanceState.getLong(KEY_ACCOUNT_DB_ID, SavedAccount.INVALID_DB_ID)
|
||||
val account_db_id =
|
||||
savedInstanceState.getLong(KEY_ACCOUNT_DB_ID, SavedAccount.INVALID_DB_ID)
|
||||
if(account_db_id != SavedAccount.INVALID_DB_ID) {
|
||||
var i = 0
|
||||
val ie = account_list.size
|
||||
|
@ -433,20 +450,14 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
|
|||
this.attachment_list.clear()
|
||||
|
||||
try {
|
||||
val array = JSONArray(sv)
|
||||
var i = 0
|
||||
val ie = array.length()
|
||||
while(i < ie) {
|
||||
val array = sv.toJsonArray()
|
||||
for( i in 0 until array.length()){
|
||||
try {
|
||||
val a = parseItem(::TootAttachment, array.optJSONObject(i))
|
||||
if(a != null) {
|
||||
attachment_list.add(PostAttachment(a))
|
||||
}
|
||||
if(a != null) attachment_list.add(PostAttachment(a))
|
||||
} catch(ex : Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
|
||||
++ i
|
||||
}
|
||||
} catch(ex : Throwable) {
|
||||
log.trace(ex)
|
||||
|
@ -497,7 +508,8 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
|
|||
addAttachment(uri, type)
|
||||
}
|
||||
} else if(Intent.ACTION_SEND_MULTIPLE == action) {
|
||||
val list_uri = sent_intent.getParcelableArrayListExtra<Uri>(Intent.EXTRA_STREAM)
|
||||
val list_uri =
|
||||
sent_intent.getParcelableArrayListExtra<Uri>(Intent.EXTRA_STREAM)
|
||||
if(list_uri != null) {
|
||||
for(uri in list_uri) {
|
||||
if(uri != null) {
|
||||
|
@ -510,7 +522,7 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
|
|||
if(Intent.ACTION_SEND == action) {
|
||||
val sv = sent_intent.getStringExtra(Intent.EXTRA_TEXT)
|
||||
if(sv != null) {
|
||||
val svEmoji = DecodeOptions(decodeEmoji = true).decodeEmoji(this,sv)
|
||||
val svEmoji = DecodeOptions(decodeEmoji = true).decodeEmoji(this, sv)
|
||||
etContent.setText(svEmoji)
|
||||
etContent.setSelection(svEmoji.length)
|
||||
}
|
||||
|
@ -521,7 +533,7 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
|
|||
|
||||
var sv : String? = intent.getStringExtra(KEY_INITIAL_TEXT)
|
||||
if(sv != null) {
|
||||
val svEmoji = DecodeOptions(decodeEmoji = true).decodeEmoji(this,sv)
|
||||
val svEmoji = DecodeOptions(decodeEmoji = true).decodeEmoji(this, sv)
|
||||
etContent.setText(svEmoji)
|
||||
etContent.setSelection(svEmoji.length)
|
||||
}
|
||||
|
@ -531,7 +543,7 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
|
|||
sv = intent.getStringExtra(KEY_REPLY_STATUS)
|
||||
if(sv != null && account != null) {
|
||||
try {
|
||||
val reply_status = TootParser(this@ActPost, account).status(JSONObject(sv))
|
||||
val reply_status = TootParser(this@ActPost, account).status(sv.toJsonObject())
|
||||
|
||||
if(reply_status != null) {
|
||||
// CW をリプライ元に合わせる
|
||||
|
@ -560,7 +572,7 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
|
|||
|
||||
// 今回メンションを追加する?
|
||||
val who_acct = account.getFullAcct(reply_status.account)
|
||||
if(mention_list.contains("@" + who_acct )) {
|
||||
if(mention_list.contains("@" + who_acct)) {
|
||||
// 既に含まれている
|
||||
} else if(! account.isMe(reply_status.account) || mention_list.isEmpty()) {
|
||||
// 自分ではない、もしくは、メンションが空
|
||||
|
@ -574,7 +586,8 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
|
|||
}
|
||||
if(sb.isNotEmpty()) {
|
||||
sb.append(' ')
|
||||
val svEmoji = DecodeOptions(decodeEmoji = true).decodeEmoji(this,sb.toString())
|
||||
val svEmoji =
|
||||
DecodeOptions(decodeEmoji = true).decodeEmoji(this, sb.toString())
|
||||
etContent.setText(svEmoji)
|
||||
etContent.setSelection(svEmoji.length)
|
||||
}
|
||||
|
@ -600,7 +613,10 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
|
|||
this.visibility = reply_status.visibility
|
||||
} else {
|
||||
// デフォルトの方が公開範囲が大きい場合、リプライ元に合わせて公開範囲を狭める
|
||||
if(TootStatus.isVisibilitySpoilRequired(this.visibility, reply_status.visibility)) {
|
||||
if(TootStatus.isVisibilitySpoilRequired(
|
||||
this.visibility,
|
||||
reply_status.visibility
|
||||
)) {
|
||||
this.visibility = reply_status.visibility
|
||||
}
|
||||
}
|
||||
|
@ -649,7 +665,7 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
|
|||
|
||||
override fun onSaveInstanceState(outState : Bundle?) {
|
||||
super.onSaveInstanceState(outState)
|
||||
|
||||
|
||||
outState ?: return
|
||||
|
||||
outState.putInt(STATE_MUSHROOM_INPUT, mushroom_input)
|
||||
|
@ -757,7 +773,7 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
|
|||
|
||||
findViewById<View>(R.id.btnPlugin).setOnClickListener(this)
|
||||
findViewById<View>(R.id.btnEmojiPicker).setOnClickListener(this)
|
||||
|
||||
|
||||
for(iv in ivMedia) {
|
||||
iv.setOnClickListener(this)
|
||||
iv.setDefaultImageResId(Styler.getAttributeResourceId(this, R.attr.ic_loading))
|
||||
|
@ -801,7 +817,8 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
|
|||
var s = EmojiDecoder.decodeShortCode(etContent.text.toString())
|
||||
length += s.codePointCount(0, s.length)
|
||||
|
||||
s = if(cbContentWarning.isChecked) EmojiDecoder.decodeShortCode(etContentWarning.text.toString()) else ""
|
||||
s =
|
||||
if(cbContentWarning.isChecked) EmojiDecoder.decodeShortCode(etContentWarning.text.toString()) else ""
|
||||
length += s.codePointCount(0, s.length)
|
||||
|
||||
val max : Int
|
||||
|
@ -817,7 +834,10 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
|
|||
|
||||
val remain = max - length
|
||||
tvCharCount.text = Integer.toString(remain)
|
||||
val color = Styler.getAttributeColor(this, if(remain < 0) R.attr.colorRegexFilterError else android.R.attr.textColorPrimary)
|
||||
val color = Styler.getAttributeColor(
|
||||
this,
|
||||
if(remain < 0) R.attr.colorRegexFilterError else android.R.attr.textColorPrimary
|
||||
)
|
||||
tvCharCount.setTextColor(color)
|
||||
}
|
||||
|
||||
|
@ -847,7 +867,12 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
|
|||
if(AcctColor.hasColorForeground(ac)) {
|
||||
btnAccount.setTextColor(ac.color_fg)
|
||||
} else {
|
||||
btnAccount.setTextColor(Styler.getAttributeColor(this, android.R.attr.textColorPrimary))
|
||||
btnAccount.setTextColor(
|
||||
Styler.getAttributeColor(
|
||||
this,
|
||||
android.R.attr.textColorPrimary
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -856,7 +881,7 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
|
|||
|
||||
if(! attachment_list.isEmpty()) {
|
||||
// 添付ファイルがあったら確認の上添付ファイルを捨てないと切り替えられない
|
||||
Utils.showToast(this, false, R.string.cant_change_account_when_attachment_specified)
|
||||
showToast(this, false, R.string.cant_change_account_when_attachment_specified)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -917,7 +942,7 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
|
|||
selectAccount(a)
|
||||
try {
|
||||
if(TootStatus.isVisibilitySpoilRequired(this.visibility, a.visibility)) {
|
||||
Utils.showToast(this@ActPost, true, R.string.spoil_visibility_for_account)
|
||||
showToast(this@ActPost, true, R.string.spoil_visibility_for_account)
|
||||
this.visibility = a.visibility
|
||||
showVisibility()
|
||||
}
|
||||
|
@ -930,6 +955,7 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
|
|||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
private fun startReplyConversion(access_info : SavedAccount) {
|
||||
val in_reply_to_url = this.in_reply_to_url
|
||||
if(in_reply_to_url == null) {
|
||||
// 下書きが古い形式の場合、URLがないので別タンスへの移動ができない
|
||||
AlertDialog.Builder(this@ActPost)
|
||||
|
@ -946,7 +972,11 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
|
|||
internal var target_status : TootStatus? = null
|
||||
override fun background(client : TootApiClient) : TootApiResult? {
|
||||
// 検索APIに他タンスのステータスのURLを投げると、自タンスのステータスを得られる
|
||||
val path = String.format(Locale.JAPAN, Column.PATH_SEARCH, Uri.encode(in_reply_to_url)) + "&resolve=1"
|
||||
val path = String.format(
|
||||
Locale.JAPAN,
|
||||
Column.PATH_SEARCH,
|
||||
in_reply_to_url.encodePercent()
|
||||
) + "&resolve=1"
|
||||
|
||||
val result = client.request(path)
|
||||
val jsonObject = result?.jsonObject
|
||||
|
@ -970,7 +1000,11 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
|
|||
in_reply_to_id = target_status.id
|
||||
setAccountWithVisibilityConversion(access_info)
|
||||
} else {
|
||||
Utils.showToast(this@ActPost, true, getString(R.string.in_reply_to_id_conversion_failed) + "\n" + result.error)
|
||||
showToast(
|
||||
this@ActPost,
|
||||
true,
|
||||
getString(R.string.in_reply_to_id_conversion_failed) + "\n" + result.error
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -1018,10 +1052,12 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
|
|||
|
||||
AlertDialog.Builder(this)
|
||||
.setTitle(R.string.media_attachment)
|
||||
.setItems(arrayOf<CharSequence>(
|
||||
getString(R.string.set_description),
|
||||
getString(R.string.delete)
|
||||
)) { _, i ->
|
||||
.setItems(
|
||||
arrayOf<CharSequence>(
|
||||
getString(R.string.set_description),
|
||||
getString(R.string.delete)
|
||||
)
|
||||
) { _, i ->
|
||||
when(i) {
|
||||
0 -> editAttachmentDescription(pa)
|
||||
1 -> deleteAttachment(pa)
|
||||
|
@ -1050,19 +1086,23 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
|
|||
private fun editAttachmentDescription(pa : PostAttachment) {
|
||||
val a = pa.attachment
|
||||
if(a == null) {
|
||||
Utils.showToast(this, true, R.string.attachment_description_cant_edit_while_uploading)
|
||||
showToast(this, true, R.string.attachment_description_cant_edit_while_uploading)
|
||||
return
|
||||
}
|
||||
|
||||
DlgTextInput.show(this, getString(R.string.attachment_description), a.description, object : DlgTextInput.Callback {
|
||||
override fun onOK(dialog : Dialog, text : String) {
|
||||
setAttachmentDescription(pa, dialog, text)
|
||||
}
|
||||
|
||||
override fun onEmptyError() {
|
||||
Utils.showToast(this@ActPost, true, R.string.description_empty)
|
||||
}
|
||||
})
|
||||
DlgTextInput.show(
|
||||
this,
|
||||
getString(R.string.attachment_description),
|
||||
a.description,
|
||||
object : DlgTextInput.Callback {
|
||||
override fun onOK(dialog : Dialog, text : String) {
|
||||
setAttachmentDescription(pa, dialog, text)
|
||||
}
|
||||
|
||||
override fun onEmptyError() {
|
||||
showToast(this@ActPost, true, R.string.description_empty)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
|
@ -1107,14 +1147,15 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
|
|||
}
|
||||
|
||||
} else {
|
||||
Utils.showToast(this@ActPost, true, result.error)
|
||||
showToast(this@ActPost, true, result.error)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun openAttachment() {
|
||||
val permissionCheck = ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
val permissionCheck =
|
||||
ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
if(permissionCheck != PackageManager.PERMISSION_GRANTED) {
|
||||
preparePermission()
|
||||
return
|
||||
|
@ -1142,12 +1183,12 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
|
|||
private fun performAttachment() {
|
||||
|
||||
if(attachment_list.size >= 4) {
|
||||
Utils.showToast(this, false, R.string.attachment_too_many)
|
||||
showToast(this, false, R.string.attachment_too_many)
|
||||
return
|
||||
}
|
||||
|
||||
if(account == null) {
|
||||
Utils.showToast(this, false, R.string.account_select_please)
|
||||
showToast(this, false, R.string.account_select_please)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -1161,7 +1202,7 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
|
|||
startActivityForResult(intent, REQUEST_CODE_ATTACHMENT)
|
||||
} catch(ex : Throwable) {
|
||||
log.trace(ex)
|
||||
Utils.showToast(this, ex, "ACTION_OPEN_DOCUMENT failed.")
|
||||
showToast(this, ex, "ACTION_OPEN_DOCUMENT failed.")
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1191,12 +1232,17 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
|
|||
// 設定からリサイズ指定を読む
|
||||
val resize_to = list_resize_max[Pref.ipResizeImage(pref)]
|
||||
|
||||
val bitmap = Utils.createResizedBitmap(log, this, uri, true, resize_to)
|
||||
val bitmap = createResizedBitmap(
|
||||
this,
|
||||
uri,
|
||||
resize_to,
|
||||
skipIfNoNeedToResizeAndRotate = true
|
||||
)
|
||||
if(bitmap != null) {
|
||||
try {
|
||||
val cache_dir = externalCacheDir
|
||||
if(cache_dir == null) {
|
||||
Utils.showToast(this, false, "getExternalCacheDir returns null.")
|
||||
showToast(this, false, "getExternalCacheDir returns null.")
|
||||
break
|
||||
}
|
||||
|
||||
|
@ -1233,7 +1279,7 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
|
|||
|
||||
} catch(ex : Throwable) {
|
||||
log.trace(ex)
|
||||
Utils.showToast(this, ex, "Resizing image failed.")
|
||||
showToast(this, ex, "Resizing image failed.")
|
||||
}
|
||||
|
||||
break
|
||||
|
@ -1254,25 +1300,26 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
|
|||
}
|
||||
}
|
||||
|
||||
@SuppressLint("StaticFieldLeak") private fun addAttachment(uri : Uri, mime_type : String?) {
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
private fun addAttachment(uri : Uri, mime_type : String?) {
|
||||
|
||||
if(attachment_list.size >= 4) {
|
||||
Utils.showToast(this, false, R.string.attachment_too_many)
|
||||
showToast(this, false, R.string.attachment_too_many)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
val account = this@ActPost.account
|
||||
if(account == null) {
|
||||
Utils.showToast(this, false, R.string.account_select_please)
|
||||
showToast(this, false, R.string.account_select_please)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if(mime_type?.isEmpty() != false) {
|
||||
Utils.showToast(this, false, R.string.mime_type_missing)
|
||||
showToast(this, false, R.string.mime_type_missing)
|
||||
return
|
||||
} else if(! acceptable_mime_types.contains(mime_type)) {
|
||||
Utils.showToast(this, true, R.string.mime_type_not_acceptable, mime_type)
|
||||
showToast(this, true, R.string.mime_type_not_acceptable, mime_type)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -1281,9 +1328,9 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
|
|||
val pa = PostAttachment(this)
|
||||
attachment_list.add(pa)
|
||||
showMediaAttachment()
|
||||
Utils.showToast(this, false, R.string.attachment_uploading)
|
||||
showToast(this, false, R.string.attachment_uploading)
|
||||
|
||||
TootTaskRunner(this,TootTaskRunner.PROGRESS_NONE).run(account , object : TootTask {
|
||||
TootTaskRunner(this, TootTaskRunner.PROGRESS_NONE).run(account, object : TootTask {
|
||||
override fun background(client : TootApiClient) : TootApiResult? {
|
||||
if(mime_type.isEmpty()) {
|
||||
return TootApiResult("mime_type is empty.")
|
||||
|
@ -1292,39 +1339,43 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
|
|||
try {
|
||||
val opener = createOpener(uri, mime_type)
|
||||
|
||||
val sv = Pref.spMediaSizeMax(pref)
|
||||
var media_size_max = 1000000 * Utils.parse_int(sv, 8)
|
||||
if(media_size_max < 1000000) media_size_max = 1000000
|
||||
val media_size_max =
|
||||
1000000 * Math.max(1, Pref.spMediaSizeMax.optInt(pref) ?: 8)
|
||||
|
||||
val content_length = getStreamSize(true, opener.open())
|
||||
if(content_length > media_size_max) {
|
||||
return TootApiResult(getString(R.string.file_size_too_big, media_size_max / 1000000))
|
||||
return TootApiResult(
|
||||
getString(
|
||||
R.string.file_size_too_big,
|
||||
media_size_max / 1000000
|
||||
)
|
||||
)
|
||||
}
|
||||
val multipart_body = MultipartBody.Builder()
|
||||
.setType(MultipartBody.FORM)
|
||||
.addFormDataPart(
|
||||
"file", getDocumentName(uri), object : RequestBody() {
|
||||
override fun contentType() : MediaType? {
|
||||
return MediaType.parse(opener.mimeType)
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
override fun contentLength() : Long {
|
||||
return content_length
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
override fun writeTo(sink : BufferedSink) {
|
||||
opener.open().use { inData ->
|
||||
val tmp = ByteArray(4096)
|
||||
while(true) {
|
||||
val r = inData.read(tmp, 0, tmp.size)
|
||||
if(r <= 0) break
|
||||
sink.write(tmp, 0, r)
|
||||
override fun contentType() : MediaType? {
|
||||
return MediaType.parse(opener.mimeType)
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
override fun contentLength() : Long {
|
||||
return content_length
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
override fun writeTo(sink : BufferedSink) {
|
||||
opener.open().use { inData ->
|
||||
val tmp = ByteArray(4096)
|
||||
while(true) {
|
||||
val r = inData.read(tmp, 0, tmp.size)
|
||||
if(r <= 0) break
|
||||
sink.write(tmp, 0, r)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
.build()
|
||||
|
||||
|
@ -1347,7 +1398,7 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
|
|||
return result
|
||||
|
||||
} catch(ex : Throwable) {
|
||||
return TootApiResult(Utils.formatError(ex, "read failed."))
|
||||
return TootApiResult(ex.withCaption("read failed."))
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1356,7 +1407,7 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
|
|||
if(pa.attachment == null) {
|
||||
pa.status = PostAttachment.STATUS_UPLOAD_FAILED
|
||||
if(result != null) {
|
||||
Utils.showToast(this@ActPost, true, result.error)
|
||||
showToast(this@ActPost, true, result.error)
|
||||
}
|
||||
} else {
|
||||
pa.status = PostAttachment.STATUS_UPLOADED
|
||||
|
@ -1385,7 +1436,7 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
|
|||
val a = pa.attachment
|
||||
if(a != null) {
|
||||
// アップロード完了
|
||||
Utils.showToast(this@ActPost, false, R.string.attachment_uploaded)
|
||||
showToast(this@ActPost, false, R.string.attachment_uploaded)
|
||||
|
||||
// 投稿欄の末尾に追記する
|
||||
val selStart = etContent.selectionStart
|
||||
|
@ -1419,7 +1470,8 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
|
|||
val values = ContentValues()
|
||||
values.put(MediaStore.Images.Media.TITLE, filename)
|
||||
values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg")
|
||||
uriCameraImage = contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
|
||||
uriCameraImage =
|
||||
contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
|
||||
|
||||
val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
|
||||
intent.putExtra(MediaStore.EXTRA_OUTPUT, uriCameraImage)
|
||||
|
@ -1427,7 +1479,7 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
|
|||
startActivityForResult(intent, REQUEST_CODE_CAMERA)
|
||||
} catch(ex : Throwable) {
|
||||
log.trace(ex)
|
||||
Utils.showToast(this, ex, "opening camera app failed.")
|
||||
showToast(this, ex, "opening camera app failed.")
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1436,11 +1488,14 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
|
|||
if(Build.VERSION.SDK_INT >= 23) {
|
||||
// No explanation needed, we can request the permission.
|
||||
|
||||
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE) // Manifest.permission.CAMERA,
|
||||
, PERMISSION_REQUEST_CODE
|
||||
ActivityCompat.requestPermissions(
|
||||
this,
|
||||
arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE) // Manifest.permission.CAMERA,
|
||||
,
|
||||
PERMISSION_REQUEST_CODE
|
||||
)
|
||||
} else {
|
||||
Utils.showToast(this, true, R.string.missing_permission_to_access_media)
|
||||
showToast(this, true, R.string.missing_permission_to_access_media)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1459,7 +1514,7 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
|
|||
++ i
|
||||
}
|
||||
if(bNotGranted) {
|
||||
Utils.showToast(this, true, R.string.missing_permission_to_access_media)
|
||||
showToast(this, true, R.string.missing_permission_to_access_media)
|
||||
} else {
|
||||
openAttachment()
|
||||
}
|
||||
|
@ -1502,7 +1557,7 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
|
|||
}
|
||||
|
||||
private fun showVisibility() {
|
||||
btnVisibility.setImageResource(Styler.getVisibilityIcon(this, visibility ))
|
||||
btnVisibility.setImageResource(Styler.getVisibilityIcon(this, visibility))
|
||||
}
|
||||
|
||||
private fun performVisibility() {
|
||||
|
@ -1541,7 +1596,7 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
|
|||
) {
|
||||
post_helper.openEmojiPickerFromMore()
|
||||
}
|
||||
|
||||
|
||||
|
||||
dialog.addAction(
|
||||
getString(R.string.clear_text)
|
||||
|
@ -1574,7 +1629,6 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
|
|||
dialog.show(this, null)
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
// post
|
||||
|
||||
|
@ -1584,7 +1638,7 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
|
|||
// アップロード中は投稿できない
|
||||
for(pa in attachment_list) {
|
||||
if(pa.status == PostAttachment.STATUS_UPLOADING) {
|
||||
Utils.showToast(this, false, R.string.media_attachment_still_uploading)
|
||||
showToast(this, false, R.string.media_attachment_still_uploading)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -1633,7 +1687,7 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
|
|||
tvReplyTo.text = DecodeOptions(
|
||||
short = true,
|
||||
decodeEmoji = true
|
||||
|
||||
|
||||
).decodeHTML(this@ActPost, account, in_reply_to_text)
|
||||
ivReply.setImageUrl(pref, Styler.calcIconRound(ivReply.layoutParams), in_reply_to_image)
|
||||
}
|
||||
|
@ -1649,7 +1703,8 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
|
|||
|
||||
private fun saveDraft() {
|
||||
val content = etContent.text.toString()
|
||||
val content_warning = if(cbContentWarning.isChecked) etContentWarning.text.toString() else ""
|
||||
val content_warning =
|
||||
if(cbContentWarning.isChecked) etContentWarning.text.toString() else ""
|
||||
val isEnquete = cbEnquete.isChecked
|
||||
|
||||
val str_choice = arrayOf(
|
||||
|
@ -1683,7 +1738,7 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
|
|||
json.put(DRAFT_CONTENT_WARNING_CHECK, cbContentWarning.isChecked)
|
||||
json.put(DRAFT_NSFW_CHECK, cbNSFW.isChecked)
|
||||
json.put(DRAFT_VISIBILITY, visibility)
|
||||
json.put(DRAFT_ACCOUNT_DB_ID, account?.db_id ?: -1L )
|
||||
json.put(DRAFT_ACCOUNT_DB_ID, account?.db_id ?: - 1L)
|
||||
json.put(DRAFT_ATTACHMENT_LIST, tmp_attachment_list)
|
||||
json.put(DRAFT_REPLY_ID, in_reply_to_id)
|
||||
json.put(DRAFT_REPLY_TEXT, in_reply_to_text)
|
||||
|
@ -1724,8 +1779,8 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
|
|||
|
||||
override fun doInBackground(vararg params : Void) : String? {
|
||||
|
||||
var content = draft.optString(DRAFT_CONTENT)
|
||||
val account_db_id = Utils.optLongX(draft, DRAFT_ACCOUNT_DB_ID, - 1L)
|
||||
var content = draft.parseString(DRAFT_CONTENT) ?: ""
|
||||
val account_db_id = draft.parseLong(DRAFT_ACCOUNT_DB_ID) ?: - 1L
|
||||
var tmp_attachment_list = draft.optJSONArray(DRAFT_ATTACHMENT_LIST)
|
||||
|
||||
val account = SavedAccount.loadAccount(this@ActPost, account_db_id)
|
||||
|
@ -1735,7 +1790,8 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
|
|||
var i = 0
|
||||
val ie = tmp_attachment_list.length()
|
||||
while(i < ie) {
|
||||
val ta = parseItem(::TootAttachment, tmp_attachment_list.optJSONObject(i))
|
||||
val ta =
|
||||
parseItem(::TootAttachment, tmp_attachment_list.optJSONObject(i))
|
||||
val text_url = ta?.text_url
|
||||
if(text_url?.isNotEmpty() == true) {
|
||||
content = content.replace(text_url, "")
|
||||
|
@ -1757,13 +1813,13 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
|
|||
this.account = account
|
||||
|
||||
// アカウントがあるなら基本的にはすべての情報を復元できるはずだが、いくつか確認が必要だ
|
||||
val api_client = TootApiClient(this@ActPost, callback=object : TootApiCallback {
|
||||
val api_client = TootApiClient(this@ActPost, callback = object : TootApiCallback {
|
||||
|
||||
override val isApiCancelled : Boolean
|
||||
get() = isCancelled
|
||||
|
||||
override fun publishApiProgress(s : String) {
|
||||
Utils.runOnMainThread { progress.setMessage(s) }
|
||||
runOnMainLooper { progress.setMessage(s) }
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -1830,19 +1886,20 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
|
|||
val content_warning_checked = draft.optBoolean(DRAFT_CONTENT_WARNING_CHECK)
|
||||
val nsfw_checked = draft.optBoolean(DRAFT_NSFW_CHECK)
|
||||
val tmp_attachment_list = draft.optJSONArray(DRAFT_ATTACHMENT_LIST)
|
||||
val reply_id = Utils.optLongX(draft, DRAFT_REPLY_ID, - 1L)
|
||||
val reply_id = draft.parseLong(DRAFT_REPLY_ID) ?: - 1L
|
||||
val reply_text = draft.optString(DRAFT_REPLY_TEXT, null)
|
||||
val reply_image = draft.optString(DRAFT_REPLY_IMAGE, null)
|
||||
val reply_url = draft.optString(DRAFT_REPLY_URL, null)
|
||||
val draft_visibility = draft.parseString(DRAFT_VISIBILITY)
|
||||
|
||||
val evEmoji = DecodeOptions(decodeEmoji = true).decodeEmoji(this@ActPost,content)
|
||||
val evEmoji = DecodeOptions(decodeEmoji = true).decodeEmoji(this@ActPost, content)
|
||||
etContent.setText(evEmoji)
|
||||
etContent.setSelection(evEmoji.length)
|
||||
etContentWarning.setText(content_warning)
|
||||
etContentWarning.setSelection(content_warning.length)
|
||||
cbContentWarning.isChecked = content_warning_checked
|
||||
cbNSFW.isChecked = nsfw_checked
|
||||
this@ActPost.visibility = visibility
|
||||
if(draft_visibility != null) this@ActPost.visibility = draft_visibility
|
||||
|
||||
cbEnquete.isChecked = draft.optBoolean(DRAFT_IS_ENQUETE, false)
|
||||
val array = draft.optJSONArray(DRAFT_ENQUETE_ITEMS)
|
||||
|
@ -2000,8 +2057,8 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
|
|||
else -> R.raw.recommended_plugin_en
|
||||
}
|
||||
|
||||
Utils.loadRawResource(this, res_id)?.let { data ->
|
||||
val text = Utils.decodeUTF8(data)
|
||||
this.loadRawResource(res_id)?.let { data ->
|
||||
val text = data.decodeUTF8()
|
||||
val viewRoot = layoutInflater.inflate(R.layout.dlg_plugin_missing, null, false)
|
||||
|
||||
val tvText = viewRoot.findViewById<TextView>(R.id.tvText)
|
||||
|
|
|
@ -17,7 +17,7 @@ import jp.juggler.subwaytooter.table.MutedWord
|
|||
import jp.juggler.subwaytooter.table.SavedAccount
|
||||
import jp.juggler.subwaytooter.util.DecodeOptions
|
||||
import jp.juggler.subwaytooter.util.LogCategory
|
||||
import jp.juggler.subwaytooter.util.Utils
|
||||
import jp.juggler.subwaytooter.util.showToast
|
||||
|
||||
class ActText : AppCompatActivity(), View.OnClickListener {
|
||||
|
||||
|
@ -225,10 +225,10 @@ class ActText : AppCompatActivity(), View.OnClickListener {
|
|||
|
||||
clipboard.primaryClip = clip
|
||||
|
||||
Utils.showToast(this, false, R.string.copy_complete)
|
||||
showToast(this, false, R.string.copy_complete)
|
||||
} catch(ex : Throwable) {
|
||||
log.trace(ex)
|
||||
Utils.showToast(this, ex, "copy failed.")
|
||||
showToast(this, ex, "copy failed.")
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -244,7 +244,7 @@ class ActText : AppCompatActivity(), View.OnClickListener {
|
|||
|
||||
} catch(ex : Throwable) {
|
||||
log.trace(ex)
|
||||
Utils.showToast(this, ex, "send failed.")
|
||||
showToast(this, ex, "send failed.")
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -252,7 +252,7 @@ class ActText : AppCompatActivity(), View.OnClickListener {
|
|||
private fun search() {
|
||||
val sv = selection
|
||||
if( sv.isEmpty() ) {
|
||||
Utils.showToast(this, false, "please select search keyword")
|
||||
showToast(this, false, "please select search keyword")
|
||||
return
|
||||
}
|
||||
try {
|
||||
|
@ -263,7 +263,7 @@ class ActText : AppCompatActivity(), View.OnClickListener {
|
|||
}
|
||||
} catch(ex : Throwable) {
|
||||
log.trace(ex)
|
||||
Utils.showToast(this, ex, "search failed.")
|
||||
showToast(this, ex, "search failed.")
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -271,7 +271,7 @@ class ActText : AppCompatActivity(), View.OnClickListener {
|
|||
private fun searchToot(resultCode : Int) {
|
||||
val sv = selection
|
||||
if(sv.isEmpty() ) {
|
||||
Utils.showToast(this, false, "please select search keyword")
|
||||
showToast(this, false, "please select search keyword")
|
||||
return
|
||||
}
|
||||
try {
|
||||
|
@ -291,10 +291,10 @@ class ActText : AppCompatActivity(), View.OnClickListener {
|
|||
for(column in App1.getAppState(this).column_list) {
|
||||
column.onMuteAppUpdated()
|
||||
}
|
||||
Utils.showToast(this, false, R.string.word_was_muted)
|
||||
showToast(this, false, R.string.word_was_muted)
|
||||
} catch(ex : Throwable) {
|
||||
log.trace(ex)
|
||||
Utils.showToast(this, ex, "muteWord failed.")
|
||||
showToast(this, ex, "muteWord failed.")
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -47,11 +47,7 @@ import jp.juggler.subwaytooter.table.PostDraft
|
|||
import jp.juggler.subwaytooter.table.SavedAccount
|
||||
import jp.juggler.subwaytooter.table.TagSet
|
||||
import jp.juggler.subwaytooter.table.UserRelation
|
||||
import jp.juggler.subwaytooter.util.CustomEmojiCache
|
||||
import jp.juggler.subwaytooter.util.CustomEmojiLister
|
||||
import jp.juggler.subwaytooter.util.LogCategory
|
||||
import jp.juggler.subwaytooter.util.ProgressResponseBody
|
||||
import jp.juggler.subwaytooter.util.Utils
|
||||
import jp.juggler.subwaytooter.util.*
|
||||
import okhttp3.Cache
|
||||
import okhttp3.CacheControl
|
||||
import okhttp3.CipherSuite
|
||||
|
@ -222,7 +218,7 @@ class App1 : Application() {
|
|||
|
||||
lateinit var ok_http_client : OkHttpClient
|
||||
|
||||
lateinit var ok_http_client2 : OkHttpClient
|
||||
private lateinit var ok_http_client2 : OkHttpClient
|
||||
|
||||
lateinit var pref : SharedPreferences
|
||||
|
||||
|
@ -531,7 +527,7 @@ class App1 : Application() {
|
|||
customTabsIntent.launchUrl(activity, Uri.parse(url))
|
||||
} catch(ex : Throwable) {
|
||||
log.trace(ex)
|
||||
Utils.showToast(activity, false, "can't open browser app")
|
||||
showToast(activity, false, "can't open browser app")
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ import java.util.HashMap
|
|||
import java.util.Locale
|
||||
|
||||
import jp.juggler.subwaytooter.util.LogCategory
|
||||
import jp.juggler.subwaytooter.util.Utils
|
||||
import jp.juggler.subwaytooter.util.parseLong
|
||||
|
||||
object AppDataExporter {
|
||||
|
||||
|
@ -69,7 +69,13 @@ object AppDataExporter {
|
|||
|
||||
}
|
||||
|
||||
else -> throw RuntimeException(String.format(Locale.JAPAN, "bad data type: JSONObject key =%s", k))
|
||||
else -> throw RuntimeException(
|
||||
String.format(
|
||||
Locale.JAPAN,
|
||||
"bad data type: JSONObject key =%s",
|
||||
k
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -94,7 +100,14 @@ object AppDataExporter {
|
|||
|
||||
JsonToken.NUMBER -> dst.put(name, reader.nextDouble())
|
||||
|
||||
else -> throw RuntimeException(String.format(Locale.JAPAN, "bad data type: %s key =%s", token, name))
|
||||
else -> throw RuntimeException(
|
||||
String.format(
|
||||
Locale.JAPAN,
|
||||
"bad data type: %s key =%s",
|
||||
token,
|
||||
name
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
reader.endObject()
|
||||
|
@ -216,7 +229,8 @@ object AppDataExporter {
|
|||
} // 無視する
|
||||
}
|
||||
reader.endObject()
|
||||
val new_id = db.insertWithOnConflict(table, null, cv, SQLiteDatabase.CONFLICT_REPLACE)
|
||||
val new_id =
|
||||
db.insertWithOnConflict(table, null, cv, SQLiteDatabase.CONFLICT_REPLACE)
|
||||
if(new_id == - 1L) {
|
||||
throw RuntimeException("importTable: invalid row_id")
|
||||
}
|
||||
|
@ -251,7 +265,13 @@ object AppDataExporter {
|
|||
(v is Float && v.isNaN()) -> writer.value(MAGIC_NAN)
|
||||
else -> writer.value(v)
|
||||
}
|
||||
else -> throw RuntimeException(String.format(Locale.JAPAN, "writePref. bad data type: Preference key =%s", k))
|
||||
else -> throw RuntimeException(
|
||||
String.format(
|
||||
Locale.JAPAN,
|
||||
"writePref. bad data type: Preference key =%s",
|
||||
k
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
writer.endObject()
|
||||
|
@ -271,7 +291,7 @@ object AppDataExporter {
|
|||
continue
|
||||
}
|
||||
|
||||
val prefItem = Pref.map.get(k)
|
||||
val prefItem = Pref.map[k]
|
||||
when(prefItem) {
|
||||
is Pref.BooleanPref -> e.putBoolean(k, reader.nextBoolean())
|
||||
is Pref.IntPref -> e.putInt(k, reader.nextInt())
|
||||
|
@ -312,16 +332,21 @@ object AppDataExporter {
|
|||
}
|
||||
|
||||
@Throws(IOException::class, JSONException::class)
|
||||
private fun readColumn(app_state : AppState, reader : JsonReader, id_map : HashMap<Long, Long>) : ArrayList<Column> {
|
||||
private fun readColumn(
|
||||
app_state : AppState,
|
||||
reader : JsonReader,
|
||||
id_map : HashMap<Long, Long>
|
||||
) : ArrayList<Column> {
|
||||
val result = ArrayList<Column>()
|
||||
reader.beginArray()
|
||||
while(reader.hasNext()) {
|
||||
val item = readJsonObject(reader)
|
||||
val old_id = Utils.optLongX(item, Column.KEY_ACCOUNT_ROW_ID, - 1L)
|
||||
val old_id = item.parseLong(Column.KEY_ACCOUNT_ROW_ID) ?: - 1L
|
||||
if(old_id == - 1L) {
|
||||
// 検索カラムは NAアカウントと紐ついている。変換の必要はない
|
||||
} else {
|
||||
val new_id = id_map[old_id] ?: throw RuntimeException("readColumn: can't convert account id")
|
||||
val new_id =
|
||||
id_map[old_id] ?: throw RuntimeException("readColumn: can't convert account id")
|
||||
item.put(Column.KEY_ACCOUNT_ROW_ID, new_id)
|
||||
}
|
||||
try {
|
||||
|
|
|
@ -35,10 +35,8 @@ import java.util.regex.Pattern
|
|||
import jp.juggler.subwaytooter.api.entity.TootStatus
|
||||
import jp.juggler.subwaytooter.table.HighlightWord
|
||||
import jp.juggler.subwaytooter.table.SavedAccount
|
||||
import jp.juggler.subwaytooter.util.LogCategory
|
||||
import jp.juggler.subwaytooter.span.MyClickableSpan
|
||||
import jp.juggler.subwaytooter.util.PostAttachment
|
||||
import jp.juggler.subwaytooter.util.Utils
|
||||
import jp.juggler.subwaytooter.util.*
|
||||
|
||||
class AppState(internal val context : Context, internal val pref : SharedPreferences) {
|
||||
|
||||
|
@ -63,11 +61,11 @@ class AppState(internal val context : Context, internal val pref : SharedPrefere
|
|||
|
||||
try {
|
||||
context.openFileOutput(fileName, Context.MODE_PRIVATE).use { os ->
|
||||
os.write(Utils.encodeUTF8(array.toString()))
|
||||
os.write(array.toString().encodeUTF8())
|
||||
}
|
||||
} catch(ex : Throwable) {
|
||||
log.trace(ex)
|
||||
Utils.showToast(context, ex, "saveColumnList failed.")
|
||||
showToast(context, ex, "saveColumnList failed.")
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -78,12 +76,12 @@ class AppState(internal val context : Context, internal val pref : SharedPrefere
|
|||
context.openFileInput(fileName).use { inData ->
|
||||
val bao = ByteArrayOutputStream(inData.available())
|
||||
IOUtils.copy(inData, bao)
|
||||
return JSONArray(Utils.decodeUTF8(bao.toByteArray()))
|
||||
return bao.toByteArray().decodeUTF8().toJsonArray()
|
||||
}
|
||||
} catch(ignored : FileNotFoundException) {
|
||||
} catch(ex : Throwable) {
|
||||
log.trace(ex)
|
||||
Utils.showToast(context, ex, "loadColumnList failed.")
|
||||
showToast(context, ex, "loadColumnList failed.")
|
||||
}
|
||||
|
||||
return null
|
||||
|
@ -128,7 +126,7 @@ class AppState(internal val context : Context, internal val pref : SharedPrefere
|
|||
|
||||
// initからプロパティにアクセスする場合、そのプロパティはinitより上で定義されていないとダメっぽい
|
||||
// そしてその他のメソッドからval プロパティにアクセスする場合、そのプロパティはメソッドより上で初期化されていないとダメっぽい
|
||||
|
||||
|
||||
init {
|
||||
this.handler = Handler()
|
||||
this.density = context.resources.displayMetrics.density
|
||||
|
@ -190,7 +188,10 @@ class AppState(internal val context : Context, internal val pref : SharedPrefere
|
|||
log.d("proc_flushSpeechQueue: tts_speak wait expired.")
|
||||
restartTTS()
|
||||
} else {
|
||||
log.d("proc_flushSpeechQueue: tts is speaking. queue_count=%d, expire_remain=%.3f", queue_count, expire_remain / 1000f
|
||||
log.d(
|
||||
"proc_flushSpeechQueue: tts is speaking. queue_count=%d, expire_remain=%.3f",
|
||||
queue_count,
|
||||
expire_remain / 1000f
|
||||
)
|
||||
handler.postDelayed(this, expire_remain)
|
||||
return
|
||||
|
@ -313,7 +314,7 @@ class AppState(internal val context : Context, internal val pref : SharedPrefere
|
|||
|
||||
if(willSpeechEnabled && tts == null && tts_status == TTS_STATUS_NONE) {
|
||||
tts_status = TTS_STATUS_INITIALIZING
|
||||
Utils.showToast(context, false, R.string.text_to_speech_initializing)
|
||||
showToast(context, false, R.string.text_to_speech_initializing)
|
||||
log.d("initializing TextToSpeech…")
|
||||
|
||||
object : AsyncTask<Void, Void, TextToSpeech?>() {
|
||||
|
@ -325,79 +326,92 @@ class AppState(internal val context : Context, internal val pref : SharedPrefere
|
|||
return tts
|
||||
}
|
||||
|
||||
internal val tts_init_listener : TextToSpeech.OnInitListener = TextToSpeech.OnInitListener { status ->
|
||||
|
||||
val tts = this.tmp_tts
|
||||
if(tts == null || TextToSpeech.SUCCESS != status) {
|
||||
Utils.showToast(context, false, R.string.text_to_speech_initialize_failed, status)
|
||||
log.d("speech initialize failed. status=%s", status)
|
||||
return@OnInitListener
|
||||
}
|
||||
|
||||
Utils.runOnMainThread {
|
||||
if(! willSpeechEnabled) {
|
||||
Utils.showToast(context, false, R.string.text_to_speech_shutdown)
|
||||
log.d("shutdown TextToSpeech…")
|
||||
tts.shutdown()
|
||||
} else {
|
||||
this@AppState.tts = tts
|
||||
tts_status = TTS_STATUS_INITIALIZED
|
||||
tts_speak_start = 0L
|
||||
tts_speak_end = 0L
|
||||
|
||||
voice_list.clear()
|
||||
try {
|
||||
val voice_set = try{
|
||||
tts.voices
|
||||
// may raise NullPointerException is tts has no collection
|
||||
}catch(ignored:Throwable){
|
||||
null
|
||||
}
|
||||
if(voice_set == null || voice_set.isEmpty()) {
|
||||
log.d("TextToSpeech.getVoices returns null or empty set.")
|
||||
} else {
|
||||
val lang = Locale.getDefault().toLanguageTag()
|
||||
for(v in voice_set) {
|
||||
log.d("Voice %s %s %s", v.name, v.locale.toLanguageTag(), lang
|
||||
)
|
||||
if(lang != v.locale.toLanguageTag()) {
|
||||
continue
|
||||
}
|
||||
voice_list.add(v)
|
||||
internal val tts_init_listener : TextToSpeech.OnInitListener =
|
||||
TextToSpeech.OnInitListener { status ->
|
||||
|
||||
val tts = this.tmp_tts
|
||||
if(tts == null || TextToSpeech.SUCCESS != status) {
|
||||
showToast(
|
||||
context,
|
||||
false,
|
||||
R.string.text_to_speech_initialize_failed,
|
||||
status
|
||||
)
|
||||
log.d("speech initialize failed. status=%s", status)
|
||||
return@OnInitListener
|
||||
}
|
||||
|
||||
runOnMainLooper {
|
||||
if(! willSpeechEnabled) {
|
||||
showToast(context, false, R.string.text_to_speech_shutdown)
|
||||
log.d("shutdown TextToSpeech…")
|
||||
tts.shutdown()
|
||||
} else {
|
||||
this@AppState.tts = tts
|
||||
tts_status = TTS_STATUS_INITIALIZED
|
||||
tts_speak_start = 0L
|
||||
tts_speak_end = 0L
|
||||
|
||||
voice_list.clear()
|
||||
try {
|
||||
val voice_set = try {
|
||||
tts.voices
|
||||
// may raise NullPointerException is tts has no collection
|
||||
} catch(ignored : Throwable) {
|
||||
null
|
||||
}
|
||||
if(voice_set == null || voice_set.isEmpty()) {
|
||||
log.d("TextToSpeech.getVoices returns null or empty set.")
|
||||
} else {
|
||||
val lang = Locale.getDefault().toLanguageTag()
|
||||
for(v in voice_set) {
|
||||
log.d(
|
||||
"Voice %s %s %s",
|
||||
v.name,
|
||||
v.locale.toLanguageTag(),
|
||||
lang
|
||||
)
|
||||
if(lang != v.locale.toLanguageTag()) {
|
||||
continue
|
||||
}
|
||||
voice_list.add(v)
|
||||
}
|
||||
}
|
||||
} catch(ex : Throwable) {
|
||||
log.trace(ex)
|
||||
log.e(ex, "TextToSpeech.getVoices raises exception.")
|
||||
}
|
||||
} catch(ex : Throwable) {
|
||||
log.trace(ex)
|
||||
log.e(ex, "TextToSpeech.getVoices raises exception.")
|
||||
|
||||
handler.post(proc_flushSpeechQueue)
|
||||
|
||||
context.registerReceiver(
|
||||
tts_receiver,
|
||||
IntentFilter(TextToSpeech.ACTION_TTS_QUEUE_PROCESSING_COMPLETED)
|
||||
)
|
||||
|
||||
// tts.setOnUtteranceProgressListener( new UtteranceProgressListener() {
|
||||
// @Override public void onStart( String utteranceId ){
|
||||
// log.d( "UtteranceProgressListener.onStart id=%s", utteranceId );
|
||||
// }
|
||||
//
|
||||
// @Override public void onDone( String utteranceId ){
|
||||
// log.d( "UtteranceProgressListener.onDone id=%s", utteranceId );
|
||||
// handler.post( proc_flushSpeechQueue );
|
||||
// }
|
||||
//
|
||||
// @Override public void onError( String utteranceId ){
|
||||
// log.d( "UtteranceProgressListener.onError id=%s", utteranceId );
|
||||
// handler.post( proc_flushSpeechQueue );
|
||||
// }
|
||||
// } );
|
||||
}
|
||||
|
||||
handler.post(proc_flushSpeechQueue)
|
||||
|
||||
context.registerReceiver(tts_receiver, IntentFilter(TextToSpeech.ACTION_TTS_QUEUE_PROCESSING_COMPLETED))
|
||||
|
||||
// tts.setOnUtteranceProgressListener( new UtteranceProgressListener() {
|
||||
// @Override public void onStart( String utteranceId ){
|
||||
// log.d( "UtteranceProgressListener.onStart id=%s", utteranceId );
|
||||
// }
|
||||
//
|
||||
// @Override public void onDone( String utteranceId ){
|
||||
// log.d( "UtteranceProgressListener.onDone id=%s", utteranceId );
|
||||
// handler.post( proc_flushSpeechQueue );
|
||||
// }
|
||||
//
|
||||
// @Override public void onError( String utteranceId ){
|
||||
// log.d( "UtteranceProgressListener.onError id=%s", utteranceId );
|
||||
// handler.post( proc_flushSpeechQueue );
|
||||
// }
|
||||
// } );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}.executeOnExecutor(App1.task_executor)
|
||||
}
|
||||
if(! willSpeechEnabled && tts != null) {
|
||||
Utils.showToast(context, false, R.string.text_to_speech_shutdown)
|
||||
showToast(context, false, R.string.text_to_speech_shutdown)
|
||||
log.d("shutdown TextToSpeech…")
|
||||
tts?.shutdown()
|
||||
tts = null
|
||||
|
|
|
@ -2,7 +2,6 @@ package jp.juggler.subwaytooter
|
|||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import android.os.AsyncTask
|
||||
import android.os.SystemClock
|
||||
import jp.juggler.subwaytooter.api.*
|
||||
|
@ -26,13 +25,8 @@ import jp.juggler.subwaytooter.table.MutedWord
|
|||
import jp.juggler.subwaytooter.table.SavedAccount
|
||||
import jp.juggler.subwaytooter.table.TagSet
|
||||
import jp.juggler.subwaytooter.table.UserRelation
|
||||
import jp.juggler.subwaytooter.util.BucketList
|
||||
import jp.juggler.subwaytooter.api.entity.*
|
||||
import jp.juggler.subwaytooter.util.LogCategory
|
||||
import jp.juggler.subwaytooter.util.VersionString
|
||||
import jp.juggler.subwaytooter.util.WordTrieTree
|
||||
import jp.juggler.subwaytooter.util.ScrollPosition
|
||||
import jp.juggler.subwaytooter.util.Utils
|
||||
import jp.juggler.subwaytooter.util.*
|
||||
|
||||
class Column(
|
||||
val app_state : AppState,
|
||||
|
@ -86,8 +80,8 @@ class Column(
|
|||
private const val PATH_ACCOUNT = "/api/v1/accounts/%d" // 1:account_id
|
||||
private const val PATH_STATUSES = "/api/v1/statuses/%d" // 1:status_id
|
||||
private const val PATH_STATUSES_CONTEXT = "/api/v1/statuses/%d/context" // 1:status_id
|
||||
const val PATH_SEARCH =
|
||||
"/api/v1/search?q=%s" // 1: query(urlencoded) , also, append "&resolve=1" if resolve non-local accounts
|
||||
const val PATH_SEARCH = "/api/v1/search?q=%s"
|
||||
// search args 1: query(urlencoded) , also, append "&resolve=1" if resolve non-local accounts
|
||||
private const val PATH_INSTANCE = "/api/v1/instance"
|
||||
private const val PATH_LIST_INFO = "/api/v1/lists/%s"
|
||||
|
||||
|
@ -161,7 +155,7 @@ class Column(
|
|||
}
|
||||
|
||||
fun loadAccount(context : Context, src : JSONObject) : SavedAccount {
|
||||
val account_db_id = Utils.optLongX(src, KEY_ACCOUNT_ROW_ID)
|
||||
val account_db_id = src.parseLong(KEY_ACCOUNT_ROW_ID) ?: - 1L
|
||||
return if(account_db_id >= 0) {
|
||||
SavedAccount.loadAccount(context, account_db_id)
|
||||
?: throw RuntimeException("missing account")
|
||||
|
@ -250,7 +244,7 @@ class Column(
|
|||
TYPE_HOME, TYPE_NOTIFICATIONS -> "/api/v1/streaming/?stream=user"
|
||||
TYPE_LOCAL -> "/api/v1/streaming/?stream=public:local"
|
||||
TYPE_FEDERATE -> "/api/v1/streaming/?stream=public"
|
||||
TYPE_HASHTAG -> "/api/v1/streaming/?stream=hashtag&tag=" + Uri.encode(hashtag) // タグ先頭の#を含まない
|
||||
TYPE_HASHTAG -> "/api/v1/streaming/?stream=hashtag&tag=" + hashtag.encodePercent() // タグ先頭の#を含まない
|
||||
TYPE_LIST_TL -> "/api/v1/streaming/?stream=list&list=" + profile_id.toString()
|
||||
else -> null
|
||||
}
|
||||
|
@ -451,27 +445,27 @@ class Column(
|
|||
hide_media_default = src.optBoolean(KEY_HIDE_MEDIA_DEFAULT)
|
||||
enable_speech = src.optBoolean(KEY_ENABLE_SPEECH)
|
||||
|
||||
regex_text = Utils.optStringX(src, KEY_REGEX_TEXT) ?: ""
|
||||
regex_text = src.parseString(KEY_REGEX_TEXT) ?: ""
|
||||
|
||||
header_bg_color = src.optInt(KEY_HEADER_BACKGROUND_COLOR)
|
||||
header_fg_color = src.optInt(KEY_HEADER_TEXT_COLOR)
|
||||
column_bg_color = src.optInt(KEY_COLUMN_BACKGROUND_COLOR)
|
||||
acct_color = src.optInt(KEY_COLUMN_ACCT_TEXT_COLOR)
|
||||
content_color = src.optInt(KEY_COLUMN_CONTENT_TEXT_COLOR)
|
||||
column_bg_image = Utils.optStringX(src, KEY_COLUMN_BACKGROUND_IMAGE) ?: ""
|
||||
column_bg_image = src.parseString(KEY_COLUMN_BACKGROUND_IMAGE) ?: ""
|
||||
column_bg_image_alpha = src.optDouble(KEY_COLUMN_BACKGROUND_IMAGE_ALPHA, 1.0).toFloat()
|
||||
|
||||
when(column_type) {
|
||||
|
||||
TYPE_CONVERSATION, TYPE_BOOSTED_BY, TYPE_FAVOURITED_BY -> status_id =
|
||||
Utils.optLongX(src, KEY_STATUS_ID)
|
||||
src.parseLong(KEY_STATUS_ID) ?: - 1L
|
||||
|
||||
TYPE_PROFILE -> {
|
||||
profile_id = Utils.optLongX(src, KEY_PROFILE_ID)
|
||||
profile_id = src.parseLong(KEY_PROFILE_ID) ?: - 1L
|
||||
profile_tab = src.optInt(KEY_PROFILE_TAB)
|
||||
}
|
||||
|
||||
TYPE_LIST_MEMBER, TYPE_LIST_TL -> profile_id = Utils.optLongX(src, KEY_PROFILE_ID)
|
||||
TYPE_LIST_MEMBER, TYPE_LIST_TL -> profile_id = src.parseLong(KEY_PROFILE_ID) ?: - 1L
|
||||
|
||||
TYPE_HASHTAG -> hashtag = src.optString(KEY_HASHTAG)
|
||||
|
||||
|
@ -939,35 +933,35 @@ class Column(
|
|||
changeList : List<AdapterChange>? = null,
|
||||
reset : Boolean = false
|
||||
) {
|
||||
if(! Utils.isMainThread) {
|
||||
if(! isMainThread) {
|
||||
throw RuntimeException("fireShowContent: not on main thread.")
|
||||
}
|
||||
viewHolder?.showContent(reason, changeList, reset)
|
||||
}
|
||||
|
||||
internal fun fireShowColumnHeader() {
|
||||
if(! Utils.isMainThread) {
|
||||
if(! isMainThread) {
|
||||
throw RuntimeException("fireShowColumnHeader: not on main thread.")
|
||||
}
|
||||
viewHolder?.showColumnHeader()
|
||||
}
|
||||
|
||||
internal fun fireColumnColor() {
|
||||
if(! Utils.isMainThread) {
|
||||
if(! isMainThread) {
|
||||
throw RuntimeException("fireColumnColor: not on main thread.")
|
||||
}
|
||||
viewHolder?.showColumnColor()
|
||||
}
|
||||
|
||||
fun fireRelativeTime() {
|
||||
if(! Utils.isMainThread) {
|
||||
if(! isMainThread) {
|
||||
throw RuntimeException("fireRelativeTime: not on main thread.")
|
||||
}
|
||||
viewHolder?.updateRelativeTime()
|
||||
}
|
||||
|
||||
fun fireRebindAdapterItems() {
|
||||
if(! Utils.isMainThread) {
|
||||
if(! isMainThread) {
|
||||
throw RuntimeException("fireRelativeTime: not on main thread.")
|
||||
}
|
||||
viewHolder?.rebindAdapterItems()
|
||||
|
@ -1504,8 +1498,8 @@ class Column(
|
|||
get() = isCancelled || is_dispose.get()
|
||||
|
||||
override fun publishApiProgress(s : String) {
|
||||
Utils.runOnMainThread {
|
||||
if(isCancelled) return@runOnMainThread
|
||||
runOnMainLooper {
|
||||
if(isCancelled) return@runOnMainLooper
|
||||
task_progress = s
|
||||
fireShowContent(reason = "loading progress", changeList = ArrayList())
|
||||
}
|
||||
|
@ -1606,7 +1600,7 @@ class Column(
|
|||
|
||||
TYPE_HASHTAG -> return getStatuses(
|
||||
client,
|
||||
String.format(Locale.JAPAN, PATH_HASHTAG, Uri.encode(hashtag))
|
||||
String.format(Locale.JAPAN, PATH_HASHTAG, hashtag.encodePercent())
|
||||
)
|
||||
|
||||
TYPE_REPORTS -> return parseReports(client, PATH_REPORTS)
|
||||
|
@ -1665,7 +1659,7 @@ class Column(
|
|||
)
|
||||
//
|
||||
} else {
|
||||
Utils.showToast(context, true, "TootContext parse failed.")
|
||||
showToast(context, true, "TootContext parse failed.")
|
||||
this.list_tmp = addOne(this.list_tmp, target_status)
|
||||
}
|
||||
|
||||
|
@ -1688,7 +1682,11 @@ class Column(
|
|||
return TootApiResult(context.getString(R.string.search_is_not_available_on_pseudo_account))
|
||||
}
|
||||
var path =
|
||||
String.format(Locale.JAPAN, PATH_SEARCH, Uri.encode(search_query))
|
||||
String.format(
|
||||
Locale.JAPAN,
|
||||
PATH_SEARCH,
|
||||
search_query.encodePercent()
|
||||
)
|
||||
if(search_resolve) path += "&resolve=1"
|
||||
|
||||
result = client.request(path)
|
||||
|
@ -1883,14 +1881,14 @@ class Column(
|
|||
|
||||
if(last_task != null) {
|
||||
if(! bSilent) {
|
||||
Utils.showToast(context, true, R.string.column_is_busy)
|
||||
showToast(context, true, R.string.column_is_busy)
|
||||
val holder = viewHolder
|
||||
if(holder != null) holder.refreshLayout.isRefreshing = false
|
||||
}
|
||||
return
|
||||
} else if(bBottom && max_id.isEmpty()) {
|
||||
if(! bSilent) {
|
||||
Utils.showToast(context, true, R.string.end_of_list)
|
||||
showToast(context, true, R.string.end_of_list)
|
||||
val holder = viewHolder
|
||||
if(holder != null) holder.refreshLayout.isRefreshing = false
|
||||
}
|
||||
|
@ -2422,8 +2420,8 @@ class Column(
|
|||
get() = isCancelled || is_dispose.get()
|
||||
|
||||
override fun publishApiProgress(s : String) {
|
||||
Utils.runOnMainThread {
|
||||
if(isCancelled) return@runOnMainThread
|
||||
runOnMainLooper {
|
||||
if(isCancelled) return@runOnMainLooper
|
||||
task_progress = s
|
||||
fireShowContent(reason = "refresh progress", changeList = ArrayList())
|
||||
}
|
||||
|
@ -2517,7 +2515,7 @@ class Column(
|
|||
|
||||
TYPE_HASHTAG -> getStatusList(
|
||||
client,
|
||||
String.format(Locale.JAPAN, PATH_HASHTAG, Uri.encode(hashtag))
|
||||
String.format(Locale.JAPAN, PATH_HASHTAG, hashtag.encodePercent())
|
||||
)
|
||||
|
||||
TYPE_SEARCH_MSP ->
|
||||
|
@ -2705,11 +2703,11 @@ class Column(
|
|||
|
||||
internal fun startGap(gap : TootGap?) {
|
||||
if(gap == null) {
|
||||
Utils.showToast(context, true, "gap is null")
|
||||
showToast(context, true, "gap is null")
|
||||
return
|
||||
}
|
||||
if(last_task != null) {
|
||||
Utils.showToast(context, true, R.string.column_is_busy)
|
||||
showToast(context, true, R.string.column_is_busy)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -2945,8 +2943,8 @@ class Column(
|
|||
get() = isCancelled || is_dispose.get()
|
||||
|
||||
override fun publishApiProgress(s : String) {
|
||||
Utils.runOnMainThread {
|
||||
if(isCancelled) return@runOnMainThread
|
||||
runOnMainLooper {
|
||||
if(isCancelled) return@runOnMainLooper
|
||||
task_progress = s
|
||||
fireShowContent(reason = "gap progress", changeList = ArrayList())
|
||||
}
|
||||
|
@ -2974,7 +2972,7 @@ class Column(
|
|||
|
||||
TYPE_HASHTAG -> getStatusList(
|
||||
client,
|
||||
String.format(Locale.JAPAN, PATH_HASHTAG, Uri.encode(hashtag))
|
||||
String.format(Locale.JAPAN, PATH_HASHTAG, hashtag.encodePercent())
|
||||
)
|
||||
|
||||
TYPE_BOOSTED_BY -> getAccountList(
|
||||
|
@ -3138,8 +3136,7 @@ class Column(
|
|||
|
||||
private fun loadSearchDesc(raw_en : Int, raw_ja : Int) : String {
|
||||
val res_id = if("ja" == context.getString(R.string.language_code)) raw_ja else raw_en
|
||||
val data = Utils.loadRawResource(context, res_id)
|
||||
return if(data == null) "?" else Utils.decodeUTF8(data)
|
||||
return context.loadRawResource(res_id)?.decodeUTF8() ?: "?"
|
||||
}
|
||||
|
||||
private var cacheHeaderDesc : String? = null
|
||||
|
|
|
@ -25,19 +25,17 @@ import java.util.regex.Pattern
|
|||
import jp.juggler.subwaytooter.action.Action_List
|
||||
import jp.juggler.subwaytooter.action.Action_Notification
|
||||
import jp.juggler.subwaytooter.table.AcctColor
|
||||
import jp.juggler.subwaytooter.util.LogCategory
|
||||
import jp.juggler.subwaytooter.util.ScrollPosition
|
||||
import jp.juggler.subwaytooter.util.Utils
|
||||
import android.support.v7.widget.ListRecyclerView
|
||||
import android.support.v7.widget.LinearLayoutManager
|
||||
import android.support.v7.widget.RecyclerView
|
||||
import jp.juggler.subwaytooter.util.*
|
||||
import jp.juggler.subwaytooter.view.ListDivider
|
||||
import java.io.Closeable
|
||||
import java.lang.reflect.Field
|
||||
|
||||
class ColumnViewHolder(
|
||||
val activity : ActMain,
|
||||
root : View
|
||||
viewRoot : View
|
||||
) : View.OnClickListener,
|
||||
SwipyRefreshLayout.OnRefreshListener,
|
||||
CompoundButton.OnCheckedChangeListener {
|
||||
|
@ -140,7 +138,7 @@ class ColumnViewHolder(
|
|||
tvRegexFilterError.text = if(message != null && message.isNotEmpty()) {
|
||||
message
|
||||
} else {
|
||||
Utils.formatError(ex, activity.resources, R.string.regex_error)
|
||||
ex.withCaption(activity.resources, R.string.regex_error)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
@ -162,7 +160,7 @@ class ColumnViewHolder(
|
|||
init {
|
||||
|
||||
if(activity.timeline_font != null) {
|
||||
Utils.scanView(root) { v ->
|
||||
viewRoot.scan { v ->
|
||||
try {
|
||||
if(v is Button) {
|
||||
// ボタンは触らない
|
||||
|
@ -175,42 +173,42 @@ class ColumnViewHolder(
|
|||
}
|
||||
}
|
||||
|
||||
flColumnBackground = root.findViewById(R.id.flColumnBackground)
|
||||
ivColumnBackgroundImage = root.findViewById(R.id.ivColumnBackgroundImage)
|
||||
llColumnHeader = root.findViewById(R.id.llColumnHeader)
|
||||
flColumnBackground = viewRoot.findViewById(R.id.flColumnBackground)
|
||||
ivColumnBackgroundImage = viewRoot.findViewById(R.id.ivColumnBackgroundImage)
|
||||
llColumnHeader = viewRoot.findViewById(R.id.llColumnHeader)
|
||||
|
||||
tvColumnIndex = root.findViewById(R.id.tvColumnIndex)
|
||||
tvColumnIndex = viewRoot.findViewById(R.id.tvColumnIndex)
|
||||
|
||||
tvColumnName = root.findViewById(R.id.tvColumnName)
|
||||
tvColumnContext = root.findViewById(R.id.tvColumnContext)
|
||||
ivColumnIcon = root.findViewById(R.id.ivColumnIcon)
|
||||
tvColumnName = viewRoot.findViewById(R.id.tvColumnName)
|
||||
tvColumnContext = viewRoot.findViewById(R.id.tvColumnContext)
|
||||
ivColumnIcon = viewRoot.findViewById(R.id.ivColumnIcon)
|
||||
|
||||
btnColumnSetting = root.findViewById(R.id.btnColumnSetting)
|
||||
btnColumnReload = root.findViewById(R.id.btnColumnReload)
|
||||
btnColumnClose = root.findViewById(R.id.btnColumnClose)
|
||||
btnColumnSetting = viewRoot.findViewById(R.id.btnColumnSetting)
|
||||
btnColumnReload = viewRoot.findViewById(R.id.btnColumnReload)
|
||||
btnColumnClose = viewRoot.findViewById(R.id.btnColumnClose)
|
||||
|
||||
tvLoading = root.findViewById(R.id.tvLoading)
|
||||
listView = root.findViewById(R.id.listView)
|
||||
tvLoading = viewRoot.findViewById(R.id.tvLoading)
|
||||
listView = viewRoot.findViewById(R.id.listView)
|
||||
|
||||
if(Pref.bpShareViewPool(activity.pref)) {
|
||||
listView.recycledViewPool = activity.viewPool
|
||||
}
|
||||
listView.itemAnimator = null
|
||||
//
|
||||
// val animator = listView.itemAnimator
|
||||
// if( animator is DefaultItemAnimator){
|
||||
// animator.supportsChangeAnimations = false
|
||||
// }
|
||||
//
|
||||
// val animator = listView.itemAnimator
|
||||
// if( animator is DefaultItemAnimator){
|
||||
// animator.supportsChangeAnimations = false
|
||||
// }
|
||||
|
||||
btnSearch = root.findViewById(R.id.btnSearch)
|
||||
etSearch = root.findViewById(R.id.etSearch)
|
||||
cbResolve = root.findViewById(R.id.cbResolve)
|
||||
btnSearch = viewRoot.findViewById(R.id.btnSearch)
|
||||
etSearch = viewRoot.findViewById(R.id.etSearch)
|
||||
cbResolve = viewRoot.findViewById(R.id.cbResolve)
|
||||
|
||||
llSearch = root.findViewById(R.id.llSearch)
|
||||
llListList = root.findViewById(R.id.llListList)
|
||||
llSearch = viewRoot.findViewById(R.id.llSearch)
|
||||
llListList = viewRoot.findViewById(R.id.llListList)
|
||||
|
||||
btnListAdd = root.findViewById(R.id.btnListAdd)
|
||||
etListName = root.findViewById(R.id.etListName)
|
||||
btnListAdd = viewRoot.findViewById(R.id.btnListAdd)
|
||||
etListName = viewRoot.findViewById(R.id.etListName)
|
||||
btnListAdd.setOnClickListener(this)
|
||||
|
||||
etListName.setOnEditorActionListener { _, actionId, _ ->
|
||||
|
@ -222,24 +220,24 @@ class ColumnViewHolder(
|
|||
handled
|
||||
}
|
||||
|
||||
llColumnSetting = root.findViewById(R.id.llColumnSetting)
|
||||
llColumnSetting = viewRoot.findViewById(R.id.llColumnSetting)
|
||||
|
||||
cbDontCloseColumn = root.findViewById(R.id.cbDontCloseColumn)
|
||||
cbWithAttachment = root.findViewById(R.id.cbWithAttachment)
|
||||
cbWithHighlight = root.findViewById(R.id.cbWithHighlight)
|
||||
cbDontShowBoost = root.findViewById(R.id.cbDontShowBoost)
|
||||
cbDontShowFollow = root.findViewById(R.id.cbDontShowFollow)
|
||||
cbDontShowFavourite = root.findViewById(R.id.cbDontShowFavourite)
|
||||
cbDontShowReply = root.findViewById(R.id.cbDontShowReply)
|
||||
cbDontStreaming = root.findViewById(R.id.cbDontStreaming)
|
||||
cbDontAutoRefresh = root.findViewById(R.id.cbDontAutoRefresh)
|
||||
cbHideMediaDefault = root.findViewById(R.id.cbHideMediaDefault)
|
||||
cbEnableSpeech = root.findViewById(R.id.cbEnableSpeech)
|
||||
etRegexFilter = root.findViewById(R.id.etRegexFilter)
|
||||
llRegexFilter = root.findViewById(R.id.llRegexFilter)
|
||||
tvRegexFilterError = root.findViewById(R.id.tvRegexFilterError)
|
||||
cbDontCloseColumn = viewRoot.findViewById(R.id.cbDontCloseColumn)
|
||||
cbWithAttachment = viewRoot.findViewById(R.id.cbWithAttachment)
|
||||
cbWithHighlight = viewRoot.findViewById(R.id.cbWithHighlight)
|
||||
cbDontShowBoost = viewRoot.findViewById(R.id.cbDontShowBoost)
|
||||
cbDontShowFollow = viewRoot.findViewById(R.id.cbDontShowFollow)
|
||||
cbDontShowFavourite = viewRoot.findViewById(R.id.cbDontShowFavourite)
|
||||
cbDontShowReply = viewRoot.findViewById(R.id.cbDontShowReply)
|
||||
cbDontStreaming = viewRoot.findViewById(R.id.cbDontStreaming)
|
||||
cbDontAutoRefresh = viewRoot.findViewById(R.id.cbDontAutoRefresh)
|
||||
cbHideMediaDefault = viewRoot.findViewById(R.id.cbHideMediaDefault)
|
||||
cbEnableSpeech = viewRoot.findViewById(R.id.cbEnableSpeech)
|
||||
etRegexFilter = viewRoot.findViewById(R.id.etRegexFilter)
|
||||
llRegexFilter = viewRoot.findViewById(R.id.llRegexFilter)
|
||||
tvRegexFilterError = viewRoot.findViewById(R.id.tvRegexFilterError)
|
||||
|
||||
btnDeleteNotification = root.findViewById(R.id.btnDeleteNotification)
|
||||
btnDeleteNotification = viewRoot.findViewById(R.id.btnDeleteNotification)
|
||||
|
||||
llColumnHeader.setOnClickListener(this)
|
||||
btnColumnSetting.setOnClickListener(this)
|
||||
|
@ -247,9 +245,9 @@ class ColumnViewHolder(
|
|||
btnColumnClose.setOnClickListener(this)
|
||||
btnDeleteNotification.setOnClickListener(this)
|
||||
|
||||
root.findViewById<View>(R.id.btnColor).setOnClickListener(this)
|
||||
viewRoot.findViewById<View>(R.id.btnColor).setOnClickListener(this)
|
||||
|
||||
this.refreshLayout = root.findViewById(R.id.swipyRefreshLayout)
|
||||
this.refreshLayout = viewRoot.findViewById(R.id.swipyRefreshLayout)
|
||||
refreshLayout.setOnRefreshListener(this)
|
||||
refreshLayout.setDistanceToTriggerSync((0.5f + 20f * activity.density).toInt())
|
||||
|
||||
|
@ -489,7 +487,7 @@ class ColumnViewHolder(
|
|||
|
||||
showColumnColor()
|
||||
|
||||
showContent(reason="onPageCreate",reset=true)
|
||||
showContent(reason = "onPageCreate", reset = true)
|
||||
} finally {
|
||||
loading_busy = false
|
||||
}
|
||||
|
@ -610,16 +608,16 @@ class ColumnViewHolder(
|
|||
// 非同期処理を開始
|
||||
val task = object : AsyncTask<Void, Void, Bitmap?>() {
|
||||
override fun doInBackground(vararg params : Void) : Bitmap? {
|
||||
try {
|
||||
val resize_max = if(screen_w > screen_h) screen_w else screen_h
|
||||
val uri = Uri.parse(url)
|
||||
return Utils.createResizedBitmap(log, activity, uri, false, resize_max)
|
||||
|
||||
return try {
|
||||
createResizedBitmap(
|
||||
activity,
|
||||
Uri.parse(url),
|
||||
if(screen_w > screen_h) screen_w else screen_h
|
||||
)
|
||||
} catch(ex : Throwable) {
|
||||
log.trace(ex)
|
||||
null
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
override fun onCancelled(bitmap : Bitmap?) {
|
||||
|
@ -744,7 +742,7 @@ class ColumnViewHolder(
|
|||
R.id.cbHideMediaDefault -> {
|
||||
column.hide_media_default = isChecked
|
||||
activity.app_state.saveColumnList()
|
||||
column.fireShowContent(reason="HideMediaDefault in ColumnSetting",reset=true)
|
||||
column.fireShowContent(reason = "HideMediaDefault in ColumnSetting", reset = true)
|
||||
}
|
||||
|
||||
R.id.cbEnableSpeech -> {
|
||||
|
@ -770,7 +768,7 @@ class ColumnViewHolder(
|
|||
App1.custom_emoji_cache.clearErrorCache()
|
||||
|
||||
if(column.isSearchColumn) {
|
||||
Utils.hideKeyboard(activity, etSearch)
|
||||
etSearch.hideKeyboard()
|
||||
etSearch.setText(column.search_query)
|
||||
cbResolve.isChecked = column.search_resolve
|
||||
}
|
||||
|
@ -779,7 +777,7 @@ class ColumnViewHolder(
|
|||
}
|
||||
|
||||
R.id.btnSearch -> {
|
||||
Utils.hideKeyboard(activity, etSearch)
|
||||
etSearch.hideKeyboard()
|
||||
column.search_query = etSearch.text.toString().trim { it <= ' ' }
|
||||
column.search_resolve = cbResolve.isChecked
|
||||
activity.app_state.saveColumnList()
|
||||
|
@ -809,7 +807,7 @@ class ColumnViewHolder(
|
|||
R.id.btnListAdd -> {
|
||||
val tv = etListName.text.toString().trim { it <= ' ' }
|
||||
if(tv.isEmpty()) {
|
||||
Utils.showToast(activity, true, R.string.list_name_empty)
|
||||
showToast(activity, true, R.string.list_name_empty)
|
||||
return
|
||||
}
|
||||
Action_List.create(activity, column.access_info, tv, null)
|
||||
|
@ -828,7 +826,7 @@ class ColumnViewHolder(
|
|||
}
|
||||
|
||||
private fun showColumnCloseButton() {
|
||||
val dont_close = column ?.dont_close ?: return
|
||||
val dont_close = column?.dont_close ?: return
|
||||
btnColumnClose.isEnabled = ! dont_close
|
||||
btnColumnClose.alpha = if(dont_close) 0.3f else 1f
|
||||
}
|
||||
|
@ -837,10 +835,10 @@ class ColumnViewHolder(
|
|||
fun updateRelativeTime() = rebindAdapterItems()
|
||||
|
||||
fun rebindAdapterItems() {
|
||||
for( childIndex in 0 until listView.childCount){
|
||||
for(childIndex in 0 until listView.childCount) {
|
||||
val adapterIndex = listView.getChildAdapterPosition(listView.getChildAt(childIndex))
|
||||
if( adapterIndex == RecyclerView.NO_POSITION) continue
|
||||
status_adapter?.notifyItemChanged( adapterIndex )
|
||||
if(adapterIndex == RecyclerView.NO_POSITION) continue
|
||||
status_adapter?.notifyItemChanged(adapterIndex)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -879,14 +877,14 @@ class ColumnViewHolder(
|
|||
}
|
||||
|
||||
internal fun showContent(
|
||||
reason:String,
|
||||
changeList : List<AdapterChange>? = null ,
|
||||
reason : String,
|
||||
changeList : List<AdapterChange>? = null,
|
||||
reset : Boolean = false
|
||||
) {
|
||||
) {
|
||||
// クラッシュレポートにadapterとリストデータの状態不整合が多かったので、
|
||||
// とりあえずリストデータ変更の通知だけは最優先で行っておく
|
||||
try {
|
||||
status_adapter?.notifyChange(reason,changeList,reset)
|
||||
status_adapter?.notifyChange(reason, changeList, reset)
|
||||
} catch(ex : Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
|
@ -934,7 +932,7 @@ class ColumnViewHolder(
|
|||
refreshLayout.isRefreshing = false
|
||||
val refreshError = column.mRefreshLoadingError
|
||||
if(refreshError.isNotEmpty()) {
|
||||
Utils.showToast(activity, true, refreshError)
|
||||
showToast(activity, true, refreshError)
|
||||
column.mRefreshLoadingError = ""
|
||||
}
|
||||
}
|
||||
|
@ -949,7 +947,7 @@ class ColumnViewHolder(
|
|||
val column = this.column
|
||||
when {
|
||||
column == null -> log.d("saveScrollPosition [%d] , column==null", page_idx)
|
||||
|
||||
|
||||
column.is_dispose.get() -> log.d(
|
||||
"saveScrollPosition [%d] , column is disposed",
|
||||
page_idx
|
||||
|
@ -981,7 +979,7 @@ class ColumnViewHolder(
|
|||
}
|
||||
}
|
||||
|
||||
fun setScrollPosition(sp : ScrollPosition, deltaDp : Float =0f) {
|
||||
fun setScrollPosition(sp : ScrollPosition, deltaDp : Float = 0f) {
|
||||
val last_adapter = listView.adapter
|
||||
if(column == null || last_adapter == null) return
|
||||
|
||||
|
@ -1002,12 +1000,14 @@ class ColumnViewHolder(
|
|||
}, 20L)
|
||||
}
|
||||
|
||||
inner class AdapterItemHeightWorkarea internal constructor(val adapter:ItemListAdapter) : Closeable {
|
||||
inner class AdapterItemHeightWorkarea internal constructor(val adapter : ItemListAdapter) :
|
||||
Closeable {
|
||||
|
||||
private val item_width : Int
|
||||
private val widthSpec : Int
|
||||
private var lastViewType : Int = - 1
|
||||
private var lastViewHolder : RecyclerView.ViewHolder? = null
|
||||
|
||||
init {
|
||||
this.item_width = listView.width - listView.paddingLeft - listView.paddingRight
|
||||
this.widthSpec = View.MeasureSpec.makeMeasureSpec(item_width, View.MeasureSpec.EXACTLY)
|
||||
|
@ -1029,7 +1029,7 @@ class ColumnViewHolder(
|
|||
return childViewHolder.itemView.measuredHeight
|
||||
}
|
||||
|
||||
|
||||
|
||||
log.d("getAdapterItemHeight idx=$adapterIndex createView")
|
||||
|
||||
val viewType = adapter.getItemViewType(adapterIndex)
|
||||
|
@ -1054,7 +1054,7 @@ class ColumnViewHolder(
|
|||
var adapterIndex = column?.toAdapterIndex(listIndex) ?: return
|
||||
|
||||
val adapter = status_adapter
|
||||
if( adapter == null) {
|
||||
if(adapter == null) {
|
||||
log.e("setListItemTop: missing status adapter")
|
||||
return
|
||||
}
|
||||
|
@ -1073,7 +1073,7 @@ class ColumnViewHolder(
|
|||
}
|
||||
|
||||
fun getListItemTop(listIndex : Int) : Int {
|
||||
|
||||
|
||||
val adapterIndex = column?.toAdapterIndex(listIndex)
|
||||
?: return 0
|
||||
|
||||
|
@ -1084,12 +1084,12 @@ class ColumnViewHolder(
|
|||
}
|
||||
|
||||
fun findFirstVisibleListItem() : Int {
|
||||
|
||||
|
||||
val adapterIndex = listLayoutManager.findFirstVisibleItemPosition()
|
||||
|
||||
|
||||
if(adapterIndex == RecyclerView.NO_POSITION)
|
||||
throw IndexOutOfBoundsException()
|
||||
|
||||
|
||||
return column?.toListIndex(adapterIndex)
|
||||
?: throw IndexOutOfBoundsException()
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ import jp.juggler.subwaytooter.dialog.DlgQRCode
|
|||
import jp.juggler.subwaytooter.table.SavedAccount
|
||||
import jp.juggler.subwaytooter.table.UserRelation
|
||||
import jp.juggler.subwaytooter.util.LogCategory
|
||||
import jp.juggler.subwaytooter.util.Utils
|
||||
import jp.juggler.subwaytooter.util.showToast
|
||||
|
||||
@SuppressLint("InflateParams")
|
||||
internal class DlgContextMenu(
|
||||
|
@ -484,14 +484,14 @@ internal class DlgContextMenu(
|
|||
R.id.btnDomainBlock -> who?.let { who ->
|
||||
// 疑似アカウントではドメインブロックできない
|
||||
if(access_info.isPseudo) {
|
||||
Utils.showToast(activity, false, R.string.domain_block_from_pseudo)
|
||||
showToast(activity, false, R.string.domain_block_from_pseudo)
|
||||
return@let
|
||||
}
|
||||
val who_host = who.host
|
||||
|
||||
// 自分のドメインではブロックできない
|
||||
if( access_info.host.equals(who_host,ignoreCase = true)) {
|
||||
Utils.showToast(activity, false, R.string.domain_block_from_local)
|
||||
showToast(activity, false, R.string.domain_block_from_local)
|
||||
return@let
|
||||
}
|
||||
AlertDialog.Builder(activity)
|
||||
|
|
|
@ -5,7 +5,7 @@ import android.content.BroadcastReceiver
|
|||
import android.content.Context
|
||||
import android.content.Intent
|
||||
|
||||
import jp.juggler.subwaytooter.util.Utils
|
||||
import jp.juggler.subwaytooter.util.showToast
|
||||
|
||||
class DownloadReceiver : BroadcastReceiver() {
|
||||
override fun onReceive(context : Context, intent : Intent?) {
|
||||
|
@ -42,9 +42,9 @@ class DownloadReceiver : BroadcastReceiver() {
|
|||
重複を回避する方法はなさそうだ…
|
||||
*/
|
||||
|
||||
Utils.showToast(context, false, context.getString(R.string.download_complete, title))
|
||||
showToast(context, false, context.getString(R.string.download_complete, title))
|
||||
} else {
|
||||
Utils.showToast(context, false, context.getString(R.string.download_failed, title))
|
||||
showToast(context, false, context.getString(R.string.download_failed, title))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,23 +12,24 @@ class EventReceiver : BroadcastReceiver() {
|
|||
internal val log = LogCategory("EventReceiver")
|
||||
const val ACTION_NOTIFICATION_DELETE = "notification_delete"
|
||||
}
|
||||
|
||||
|
||||
override fun onReceive(context : Context, intent : Intent?) {
|
||||
if(intent != null) {
|
||||
val action = intent.action
|
||||
|
||||
if(Intent.ACTION_BOOT_COMPLETED == action) {
|
||||
PollingWorker.queueBootCompleted(context)
|
||||
when {
|
||||
Intent.ACTION_BOOT_COMPLETED == action -> PollingWorker.queueBootCompleted(context)
|
||||
Intent.ACTION_MY_PACKAGE_REPLACED == action -> PollingWorker.queuePackageReplaced(
|
||||
context
|
||||
)
|
||||
|
||||
} else if(Intent.ACTION_MY_PACKAGE_REPLACED == action) {
|
||||
PollingWorker.queuePackageReplaced(context)
|
||||
ACTION_NOTIFICATION_DELETE == action -> {
|
||||
val db_id = intent.getLongExtra(PollingWorker.EXTRA_DB_ID, - 1L)
|
||||
PollingWorker.queueNotificationDeleted(context, db_id)
|
||||
|
||||
}
|
||||
|
||||
} else if(ACTION_NOTIFICATION_DELETE == action) {
|
||||
val db_id = intent.getLongExtra(PollingWorker.EXTRA_DB_ID, - 1L)
|
||||
PollingWorker.queueNotificationDeleted(context, db_id)
|
||||
|
||||
} else {
|
||||
log.e("onReceive: unsupported action %s", action)
|
||||
else -> log.e("onReceive: unsupported action %s", action)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ package jp.juggler.subwaytooter
|
|||
|
||||
import android.content.Context
|
||||
import android.graphics.Typeface
|
||||
import android.net.Uri
|
||||
import android.os.SystemClock
|
||||
import android.support.v4.content.ContextCompat
|
||||
import android.support.v4.view.ViewCompat
|
||||
|
@ -55,9 +54,9 @@ internal class ItemViewHolder(
|
|||
val viewRoot : View
|
||||
|
||||
private var bSimpleList : Boolean = false
|
||||
|
||||
|
||||
lateinit var column : Column
|
||||
|
||||
|
||||
private lateinit var list_adapter : ItemListAdapter
|
||||
private lateinit var llBoosted : View
|
||||
private lateinit var ivBoosted : ImageView
|
||||
|
@ -87,7 +86,7 @@ internal class ItemViewHolder(
|
|||
private lateinit var tvContent : MyTextView
|
||||
|
||||
private lateinit var flMedia : View
|
||||
private lateinit var llMedia:View
|
||||
private lateinit var llMedia : View
|
||||
private lateinit var btnShowMedia : TextView
|
||||
private lateinit var ivMedia1 : MyNetworkImageView
|
||||
private lateinit var ivMedia2 : MyNetworkImageView
|
||||
|
@ -127,7 +126,7 @@ internal class ItemViewHolder(
|
|||
private var boost_account : TootAccount? = null
|
||||
private var follow_account : TootAccount? = null
|
||||
|
||||
private var boost_time :Long = 0L
|
||||
private var boost_time : Long = 0L
|
||||
|
||||
private val content_color_default : Int
|
||||
private var acct_color : Int = 0
|
||||
|
@ -215,7 +214,12 @@ internal class ItemViewHolder(
|
|||
|
||||
}
|
||||
|
||||
fun bind(list_adapter : ItemListAdapter, column : Column, bSimpleList : Boolean, item : TimelineItem) {
|
||||
fun bind(
|
||||
list_adapter : ItemListAdapter,
|
||||
column : Column,
|
||||
bSimpleList : Boolean,
|
||||
item : TimelineItem
|
||||
) {
|
||||
this.list_adapter = list_adapter
|
||||
this.column = column
|
||||
this.bSimpleList = bSimpleList
|
||||
|
@ -223,7 +227,7 @@ internal class ItemViewHolder(
|
|||
this.access_info = column.access_info
|
||||
|
||||
if(activity.timeline_font != null || activity.timeline_font_bold != null) {
|
||||
Utils.scanView(this.viewRoot) { v ->
|
||||
viewRoot.scan { v ->
|
||||
try {
|
||||
if(v is Button) {
|
||||
// ボタンは太字なので触らない
|
||||
|
@ -307,8 +311,7 @@ internal class ItemViewHolder(
|
|||
llSearchTag.visibility = View.GONE
|
||||
llList.visibility = View.GONE
|
||||
llExtra.removeAllViews()
|
||||
|
||||
|
||||
|
||||
var c : Int
|
||||
c = if(column.content_color != 0) column.content_color else content_color_default
|
||||
tvBoosted.setTextColor(c)
|
||||
|
@ -330,7 +333,6 @@ internal class ItemViewHolder(
|
|||
// tvBoostedAcct.setTextColor( c );
|
||||
// tvFollowerAcct.setTextColor( c );
|
||||
// tvAcct.setTextColor( c );
|
||||
|
||||
|
||||
this.item = item
|
||||
when(item) {
|
||||
|
@ -345,16 +347,12 @@ internal class ItemViewHolder(
|
|||
val reblog = item.reblog
|
||||
if(reblog != null) {
|
||||
showBoost(
|
||||
item.account
|
||||
,
|
||||
item.time_created_at
|
||||
,
|
||||
R.attr.btn_boost
|
||||
,
|
||||
Utils.formatSpannable1(
|
||||
item.account,
|
||||
item.time_created_at,
|
||||
R.attr.btn_boost,
|
||||
item.account.decoded_display_name.intoStringResource(
|
||||
activity,
|
||||
R.string.display_name_boosted_by,
|
||||
item.account.decoded_display_name
|
||||
R.string.display_name_boosted_by
|
||||
)
|
||||
)
|
||||
showStatus(activity, reblog)
|
||||
|
@ -374,16 +372,12 @@ internal class ItemViewHolder(
|
|||
when(n.type) {
|
||||
TootNotification.TYPE_FAVOURITE -> {
|
||||
if(n_account != null) showBoost(
|
||||
n_account
|
||||
,
|
||||
n.time_created_at
|
||||
,
|
||||
if(access_info.isNicoru(n_account)) R.attr.ic_nicoru else R.attr.btn_favourite
|
||||
,
|
||||
Utils.formatSpannable1(
|
||||
n_account,
|
||||
n.time_created_at,
|
||||
if(access_info.isNicoru(n_account)) R.attr.ic_nicoru else R.attr.btn_favourite,
|
||||
n_account.decoded_display_name.intoStringResource(
|
||||
activity,
|
||||
R.string.display_name_favourited_by,
|
||||
n_account.decoded_display_name
|
||||
R.string.display_name_favourited_by
|
||||
)
|
||||
)
|
||||
if(n_status != null) showStatus(activity, n_status)
|
||||
|
@ -391,16 +385,12 @@ internal class ItemViewHolder(
|
|||
|
||||
TootNotification.TYPE_REBLOG -> {
|
||||
if(n_account != null) showBoost(
|
||||
n_account
|
||||
,
|
||||
n.time_created_at
|
||||
,
|
||||
R.attr.btn_boost
|
||||
,
|
||||
Utils.formatSpannable1(
|
||||
n_account,
|
||||
n.time_created_at,
|
||||
R.attr.btn_boost,
|
||||
n_account.decoded_display_name.intoStringResource(
|
||||
activity,
|
||||
R.string.display_name_boosted_by,
|
||||
n_account.decoded_display_name
|
||||
R.string.display_name_boosted_by
|
||||
)
|
||||
)
|
||||
if(n_status != null) showStatus(activity, n_status)
|
||||
|
@ -410,16 +400,12 @@ internal class ItemViewHolder(
|
|||
TootNotification.TYPE_FOLLOW -> {
|
||||
if(n_account != null) {
|
||||
showBoost(
|
||||
n_account
|
||||
,
|
||||
n.time_created_at
|
||||
,
|
||||
R.attr.ic_follow_plus
|
||||
,
|
||||
Utils.formatSpannable1(
|
||||
n_account,
|
||||
n.time_created_at,
|
||||
R.attr.ic_follow_plus,
|
||||
n_account.decoded_display_name.intoStringResource(
|
||||
activity,
|
||||
R.string.display_name_followed_by,
|
||||
n_account.decoded_display_name
|
||||
R.string.display_name_followed_by
|
||||
)
|
||||
)
|
||||
showAccount(n_account)
|
||||
|
@ -429,16 +415,12 @@ internal class ItemViewHolder(
|
|||
TootNotification.TYPE_MENTION -> {
|
||||
if(! bSimpleList) {
|
||||
if(n_account != null) showBoost(
|
||||
n_account
|
||||
,
|
||||
n.time_created_at
|
||||
,
|
||||
R.attr.btn_reply
|
||||
,
|
||||
Utils.formatSpannable1(
|
||||
n_account,
|
||||
n.time_created_at,
|
||||
R.attr.btn_reply,
|
||||
n_account.decoded_display_name.intoStringResource(
|
||||
activity,
|
||||
R.string.display_name_replied_by,
|
||||
n_account.decoded_display_name
|
||||
R.string.display_name_replied_by
|
||||
)
|
||||
)
|
||||
}
|
||||
|
@ -485,7 +467,11 @@ internal class ItemViewHolder(
|
|||
private fun showAccount(who : TootAccount) {
|
||||
follow_account = who
|
||||
llFollow.visibility = View.VISIBLE
|
||||
ivFollow.setImageUrl(activity.pref, Styler.calcIconRound(ivFollow.layoutParams), access_info.supplyBaseUrl(who.avatar_static))
|
||||
ivFollow.setImageUrl(
|
||||
activity.pref,
|
||||
Styler.calcIconRound(ivFollow.layoutParams),
|
||||
access_info.supplyBaseUrl(who.avatar_static)
|
||||
)
|
||||
tvFollowerName.text = who.decoded_display_name
|
||||
follow_invalidator.register(who.decoded_display_name)
|
||||
|
||||
|
@ -615,7 +601,7 @@ internal class ItemViewHolder(
|
|||
else -> ! status.sensitive
|
||||
}
|
||||
val is_shown = MediaShown.isShown(status, default_shown)
|
||||
|
||||
|
||||
btnShowMedia.visibility = if(! is_shown) View.VISIBLE else View.GONE
|
||||
llMedia.visibility = if(! is_shown) View.GONE else View.VISIBLE
|
||||
setMedia(ivMedia1, status, media_attachments, 0)
|
||||
|
@ -696,17 +682,16 @@ internal class ItemViewHolder(
|
|||
tvTime.text = sb
|
||||
}
|
||||
|
||||
fun updateRelativeTime() {
|
||||
val boost_time = this.boost_time
|
||||
if( boost_time != 0L ){
|
||||
tvBoostedTime.text = TootStatus.formatTime(tvBoostedTime.context, boost_time, true)
|
||||
}
|
||||
val status_showing = this.status_showing
|
||||
if( status_showing != null ){
|
||||
showStatusTime(activity, status_showing)
|
||||
}
|
||||
}
|
||||
|
||||
// fun updateRelativeTime() {
|
||||
// val boost_time = this.boost_time
|
||||
// if(boost_time != 0L) {
|
||||
// tvBoostedTime.text = TootStatus.formatTime(tvBoostedTime.context, boost_time, true)
|
||||
// }
|
||||
// val status_showing = this.status_showing
|
||||
// if(status_showing != null) {
|
||||
// showStatusTime(activity, status_showing)
|
||||
// }
|
||||
// }
|
||||
|
||||
private fun setAcct(tv : TextView, acctLong : String, acctShort : String?) {
|
||||
|
||||
|
@ -834,7 +819,7 @@ internal class ItemViewHolder(
|
|||
ContentWarning.save(status, new_shown)
|
||||
|
||||
// 1個だけ開閉するのではなく、例えば通知TLにある複数の要素をまとめて開閉するなどある
|
||||
list_adapter.notifyChange(reason="ContentWarning onClick", reset=true)
|
||||
list_adapter.notifyChange(reason = "ContentWarning onClick", reset = true)
|
||||
|
||||
}
|
||||
|
||||
|
@ -999,7 +984,7 @@ internal class ItemViewHolder(
|
|||
|
||||
is TootTag -> {
|
||||
// search_tag は#を含まない
|
||||
val tagEncoded = Uri.encode(item.name)
|
||||
val tagEncoded = item.name.encodePercent()
|
||||
val host = access_info.host
|
||||
val url = "https://$host/tags/$tagEncoded"
|
||||
Action_HashTag.timelineOtherInstance(
|
||||
|
@ -1203,7 +1188,7 @@ internal class ItemViewHolder(
|
|||
val now = System.currentTimeMillis()
|
||||
val remain = enquete.time_start + NicoEnquete.ENQUETE_EXPIRE - now
|
||||
if(remain <= 0) {
|
||||
Utils.showToast(context, false, R.string.enquete_was_end)
|
||||
showToast(context, false, R.string.enquete_was_end)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -1228,15 +1213,15 @@ internal class ItemViewHolder(
|
|||
|
||||
val data = result.jsonObject
|
||||
if(data != null) {
|
||||
val message = Utils.optStringX(data, "message") ?: "?"
|
||||
val message = data.parseString("message") ?: "?"
|
||||
val valid = data.optBoolean("valid")
|
||||
if(valid) {
|
||||
Utils.showToast(context, false, R.string.enquete_voted)
|
||||
showToast(context, false, R.string.enquete_voted)
|
||||
} else {
|
||||
Utils.showToast(context, true, R.string.enquete_vote_failed, message)
|
||||
showToast(context, true, R.string.enquete_vote_failed, message)
|
||||
}
|
||||
} else {
|
||||
Utils.showToast(context, true, result.error)
|
||||
showToast(context, true, result.error)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1697,7 +1682,6 @@ internal class ItemViewHolder(
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,51 +0,0 @@
|
|||
package jp.juggler.subwaytooter;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.util.Log;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.bumptech.glide.GlideBuilder;
|
||||
import com.bumptech.glide.Registry;
|
||||
import com.bumptech.glide.annotation.GlideModule;
|
||||
import com.bumptech.glide.load.DecodeFormat;
|
||||
import com.bumptech.glide.load.engine.bitmap_recycle.LruBitmapPool;
|
||||
import com.bumptech.glide.load.engine.cache.LruResourceCache;
|
||||
import com.bumptech.glide.load.engine.cache.MemorySizeCalculator;
|
||||
import com.bumptech.glide.load.engine.executor.GlideExecutor;
|
||||
import com.bumptech.glide.module.AppGlideModule;
|
||||
import com.bumptech.glide.request.RequestOptions;
|
||||
|
||||
import jp.juggler.subwaytooter.util.LogCategory;
|
||||
|
||||
import static com.bumptech.glide.load.engine.executor.GlideExecutor.newDiskCacheExecutor;
|
||||
import static com.bumptech.glide.load.engine.executor.GlideExecutor.newSourceExecutor;
|
||||
|
||||
@GlideModule
|
||||
public class MyAppGlideModule extends AppGlideModule {
|
||||
static final LogCategory log = new LogCategory( "MyAppGlideModule" );
|
||||
|
||||
// v3との互換性のためにAndroidManifestを読むかどうか(デフォルトtrue)
|
||||
@Override public boolean isManifestParsingEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override public void registerComponents( @NonNull Context context, @NonNull Glide glide, @NonNull Registry registry) {
|
||||
// デフォルト実装は何もしないらしい
|
||||
super.registerComponents( context,glide,registry );
|
||||
|
||||
// App1を初期化してからOkHttp3Factoryと連動させる
|
||||
App1.Companion.prepare( context.getApplicationContext() );
|
||||
App1.Companion.registerGlideComponents(context,glide,registry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyOptions(Context context, GlideBuilder builder) {
|
||||
// デフォルト実装は何もしないらしい
|
||||
super.applyOptions( context,builder );
|
||||
|
||||
// App1を初期化してから色々する
|
||||
App1.Companion.prepare( context.getApplicationContext() );
|
||||
App1.Companion.applyGlideOptions(context,builder);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package jp.juggler.subwaytooter
|
||||
|
||||
import android.content.Context
|
||||
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.GlideBuilder
|
||||
import com.bumptech.glide.Registry
|
||||
import com.bumptech.glide.annotation.GlideModule
|
||||
import com.bumptech.glide.module.AppGlideModule
|
||||
|
||||
@GlideModule
|
||||
class MyAppGlideModule : AppGlideModule() {
|
||||
|
||||
// v3との互換性のためにAndroidManifestを読むかどうか(デフォルトtrue)
|
||||
override fun isManifestParsingEnabled() : Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun registerComponents(context : Context, glide : Glide, registry : Registry) {
|
||||
// デフォルト実装は何もしないらしい
|
||||
super.registerComponents(context, glide, registry)
|
||||
|
||||
// App1を初期化してからOkHttp3Factoryと連動させる
|
||||
App1.prepare(context.applicationContext)
|
||||
App1.registerGlideComponents(context, glide, registry)
|
||||
}
|
||||
|
||||
override fun applyOptions(context : Context, builder : GlideBuilder) {
|
||||
// デフォルト実装は何もしないらしい
|
||||
super.applyOptions(context, builder)
|
||||
|
||||
// App1を初期化してから色々する
|
||||
App1.prepare(context.applicationContext)
|
||||
App1.applyGlideOptions(context, builder)
|
||||
}
|
||||
|
||||
}
|
|
@ -148,7 +148,12 @@ class PollingWorker private constructor(c : Context) {
|
|||
}
|
||||
|
||||
// タスクの追加
|
||||
private fun addTask(context : Context, removeOld : Boolean, task_id : Int, taskDataArg : JSONObject?) {
|
||||
private fun addTask(
|
||||
context : Context,
|
||||
removeOld : Boolean,
|
||||
task_id : Int,
|
||||
taskDataArg : JSONObject?
|
||||
) {
|
||||
try {
|
||||
val taskData = taskDataArg ?: JSONObject()
|
||||
taskData.put(EXTRA_TASK_ID, task_id)
|
||||
|
@ -264,7 +269,10 @@ class PollingWorker private constructor(c : Context) {
|
|||
// ジョブが完了した?
|
||||
val now = SystemClock.elapsedRealtime()
|
||||
if(! pw.hasJob(JOB_FCM)) {
|
||||
log.d("handleFCMMessage: JOB_FCM completed. time=%.2f", (now - time_start) / 1000f)
|
||||
log.d(
|
||||
"handleFCMMessage: JOB_FCM completed. time=%.2f",
|
||||
(now - time_start) / 1000f
|
||||
)
|
||||
break
|
||||
}
|
||||
// ジョブの状況を通知する
|
||||
|
@ -328,7 +336,10 @@ class PollingWorker private constructor(c : Context) {
|
|||
this.wifi_manager = context.applicationContext.getSystemService(Context.WIFI_SERVICE) as? WifiManager
|
||||
?: throw NotImplementedError("missing WifiManager system service")
|
||||
|
||||
power_lock = power_manager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, PollingWorker::class.java.name)
|
||||
power_lock = power_manager.newWakeLock(
|
||||
PowerManager.PARTIAL_WAKE_LOCK,
|
||||
PollingWorker::class.java.name
|
||||
)
|
||||
power_lock.setReferenceCounted(false)
|
||||
|
||||
wifi_lock = wifi_manager.createWifiLock(PollingWorker::class.java.name)
|
||||
|
@ -719,7 +730,7 @@ class PollingWorker private constructor(c : Context) {
|
|||
this.taskId = taskId
|
||||
|
||||
var process_db_id = - 1L //
|
||||
|
||||
|
||||
when(taskId) {
|
||||
TASK_APP_DATA_IMPORT_BEFORE -> {
|
||||
scheduler.cancelAll()
|
||||
|
@ -735,13 +746,14 @@ class PollingWorker private constructor(c : Context) {
|
|||
mBusyAppDataImportBefore.set(false)
|
||||
return
|
||||
}
|
||||
|
||||
TASK_APP_DATA_IMPORT_AFTER -> {
|
||||
mBusyAppDataImportAfter.set(false)
|
||||
mBusyAppDataImportBefore.set(false)
|
||||
NotificationTracking.resetPostAll()
|
||||
// fall
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// アプリデータのインポート処理がビジーな間、他のジョブは実行されない
|
||||
|
@ -755,7 +767,6 @@ class PollingWorker private constructor(c : Context) {
|
|||
|
||||
TASK_PACKAGE_REPLACED -> NotificationTracking.resetPostAll()
|
||||
|
||||
|
||||
// デバイストークンが更新された
|
||||
TASK_FCM_DEVICE_TOKEN -> {
|
||||
}
|
||||
|
@ -763,7 +774,7 @@ class PollingWorker private constructor(c : Context) {
|
|||
// プッシュ通知が届いた
|
||||
TASK_FCM_MESSAGE -> {
|
||||
var bDone = false
|
||||
val tag = Utils.optStringX(taskData, EXTRA_TAG)
|
||||
val tag = taskData.parseString(EXTRA_TAG)
|
||||
if(tag != null) {
|
||||
for(sa in SavedAccount.loadByTag(context, tag)) {
|
||||
NotificationTracking.resetLastLoad(sa.db_id)
|
||||
|
@ -776,31 +787,37 @@ class PollingWorker private constructor(c : Context) {
|
|||
NotificationTracking.resetLastLoad()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TASK_NOTIFICATION_CLEAR -> {
|
||||
val db_id = Utils.optLongX(taskData, EXTRA_DB_ID, - 1L)
|
||||
deleteCacheData(db_id)
|
||||
val db_id = taskData.parseLong(EXTRA_DB_ID)
|
||||
log.d("Notification clear! db_id=%s", db_id)
|
||||
if(db_id != null) {
|
||||
deleteCacheData(db_id)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
TASK_NOTIFICATION_DELETE -> {
|
||||
val db_id = Utils.optLongX(taskData, EXTRA_DB_ID, - 1L)
|
||||
val db_id = taskData.parseLong(EXTRA_DB_ID)
|
||||
log.d("Notification deleted! db_id=%s", db_id)
|
||||
NotificationTracking.updateRead(db_id)
|
||||
if(db_id != null) {
|
||||
NotificationTracking.updateRead(db_id)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
TASK_NOTIFICATION_CLICK -> {
|
||||
val db_id = Utils.optLongX(taskData, EXTRA_DB_ID, - 1L)
|
||||
val db_id = taskData.parseLong(EXTRA_DB_ID)
|
||||
log.d("Notification clicked! db_id=%s", db_id)
|
||||
|
||||
// 通知をキャンセル
|
||||
notification_manager.cancel(db_id.toString(), NOTIFICATION_ID)
|
||||
// DB更新処理
|
||||
NotificationTracking.updateRead(db_id)
|
||||
if(db_id != null) {
|
||||
// 通知をキャンセル
|
||||
notification_manager.cancel(db_id.toString(), NOTIFICATION_ID)
|
||||
// DB更新処理
|
||||
NotificationTracking.updateRead(db_id)
|
||||
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
loadCustomStreamListenerSetting()
|
||||
|
@ -868,11 +885,11 @@ class PollingWorker private constructor(c : Context) {
|
|||
|
||||
// インストールIDを生成する前に、各データの通知登録キャッシュをクリアする
|
||||
// トークンがまだ生成されていない場合、このメソッドは null を返します。
|
||||
private fun prepareInstallId():String? {
|
||||
private fun prepareInstallId() : String? {
|
||||
val prefDevice = PrefDevice.prefDevice(context)
|
||||
|
||||
var sv = prefDevice.getString(PrefDevice.KEY_INSTALL_ID, null)
|
||||
if(sv?.isNotEmpty() == true ) return sv
|
||||
if(sv?.isNotEmpty() == true) return sv
|
||||
SavedAccount.clearRegistrationCache()
|
||||
|
||||
try {
|
||||
|
@ -884,7 +901,8 @@ class PollingWorker private constructor(c : Context) {
|
|||
log.e("getInstallId: missing device token.")
|
||||
return null
|
||||
} else {
|
||||
prefDevice.edit().putString(PrefDevice.KEY_DEVICE_TOKEN, device_token).apply()
|
||||
prefDevice.edit().putString(PrefDevice.KEY_DEVICE_TOKEN, device_token)
|
||||
.apply()
|
||||
}
|
||||
} catch(ex : Throwable) {
|
||||
log.e("getInstallId: could not get device token.")
|
||||
|
@ -905,11 +923,16 @@ class PollingWorker private constructor(c : Context) {
|
|||
val body = response.body()?.string()
|
||||
|
||||
if(! response.isSuccessful || body?.isEmpty() != false) {
|
||||
log.e(TootApiClient.formatResponse(response, "getInstallId: get /counter failed."))
|
||||
log.e(
|
||||
TootApiClient.formatResponse(
|
||||
response,
|
||||
"getInstallId: get /counter failed."
|
||||
)
|
||||
)
|
||||
return null
|
||||
}
|
||||
|
||||
sv = Utils.digestSHA256(device_token + UUID.randomUUID() + body)
|
||||
sv = (device_token + UUID.randomUUID() + body).digestSHA256()
|
||||
prefDevice.edit().putString(PrefDevice.KEY_INSTALL_ID, sv).apply()
|
||||
|
||||
return sv
|
||||
|
@ -928,13 +951,22 @@ class PollingWorker private constructor(c : Context) {
|
|||
// 通知タップ時のPendingIntent
|
||||
val intent_click = Intent(context, ActCallback::class.java)
|
||||
intent_click.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
val pi_click = PendingIntent.getActivity(context, 3, intent_click, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
val pi_click = PendingIntent.getActivity(
|
||||
context,
|
||||
3,
|
||||
intent_click,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
)
|
||||
|
||||
val builder = if(Build.VERSION.SDK_INT >= 26) {
|
||||
// Android 8 から、通知のスタイルはユーザが管理することになった
|
||||
// NotificationChannel を端末に登録しておけば、チャネルごとに管理画面が作られる
|
||||
val channel = NotificationHelper.createNotificationChannel(
|
||||
context, "ErrorNotification", "Error", null, 2 /* NotificationManager.IMPORTANCE_LOW */
|
||||
context,
|
||||
"ErrorNotification",
|
||||
"Error",
|
||||
null,
|
||||
2 /* NotificationManager.IMPORTANCE_LOW */
|
||||
)
|
||||
NotificationCompat.Builder(context, channel.id)
|
||||
} else {
|
||||
|
@ -945,7 +977,12 @@ class PollingWorker private constructor(c : Context) {
|
|||
.setContentIntent(pi_click)
|
||||
.setAutoCancel(true)
|
||||
.setSmallIcon(R.drawable.ic_notification) // ここは常に白テーマのアイコンを使う
|
||||
.setColor(ContextCompat.getColor(context, R.color.Light_colorAccent)) // ここは常に白テーマの色を使う
|
||||
.setColor(
|
||||
ContextCompat.getColor(
|
||||
context,
|
||||
R.color.Light_colorAccent
|
||||
)
|
||||
) // ここは常に白テーマの色を使う
|
||||
.setWhen(System.currentTimeMillis())
|
||||
.setGroup(context.packageName + ":" + "Error")
|
||||
|
||||
|
@ -974,7 +1011,7 @@ class PollingWorker private constructor(c : Context) {
|
|||
mCustomStreamListenerSecret = null
|
||||
val jsonString = Pref.spStreamListenerConfigData(pref)
|
||||
mCustomStreamListenerSettingString = jsonString
|
||||
if(jsonString.isNotEmpty() ){
|
||||
if(jsonString.isNotEmpty()) {
|
||||
try {
|
||||
mCustomStreamListenerSetting = JsonValue.readHjson(jsonString).asObject()
|
||||
mCustomStreamListenerSecret = Pref.spStreamListenerSecret(pref)
|
||||
|
@ -985,7 +1022,8 @@ class PollingWorker private constructor(c : Context) {
|
|||
}
|
||||
}
|
||||
|
||||
internal inner class AccountThread(val account : SavedAccount) : Thread(), CurrentCallCallback {
|
||||
internal inner class AccountThread(val account : SavedAccount) : Thread(),
|
||||
CurrentCallCallback {
|
||||
|
||||
private var current_call : Call? = null
|
||||
|
||||
|
@ -1031,7 +1069,7 @@ class PollingWorker private constructor(c : Context) {
|
|||
&& ! account.notification_boost
|
||||
&& ! account.notification_favourite
|
||||
&& ! account.notification_follow
|
||||
) {
|
||||
) {
|
||||
unregisterDeviceToken()
|
||||
return
|
||||
}
|
||||
|
@ -1079,13 +1117,18 @@ class PollingWorker private constructor(c : Context) {
|
|||
return
|
||||
}
|
||||
|
||||
val post_data = ("instance_url=" + Uri.encode("https://" + account.host)
|
||||
+ "&app_id=" + Uri.encode(context.packageName)
|
||||
val post_data = ("instance_url=" + ("https://" + account.host).encodePercent()
|
||||
+ "&app_id=" + context.packageName.encodePercent()
|
||||
+ "&tag=" + tag)
|
||||
|
||||
val request = Request.Builder()
|
||||
.url(APP_SERVER + "/unregister")
|
||||
.post(RequestBody.create(TootApiClient.MEDIA_TYPE_FORM_URL_ENCODED, post_data))
|
||||
.post(
|
||||
RequestBody.create(
|
||||
TootApiClient.MEDIA_TYPE_FORM_URL_ENCODED,
|
||||
post_data
|
||||
)
|
||||
)
|
||||
.build()
|
||||
|
||||
val call = App1.ok_http_client.newCall(request)
|
||||
|
@ -1138,18 +1181,18 @@ class PollingWorker private constructor(c : Context) {
|
|||
}
|
||||
|
||||
if(tag?.isEmpty() != false) {
|
||||
account.notification_tag = Utils.digestSHA256(job.install_id + account.db_id + account.acct)
|
||||
account.notification_tag =
|
||||
(job.install_id + account.db_id + account.acct).digestSHA256()
|
||||
tag = account.notification_tag
|
||||
account.saveNotificationTag()
|
||||
}
|
||||
|
||||
val reg_key = Utils.digestSHA256(
|
||||
tag
|
||||
+ access_token
|
||||
+ device_token
|
||||
+ (if(mCustomStreamListenerSecret == null) "" else mCustomStreamListenerSecret)
|
||||
+ if(mCustomStreamListenerSettingString == null) "" else mCustomStreamListenerSettingString
|
||||
)
|
||||
val reg_key = (tag
|
||||
+ access_token
|
||||
+ device_token
|
||||
+ (if(mCustomStreamListenerSecret == null) "" else mCustomStreamListenerSecret)
|
||||
+ if(mCustomStreamListenerSettingString == null) "" else mCustomStreamListenerSettingString
|
||||
).digestSHA256()
|
||||
|
||||
val now = System.currentTimeMillis()
|
||||
if(reg_key == account.register_key && now - account.register_time < 3600000 * 3) {
|
||||
|
@ -1159,8 +1202,8 @@ class PollingWorker private constructor(c : Context) {
|
|||
}
|
||||
|
||||
val post_data = StringBuilder()
|
||||
.append("instance_url=").append(Uri.encode("https://" + account.host))
|
||||
.append("&app_id=").append(Uri.encode(context.packageName))
|
||||
.append("instance_url=").append(("https://" + account.host).encodePercent())
|
||||
.append("&app_id=").append(context.packageName.encodePercent())
|
||||
.append("&tag=").append(tag)
|
||||
.append("&access_token=").append(access_token)
|
||||
.append("&device_token=").append(device_token)
|
||||
|
@ -1169,13 +1212,18 @@ class PollingWorker private constructor(c : Context) {
|
|||
val appSecret = mCustomStreamListenerSecret
|
||||
|
||||
if(jsonString != null && appSecret != null) {
|
||||
post_data.append("&user_config=").append(Uri.encode(jsonString))
|
||||
post_data.append("&app_secret=").append(Uri.encode(appSecret))
|
||||
post_data.append("&user_config=").append(jsonString.encodePercent())
|
||||
post_data.append("&app_secret=").append(appSecret.encodePercent())
|
||||
}
|
||||
|
||||
val request = Request.Builder()
|
||||
.url(APP_SERVER + "/register")
|
||||
.post(RequestBody.create(TootApiClient.MEDIA_TYPE_FORM_URL_ENCODED, post_data.toString()))
|
||||
.post(
|
||||
RequestBody.create(
|
||||
TootApiClient.MEDIA_TYPE_FORM_URL_ENCODED,
|
||||
post_data.toString()
|
||||
)
|
||||
)
|
||||
.build()
|
||||
|
||||
val call = App1.ok_http_client.newCall(request)
|
||||
|
@ -1210,20 +1258,21 @@ class PollingWorker private constructor(c : Context) {
|
|||
this.parser = TootParser(context, account)
|
||||
|
||||
// まずキャッシュされたデータを処理する
|
||||
if(nr.last_data != null) {
|
||||
try {
|
||||
val array = JSONArray(nr.last_data)
|
||||
try {
|
||||
val last_data = nr.last_data
|
||||
if(last_data != null) {
|
||||
val array = last_data.toJsonArray()
|
||||
for(i in array.length() - 1 downTo 0) {
|
||||
if(job.isJobCancelled) return
|
||||
val src = array.optJSONObject(i)
|
||||
update_sub(src)
|
||||
}
|
||||
} catch(ex : JSONException) {
|
||||
log.trace(ex)
|
||||
}
|
||||
|
||||
} catch(ex : JSONException) {
|
||||
log.trace(ex)
|
||||
}
|
||||
|
||||
|
||||
if(job.isJobCancelled) return
|
||||
|
||||
// 前回の更新から一定時刻が経過したら新しいデータを読んでリストに追加する
|
||||
|
@ -1283,8 +1332,8 @@ class PollingWorker private constructor(c : Context) {
|
|||
if(job.isJobCancelled) return
|
||||
|
||||
dstListJson.sortWith(Comparator { a, b ->
|
||||
val la = Utils.optLongX(a, KEY_TIME, 0)
|
||||
val lb = Utils.optLongX(b, KEY_TIME, 0)
|
||||
val la = a.parseLong(KEY_TIME) ?: 0
|
||||
val lb = b.parseLong(KEY_TIME) ?: 0
|
||||
// 新しい順
|
||||
return@Comparator if(la < lb) 1 else if(la > lb) - 1 else 0
|
||||
})
|
||||
|
@ -1304,19 +1353,24 @@ class PollingWorker private constructor(c : Context) {
|
|||
private fun update_sub(src : JSONObject) {
|
||||
|
||||
if(nr.nid_read == 0L || nr.nid_show == 0L) {
|
||||
log.d("update_sub account_db_id=%s, nid_read=%s, nid_show=%s", account.db_id, nr.nid_read, nr.nid_show)
|
||||
log.d(
|
||||
"update_sub account_db_id=%s, nid_read=%s, nid_show=%s",
|
||||
account.db_id,
|
||||
nr.nid_read,
|
||||
nr.nid_show
|
||||
)
|
||||
}
|
||||
|
||||
val id = Utils.optLongX(src, "id")
|
||||
val id = src.parseLong("id")
|
||||
|
||||
if(duplicate_check.contains(id)) return
|
||||
if(id == null || duplicate_check.contains(id)) return
|
||||
duplicate_check.add(id)
|
||||
|
||||
if(id > nid_last_show) {
|
||||
nid_last_show = id
|
||||
}
|
||||
|
||||
val type = Utils.optStringX(src, "type")
|
||||
val type = src.parseString("type")
|
||||
|
||||
if(id <= nr.nid_read) {
|
||||
// log.d("update_sub: ignore data that id=%s, <= read id %s ",id,nr.nid_read);
|
||||
|
@ -1326,7 +1380,11 @@ class PollingWorker private constructor(c : Context) {
|
|||
}
|
||||
|
||||
if(id > nr.nid_show) {
|
||||
log.d("update_sub: found new data that id=%s, greater than shown id %s ", id, nr.nid_show)
|
||||
log.d(
|
||||
"update_sub: found new data that id=%s, greater than shown id %s ",
|
||||
id,
|
||||
nr.nid_show
|
||||
)
|
||||
// 種別チェックより先に「表示済み」idの更新を行う
|
||||
nr.nid_show = id
|
||||
}
|
||||
|
@ -1359,7 +1417,10 @@ class PollingWorker private constructor(c : Context) {
|
|||
|
||||
private fun getNotificationLine(type : String, display_name : CharSequence) : String {
|
||||
if(TootNotification.TYPE_FAVOURITE == type) {
|
||||
return "- " + context.getString(R.string.display_name_favourited_by, display_name)
|
||||
return "- " + context.getString(
|
||||
R.string.display_name_favourited_by,
|
||||
display_name
|
||||
)
|
||||
}
|
||||
if(TootNotification.TYPE_REBLOG == type) {
|
||||
return "- " + context.getString(R.string.display_name_boosted_by, display_name)
|
||||
|
@ -1396,7 +1457,11 @@ class PollingWorker private constructor(c : Context) {
|
|||
// 先頭にあるデータが同じなら、通知を更新しない
|
||||
// このマーカーは端末再起動時にリセットされるので、再起動後は通知が出るはず
|
||||
|
||||
log.d("showNotification[%s] id=%s is already shown.", account.acct, item.notification.id)
|
||||
log.d(
|
||||
"showNotification[%s] id=%s is already shown.",
|
||||
account.acct,
|
||||
item.notification.id
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
@ -1408,15 +1473,26 @@ class PollingWorker private constructor(c : Context) {
|
|||
// 通知タップ時のPendingIntent
|
||||
val intent_click = Intent(context, ActCallback::class.java)
|
||||
intent_click.action = ActCallback.ACTION_NOTIFICATION_CLICK
|
||||
intent_click.data = Uri.parse("subwaytooter://notification_click/?db_id=" + account.db_id)
|
||||
intent_click.data =
|
||||
Uri.parse("subwaytooter://notification_click/?db_id=" + account.db_id)
|
||||
intent_click.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
val pi_click = PendingIntent.getActivity(context, 256 + account.db_id.toInt(), intent_click, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
val pi_click = PendingIntent.getActivity(
|
||||
context,
|
||||
256 + account.db_id.toInt(),
|
||||
intent_click,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
)
|
||||
|
||||
// 通知を消去した時のPendingIntent
|
||||
val intent_delete = Intent(context, EventReceiver::class.java)
|
||||
intent_delete.action = EventReceiver.ACTION_NOTIFICATION_DELETE
|
||||
intent_delete.putExtra(EXTRA_DB_ID, account.db_id)
|
||||
val pi_delete = PendingIntent.getBroadcast(context, Integer.MAX_VALUE - account.db_id.toInt(), intent_delete, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
val pi_delete = PendingIntent.getBroadcast(
|
||||
context,
|
||||
Integer.MAX_VALUE - account.db_id.toInt(),
|
||||
intent_delete,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
)
|
||||
|
||||
log.d("showNotification[%s] creating notification(2)", account.acct)
|
||||
|
||||
|
@ -1434,7 +1510,12 @@ class PollingWorker private constructor(c : Context) {
|
|||
.setDeleteIntent(pi_delete)
|
||||
.setAutoCancel(true)
|
||||
.setSmallIcon(R.drawable.ic_notification) // ここは常に白テーマのアイコンを使う
|
||||
.setColor(ContextCompat.getColor(context, R.color.Light_colorAccent)) // ここは常に白テーマの色を使う
|
||||
.setColor(
|
||||
ContextCompat.getColor(
|
||||
context,
|
||||
R.color.Light_colorAccent
|
||||
)
|
||||
) // ここは常に白テーマの色を使う
|
||||
.setWhen(item.notification.time_created_at)
|
||||
|
||||
// Android 7.0 ではグループを指定しないと勝手に通知が束ねられてしまう。
|
||||
|
@ -1448,7 +1529,7 @@ class PollingWorker private constructor(c : Context) {
|
|||
|
||||
var iv = 0
|
||||
|
||||
if( Pref.bpNotificationSound(pref) ) {
|
||||
if(Pref.bpNotificationSound(pref)) {
|
||||
|
||||
var sound_uri : Uri? = null
|
||||
|
||||
|
@ -1504,7 +1585,10 @@ class PollingWorker private constructor(c : Context) {
|
|||
|
||||
log.d("showNotification[%s] creating notification(7)", account.acct)
|
||||
|
||||
var a = getNotificationLine(item.notification.type, item.notification.account?.decoded_display_name ?: "?")
|
||||
var a = getNotificationLine(
|
||||
item.notification.type,
|
||||
item.notification.account?.decoded_display_name ?: "?"
|
||||
)
|
||||
val acct = item.access_info.acct
|
||||
if(data_list.size == 1) {
|
||||
builder.setContentTitle(a)
|
||||
|
@ -1520,7 +1604,10 @@ class PollingWorker private constructor(c : Context) {
|
|||
for(i in 0 .. 4) {
|
||||
if(i >= data_list.size) break
|
||||
item = data_list[i]
|
||||
a = getNotificationLine(item.notification.type, item.notification.account?.decoded_display_name ?: "?")
|
||||
a = getNotificationLine(
|
||||
item.notification.type,
|
||||
item.notification.account?.decoded_display_name ?: "?"
|
||||
)
|
||||
style.addLine(a)
|
||||
}
|
||||
builder.setStyle(style)
|
||||
|
@ -1544,22 +1631,25 @@ class PollingWorker private constructor(c : Context) {
|
|||
val duplicate_check = HashSet<Long>()
|
||||
|
||||
val dst_array = ArrayList<JSONObject>()
|
||||
if(nr.last_data != null) {
|
||||
try {
|
||||
// まずキャッシュされたデータを処理する
|
||||
try {
|
||||
val array = JSONArray(nr.last_data)
|
||||
val last_data = nr.last_data
|
||||
if(last_data != null) {
|
||||
val array = last_data.toJsonArray()
|
||||
for(i in array.length() - 1 downTo 0) {
|
||||
val src = array.optJSONObject(i)
|
||||
val id = Utils.optLongX(src, "id")
|
||||
dst_array.add(src)
|
||||
duplicate_check.add(id)
|
||||
log.d("add old. id=%s", id)
|
||||
val id = src.parseLong("id")
|
||||
if(id != null) {
|
||||
dst_array.add(src)
|
||||
duplicate_check.add(id)
|
||||
log.d("add old. id=%s", id)
|
||||
}
|
||||
}
|
||||
} catch(ex : JSONException) {
|
||||
log.trace(ex)
|
||||
}
|
||||
|
||||
} catch(ex : JSONException) {
|
||||
log.trace(ex)
|
||||
}
|
||||
|
||||
for(item in data.list) {
|
||||
try {
|
||||
if(duplicate_check.contains(item.id)) {
|
||||
|
@ -1590,8 +1680,8 @@ class PollingWorker private constructor(c : Context) {
|
|||
|
||||
// 新しい順にソート
|
||||
Collections.sort(dst_array, Comparator { a, b ->
|
||||
val la = Utils.optLongX(a, KEY_TIME, 0)
|
||||
val lb = Utils.optLongX(b, KEY_TIME, 0)
|
||||
val la = a.parseLong(KEY_TIME) ?: 0
|
||||
val lb = b.parseLong(KEY_TIME) ?: 0
|
||||
// 新しい順
|
||||
if(la < lb) return@Comparator + 1
|
||||
if(la > lb) - 1 else 0
|
||||
|
|
|
@ -3,6 +3,7 @@ package jp.juggler.subwaytooter
|
|||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.preference.PreferenceManager
|
||||
import jp.juggler.subwaytooter.util.optInt
|
||||
|
||||
object Pref {
|
||||
|
||||
|
@ -99,6 +100,8 @@ object Pref {
|
|||
override fun put(editor : SharedPreferences.Editor, v : String) {
|
||||
editor.putString(key, v)
|
||||
}
|
||||
|
||||
fun optInt(pref : SharedPreferences) = invoke(pref).optInt() ?: defVal.optInt()
|
||||
}
|
||||
|
||||
// boolean
|
||||
|
|
|
@ -4,8 +4,6 @@ import android.content.Context
|
|||
import android.content.SharedPreferences
|
||||
import android.os.Handler
|
||||
|
||||
import org.json.JSONObject
|
||||
|
||||
import java.net.ProtocolException
|
||||
import java.util.LinkedList
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
|
@ -20,8 +18,9 @@ import jp.juggler.subwaytooter.api.TootParser
|
|||
import jp.juggler.subwaytooter.api.entity.TootPayload
|
||||
import jp.juggler.subwaytooter.table.SavedAccount
|
||||
import jp.juggler.subwaytooter.util.LogCategory
|
||||
import jp.juggler.subwaytooter.util.Utils
|
||||
import jp.juggler.subwaytooter.util.WordTrieTree
|
||||
import jp.juggler.subwaytooter.util.runOnMainLooper
|
||||
import jp.juggler.subwaytooter.util.toJsonObject
|
||||
import okhttp3.Response
|
||||
import okhttp3.WebSocket
|
||||
import okhttp3.WebSocketListener
|
||||
|
@ -103,7 +102,7 @@ internal class StreamReader(
|
|||
override fun onMessage(webSocket : WebSocket, text : String) {
|
||||
// log.d( "WebSocket onMessage. url=%s, message=%s", webSocket.request().url(), text );
|
||||
try {
|
||||
val obj = JSONObject(text)
|
||||
val obj = text.toJsonObject()
|
||||
|
||||
val event = obj.optString("event")
|
||||
if(event == null || event.isEmpty()) {
|
||||
|
@ -117,9 +116,9 @@ internal class StreamReader(
|
|||
return
|
||||
}
|
||||
|
||||
Utils.runOnMainThread {
|
||||
runOnMainLooper {
|
||||
synchronized(this) {
|
||||
if(bDisposed.get()) return@runOnMainThread
|
||||
if(bDisposed.get()) return@runOnMainLooper
|
||||
for(callback in callback_list) {
|
||||
try {
|
||||
callback(event, payload)
|
||||
|
@ -250,7 +249,7 @@ internal class StreamReader(
|
|||
streamCallback : (event_type : String, item : Any?) -> Unit
|
||||
) {
|
||||
val reader = prepareReader(accessInfo, endPoint, highlightTrie)
|
||||
|
||||
|
||||
reader.addCallback(streamCallback)
|
||||
|
||||
if(! reader.bListening.get()) {
|
||||
|
|
|
@ -6,7 +6,7 @@ import android.widget.Button
|
|||
import android.widget.TextView
|
||||
import jp.juggler.subwaytooter.table.SavedAccount
|
||||
import jp.juggler.subwaytooter.util.LogCategory
|
||||
import jp.juggler.subwaytooter.util.Utils
|
||||
import jp.juggler.subwaytooter.util.scan
|
||||
|
||||
internal abstract class ViewHolderHeaderBase(val activity : ActMain, val viewRoot : View) :
|
||||
RecyclerView.ViewHolder(viewRoot) {
|
||||
|
@ -19,7 +19,7 @@ internal abstract class ViewHolderHeaderBase(val activity : ActMain, val viewRoo
|
|||
internal lateinit var access_info : SavedAccount
|
||||
|
||||
init {
|
||||
Utils.scanView(viewRoot) { v ->
|
||||
viewRoot.scan { v ->
|
||||
try {
|
||||
if(v is Button) {
|
||||
// ボタンは太字なので触らない
|
||||
|
|
|
@ -8,7 +8,7 @@ import android.widget.TextView
|
|||
import jp.juggler.subwaytooter.api.entity.TootInstance
|
||||
import jp.juggler.subwaytooter.util.DecodeOptions
|
||||
import jp.juggler.subwaytooter.util.LogCategory
|
||||
import jp.juggler.subwaytooter.util.Utils
|
||||
import jp.juggler.subwaytooter.util.showToast
|
||||
import jp.juggler.subwaytooter.view.MyLinkMovementMethod
|
||||
import jp.juggler.subwaytooter.view.MyNetworkImageView
|
||||
|
||||
|
@ -149,7 +149,7 @@ internal class ViewHolderHeaderInstance(
|
|||
|
||||
} catch(ex : Throwable) {
|
||||
log.e(ex, "startActivity failed. mail=$email")
|
||||
Utils.showToast(activity, true, R.string.missing_mail_app)
|
||||
showToast(activity, true, R.string.missing_mail_app)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -164,7 +164,7 @@ internal class ViewHolderHeaderInstance(
|
|||
|
||||
} catch(ex : Throwable) {
|
||||
log.e(ex, "startActivity failed. thumbnail=$thumbnail")
|
||||
Utils.showToast(activity, true, "missing web browser")
|
||||
showToast(activity, true, "missing web browser")
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ import jp.juggler.subwaytooter.table.AcctColor
|
|||
import jp.juggler.subwaytooter.table.UserRelation
|
||||
import jp.juggler.subwaytooter.span.EmojiImageSpan
|
||||
import jp.juggler.subwaytooter.util.NetworkEmojiInvalidator
|
||||
import jp.juggler.subwaytooter.util.Utils
|
||||
import jp.juggler.subwaytooter.util.intoStringResource
|
||||
import jp.juggler.subwaytooter.view.MyLinkMovementMethod
|
||||
import jp.juggler.subwaytooter.view.MyNetworkImageView
|
||||
|
||||
|
@ -219,11 +219,11 @@ internal class ViewHolderHeaderProfile(
|
|||
llMoved.visibility = View.VISIBLE
|
||||
tvMoved.visibility = View.VISIBLE
|
||||
|
||||
val caption = Utils.formatSpannable1(
|
||||
val caption = who.decodeDisplayName(activity).intoStringResource(
|
||||
activity,
|
||||
R.string.account_moved_to,
|
||||
who.decodeDisplayName(activity)
|
||||
R.string.account_moved_to
|
||||
)
|
||||
|
||||
tvMoved.text = caption
|
||||
moved_caption_invalidator.register(caption)
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package jp.juggler.subwaytooter.action
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
|
||||
import org.json.JSONObject
|
||||
|
||||
|
@ -17,9 +16,7 @@ import jp.juggler.subwaytooter.api.entity.TootRelationShip
|
|||
import jp.juggler.subwaytooter.api.entity.parseList
|
||||
import jp.juggler.subwaytooter.table.SavedAccount
|
||||
import jp.juggler.subwaytooter.table.UserRelation
|
||||
import jp.juggler.subwaytooter.util.LogCategory
|
||||
import jp.juggler.subwaytooter.util.TootAccountOrNullCallback
|
||||
import jp.juggler.subwaytooter.util.Utils
|
||||
import jp.juggler.subwaytooter.util.*
|
||||
|
||||
// ユーザ名からアカウントIDを取得する
|
||||
internal fun findAccountByName(
|
||||
|
@ -34,7 +31,8 @@ internal fun findAccountByName(
|
|||
internal var who : TootAccount? = null
|
||||
|
||||
override fun background(client : TootApiClient) : TootApiResult? {
|
||||
val path = "/api/v1/accounts/search" + "?q=" + Uri.encode(user)
|
||||
|
||||
val path = "/api/v1/accounts/search" + "?q=" + user.encodePercent()
|
||||
|
||||
val result = client.request(path)
|
||||
val array = result?.jsonArray
|
||||
|
@ -42,7 +40,10 @@ internal fun findAccountByName(
|
|||
for(i in 0 until array.length()) {
|
||||
val a = TootAccount.parse(activity, access_info, array.optJSONObject(i))
|
||||
if(a != null) {
|
||||
if(a.username == user && access_info.getFullAcct(a).equals(user + "@" + host, ignoreCase = true)) {
|
||||
if(a.username == user && access_info.getFullAcct(a).equals(
|
||||
user + "@" + host,
|
||||
ignoreCase = true
|
||||
)) {
|
||||
who = a
|
||||
break
|
||||
}
|
||||
|
@ -93,7 +94,7 @@ internal fun addPseudoAccount(
|
|||
val log = LogCategory("addPseudoAccount")
|
||||
log.trace(ex)
|
||||
log.e(ex, "failed.")
|
||||
Utils.showToast(context, ex, "addPseudoAccount failed.")
|
||||
showToast(context, ex, "addPseudoAccount failed.")
|
||||
}
|
||||
|
||||
return null
|
||||
|
@ -108,7 +109,10 @@ fun makeAccountListNonPseudo(
|
|||
val list_other_host = ArrayList<SavedAccount>()
|
||||
for(a in SavedAccount.loadAccountList(context)) {
|
||||
if(a.isPseudo) continue
|
||||
(if(pickup_host == null || pickup_host.equals(a.host, ignoreCase = true)) list_same_host else list_other_host).add(a)
|
||||
(if(pickup_host == null || pickup_host.equals(
|
||||
a.host,
|
||||
ignoreCase = true
|
||||
)) list_same_host else list_other_host).add(a)
|
||||
}
|
||||
SavedAccount.sort(list_same_host)
|
||||
SavedAccount.sort(list_other_host)
|
||||
|
@ -134,7 +138,7 @@ internal fun loadRelation1(
|
|||
val r2 = rr.result
|
||||
val jsonArray = r2?.jsonArray
|
||||
if(jsonArray != null) {
|
||||
val list = parseList(::TootRelationShip,jsonArray)
|
||||
val list = parseList(::TootRelationShip, jsonArray)
|
||||
if(list.isNotEmpty()) {
|
||||
rr.relation = saveUserRelation(access_info, list[0])
|
||||
}
|
||||
|
@ -147,7 +151,10 @@ const val NOT_CROSS_ACCOUNT = 1
|
|||
const val CROSS_ACCOUNT_SAME_INSTANCE = 2
|
||||
const val CROSS_ACCOUNT_REMOTE_INSTANCE = 3
|
||||
|
||||
internal fun calcCrossAccountMode(timeline_account : SavedAccount, action_account : SavedAccount) : Int {
|
||||
internal fun calcCrossAccountMode(
|
||||
timeline_account : SavedAccount,
|
||||
action_account : SavedAccount
|
||||
) : Int {
|
||||
return if(! timeline_account.host.equals(action_account.host, ignoreCase = true)) {
|
||||
CROSS_ACCOUNT_REMOTE_INSTANCE
|
||||
} else if(! timeline_account.acct.equals(action_account.acct, ignoreCase = true)) {
|
||||
|
|
|
@ -21,7 +21,7 @@ import jp.juggler.subwaytooter.dialog.AccountPicker
|
|||
import jp.juggler.subwaytooter.dialog.DlgTextInput
|
||||
import jp.juggler.subwaytooter.dialog.LoginForm
|
||||
import jp.juggler.subwaytooter.util.LogCategory
|
||||
import jp.juggler.subwaytooter.util.Utils
|
||||
import jp.juggler.subwaytooter.util.showToast
|
||||
import org.json.JSONObject
|
||||
|
||||
object Action_Account {
|
||||
|
@ -81,7 +81,7 @@ object Action_Account {
|
|||
}
|
||||
|
||||
override fun onEmptyError() {
|
||||
Utils.showToast(activity, true, R.string.token_not_specified)
|
||||
showToast(activity, true, R.string.token_not_specified)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
@ -90,7 +90,7 @@ object Action_Account {
|
|||
// 疑似アカウントを追加
|
||||
val a = addPseudoAccount(activity, instance)
|
||||
if(a != null) {
|
||||
Utils.showToast(activity, false, R.string.server_confirmed)
|
||||
showToast(activity, false, R.string.server_confirmed)
|
||||
val pos = App1.getAppState(activity).column_list.size
|
||||
activity.addColumn(pos, a, Column.TYPE_LOCAL)
|
||||
|
||||
|
@ -114,7 +114,7 @@ object Action_Account {
|
|||
.setNeutralButton(R.string.close, null)
|
||||
.show()
|
||||
} else {
|
||||
Utils.showToast(activity, true, error)
|
||||
showToast(activity, true, error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ import jp.juggler.subwaytooter.App1
|
|||
import jp.juggler.subwaytooter.R
|
||||
import jp.juggler.subwaytooter.api.entity.TootApplication
|
||||
import jp.juggler.subwaytooter.table.MutedApp
|
||||
import jp.juggler.subwaytooter.util.Utils
|
||||
import jp.juggler.subwaytooter.util.showToast
|
||||
|
||||
object Action_App {
|
||||
|
||||
|
@ -23,7 +23,7 @@ object Action_App {
|
|||
for(column in App1.getAppState(activity).column_list) {
|
||||
column.onMuteAppUpdated()
|
||||
}
|
||||
Utils.showToast(activity, false, R.string.app_was_muted)
|
||||
showToast(activity, false, R.string.app_was_muted)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package jp.juggler.subwaytooter.action
|
||||
|
||||
import android.net.Uri
|
||||
import android.support.v7.app.AlertDialog
|
||||
|
||||
import jp.juggler.subwaytooter.ActMain
|
||||
|
@ -19,7 +18,8 @@ import jp.juggler.subwaytooter.table.AcctColor
|
|||
import jp.juggler.subwaytooter.table.SavedAccount
|
||||
import jp.juggler.subwaytooter.table.UserRelation
|
||||
import jp.juggler.subwaytooter.util.EmptyCallback
|
||||
import jp.juggler.subwaytooter.util.Utils
|
||||
import jp.juggler.subwaytooter.util.encodePercent
|
||||
import jp.juggler.subwaytooter.util.showToast
|
||||
import okhttp3.Request
|
||||
import okhttp3.RequestBody
|
||||
|
||||
|
@ -36,7 +36,7 @@ object Action_Follow {
|
|||
callback : EmptyCallback? = null
|
||||
) {
|
||||
if(access_info.isMe(who)) {
|
||||
Utils.showToast(activity, false, R.string.it_is_you)
|
||||
showToast(activity, false, R.string.it_is_you)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -161,7 +161,7 @@ object Action_Follow {
|
|||
// リモートフォローする
|
||||
val request_builder = Request.Builder().post(
|
||||
RequestBody.create(
|
||||
TootApiClient.MEDIA_TYPE_FORM_URL_ENCODED, "uri=" + Uri.encode(who.acct)
|
||||
TootApiClient.MEDIA_TYPE_FORM_URL_ENCODED, "uri=" + who.acct.encodePercent()
|
||||
))
|
||||
|
||||
result = client.request("/api/v1/follows", request_builder)
|
||||
|
@ -205,18 +205,18 @@ object Action_Follow {
|
|||
|
||||
if(bFollow && relation .getRequested(who)) {
|
||||
// 鍵付きアカウントにフォローリクエストを申請した状態
|
||||
Utils.showToast(activity, false, R.string.follow_requested)
|
||||
showToast(activity, false, R.string.follow_requested)
|
||||
} else if(! bFollow && relation .getRequested(who)) {
|
||||
Utils.showToast(activity, false, R.string.follow_request_cant_remove_by_sender)
|
||||
showToast(activity, false, R.string.follow_request_cant_remove_by_sender)
|
||||
} else {
|
||||
// ローカル操作成功、もしくはリモートフォロー成功
|
||||
if(callback != null) callback()
|
||||
}
|
||||
|
||||
} else if(bFollow && who.locked && ( result.response?.code() ?: -1) == 422) {
|
||||
Utils.showToast(activity, false, R.string.cant_follow_locked_user)
|
||||
showToast(activity, false, R.string.cant_follow_locked_user)
|
||||
} else {
|
||||
Utils.showToast(activity, false, result.error)
|
||||
showToast(activity, false, result.error)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -233,7 +233,7 @@ object Action_Follow {
|
|||
callback : EmptyCallback? =null
|
||||
) {
|
||||
if(access_info.isMe(acct)) {
|
||||
Utils.showToast(activity, false, R.string.it_is_you)
|
||||
showToast(activity, false, R.string.it_is_you)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -294,7 +294,7 @@ object Action_Follow {
|
|||
|
||||
val request_builder = Request.Builder().post(
|
||||
RequestBody.create(
|
||||
TootApiClient.MEDIA_TYPE_FORM_URL_ENCODED, "uri=" + Uri.encode(acct)
|
||||
TootApiClient.MEDIA_TYPE_FORM_URL_ENCODED, "uri=" + acct.encodePercent()
|
||||
))
|
||||
|
||||
var result = client.request("/api/v1/follows", request_builder)
|
||||
|
@ -323,9 +323,9 @@ object Action_Follow {
|
|||
if(callback != null) callback()
|
||||
|
||||
} else if(locked && (result.response?.code() ?: -1 )== 422) {
|
||||
Utils.showToast(activity, false, R.string.cant_follow_locked_user)
|
||||
showToast(activity, false, R.string.cant_follow_locked_user)
|
||||
} else {
|
||||
Utils.showToast(activity, false, result.error)
|
||||
showToast(activity, false, result.error)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -383,7 +383,7 @@ object Action_Follow {
|
|||
activity : ActMain, access_info : SavedAccount, who : TootAccount, bAllow : Boolean
|
||||
) {
|
||||
if(access_info.isMe(who)) {
|
||||
Utils.showToast(activity, false, R.string.it_is_you)
|
||||
showToast(activity, false, R.string.it_is_you)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -407,9 +407,9 @@ object Action_Follow {
|
|||
column.removeFollowRequest(access_info, who.id)
|
||||
}
|
||||
|
||||
Utils.showToast(activity, false, if(bAllow) R.string.follow_request_authorized else R.string.follow_request_rejected, who.decoded_display_name)
|
||||
showToast(activity, false, if(bAllow) R.string.follow_request_authorized else R.string.follow_request_rejected, who.decoded_display_name)
|
||||
} else {
|
||||
Utils.showToast(activity, false, result.error)
|
||||
showToast(activity, false, result.error)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
package jp.juggler.subwaytooter.action
|
||||
|
||||
import android.net.Uri
|
||||
|
||||
import java.util.ArrayList
|
||||
|
||||
import jp.juggler.subwaytooter.ActMain
|
||||
|
@ -14,7 +12,8 @@ import jp.juggler.subwaytooter.api.TootTask
|
|||
import jp.juggler.subwaytooter.api.TootTaskRunner
|
||||
import jp.juggler.subwaytooter.dialog.AccountPicker
|
||||
import jp.juggler.subwaytooter.table.SavedAccount
|
||||
import jp.juggler.subwaytooter.util.Utils
|
||||
import jp.juggler.subwaytooter.util.encodePercent
|
||||
import jp.juggler.subwaytooter.util.showToast
|
||||
import okhttp3.Request
|
||||
import okhttp3.RequestBody
|
||||
|
||||
|
@ -61,7 +60,7 @@ object Action_Instance {
|
|||
) {
|
||||
|
||||
if(access_info.host.equals(domain, ignoreCase = true)) {
|
||||
Utils.showToast(activity, false, R.string.it_is_you)
|
||||
showToast(activity, false, R.string.it_is_you)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -69,7 +68,7 @@ object Action_Instance {
|
|||
override fun background(client : TootApiClient) : TootApiResult? {
|
||||
|
||||
val body = RequestBody.create(
|
||||
TootApiClient.MEDIA_TYPE_FORM_URL_ENCODED, "domain=" + Uri.encode(domain)
|
||||
TootApiClient.MEDIA_TYPE_FORM_URL_ENCODED, "domain=" + domain.encodePercent()
|
||||
)
|
||||
|
||||
var request_builder = Request.Builder()
|
||||
|
@ -87,10 +86,10 @@ object Action_Instance {
|
|||
column.onDomainBlockChanged(access_info, domain, bBlock)
|
||||
}
|
||||
|
||||
Utils.showToast(activity, false, if(bBlock) R.string.block_succeeded else R.string.unblock_succeeded)
|
||||
showToast(activity, false, if(bBlock) R.string.block_succeeded else R.string.unblock_succeeded)
|
||||
|
||||
} else {
|
||||
Utils.showToast(activity, false, result.error)
|
||||
showToast(activity, false, result.error)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -11,7 +11,8 @@ import jp.juggler.subwaytooter.api.TootTaskRunner
|
|||
import jp.juggler.subwaytooter.api.entity.TootList
|
||||
import jp.juggler.subwaytooter.api.entity.parseItem
|
||||
import jp.juggler.subwaytooter.table.SavedAccount
|
||||
import jp.juggler.subwaytooter.util.Utils
|
||||
import jp.juggler.subwaytooter.util.showToast
|
||||
import jp.juggler.subwaytooter.util.withCaption
|
||||
import okhttp3.Request
|
||||
import okhttp3.RequestBody
|
||||
|
||||
|
@ -34,7 +35,7 @@ object Action_List {
|
|||
try {
|
||||
content.put("title", title)
|
||||
} catch(ex : Throwable) {
|
||||
return TootApiResult(Utils.formatError(ex, "can't encoding json parameter."))
|
||||
return TootApiResult(ex.withCaption("can't encoding json parameter."))
|
||||
}
|
||||
|
||||
val request_builder = Request.Builder().post(
|
||||
|
@ -60,11 +61,11 @@ object Action_List {
|
|||
column.onListListUpdated(access_info)
|
||||
}
|
||||
|
||||
Utils.showToast(activity, false, R.string.list_created)
|
||||
showToast(activity, false, R.string.list_created)
|
||||
|
||||
callback?.onCreated(list)
|
||||
} else {
|
||||
Utils.showToast(activity, false, result.error)
|
||||
showToast(activity, false, result.error)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -88,10 +89,10 @@ object Action_List {
|
|||
column.onListListUpdated(access_info)
|
||||
}
|
||||
|
||||
Utils.showToast(activity, false, R.string.delete_succeeded)
|
||||
showToast(activity, false, R.string.delete_succeeded)
|
||||
|
||||
} else {
|
||||
Utils.showToast(activity, false, result.error)
|
||||
showToast(activity, false, result.error)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
package jp.juggler.subwaytooter.action
|
||||
|
||||
import android.net.Uri
|
||||
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
|
||||
|
@ -19,7 +17,9 @@ import jp.juggler.subwaytooter.api.entity.TootRelationShip
|
|||
import jp.juggler.subwaytooter.api.entity.parseList
|
||||
import jp.juggler.subwaytooter.dialog.DlgConfirm
|
||||
import jp.juggler.subwaytooter.table.SavedAccount
|
||||
import jp.juggler.subwaytooter.util.Utils
|
||||
import jp.juggler.subwaytooter.util.encodePercent
|
||||
import jp.juggler.subwaytooter.util.showToast
|
||||
import jp.juggler.subwaytooter.util.withCaption
|
||||
import okhttp3.Request
|
||||
import okhttp3.RequestBody
|
||||
|
||||
|
@ -32,7 +32,12 @@ object Action_ListMember {
|
|||
}
|
||||
|
||||
fun add(
|
||||
activity : ActMain, access_info : SavedAccount, list_id : Long, local_who : TootAccount, bFollow : Boolean, callback : Callback?
|
||||
activity : ActMain,
|
||||
access_info : SavedAccount,
|
||||
list_id : Long,
|
||||
local_who : TootAccount,
|
||||
bFollow : Boolean,
|
||||
callback : Callback?
|
||||
) {
|
||||
TootTaskRunner(activity).run(access_info, object : TootTask {
|
||||
override fun background(client : TootApiClient) : TootApiResult? {
|
||||
|
@ -49,27 +54,34 @@ object Action_ListMember {
|
|||
val request_builder = Request.Builder().post(
|
||||
RequestBody.create(
|
||||
TootApiClient.MEDIA_TYPE_FORM_URL_ENCODED, "" // 空データ
|
||||
))
|
||||
)
|
||||
)
|
||||
|
||||
result = client.request("/api/v1/accounts/" + local_who.id + "/follow", request_builder)
|
||||
result = client.request(
|
||||
"/api/v1/accounts/" + local_who.id + "/follow",
|
||||
request_builder
|
||||
)
|
||||
} else {
|
||||
// リモートフォローする
|
||||
val request_builder = Request.Builder().post(
|
||||
RequestBody.create(
|
||||
TootApiClient.MEDIA_TYPE_FORM_URL_ENCODED, "uri=" + Uri.encode(local_who.acct)
|
||||
))
|
||||
TootApiClient.MEDIA_TYPE_FORM_URL_ENCODED,
|
||||
"uri=" + local_who.acct.encodePercent()
|
||||
)
|
||||
)
|
||||
|
||||
result = client.request("/api/v1/follows", request_builder)
|
||||
val jsonObject = result?.jsonObject ?:return result
|
||||
val jsonObject = result?.jsonObject ?: return result
|
||||
|
||||
val a = TootAccount.parse(activity, access_info,jsonObject) ?: return TootApiResult("parse error.")
|
||||
val a = TootAccount.parse(activity, access_info, jsonObject)
|
||||
?: return TootApiResult("parse error.")
|
||||
|
||||
// リモートフォローの後にリレーションシップを取得しなおす
|
||||
result = client.request("/api/v1/accounts/relationships?id[]=" + a.id)
|
||||
}
|
||||
val jsonArray = result?.jsonArray ?: return result
|
||||
|
||||
val relation_list = parseList(::TootRelationShip,jsonArray)
|
||||
val relation_list = parseList(::TootRelationShip, jsonArray)
|
||||
relation = if(relation_list.isEmpty()) null else relation_list[0]
|
||||
|
||||
if(relation == null) {
|
||||
|
@ -95,13 +107,14 @@ object Action_ListMember {
|
|||
account_ids.put(local_who.id.toString())
|
||||
content.put("account_ids", account_ids)
|
||||
} catch(ex : Throwable) {
|
||||
return TootApiResult(Utils.formatError(ex, "can't encoding json parameter."))
|
||||
return TootApiResult(ex.withCaption("can't encoding json parameter."))
|
||||
}
|
||||
|
||||
val request_builder = Request.Builder().post(
|
||||
RequestBody.create(
|
||||
TootApiClient.MEDIA_TYPE_JSON, content.toString()
|
||||
))
|
||||
)
|
||||
)
|
||||
|
||||
return client.request("/api/v1/lists/$list_id/accounts", request_builder)
|
||||
|
||||
|
@ -122,7 +135,7 @@ object Action_ListMember {
|
|||
// フォロー状態の更新を表示に反映させる
|
||||
if(bFollow) activity.showColumnMatchAccount(access_info)
|
||||
|
||||
Utils.showToast(activity, false, R.string.list_member_added)
|
||||
showToast(activity, false, R.string.list_member_added)
|
||||
|
||||
bSuccess = true
|
||||
|
||||
|
@ -130,13 +143,26 @@ object Action_ListMember {
|
|||
val response = result.response
|
||||
val error = result.error
|
||||
if(response != null
|
||||
&& response .code() == 422
|
||||
&& error != null && reFollowError.matcher(error ).find()) {
|
||||
&& response.code() == 422
|
||||
&& error != null && reFollowError.matcher(error).find()) {
|
||||
|
||||
if(! bFollow) {
|
||||
DlgConfirm.openSimple(
|
||||
activity, activity.getString(R.string.list_retry_with_follow, access_info.getFullAcct(local_who))
|
||||
) { Action_ListMember.add(activity, access_info, list_id, local_who, true, callback) }
|
||||
activity,
|
||||
activity.getString(
|
||||
R.string.list_retry_with_follow,
|
||||
access_info.getFullAcct(local_who)
|
||||
)
|
||||
) {
|
||||
Action_ListMember.add(
|
||||
activity,
|
||||
access_info,
|
||||
list_id,
|
||||
local_who,
|
||||
true,
|
||||
callback
|
||||
)
|
||||
}
|
||||
} else {
|
||||
android.app.AlertDialog.Builder(activity)
|
||||
.setCancelable(true)
|
||||
|
@ -147,7 +173,7 @@ object Action_ListMember {
|
|||
return
|
||||
}
|
||||
|
||||
Utils.showToast(activity, true, error)
|
||||
showToast(activity, true, error)
|
||||
|
||||
}
|
||||
} finally {
|
||||
|
@ -159,12 +185,17 @@ object Action_ListMember {
|
|||
}
|
||||
|
||||
fun delete(
|
||||
activity : ActMain, access_info : SavedAccount, list_id : Long, local_who : TootAccount, callback : Callback?
|
||||
activity : ActMain,
|
||||
access_info : SavedAccount,
|
||||
list_id : Long,
|
||||
local_who : TootAccount,
|
||||
callback : Callback?
|
||||
) {
|
||||
TootTaskRunner(activity).run(access_info, object : TootTask {
|
||||
override fun background(client : TootApiClient) : TootApiResult? {
|
||||
return client.request(
|
||||
"/api/v1/lists/" + list_id + "/accounts?account_ids[]=" + local_who.id, Request.Builder().delete()
|
||||
"/api/v1/lists/" + list_id + "/accounts?account_ids[]=" + local_who.id,
|
||||
Request.Builder().delete()
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -181,12 +212,12 @@ object Action_ListMember {
|
|||
column.onListMemberUpdated(access_info, list_id, local_who, false)
|
||||
}
|
||||
|
||||
Utils.showToast(activity, false, R.string.delete_succeeded)
|
||||
showToast(activity, false, R.string.delete_succeeded)
|
||||
|
||||
bSuccess = true
|
||||
|
||||
} else {
|
||||
Utils.showToast(activity, false, result.error)
|
||||
showToast(activity, false, result.error)
|
||||
}
|
||||
} finally {
|
||||
callback?.onListMemberUpdated(false, bSuccess)
|
||||
|
|
|
@ -12,7 +12,7 @@ import jp.juggler.subwaytooter.api.TootTask
|
|||
import jp.juggler.subwaytooter.api.TootTaskRunner
|
||||
import jp.juggler.subwaytooter.api.entity.TootNotification
|
||||
import jp.juggler.subwaytooter.table.SavedAccount
|
||||
import jp.juggler.subwaytooter.util.Utils
|
||||
import jp.juggler.subwaytooter.util.showToast
|
||||
import okhttp3.Request
|
||||
import okhttp3.RequestBody
|
||||
|
||||
|
@ -49,9 +49,9 @@ object Action_Notification {
|
|||
column.removeNotifications()
|
||||
}
|
||||
}
|
||||
Utils.showToast(activity, false, R.string.delete_succeeded)
|
||||
showToast(activity, false, R.string.delete_succeeded)
|
||||
} else {
|
||||
Utils.showToast(activity, false, result.error)
|
||||
showToast(activity, false, result.error)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -81,9 +81,9 @@ object Action_Notification {
|
|||
for(column in App1.getAppState(activity).column_list) {
|
||||
column.removeNotificationOne(access_info, notification)
|
||||
}
|
||||
Utils.showToast(activity, true, R.string.delete_succeeded)
|
||||
showToast(activity, true, R.string.delete_succeeded)
|
||||
} else {
|
||||
Utils.showToast(activity, true, result.error)
|
||||
showToast(activity, true, result.error)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -24,7 +24,8 @@ import jp.juggler.subwaytooter.table.AcctColor
|
|||
import jp.juggler.subwaytooter.table.SavedAccount
|
||||
import jp.juggler.subwaytooter.util.EmptyCallback
|
||||
import jp.juggler.subwaytooter.util.LogCategory
|
||||
import jp.juggler.subwaytooter.util.Utils
|
||||
import jp.juggler.subwaytooter.util.showToast
|
||||
import jp.juggler.subwaytooter.util.encodePercent
|
||||
import okhttp3.Request
|
||||
import okhttp3.RequestBody
|
||||
|
||||
|
@ -71,7 +72,7 @@ object Action_Toot {
|
|||
callback : EmptyCallback?
|
||||
) {
|
||||
if(App1.getAppState(activity).isBusyFav(access_info, arg_status)) {
|
||||
Utils.showToast(activity, false, R.string.wait_previous_operation)
|
||||
showToast(activity, false, R.string.wait_previous_operation)
|
||||
return
|
||||
}
|
||||
//
|
||||
|
@ -87,7 +88,9 @@ object Action_Toot {
|
|||
var target_status : TootStatus?
|
||||
if(nCrossAccountMode == CROSS_ACCOUNT_REMOTE_INSTANCE) {
|
||||
// 検索APIに他タンスのステータスのURLを投げると、自タンスのステータスを得られる
|
||||
val path = String.format(Locale.JAPAN, Column.PATH_SEARCH, Uri.encode(arg_status.url)) + "&resolve=1"
|
||||
val status_url = arg_status.url
|
||||
if( status_url?.isEmpty()!=false) return TootApiResult("missing status URL")
|
||||
val path = String.format(Locale.JAPAN, Column.PATH_SEARCH, status_url.encodePercent()) + "&resolve=1"
|
||||
|
||||
result = client.request(path)
|
||||
val jsonObject = result?.jsonObject ?: return result
|
||||
|
@ -169,7 +172,7 @@ object Action_Toot {
|
|||
|
||||
}
|
||||
|
||||
else -> Utils.showToast(activity, true, result.error)
|
||||
else -> showToast(activity, true, result.error)
|
||||
}
|
||||
// 結果に関わらず、更新中状態から復帰させる
|
||||
activity.showColumnMatchAccount(access_info)
|
||||
|
@ -214,7 +217,7 @@ object Action_Toot {
|
|||
|
||||
// アカウントからステータスにブースト操作を行っているなら、何もしない
|
||||
if(App1.getAppState(activity).isBusyBoost(access_info, arg_status)) {
|
||||
Utils.showToast(activity, false, R.string.wait_previous_operation)
|
||||
showToast(activity, false, R.string.wait_previous_operation)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -223,7 +226,7 @@ object Action_Toot {
|
|||
// if(arg_status.reblogged) {
|
||||
// if(App1.getAppState(activity).isBusyFav(access_info, arg_status) || arg_status.favourited) {
|
||||
// // FAVがついているか、FAV操作中はBoostを外せない
|
||||
// Utils.showToast(activity, false, R.string.cant_remove_boost_while_favourited)
|
||||
// showToast(activity, false, R.string.cant_remove_boost_while_favourited)
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
|
@ -264,8 +267,10 @@ object Action_Toot {
|
|||
|
||||
var target_status : TootStatus?
|
||||
if(nCrossAccountMode == CROSS_ACCOUNT_REMOTE_INSTANCE) {
|
||||
val status_url = arg_status.url
|
||||
if( status_url?.isEmpty()!=false) return TootApiResult("missing status URL")
|
||||
// 検索APIに他タンスのステータスのURLを投げると、自タンスのステータスを得られる
|
||||
val path = String.format(Locale.JAPAN, Column.PATH_SEARCH, Uri.encode(arg_status.url)) + "&resolve=1"
|
||||
val path = String.format(Locale.JAPAN, Column.PATH_SEARCH, status_url.encodePercent() ) + "&resolve=1"
|
||||
|
||||
result = client.request(path)
|
||||
val jsonObject = result?.jsonObject ?: return result
|
||||
|
@ -346,7 +351,7 @@ object Action_Toot {
|
|||
if(callback != null) callback()
|
||||
}
|
||||
|
||||
else -> Utils.showToast(activity, true, result.error)
|
||||
else -> showToast(activity, true, result.error)
|
||||
}
|
||||
|
||||
// 結果に関わらず、更新中状態から復帰させる
|
||||
|
@ -374,12 +379,12 @@ object Action_Toot {
|
|||
if(result == null) return // cancelled.
|
||||
|
||||
if(result.jsonObject != null) {
|
||||
Utils.showToast(activity, false, R.string.delete_succeeded)
|
||||
showToast(activity, false, R.string.delete_succeeded)
|
||||
for(column in App1.getAppState(activity).column_list) {
|
||||
column.removeStatus(access_info, status_id)
|
||||
}
|
||||
} else {
|
||||
Utils.showToast(activity, false, result.error)
|
||||
showToast(activity, false, result.error)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -554,7 +559,7 @@ object Action_Toot {
|
|||
}
|
||||
} else {
|
||||
// 検索APIに他タンスのステータスのURLを投げると、自タンスのステータスを得られる
|
||||
val path = String.format(Locale.JAPAN, Column.PATH_SEARCH, Uri.encode(remote_status_url)) + "&resolve=1"
|
||||
val path = String.format(Locale.JAPAN, Column.PATH_SEARCH, remote_status_url.encodePercent()) + "&resolve=1"
|
||||
result = client.request(path)
|
||||
val jsonObject = result?.jsonObject
|
||||
if(jsonObject != null) {
|
||||
|
@ -578,7 +583,7 @@ object Action_Toot {
|
|||
if(local_status_id != - 1L) {
|
||||
conversationLocal(activity, pos, access_info, local_status_id)
|
||||
} else {
|
||||
Utils.showToast(activity, true, result.error)
|
||||
showToast(activity, true, result.error)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -636,7 +641,7 @@ object Action_Toot {
|
|||
}
|
||||
}
|
||||
}
|
||||
else -> Utils.showToast(activity, true, result.error)
|
||||
else -> showToast(activity, true, result.error)
|
||||
}
|
||||
|
||||
// 結果に関わらず、更新中状態から復帰させる
|
||||
|
@ -691,7 +696,7 @@ object Action_Toot {
|
|||
internal var local_status : TootStatus? = null
|
||||
override fun background(client : TootApiClient) : TootApiResult? {
|
||||
// 検索APIに他タンスのステータスのURLを投げると、自タンスのステータスを得られる
|
||||
val path = String.format(Locale.JAPAN, Column.PATH_SEARCH, Uri.encode(remote_status_url)) + "&resolve=1"
|
||||
val path = String.format(Locale.JAPAN, Column.PATH_SEARCH,remote_status_url.encodePercent()) + "&resolve=1"
|
||||
|
||||
val result = client.request(path)
|
||||
val jsonObject = result?.jsonObject
|
||||
|
@ -715,7 +720,7 @@ object Action_Toot {
|
|||
if(ls != null) {
|
||||
reply(activity, access_info, ls)
|
||||
} else {
|
||||
Utils.showToast(activity, true, result.error)
|
||||
showToast(activity, true, result.error)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -759,9 +764,9 @@ object Action_Toot {
|
|||
}
|
||||
}
|
||||
}
|
||||
Utils.showToast(activity, true, if(bMute) R.string.mute_succeeded else R.string.unmute_succeeded)
|
||||
showToast(activity, true, if(bMute) R.string.mute_succeeded else R.string.unmute_succeeded)
|
||||
} else {
|
||||
Utils.showToast(activity, true, result.error)
|
||||
showToast(activity, true, result.error)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
package jp.juggler.subwaytooter.action
|
||||
|
||||
import android.net.Uri
|
||||
|
||||
import org.json.JSONObject
|
||||
|
||||
import java.util.Locale
|
||||
|
@ -25,8 +23,7 @@ import jp.juggler.subwaytooter.dialog.ReportForm
|
|||
import jp.juggler.subwaytooter.table.AcctColor
|
||||
import jp.juggler.subwaytooter.table.SavedAccount
|
||||
import jp.juggler.subwaytooter.table.UserRelation
|
||||
import jp.juggler.subwaytooter.util.TootApiResultCallback
|
||||
import jp.juggler.subwaytooter.util.Utils
|
||||
import jp.juggler.subwaytooter.util.*
|
||||
import okhttp3.Request
|
||||
import okhttp3.RequestBody
|
||||
|
||||
|
@ -42,7 +39,7 @@ object Action_User {
|
|||
) {
|
||||
|
||||
if(access_info.isMe(who)) {
|
||||
Utils.showToast(activity, false, R.string.it_is_you)
|
||||
showToast(activity, false, R.string.it_is_you)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -75,7 +72,7 @@ object Action_User {
|
|||
if(relation != null) {
|
||||
// 未確認だが、自分をミュートしようとするとリクエストは成功するがレスポンス中のmutingはfalseになるはず
|
||||
if(bMute && ! relation.muting) {
|
||||
Utils.showToast(activity, false, R.string.not_muted)
|
||||
showToast(activity, false, R.string.not_muted)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -93,10 +90,10 @@ object Action_User {
|
|||
}
|
||||
}
|
||||
|
||||
Utils.showToast(activity, false, if(relation.muting) R.string.mute_succeeded else R.string.unmute_succeeded)
|
||||
showToast(activity, false, if(relation.muting) R.string.mute_succeeded else R.string.unmute_succeeded)
|
||||
|
||||
} else {
|
||||
Utils.showToast(activity, false, result.error)
|
||||
showToast(activity, false, result.error)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -108,7 +105,7 @@ object Action_User {
|
|||
activity : ActMain, access_info : SavedAccount, who : TootAccount, bBlock : Boolean
|
||||
) {
|
||||
if(access_info.isMe(who)) {
|
||||
Utils.showToast(activity, false, R.string.it_is_you)
|
||||
showToast(activity, false, R.string.it_is_you)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -142,7 +139,7 @@ object Action_User {
|
|||
|
||||
// 自分をブロックしようとすると、blocking==falseで帰ってくる
|
||||
if(bBlock && ! relation.blocking) {
|
||||
Utils.showToast(activity, false, R.string.not_blocked)
|
||||
showToast(activity, false, R.string.not_blocked)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -160,10 +157,10 @@ object Action_User {
|
|||
}
|
||||
}
|
||||
|
||||
Utils.showToast(activity, false, if(relation.blocking) R.string.block_succeeded else R.string.unblock_succeeded)
|
||||
showToast(activity, false, if(relation.blocking) R.string.block_succeeded else R.string.unblock_succeeded)
|
||||
|
||||
} else {
|
||||
Utils.showToast(activity, false, result.error)
|
||||
showToast(activity, false, result.error)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -196,7 +193,8 @@ object Action_User {
|
|||
|
||||
internal var who_local : TootAccount? = null
|
||||
override fun background(client : TootApiClient) : TootApiResult? {
|
||||
val path = String.format(Locale.JAPAN, Column.PATH_SEARCH, Uri.encode(who_url)) + "&resolve=1"
|
||||
|
||||
val path = String.format(Locale.JAPAN, Column.PATH_SEARCH, who_url.encodePercent()) + "&resolve=1"
|
||||
val result = client.request(path)
|
||||
val jsonObject = result?.jsonObject
|
||||
|
||||
|
@ -219,7 +217,7 @@ object Action_User {
|
|||
if(wl != null) {
|
||||
activity.addColumn(pos, access_info, Column.TYPE_PROFILE, wl.id)
|
||||
} else {
|
||||
Utils.showToast(activity, true, result.error)
|
||||
showToast(activity, true, result.error)
|
||||
|
||||
// 仕方ないのでchrome tab で開く
|
||||
App1.openCustomTab(activity, who_url)
|
||||
|
@ -336,14 +334,14 @@ object Action_User {
|
|||
onReportComplete : TootApiResultCallback
|
||||
) {
|
||||
if(access_info.isMe(who)) {
|
||||
Utils.showToast(activity, false, R.string.it_is_you)
|
||||
showToast(activity, false, R.string.it_is_you)
|
||||
return
|
||||
}
|
||||
|
||||
TootTaskRunner(activity).run(access_info, object : TootTask {
|
||||
override fun background(client : TootApiClient) : TootApiResult? {
|
||||
val sb = ("account_id=" + who.id.toString()
|
||||
+ "&comment=" + Uri.encode(comment)
|
||||
+ "&comment=" + comment.encodePercent()
|
||||
+ "&status_ids[]=" + status.id.toString())
|
||||
|
||||
val request_builder = Request.Builder().post(
|
||||
|
@ -360,9 +358,9 @@ object Action_User {
|
|||
if(result.jsonObject != null) {
|
||||
onReportComplete(result)
|
||||
|
||||
Utils.showToast(activity, false, R.string.report_completed)
|
||||
showToast(activity, false, R.string.report_completed)
|
||||
} else {
|
||||
Utils.showToast(activity, true, result.error)
|
||||
showToast(activity, true, result.error)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -373,7 +371,7 @@ object Action_User {
|
|||
activity : ActMain, access_info : SavedAccount, who : TootAccount, bShow : Boolean
|
||||
) {
|
||||
if(access_info.isMe(who)) {
|
||||
Utils.showToast(activity, false, R.string.it_is_you)
|
||||
showToast(activity, false, R.string.it_is_you)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -386,7 +384,7 @@ object Action_User {
|
|||
try {
|
||||
content.put("reblogs", bShow)
|
||||
} catch(ex : Throwable) {
|
||||
return TootApiResult(Utils.formatError(ex, "json encoding error"))
|
||||
return TootApiResult(ex.withCaption("json encoding error"))
|
||||
}
|
||||
|
||||
val request_builder = Request.Builder().post(
|
||||
|
@ -408,9 +406,9 @@ object Action_User {
|
|||
|
||||
if(relation != null) {
|
||||
saveUserRelation(access_info, relation)
|
||||
Utils.showToast(activity, true, R.string.operation_succeeded)
|
||||
showToast(activity, true, R.string.operation_succeeded)
|
||||
} else {
|
||||
Utils.showToast(activity, true, result.error)
|
||||
showToast(activity, true, result.error)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -2,7 +2,6 @@ package jp.juggler.subwaytooter.api
|
|||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.net.Uri
|
||||
|
||||
import org.json.JSONException
|
||||
import org.json.JSONObject
|
||||
|
@ -66,10 +65,9 @@ class TootApiClient(
|
|||
private val reStartJsonObject = Pattern.compile("\\A\\s*\\{")
|
||||
private val reWhiteSpace = Pattern.compile("\\s+")
|
||||
|
||||
private val mspTokenUrl = "http://mastodonsearch.jp/api/v1.0.1/utoken"
|
||||
private val mspSearchUrl = "http://mastodonsearch.jp/api/v1.0.1/cross"
|
||||
private val mspApiKey = "e53de7f66130208f62d1808672bf6320523dcd0873dc69bc"
|
||||
|
||||
private const val mspTokenUrl = "http://mastodonsearch.jp/api/v1.0.1/utoken"
|
||||
private const val mspSearchUrl = "http://mastodonsearch.jp/api/v1.0.1/cross"
|
||||
private const val mspApiKey = "e53de7f66130208f62d1808672bf6320523dcd0873dc69bc"
|
||||
|
||||
fun getMspMaxId(array : JSONArray, max_id : String) : String {
|
||||
// max_id の更新
|
||||
|
@ -92,7 +90,7 @@ class TootApiClient(
|
|||
// returns the number for "from" parameter of next page.
|
||||
// returns "" if no more next page.
|
||||
fun getTootsearchMaxId(root : JSONObject, old : String) : String {
|
||||
val old_from = Utils.parse_int(old, 0)
|
||||
val old_from = old.optInt() ?: 0
|
||||
val hits2 = getTootsearchHits(root)
|
||||
if(hits2 != null) {
|
||||
val size = hits2.length()
|
||||
|
@ -101,9 +99,7 @@ class TootApiClient(
|
|||
return ""
|
||||
}
|
||||
|
||||
val DEFAULT_JSON_ERROR_PARSER = { json : JSONObject ->
|
||||
Utils.optStringX(json, "error")
|
||||
}
|
||||
val DEFAULT_JSON_ERROR_PARSER = { json : JSONObject -> json.parseString("error") }
|
||||
|
||||
internal fun simplifyErrorHtml(
|
||||
response : Response,
|
||||
|
@ -113,8 +109,7 @@ class TootApiClient(
|
|||
|
||||
// JSONObjectとして解釈できるならエラーメッセージを検出する
|
||||
try {
|
||||
val data = JSONObject(sv)
|
||||
val error_message = jsonErrorParser(data)
|
||||
val error_message = jsonErrorParser(sv.toJsonObject())
|
||||
if(error_message?.isNotEmpty() == true) {
|
||||
return error_message
|
||||
}
|
||||
|
@ -221,7 +216,12 @@ class TootApiClient(
|
|||
null == result.error
|
||||
|
||||
} catch(ex : Throwable) {
|
||||
result.setError(result.caption + ": " + Utils.formatError(ex, context.resources, R.string.network_error))
|
||||
result.setError(
|
||||
"${result.caption}: ${ex.withCaption(
|
||||
context.resources,
|
||||
R.string.network_error
|
||||
)}"
|
||||
)
|
||||
false
|
||||
}
|
||||
}
|
||||
|
@ -240,7 +240,13 @@ class TootApiClient(
|
|||
|
||||
val request = response.request()
|
||||
if(request != null) {
|
||||
publishApiProgress(context.getString(R.string.reading_api, request.method(), progressPath ?: result.caption))
|
||||
publishApiProgress(
|
||||
context.getString(
|
||||
R.string.reading_api,
|
||||
request.method(),
|
||||
progressPath ?: result.caption
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
val bodyString = response.body()?.string()
|
||||
|
@ -281,7 +287,8 @@ class TootApiClient(
|
|||
|
||||
} catch(ex : Throwable) {
|
||||
log.trace(ex)
|
||||
result.error = formatResponse(response, result.caption, result.bodyString ?: NO_INFORMATION)
|
||||
result.error =
|
||||
formatResponse(response, result.caption, result.bodyString ?: NO_INFORMATION)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
@ -300,10 +307,10 @@ class TootApiClient(
|
|||
?: return if(isApiCancelled) null else result
|
||||
|
||||
if(reStartJsonArray.matcher(bodyString).find()) {
|
||||
result.data = JSONArray(bodyString)
|
||||
result.data = bodyString.toJsonArray()
|
||||
|
||||
} else if(reStartJsonObject.matcher(bodyString).find()) {
|
||||
val json = JSONObject(bodyString)
|
||||
val json = bodyString.toJsonObject()
|
||||
val error_message = jsonErrorParser(json)
|
||||
if(error_message != null) {
|
||||
result.error = error_message
|
||||
|
@ -316,7 +323,8 @@ class TootApiClient(
|
|||
|
||||
} catch(ex : Throwable) {
|
||||
log.trace(ex)
|
||||
result.error = formatResponse(response, result.caption, result.bodyString ?: NO_INFORMATION)
|
||||
result.error =
|
||||
formatResponse(response, result.caption, result.bodyString ?: NO_INFORMATION)
|
||||
}
|
||||
return result
|
||||
|
||||
|
@ -324,7 +332,10 @@ class TootApiClient(
|
|||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
fun request(path : String, request_builder : Request.Builder = Request.Builder()) : TootApiResult? {
|
||||
fun request(
|
||||
path : String,
|
||||
request_builder : Request.Builder = Request.Builder()
|
||||
) : TootApiResult? {
|
||||
val result = TootApiResult.makeWithCaption(instance)
|
||||
if(result.error != null) return result
|
||||
|
||||
|
@ -332,19 +343,19 @@ class TootApiClient(
|
|||
|
||||
try {
|
||||
if(! sendRequest(result) {
|
||||
|
||||
log.d("request: $path")
|
||||
|
||||
request_builder.url("https://" + instance + path)
|
||||
|
||||
val access_token = account.getAccessToken()
|
||||
if(access_token?.isNotEmpty() == true) {
|
||||
request_builder.header("Authorization", "Bearer " + access_token)
|
||||
}
|
||||
|
||||
request_builder.build()
|
||||
|
||||
}) return result
|
||||
|
||||
log.d("request: $path")
|
||||
|
||||
request_builder.url("https://" + instance + path)
|
||||
|
||||
val access_token = account.getAccessToken()
|
||||
if(access_token?.isNotEmpty() == true) {
|
||||
request_builder.header("Authorization", "Bearer " + access_token)
|
||||
}
|
||||
|
||||
request_builder.build()
|
||||
|
||||
}) return result
|
||||
|
||||
return parseJson(result)
|
||||
} finally {
|
||||
|
@ -358,8 +369,8 @@ class TootApiClient(
|
|||
val result = TootApiResult.makeWithCaption(instance)
|
||||
if(result.error != null) return result
|
||||
if(! sendRequest(result) {
|
||||
Request.Builder().url("https://$instance/api/v1/instance").build()
|
||||
}) return result
|
||||
Request.Builder().url("https://$instance/api/v1/instance").build()
|
||||
}) return result
|
||||
return parseJson(result)
|
||||
}
|
||||
|
||||
|
@ -371,14 +382,18 @@ class TootApiClient(
|
|||
|
||||
// OAuth2 クライアント登録
|
||||
if(! sendRequest(result) {
|
||||
Request.Builder()
|
||||
.url("https://$instance/api/v1/apps")
|
||||
.post(RequestBody.create(MEDIA_TYPE_FORM_URL_ENCODED, "client_name=" + Uri.encode(clientName)
|
||||
+ "&redirect_uris=" + Uri.encode(REDIRECT_URL)
|
||||
+ "&scopes=read write follow"
|
||||
))
|
||||
.build()
|
||||
}) return result
|
||||
Request.Builder()
|
||||
.url("https://$instance/api/v1/apps")
|
||||
.post(
|
||||
RequestBody.create(
|
||||
MEDIA_TYPE_FORM_URL_ENCODED,
|
||||
"client_name=" + clientName.encodePercent()
|
||||
+ "&redirect_uris=" + REDIRECT_URL.encodePercent()
|
||||
+ "&scopes=read write follow"
|
||||
)
|
||||
)
|
||||
.build()
|
||||
}) return result
|
||||
|
||||
return parseJson(result)
|
||||
}
|
||||
|
@ -392,19 +407,30 @@ class TootApiClient(
|
|||
if(result.error != null) return result
|
||||
|
||||
if(! sendRequest(result) {
|
||||
Request.Builder()
|
||||
.url("https://$instance/oauth/token")
|
||||
.post(RequestBody.create(MEDIA_TYPE_FORM_URL_ENCODED, "grant_type=client_credentials"
|
||||
+ "&client_id=" + Uri.encode(client_info.optString("client_id"))
|
||||
+ "&client_secret=" + Uri.encode(client_info.optString("client_secret"))
|
||||
))
|
||||
.build()
|
||||
}) return result
|
||||
|
||||
val client_id = client_info.parseString("client_id")
|
||||
?: return result.setError("missing client_id")
|
||||
|
||||
val client_secret = client_info.parseString("client_secret")
|
||||
?: return result.setError("missing client_secret")
|
||||
|
||||
Request.Builder()
|
||||
.url("https://$instance/oauth/token")
|
||||
.post(
|
||||
RequestBody.create(
|
||||
MEDIA_TYPE_FORM_URL_ENCODED,
|
||||
"grant_type=client_credentials"
|
||||
+ "&client_id=" + client_id.encodePercent()
|
||||
+ "&client_secret=" + client_secret.encodePercent()
|
||||
)
|
||||
)
|
||||
.build()
|
||||
}) return result
|
||||
|
||||
val r2 = parseJson(result)
|
||||
val jsonObject = r2?.jsonObject ?: return r2
|
||||
|
||||
val sv = Utils.optStringX(jsonObject, "access_token")
|
||||
val sv = jsonObject.parseString("access_token")
|
||||
if(sv?.isNotEmpty() == true) {
|
||||
result.data = sv
|
||||
} else {
|
||||
|
@ -420,24 +446,24 @@ class TootApiClient(
|
|||
if(result.error != null) return result
|
||||
|
||||
if(! sendRequest(result) {
|
||||
Request.Builder()
|
||||
.url("https://$instance/api/v1/apps/verify_credentials")
|
||||
.header("Authorization", "Bearer $client_credential")
|
||||
.build()
|
||||
}) return result
|
||||
Request.Builder()
|
||||
.url("https://$instance/api/v1/apps/verify_credentials")
|
||||
.header("Authorization", "Bearer $client_credential")
|
||||
.build()
|
||||
}) return result
|
||||
|
||||
return parseJson(result)
|
||||
}
|
||||
|
||||
internal fun prepareBrowserUrl(client_info : JSONObject) : String {
|
||||
// 認証ページURLを作る
|
||||
internal fun prepareBrowserUrl(client_info : JSONObject) : String? {
|
||||
val account = this.account
|
||||
|
||||
// 認証ページURLを作る
|
||||
val client_id = client_info.parseString("client_id") ?: return null
|
||||
|
||||
return ("https://" + instance + "/oauth/authorize"
|
||||
+ "?client_id=" + Uri.encode(Utils.optStringX(client_info, "client_id"))
|
||||
+ "?client_id=" + client_id.encodePercent()
|
||||
+ "&response_type=code"
|
||||
+ "&redirect_uri=" + Uri.encode(REDIRECT_URL)
|
||||
+ "&redirect_uri=" + REDIRECT_URL.encodePercent()
|
||||
+ "&scope=read+write+follow"
|
||||
+ "&scopes=read+write+follow"
|
||||
+ "&state=" + (if(account != null) "db:" + account.db_id else "host:" + instance)
|
||||
|
@ -458,7 +484,7 @@ class TootApiClient(
|
|||
val client_info = ClientInfo.load(instance, client_name)
|
||||
if(client_info != null) {
|
||||
|
||||
var client_credential = Utils.optStringX(client_info, KEY_CLIENT_CREDENTIAL)
|
||||
var client_credential = client_info.parseString(KEY_CLIENT_CREDENTIAL)
|
||||
|
||||
// client_credential をまだ取得していないなら取得する
|
||||
if(client_credential?.isEmpty() != false) {
|
||||
|
@ -501,30 +527,36 @@ class TootApiClient(
|
|||
|
||||
val instance = result.caption // same to instance
|
||||
val client_name = if(clientNameArg.isNotEmpty()) clientNameArg else DEFAULT_CLIENT_NAME
|
||||
val client_info = ClientInfo.load(instance, client_name) ?: return result.setError("missing client id")
|
||||
val client_info =
|
||||
ClientInfo.load(instance, client_name) ?: return result.setError("missing client id")
|
||||
|
||||
if(! sendRequest(result) {
|
||||
|
||||
val post_content = ("grant_type=authorization_code"
|
||||
+ "&code=" + Uri.encode(code)
|
||||
+ "&client_id=" + Uri.encode(Utils.optStringX(client_info, "client_id"))
|
||||
+ "&redirect_uri=" + Uri.encode(REDIRECT_URL)
|
||||
+ "&client_secret=" + Uri.encode(Utils.optStringX(client_info, "client_secret"))
|
||||
+ "&scope=read+write+follow"
|
||||
+ "&scopes=read+write+follow")
|
||||
|
||||
Request.Builder()
|
||||
.url("https://$instance/oauth/token")
|
||||
.post(RequestBody.create(MEDIA_TYPE_FORM_URL_ENCODED, post_content))
|
||||
.build()
|
||||
|
||||
}) return result
|
||||
|
||||
val client_id = client_info.parseString("client_id")
|
||||
val client_secret = client_info.parseString("client_secret")
|
||||
if(client_id == null) return result.setError("missing client_id ")
|
||||
if(client_secret == null) return result.setError("missing client_secret")
|
||||
|
||||
val post_content = ("grant_type=authorization_code"
|
||||
+ "&code=" + code.encodePercent()
|
||||
+ "&client_id=" + client_id.encodePercent()
|
||||
+ "&redirect_uri=" + REDIRECT_URL.encodePercent()
|
||||
+ "&client_secret=" + client_secret.encodePercent()
|
||||
+ "&scope=read+write+follow"
|
||||
+ "&scopes=read+write+follow")
|
||||
|
||||
Request.Builder()
|
||||
.url("https://$instance/oauth/token")
|
||||
.post(RequestBody.create(MEDIA_TYPE_FORM_URL_ENCODED, post_content))
|
||||
.build()
|
||||
|
||||
}) return result
|
||||
|
||||
val r2 = parseJson(result)
|
||||
val token_info = r2?.jsonObject ?: return r2
|
||||
|
||||
// {"access_token":"******","token_type":"bearer","scope":"read","created_at":1492334641}
|
||||
val access_token = Utils.optStringX(token_info, "access_token")
|
||||
val access_token = token_info.parseString("access_token")
|
||||
if(access_token?.isEmpty() != false) {
|
||||
return result.setError("missing access_token in the response.")
|
||||
}
|
||||
|
@ -543,11 +575,11 @@ class TootApiClient(
|
|||
|
||||
// 認証されたアカウントのユーザ情報を取得する
|
||||
if(! sendRequest(result) {
|
||||
Request.Builder()
|
||||
.url("https://$instance/api/v1/accounts/verify_credentials")
|
||||
.header("Authorization", "Bearer $access_token")
|
||||
.build()
|
||||
}) return result
|
||||
Request.Builder()
|
||||
.url("https://$instance/api/v1/accounts/verify_credentials")
|
||||
.header("Authorization", "Bearer $access_token")
|
||||
.build()
|
||||
}) return result
|
||||
|
||||
val r2 = parseJson(result)
|
||||
if(r2?.jsonObject != null) {
|
||||
|
@ -563,13 +595,13 @@ class TootApiClient(
|
|||
fun searchMsp(query : String, max_id : String) : TootApiResult? {
|
||||
|
||||
// ユーザトークンを読む
|
||||
var user_token :String? = Pref.spMspUserToken(pref)
|
||||
var user_token : String? = Pref.spMspUserToken(pref)
|
||||
|
||||
for(nTry in 0 until 3) {
|
||||
if(callback.isApiCancelled) return null
|
||||
|
||||
// ユーザトークンがなければ取得する
|
||||
if( user_token == null || user_token.isEmpty() ){
|
||||
if(user_token == null || user_token.isEmpty()) {
|
||||
|
||||
callback.publishApiProgress("get MSP user token...")
|
||||
|
||||
|
@ -577,17 +609,17 @@ class TootApiClient(
|
|||
if(result.error != null) return result
|
||||
|
||||
if(! sendRequest(result) {
|
||||
Request.Builder()
|
||||
.url(mspTokenUrl + "?apikey=" + Uri.encode(mspApiKey))
|
||||
.build()
|
||||
}) return result
|
||||
Request.Builder()
|
||||
.url(mspTokenUrl + "?apikey=" + mspApiKey.encodePercent())
|
||||
.build()
|
||||
}) return result
|
||||
|
||||
val r2 = parseJson(result) { json ->
|
||||
val error = Utils.optStringX(json, "error")
|
||||
val error = json.parseString("error")
|
||||
if(error == null) {
|
||||
null
|
||||
} else {
|
||||
val type = Utils.optStringX(json, "type")
|
||||
val type = json.parseString("type")
|
||||
"error: $type $error"
|
||||
}
|
||||
}
|
||||
|
@ -595,8 +627,8 @@ class TootApiClient(
|
|||
user_token = jsonObject.optJSONObject("result")?.optString("token")
|
||||
if(user_token?.isEmpty() != false) {
|
||||
return result.setError("Can't get MSP user token. response=${result.bodyString}")
|
||||
}else{
|
||||
pref.edit().put( Pref.spMspUserToken,user_token).apply()
|
||||
} else {
|
||||
pref.edit().put(Pref.spMspUserToken, user_token).apply()
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -606,18 +638,18 @@ class TootApiClient(
|
|||
if(result.error != null) return result
|
||||
|
||||
if(! sendRequest(result) {
|
||||
val url = (mspSearchUrl
|
||||
+ "?apikey=" + Uri.encode(mspApiKey)
|
||||
+ "&utoken=" + Uri.encode(user_token)
|
||||
+ "&q=" + Uri.encode(query)
|
||||
+ "&max=" + Uri.encode(max_id))
|
||||
|
||||
Request.Builder().url(url).build()
|
||||
}) return result
|
||||
val url = (mspSearchUrl
|
||||
+ "?apikey=" + mspApiKey.encodePercent()
|
||||
+ "&utoken=" + user_token.encodePercent()
|
||||
+ "&q=" + query.encodePercent()
|
||||
+ "&max=" + max_id.encodePercent())
|
||||
|
||||
Request.Builder().url(url).build()
|
||||
}) return result
|
||||
|
||||
var isUserTokenError = false
|
||||
val r2 = parseJson(result) { json ->
|
||||
val error = Utils.optStringX(json, "error")
|
||||
val error = json.parseString("error")
|
||||
if(error == null) {
|
||||
null
|
||||
} else {
|
||||
|
@ -627,7 +659,7 @@ class TootApiClient(
|
|||
isUserTokenError = true
|
||||
}
|
||||
|
||||
val type = Utils.optStringX(json, "type")
|
||||
val type = json.parseString("type")
|
||||
"API returns error: $type $error"
|
||||
}
|
||||
}
|
||||
|
@ -645,16 +677,16 @@ class TootApiClient(
|
|||
if(result.error != null) return result
|
||||
|
||||
if(! sendRequest(result) {
|
||||
val url = ("https://tootsearch.chotto.moe/api/v1/search"
|
||||
+ "?sort=" + Uri.encode("created_at:desc")
|
||||
+ "&from=" + max_id
|
||||
+ "&q=" + Uri.encode(query))
|
||||
|
||||
Request.Builder()
|
||||
.url(url)
|
||||
.build()
|
||||
|
||||
}) return result
|
||||
val url = ("https://tootsearch.chotto.moe/api/v1/search"
|
||||
+ "?sort=" + "created_at:desc".encodePercent()
|
||||
+ "&from=" + max_id.encodePercent()
|
||||
+ "&q=" + query.encodePercent())
|
||||
|
||||
Request.Builder()
|
||||
.url(url)
|
||||
.build()
|
||||
|
||||
}) return result
|
||||
|
||||
return parseJson(result)
|
||||
}
|
||||
|
@ -668,13 +700,13 @@ class TootApiClient(
|
|||
if(result.error != null) return result
|
||||
|
||||
if(! sendRequest(result, progressPath = url) {
|
||||
Request.Builder().url(url).build()
|
||||
}) return result
|
||||
Request.Builder().url(url).build()
|
||||
}) return result
|
||||
return parseString(result)
|
||||
|
||||
}
|
||||
|
||||
fun webSocket(path : String, ws_listener : WebSocketListener) : TootApiResult? {
|
||||
fun webSocket(path : String, ws_listener : WebSocketListener) : TootApiResult? {
|
||||
val result = TootApiResult.makeWithCaption(instance)
|
||||
if(result.error != null) return result
|
||||
val account = this.account ?: return TootApiResult("account is null")
|
||||
|
@ -682,11 +714,11 @@ class TootApiClient(
|
|||
var url = "wss://$instance$path"
|
||||
|
||||
val request_builder = Request.Builder()
|
||||
|
||||
|
||||
val access_token = account.getAccessToken()
|
||||
if(access_token?.isNotEmpty() == true) {
|
||||
val delm = if(- 1 != url.indexOf('?')) '&' else '?'
|
||||
url = url + delm + "access_token=" + Uri.encode(access_token)
|
||||
url = url + delm + "access_token=" + access_token.encodePercent()
|
||||
}
|
||||
|
||||
val request = request_builder.url(url).build()
|
||||
|
@ -699,7 +731,8 @@ class TootApiClient(
|
|||
result.data = ws
|
||||
} catch(ex : Throwable) {
|
||||
log.trace(ex)
|
||||
result.error = result.caption + ": " + Utils.formatError(ex, context.resources, R.string.network_error)
|
||||
result.error =
|
||||
"${result.caption}: ${ex.withCaption(context.resources, R.string.network_error)}"
|
||||
}
|
||||
return result
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package jp.juggler.subwaytooter.api.entity
|
||||
|
||||
import org.json.JSONObject
|
||||
import jp.juggler.subwaytooter.util.Utils
|
||||
import jp.juggler.subwaytooter.util.parseString
|
||||
|
||||
class CustomEmoji(
|
||||
val shortcode : String, // shortcode (コロンを含まない)
|
||||
|
@ -12,7 +12,7 @@ class CustomEmoji(
|
|||
constructor(src : JSONObject) : this(
|
||||
shortcode = src.notEmptyOrThrow("shortcode"),
|
||||
url = src.notEmptyOrThrow("url"),
|
||||
static_url = Utils.optStringX(src, "static_url")
|
||||
static_url = src.parseString("static_url")
|
||||
)
|
||||
|
||||
override val mapKey : String
|
||||
|
|
|
@ -2,7 +2,7 @@ package jp.juggler.subwaytooter.api.entity
|
|||
|
||||
import jp.juggler.subwaytooter.api.TootParser
|
||||
import jp.juggler.subwaytooter.util.LogCategory
|
||||
import jp.juggler.subwaytooter.util.Utils
|
||||
import jp.juggler.subwaytooter.util.parseString
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONException
|
||||
import org.json.JSONObject
|
||||
|
@ -15,7 +15,11 @@ object EntityUtil {
|
|||
////////////////////////////////////////
|
||||
|
||||
// JSONObjectを渡してEntityを生成するコードのnullチェックと例外補足
|
||||
inline fun <reified T> parseItem(factory : (src : JSONObject) -> T, src : JSONObject?, log : LogCategory = EntityUtil.log) : T? {
|
||||
inline fun <reified T> parseItem(
|
||||
factory : (src : JSONObject) -> T,
|
||||
src : JSONObject?,
|
||||
log : LogCategory = EntityUtil.log
|
||||
) : T? {
|
||||
if(src == null) return null
|
||||
return try {
|
||||
factory(src)
|
||||
|
@ -26,7 +30,11 @@ inline fun <reified T> parseItem(factory : (src : JSONObject) -> T, src : JSONOb
|
|||
}
|
||||
}
|
||||
|
||||
inline fun <reified T> parseList(factory : (src : JSONObject) -> T, src : JSONArray?, log : LogCategory = EntityUtil.log) : ArrayList<T> {
|
||||
inline fun <reified T> parseList(
|
||||
factory : (src : JSONObject) -> T,
|
||||
src : JSONArray?,
|
||||
log : LogCategory = EntityUtil.log
|
||||
) : ArrayList<T> {
|
||||
val dst = ArrayList<T>()
|
||||
if(src != null) {
|
||||
val src_length = src.length()
|
||||
|
@ -41,7 +49,11 @@ inline fun <reified T> parseList(factory : (src : JSONObject) -> T, src : JSONAr
|
|||
return dst
|
||||
}
|
||||
|
||||
inline fun <reified T> parseListOrNull(factory : (src : JSONObject) -> T, src : JSONArray?, log : LogCategory = EntityUtil.log) : ArrayList<T>? {
|
||||
inline fun <reified T> parseListOrNull(
|
||||
factory : (src : JSONObject) -> T,
|
||||
src : JSONArray?,
|
||||
log : LogCategory = EntityUtil.log
|
||||
) : ArrayList<T>? {
|
||||
if(src != null) {
|
||||
val src_length = src.length()
|
||||
if(src_length > 0) {
|
||||
|
@ -68,7 +80,7 @@ inline fun <reified K, reified V> parseMap(
|
|||
val size = src.length()
|
||||
for(i in 0 until size) {
|
||||
val item = parseItem(factory, src.optJSONObject(i), log)
|
||||
if(item != null) dst.put(item.mapKey, item)
|
||||
if(item != null) dst[item.mapKey] = item
|
||||
}
|
||||
}
|
||||
return dst
|
||||
|
@ -85,7 +97,7 @@ inline fun <reified K, reified V> parseMapOrNull(
|
|||
val dst = HashMap<K, V>()
|
||||
for(i in 0 until size) {
|
||||
val item = parseItem(factory, src.optJSONObject(i), log)
|
||||
if(item != null) dst.put(item.mapKey, item)
|
||||
if(item != null) dst[item.mapKey] = item
|
||||
}
|
||||
if(dst.isNotEmpty()) return dst
|
||||
}
|
||||
|
@ -95,7 +107,12 @@ inline fun <reified K, reified V> parseMapOrNull(
|
|||
|
||||
////////////////////////////////////////
|
||||
|
||||
inline fun <reified T> parseItem(factory : (parser : TootParser, src : JSONObject) -> T, parser : TootParser, src : JSONObject?, log : LogCategory = EntityUtil.log) : T? {
|
||||
inline fun <reified T> parseItem(
|
||||
factory : (parser : TootParser, src : JSONObject) -> T,
|
||||
parser : TootParser,
|
||||
src : JSONObject?,
|
||||
log : LogCategory = EntityUtil.log
|
||||
) : T? {
|
||||
if(src == null) return null
|
||||
return try {
|
||||
factory(parser, src)
|
||||
|
@ -106,7 +123,12 @@ inline fun <reified T> parseItem(factory : (parser : TootParser, src : JSONObjec
|
|||
}
|
||||
}
|
||||
|
||||
inline fun <reified T> parseList(factory : (parser : TootParser, src : JSONObject) -> T, parser : TootParser, src : JSONArray?, log : LogCategory = EntityUtil.log) : ArrayList<T> {
|
||||
inline fun <reified T> parseList(
|
||||
factory : (parser : TootParser, src : JSONObject) -> T,
|
||||
parser : TootParser,
|
||||
src : JSONArray?,
|
||||
log : LogCategory = EntityUtil.log
|
||||
) : ArrayList<T> {
|
||||
val dst = ArrayList<T>()
|
||||
if(src != null) {
|
||||
val src_length = src.length()
|
||||
|
@ -122,7 +144,12 @@ inline fun <reified T> parseList(factory : (parser : TootParser, src : JSONObjec
|
|||
}
|
||||
|
||||
@Suppress("unused")
|
||||
inline fun <reified T> parseListOrNull(factory : (parser : TootParser, src : JSONObject) -> T, parser : TootParser, src : JSONArray?, log : LogCategory = EntityUtil.log) : ArrayList<T>? {
|
||||
inline fun <reified T> parseListOrNull(
|
||||
factory : (parser : TootParser, src : JSONObject) -> T,
|
||||
parser : TootParser,
|
||||
src : JSONArray?,
|
||||
log : LogCategory = EntityUtil.log
|
||||
) : ArrayList<T>? {
|
||||
if(src != null) {
|
||||
val src_length = src.length()
|
||||
if(src_length > 0) {
|
||||
|
@ -140,10 +167,10 @@ inline fun <reified T> parseListOrNull(factory : (parser : TootParser, src : JSO
|
|||
|
||||
////////////////////////////////////////
|
||||
|
||||
fun <T: TootAttachmentLike> ArrayList<T>.encodeJson() : JSONArray {
|
||||
fun <T : TootAttachmentLike> ArrayList<T>.encodeJson() : JSONArray {
|
||||
val a = JSONArray()
|
||||
for(ta in this) {
|
||||
if( ta is TootAttachment) {
|
||||
if(ta is TootAttachment) {
|
||||
try {
|
||||
a.put(ta.json)
|
||||
} catch(ex : JSONException) {
|
||||
|
@ -156,8 +183,7 @@ fun <T: TootAttachmentLike> ArrayList<T>.encodeJson() : JSONArray {
|
|||
|
||||
////////////////////////////////////////
|
||||
|
||||
fun notEmptyOrThrow(name : String, value : String?)
|
||||
= if(value?.isNotEmpty() == true) value else throw RuntimeException("$name is empty")
|
||||
fun notEmptyOrThrow(name : String, value : String?) =
|
||||
if(value?.isNotEmpty() == true) value else throw RuntimeException("$name is empty")
|
||||
|
||||
fun JSONObject.notEmptyOrThrow(name : String)
|
||||
= notEmptyOrThrow(name, Utils.optStringX(this, name))
|
||||
fun JSONObject.notEmptyOrThrow(name : String) = notEmptyOrThrow(name, this.parseString(name))
|
||||
|
|
|
@ -8,9 +8,7 @@ import java.util.ArrayList
|
|||
import java.util.regex.Pattern
|
||||
|
||||
import jp.juggler.subwaytooter.table.SavedAccount
|
||||
import jp.juggler.subwaytooter.util.DecodeOptions
|
||||
import jp.juggler.subwaytooter.util.LogCategory
|
||||
import jp.juggler.subwaytooter.util.Utils
|
||||
import jp.juggler.subwaytooter.util.*
|
||||
|
||||
@Suppress("MemberVisibilityCanPrivate")
|
||||
class NicoEnquete(
|
||||
|
@ -31,17 +29,17 @@ class NicoEnquete(
|
|||
val items : ArrayList<Spannable>?
|
||||
|
||||
// 結果の数値 // null or array of number
|
||||
val ratios : ArrayList<Float>?
|
||||
private val ratios : ArrayList<Float>?
|
||||
|
||||
// 結果の数値のテキスト // null or array of string
|
||||
val ratios_text : ArrayList<String>?
|
||||
private val ratios_text : ArrayList<String>?
|
||||
|
||||
// 以下はJSONには存在しないが内部で使う
|
||||
val time_start : Long
|
||||
val status_id : Long
|
||||
|
||||
init {
|
||||
this.type = Utils.optStringX(src, "type")
|
||||
this.type = src.parseString( "type")
|
||||
|
||||
this.question = DecodeOptions(
|
||||
short = true,
|
||||
|
@ -50,7 +48,7 @@ class NicoEnquete(
|
|||
linkTag = status,
|
||||
emojiMapCustom = status.custom_emojis,
|
||||
emojiMapProfile = status.profile_emojis
|
||||
).decodeHTML(context, access_info, Utils.optStringX(src, "question") ?: "?")
|
||||
).decodeHTML(context, access_info, src.parseString( "question") ?: "?")
|
||||
|
||||
this.items = parseChoiceList(context, status, parseStringArray(src, "items"))
|
||||
|
||||
|
@ -73,7 +71,7 @@ class NicoEnquete(
|
|||
const val TYPE_ENQUETE_RESULT = "enquete_result"
|
||||
|
||||
@Suppress("HasPlatformType")
|
||||
val reWhitespace = Pattern.compile("[\\s\\t\\x0d\\x0a]+")
|
||||
private val reWhitespace = Pattern.compile("[\\s\\t\\x0d\\x0a]+")
|
||||
|
||||
fun parse(
|
||||
context : Context,
|
||||
|
@ -89,7 +87,7 @@ class NicoEnquete(
|
|||
access_info,
|
||||
status,
|
||||
list_attachment,
|
||||
JSONObject(jsonString)
|
||||
jsonString.toJsonObject()
|
||||
)
|
||||
} catch(ex : Throwable) {
|
||||
log.trace(ex)
|
||||
|
@ -97,15 +95,10 @@ class NicoEnquete(
|
|||
}
|
||||
}
|
||||
|
||||
private fun parseStringArray(src : JSONObject, name : String) : ArrayList<String>? {
|
||||
private fun parseStringArray(src : JSONObject, name : String) :ArrayList<String>?{
|
||||
val array = src.optJSONArray(name)
|
||||
if(array != null) {
|
||||
val size = array.length()
|
||||
val dst = ArrayList<String>(size)
|
||||
for(i in 0 until size) {
|
||||
val sv = Utils.optStringX(array, i)
|
||||
if(sv != null) dst.add(sv)
|
||||
}
|
||||
val dst = array.toStringArrayList()
|
||||
if(dst.isNotEmpty()) return dst
|
||||
}
|
||||
return null
|
||||
|
@ -140,7 +133,7 @@ class NicoEnquete(
|
|||
emojiMapProfile = status.profile_emojis
|
||||
).decodeEmoji(context,
|
||||
reWhitespace
|
||||
.matcher(Utils.sanitizeBDI(stringArray[i]))
|
||||
.matcher(stringArray[i].sanitizeBDI())
|
||||
.replaceAll(" ")
|
||||
)
|
||||
)
|
||||
|
|
|
@ -2,7 +2,8 @@ package jp.juggler.subwaytooter.api.entity
|
|||
|
||||
import org.json.JSONObject
|
||||
|
||||
import jp.juggler.subwaytooter.util.Utils
|
||||
import jp.juggler.subwaytooter.util.parseLong
|
||||
import jp.juggler.subwaytooter.util.parseString
|
||||
|
||||
class NicoProfileEmoji(
|
||||
val url : String,
|
||||
|
@ -14,8 +15,8 @@ class NicoProfileEmoji(
|
|||
constructor(src : JSONObject) : this(
|
||||
url = src.notEmptyOrThrow("url"),
|
||||
shortcode = src.notEmptyOrThrow("shortcode"),
|
||||
account_url = Utils.optStringX(src, "account_url"),
|
||||
account_id = Utils.optLongX(src, "account_id", TootAccount.INVALID_ID)
|
||||
account_url = src.parseString("account_url"),
|
||||
account_id = src.parseLong("account_id") ?: TootAccount.INVALID_ID
|
||||
)
|
||||
|
||||
override val mapKey : String
|
||||
|
|
|
@ -3,8 +3,7 @@ package jp.juggler.subwaytooter.api.entity
|
|||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import android.text.Spannable
|
||||
|
||||
import jp.juggler.subwaytooter.util.DecodeOptions
|
||||
import jp.juggler.subwaytooter.util.*
|
||||
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
|
@ -12,16 +11,12 @@ import org.json.JSONObject
|
|||
import java.util.ArrayList
|
||||
import java.util.regex.Pattern
|
||||
|
||||
import jp.juggler.subwaytooter.util.LinkClickContext
|
||||
import jp.juggler.subwaytooter.util.LogCategory
|
||||
import jp.juggler.subwaytooter.util.Utils
|
||||
|
||||
open class TootAccount(
|
||||
context : Context,
|
||||
accessInfo : LinkClickContext,
|
||||
src : JSONObject,
|
||||
serviceType : ServiceType
|
||||
) :TimelineItem(){
|
||||
) : TimelineItem() {
|
||||
|
||||
//URL of the user's profile page (can be remote)
|
||||
// https://mastodon.juggler.jp/@tateisu
|
||||
|
@ -33,7 +28,7 @@ open class TootAccount(
|
|||
|
||||
// Equals username for local users, includes @domain for remote ones
|
||||
val acct : String
|
||||
|
||||
|
||||
// The username of the account /[A-Za-z0-9_]{1,30}/
|
||||
val username : String
|
||||
|
||||
|
@ -82,27 +77,27 @@ open class TootAccount(
|
|||
|
||||
val source : Source?
|
||||
|
||||
val profile_emojis : HashMap<String,NicoProfileEmoji>?
|
||||
val profile_emojis : HashMap<String, NicoProfileEmoji>?
|
||||
|
||||
val moved : TootAccount?
|
||||
|
||||
init {
|
||||
var sv : String?
|
||||
|
||||
|
||||
// 絵文字データは先に読んでおく
|
||||
this.profile_emojis = parseMapOrNull(::NicoProfileEmoji,src.optJSONArray("profile_emojis"))
|
||||
this.profile_emojis = parseMapOrNull(::NicoProfileEmoji, src.optJSONArray("profile_emojis"))
|
||||
|
||||
// 疑似アカウントにacctとusernameだけ
|
||||
this.url = Utils.optStringX(src,"url")
|
||||
this.username = src.notEmptyOrThrow( "username")
|
||||
this.url = src.parseString("url")
|
||||
this.username = src.notEmptyOrThrow("username")
|
||||
|
||||
//
|
||||
sv = Utils.optStringX(src, "display_name")
|
||||
this.display_name = if(sv?.isNotEmpty() == true) Utils.sanitizeBDI(sv) else username
|
||||
sv = src.parseString("display_name")
|
||||
this.display_name = if(sv?.isNotEmpty() == true) sv.sanitizeBDI() else username
|
||||
this.decoded_display_name = decodeDisplayName(context)
|
||||
|
||||
//
|
||||
this.note = Utils.optStringX(src, "note")
|
||||
this.note = src.parseString("note")
|
||||
this.decoded_note = DecodeOptions(
|
||||
short = true,
|
||||
decodeEmoji = true,
|
||||
|
@ -110,7 +105,8 @@ open class TootAccount(
|
|||
).decodeHTML(context, accessInfo, this.note)
|
||||
|
||||
this.source = parseSource(src.optJSONObject("source"))
|
||||
this.moved = src.optJSONObject("moved")?.let { TootAccount(context, accessInfo, it, serviceType) }
|
||||
this.moved =
|
||||
src.optJSONObject("moved")?.let { TootAccount(context, accessInfo, it, serviceType) }
|
||||
this.locked = src.optBoolean("locked")
|
||||
|
||||
when(serviceType) {
|
||||
|
@ -118,53 +114,53 @@ open class TootAccount(
|
|||
|
||||
val hostAccess = accessInfo.host
|
||||
|
||||
this.id = Utils.optLongX(src, "id", INVALID_ID)
|
||||
this.id = src.parseLong("id") ?: INVALID_ID
|
||||
|
||||
this.acct = src.notEmptyOrThrow("acct")
|
||||
this.host = findHostFromUrl(acct,hostAccess,url)
|
||||
this.host = findHostFromUrl(acct, hostAccess, url)
|
||||
?: throw RuntimeException("can't find host from acct or url")
|
||||
|
||||
this.followers_count = Utils.optLongX(src, "followers_count")
|
||||
this.following_count = Utils.optLongX(src, "following_count")
|
||||
this.statuses_count = Utils.optLongX(src, "statuses_count")
|
||||
this.followers_count = src.parseLong("followers_count")
|
||||
this.following_count = src.parseLong("following_count")
|
||||
this.statuses_count = src.parseLong("statuses_count")
|
||||
|
||||
this.created_at = Utils.optStringX(src, "created_at")
|
||||
this.created_at = src.parseString("created_at")
|
||||
this.time_created_at = TootStatus.parseTime(this.created_at)
|
||||
|
||||
this.avatar = Utils.optStringX(src, "avatar")
|
||||
this.avatar_static = Utils.optStringX(src, "avatar_static")
|
||||
this.header = Utils.optStringX(src, "header")
|
||||
this.header_static = Utils.optStringX(src, "header_static")
|
||||
this.avatar = src.parseString("avatar")
|
||||
this.avatar_static = src.parseString("avatar_static")
|
||||
this.header = src.parseString("header")
|
||||
this.header_static = src.parseString("header_static")
|
||||
}
|
||||
|
||||
ServiceType.TOOTSEARCH -> {
|
||||
// tootsearch のアカウントのIDはどのタンス上のものか分からないので役に立たない
|
||||
this.id = INVALID_ID // Utils.optLongX(src, "id", INVALID_ID)
|
||||
this.id = INVALID_ID // src.parseLong( "id", INVALID_ID)
|
||||
|
||||
sv = src.notEmptyOrThrow("acct")
|
||||
this.host = findHostFromUrl(sv,null,url)
|
||||
this.host = findHostFromUrl(sv, null, url)
|
||||
?: throw RuntimeException("can't find host from acct or url")
|
||||
this.acct = this.username + "@" + this.host
|
||||
|
||||
this.followers_count = Utils.optLongX(src, "followers_count")
|
||||
this.following_count = Utils.optLongX(src, "following_count")
|
||||
this.statuses_count = Utils.optLongX(src, "statuses_count")
|
||||
this.followers_count = src.parseLong("followers_count")
|
||||
this.following_count = src.parseLong("following_count")
|
||||
this.statuses_count = src.parseLong("statuses_count")
|
||||
|
||||
this.created_at = Utils.optStringX(src, "created_at")
|
||||
this.created_at = src.parseString("created_at")
|
||||
this.time_created_at = TootStatus.parseTime(this.created_at)
|
||||
|
||||
this.avatar = Utils.optStringX(src, "avatar")
|
||||
this.avatar_static = Utils.optStringX(src, "avatar_static")
|
||||
this.header = Utils.optStringX(src, "header")
|
||||
this.header_static = Utils.optStringX(src, "header_static")
|
||||
this.avatar = src.parseString("avatar")
|
||||
this.avatar_static = src.parseString("avatar_static")
|
||||
this.header = src.parseString("header")
|
||||
this.header_static = src.parseString("header_static")
|
||||
}
|
||||
|
||||
ServiceType.MSP -> {
|
||||
this.id = Utils.optLongX(src, "id", INVALID_ID)
|
||||
this.id = src.parseLong("id") ?: INVALID_ID
|
||||
|
||||
// MSPはLTLの情報しか持ってないのでacctは常にホスト名部分を持たない
|
||||
this.host = findHostFromUrl(null,null,url)
|
||||
?:throw RuntimeException("can't find host from url")
|
||||
this.host = findHostFromUrl(null, null, url)
|
||||
?: throw RuntimeException("can't find host from url")
|
||||
this.acct = this.username + "@" + host
|
||||
|
||||
this.followers_count = null
|
||||
|
@ -174,7 +170,7 @@ open class TootAccount(
|
|||
this.created_at = null
|
||||
this.time_created_at = 0L
|
||||
|
||||
val avatar = Utils.optStringX(src, "avatar")
|
||||
val avatar = src.parseString("avatar")
|
||||
this.avatar = avatar
|
||||
this.avatar_static = avatar
|
||||
this.header = null
|
||||
|
@ -196,8 +192,8 @@ open class TootAccount(
|
|||
val note : String?
|
||||
|
||||
init {
|
||||
this.privacy = Utils.optStringX(src, "privacy")
|
||||
this.note = Utils.optStringX(src, "note")
|
||||
this.privacy = src.parseString("privacy")
|
||||
this.note = src.parseString("note")
|
||||
// nullになることがあるが、falseと同じ扱いでよい
|
||||
this.sensitive = src.optBoolean("sensitive", false)
|
||||
}
|
||||
|
@ -225,7 +221,8 @@ open class TootAccount(
|
|||
private val reWhitespace = Pattern.compile("[\\s\\t\\x0d\\x0a]+")
|
||||
|
||||
@Suppress("HasPlatformType")
|
||||
val reAccountUrl = Pattern.compile("\\Ahttps://([A-Za-z0-9.-]+)/@([A-Za-z0-9_]+)(?:\\z|[?#])")
|
||||
val reAccountUrl =
|
||||
Pattern.compile("\\Ahttps://([A-Za-z0-9.-]+)/@([A-Za-z0-9_]+)(?:\\z|[?#])")
|
||||
|
||||
fun parse(
|
||||
context : Context,
|
||||
|
@ -254,7 +251,11 @@ open class TootAccount(
|
|||
}
|
||||
}
|
||||
|
||||
fun parseList(context : Context, account : LinkClickContext, array : JSONArray?) : ArrayList<TootAccount> {
|
||||
fun parseList(
|
||||
context : Context,
|
||||
account : LinkClickContext,
|
||||
array : JSONArray?
|
||||
) : ArrayList<TootAccount> {
|
||||
val result = ArrayList<TootAccount>()
|
||||
if(array != null) {
|
||||
val array_size = array.length()
|
||||
|
@ -269,10 +270,10 @@ open class TootAccount(
|
|||
}
|
||||
|
||||
// Tootsearch用。URLやUriを使ってアカウントのインスタンス名を調べる
|
||||
fun findHostFromUrl(acct:String? ,accessHost:String?, url : String?) : String? {
|
||||
|
||||
fun findHostFromUrl(acct : String?, accessHost : String?, url : String?) : String? {
|
||||
|
||||
// acctから調べる
|
||||
if( acct != null ) {
|
||||
if(acct != null) {
|
||||
val pos = acct.indexOf('@')
|
||||
if(pos != - 1) {
|
||||
val host = acct.substring(pos + 1)
|
||||
|
@ -281,12 +282,12 @@ open class TootAccount(
|
|||
}
|
||||
|
||||
// accessHostから調べる
|
||||
if( accessHost != null ){
|
||||
if(accessHost != null) {
|
||||
return accessHost
|
||||
}
|
||||
|
||||
|
||||
// URLから調べる
|
||||
if( url != null ) {
|
||||
if(url != null) {
|
||||
try {
|
||||
// たぶんどんなURLでもauthorityの部分にホスト名が来るだろう(慢心)
|
||||
val uri = Uri.parse(url)
|
||||
|
@ -295,7 +296,7 @@ open class TootAccount(
|
|||
log.e(ex, "findHostFromUrl: can't parse host from URL $url")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,14 +2,15 @@ package jp.juggler.subwaytooter.api.entity
|
|||
|
||||
import org.json.JSONObject
|
||||
|
||||
import jp.juggler.subwaytooter.util.Utils
|
||||
import jp.juggler.subwaytooter.util.parseString
|
||||
|
||||
class TootApplication (
|
||||
class TootApplication(
|
||||
val name : String?,
|
||||
@Suppress("unused") private val website : String?
|
||||
){
|
||||
constructor(src:JSONObject):this(
|
||||
name = Utils.optStringX(src, "name"),
|
||||
website = Utils.optStringX(src, "website")
|
||||
) {
|
||||
|
||||
constructor(src : JSONObject) : this(
|
||||
name = src.parseString("name"),
|
||||
website = src.parseString("website")
|
||||
)
|
||||
}
|
||||
|
|
|
@ -5,12 +5,13 @@ import android.content.SharedPreferences
|
|||
import org.json.JSONObject
|
||||
|
||||
import jp.juggler.subwaytooter.Pref
|
||||
import jp.juggler.subwaytooter.util.Utils
|
||||
import jp.juggler.subwaytooter.util.parseLong
|
||||
import jp.juggler.subwaytooter.util.parseString
|
||||
|
||||
class TootAttachment(src : JSONObject) : TootAttachmentLike {
|
||||
|
||||
val json : JSONObject
|
||||
|
||||
|
||||
// ID of the attachment
|
||||
val id : Long
|
||||
|
||||
|
@ -39,13 +40,13 @@ class TootAttachment(src : JSONObject) : TootAttachmentLike {
|
|||
|
||||
init {
|
||||
json = src
|
||||
id = Utils.optLongX(src, "id")
|
||||
type = Utils.optStringX(src, "type")
|
||||
url = Utils.optStringX(src, "url")
|
||||
remote_url = Utils.optStringX(src, "remote_url")
|
||||
preview_url = Utils.optStringX(src, "preview_url")
|
||||
text_url = Utils.optStringX(src, "text_url")
|
||||
description = Utils.optStringX(src, "description")
|
||||
id = src.parseLong("id") ?: - 1L
|
||||
type = src.parseString("type")
|
||||
url = src.parseString("url")
|
||||
remote_url = src.parseString("remote_url")
|
||||
preview_url = src.parseString("preview_url")
|
||||
text_url = src.parseString("text_url")
|
||||
description = src.parseString("description")
|
||||
}
|
||||
|
||||
override val urlForThumbnail : String?
|
||||
|
@ -57,20 +58,21 @@ class TootAttachment(src : JSONObject) : TootAttachmentLike {
|
|||
}
|
||||
|
||||
fun getLargeUrl(pref : SharedPreferences) : String? {
|
||||
return if( Pref.bpPriorLocalURL(pref) ){
|
||||
if( url?.isNotEmpty() ==true) url else remote_url
|
||||
return if(Pref.bpPriorLocalURL(pref)) {
|
||||
if(url?.isNotEmpty() == true) url else remote_url
|
||||
} else {
|
||||
if( remote_url?.isNotEmpty() == true ) remote_url else url
|
||||
if(remote_url?.isNotEmpty() == true) remote_url else url
|
||||
}
|
||||
}
|
||||
|
||||
fun getLargeUrlList(pref : SharedPreferences) : ArrayList<String> {
|
||||
val result = ArrayList<String>()
|
||||
if( Pref.bpPriorLocalURL(pref) ){
|
||||
if( url?.isNotEmpty() ==true) result.add(url)
|
||||
if( remote_url?.isNotEmpty()==true) result.add( remote_url)
|
||||
if(Pref.bpPriorLocalURL(pref)) {
|
||||
if(url?.isNotEmpty() == true) result.add(url)
|
||||
if(remote_url?.isNotEmpty() == true) result.add(remote_url)
|
||||
} else {
|
||||
if( remote_url?.isNotEmpty()==true) result.add( remote_url)
|
||||
if( url?.isNotEmpty() ==true) result.add(url)
|
||||
if(remote_url?.isNotEmpty() == true) result.add(remote_url)
|
||||
if(url?.isNotEmpty() == true) result.add(url)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package jp.juggler.subwaytooter.api.entity
|
||||
|
||||
import jp.juggler.subwaytooter.util.Utils
|
||||
import jp.juggler.subwaytooter.util.parseString
|
||||
import org.json.JSONArray
|
||||
|
||||
class TootAttachmentMSP(
|
||||
|
@ -27,7 +27,7 @@ class TootAttachmentMSP(
|
|||
val result = ArrayList<TootAttachmentLike>()
|
||||
result.ensureCapacity(array_size)
|
||||
for(i in 0 until array_size) {
|
||||
val sv = Utils.optStringX(array, i)
|
||||
val sv = array.parseString( i)
|
||||
if(sv != null && sv.isNotBlank()) {
|
||||
result.add(TootAttachmentMSP(sv))
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ package jp.juggler.subwaytooter.api.entity
|
|||
|
||||
import org.json.JSONObject
|
||||
|
||||
import jp.juggler.subwaytooter.util.Utils
|
||||
import jp.juggler.subwaytooter.util.parseString
|
||||
|
||||
class TootCard(
|
||||
|
||||
|
@ -26,16 +26,16 @@ class TootCard(
|
|||
) {
|
||||
|
||||
constructor(src : JSONObject) : this(
|
||||
url = Utils.optStringX(src, "url"),
|
||||
title = Utils.optStringX(src, "title"),
|
||||
description = Utils.optStringX(src, "description"),
|
||||
image = Utils.optStringX(src, "image"),
|
||||
url = src.parseString("url"),
|
||||
title = src.parseString("title"),
|
||||
description = src.parseString("description"),
|
||||
image = src.parseString("image"),
|
||||
|
||||
type = Utils.optStringX(src, "type"),
|
||||
author_name = Utils.optStringX(src, "author_name"),
|
||||
author_url = Utils.optStringX(src, "author_url"),
|
||||
provider_name = Utils.optStringX(src, "provider_name"),
|
||||
provider_url = Utils.optStringX(src, "provider_url")
|
||||
type = src.parseString("type"),
|
||||
author_name = src.parseString("author_name"),
|
||||
author_url = src.parseString("author_url"),
|
||||
provider_name = src.parseString("provider_name"),
|
||||
provider_url = src.parseString("provider_url")
|
||||
|
||||
)
|
||||
}
|
||||
|
|
|
@ -2,8 +2,9 @@ package jp.juggler.subwaytooter.api.entity
|
|||
|
||||
import org.json.JSONObject
|
||||
|
||||
import jp.juggler.subwaytooter.util.Utils
|
||||
import jp.juggler.subwaytooter.util.VersionString
|
||||
import jp.juggler.subwaytooter.util.parseLong
|
||||
import jp.juggler.subwaytooter.util.parseString
|
||||
|
||||
class TootInstance(src : JSONObject) {
|
||||
|
||||
|
@ -35,16 +36,15 @@ class TootInstance(src : JSONObject) {
|
|||
|
||||
// XXX: urls をパースしてない。使ってないから…
|
||||
|
||||
|
||||
init {
|
||||
this.uri = Utils.optStringX(src, "uri")
|
||||
this.title = Utils.optStringX(src, "title")
|
||||
this.description = Utils.optStringX(src, "description")
|
||||
this.email = Utils.optStringX(src, "email")
|
||||
this.version = Utils.optStringX(src, "version")
|
||||
this.uri = src.parseString("uri")
|
||||
this.title = src.parseString("title")
|
||||
this.description = src.parseString("description")
|
||||
this.email = src.parseString("email")
|
||||
this.version = src.parseString("version")
|
||||
this.decoded_version = VersionString(version)
|
||||
this.stats = parseItem(::Stats,src.optJSONObject("stats"))
|
||||
this.thumbnail = Utils.optStringX(src, "thumbnail")
|
||||
this.stats = parseItem(::Stats, src.optJSONObject("stats"))
|
||||
this.thumbnail = src.parseString("thumbnail")
|
||||
}
|
||||
|
||||
class Stats(src : JSONObject) {
|
||||
|
@ -53,9 +53,9 @@ class TootInstance(src : JSONObject) {
|
|||
val domain_count : Long
|
||||
|
||||
init {
|
||||
this.user_count = Utils.optLongX(src, "user_count", - 1L)
|
||||
this.status_count = Utils.optLongX(src, "status_count", - 1L)
|
||||
this.domain_count = Utils.optLongX(src, "domain_count", - 1L)
|
||||
this.user_count = src.parseLong("user_count") ?: - 1L
|
||||
this.status_count = src.parseLong("status_count") ?: - 1L
|
||||
this.domain_count = src.parseLong("domain_count") ?: - 1L
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,8 @@ import org.json.JSONObject
|
|||
import java.util.ArrayList
|
||||
import java.util.regex.Pattern
|
||||
|
||||
import jp.juggler.subwaytooter.util.Utils
|
||||
import jp.juggler.subwaytooter.util.parseLong
|
||||
import jp.juggler.subwaytooter.util.parseString
|
||||
|
||||
class TootList(
|
||||
val id : Long,
|
||||
|
@ -25,13 +26,13 @@ class TootList(
|
|||
}
|
||||
|
||||
constructor(src : JSONObject) : this(
|
||||
id = Utils.optLongX(src, "id"),
|
||||
title = Utils.optStringX(src, "title")
|
||||
id = src.parseLong("id") ?: - 1L,
|
||||
title = src.parseString("title")
|
||||
)
|
||||
|
||||
companion object {
|
||||
private var log = LogCategory("TootList")
|
||||
|
||||
|
||||
private val reNumber = Pattern.compile("(\\d+)")
|
||||
|
||||
private fun makeTitleForSort(title : String?) : ArrayList<Any> {
|
||||
|
@ -62,12 +63,12 @@ class TootList(
|
|||
}
|
||||
return list
|
||||
}
|
||||
|
||||
private fun compareLong(a:Long ,b:Long):Int{
|
||||
|
||||
private fun compareLong(a : Long, b : Long) : Int {
|
||||
return a.compareTo(b)
|
||||
}
|
||||
|
||||
private fun compareString(a:String ,b:String):Int{
|
||||
private fun compareString(a : String, b : String) : Int {
|
||||
return a.compareTo(b)
|
||||
}
|
||||
}
|
||||
|
@ -94,7 +95,7 @@ class TootList(
|
|||
if(ob == null) 0 else - 1
|
||||
} else if(ob == null) {
|
||||
1
|
||||
}else {
|
||||
} else {
|
||||
|
||||
when {
|
||||
oa is Long && ob is Long -> compareLong(oa, ob)
|
||||
|
@ -102,10 +103,11 @@ class TootList(
|
|||
else -> (ob is Long).b2i() - (oa is Long).b2i()
|
||||
}
|
||||
}
|
||||
log.d("%s %s %s"
|
||||
,oa
|
||||
,if(delta<0) "<" else if(delta>0)">" else "="
|
||||
,ob
|
||||
log.d(
|
||||
"%s %s %s"
|
||||
, oa
|
||||
, if(delta < 0) "<" else if(delta > 0) ">" else "="
|
||||
, ob
|
||||
)
|
||||
if(delta != 0) return delta
|
||||
++ i
|
||||
|
|
|
@ -2,7 +2,7 @@ package jp.juggler.subwaytooter.api.entity
|
|||
|
||||
import org.json.JSONObject
|
||||
|
||||
import jp.juggler.subwaytooter.util.Utils
|
||||
import jp.juggler.subwaytooter.util.parseLong
|
||||
|
||||
class TootMention(
|
||||
val id : Long, // Account ID
|
||||
|
@ -12,7 +12,7 @@ class TootMention(
|
|||
) {
|
||||
|
||||
constructor(src : JSONObject) : this(
|
||||
id = Utils.optLongX(src, "id"),
|
||||
id = src.parseLong("id") ?: -1L,
|
||||
url = src.notEmptyOrThrow("url"),
|
||||
acct = src.notEmptyOrThrow("acct"),
|
||||
username = src.notEmptyOrThrow("username")
|
||||
|
|
|
@ -3,7 +3,8 @@ package jp.juggler.subwaytooter.api.entity
|
|||
import org.json.JSONObject
|
||||
|
||||
import jp.juggler.subwaytooter.api.TootParser
|
||||
import jp.juggler.subwaytooter.util.Utils
|
||||
import jp.juggler.subwaytooter.util.parseLong
|
||||
import jp.juggler.subwaytooter.util.parseString
|
||||
|
||||
class TootNotification(
|
||||
val json : JSONObject,
|
||||
|
@ -12,7 +13,7 @@ class TootNotification(
|
|||
private val created_at : String?, // The time the notification was created
|
||||
val account : TootAccount?, // The Account sending the notification to the user
|
||||
val status : TootStatus? // The Status associated with the notification, if applicable
|
||||
) :TimelineItem(){
|
||||
) : TimelineItem() {
|
||||
|
||||
val time_created_at : Long
|
||||
|
||||
|
@ -22,10 +23,15 @@ class TootNotification(
|
|||
|
||||
constructor(parser : TootParser, src : JSONObject) : this(
|
||||
json = src,
|
||||
id = Utils.optLongX(src, "id"),
|
||||
id = src.parseLong("id") ?: - 1L,
|
||||
type = src.notEmptyOrThrow("type"),
|
||||
created_at = Utils.optStringX(src,"created_at"),
|
||||
account = TootAccount.parse(parser.context, parser.accessInfo, src.optJSONObject("account"), ServiceType.MASTODON),
|
||||
created_at = src.parseString("created_at"),
|
||||
account = TootAccount.parse(
|
||||
parser.context,
|
||||
parser.accessInfo,
|
||||
src.optJSONObject("account"),
|
||||
ServiceType.MASTODON
|
||||
),
|
||||
status = TootStatus.parse(parser, src.optJSONObject("status"), ServiceType.MASTODON)
|
||||
)
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ package jp.juggler.subwaytooter.api.entity
|
|||
|
||||
import jp.juggler.subwaytooter.api.TootParser
|
||||
import jp.juggler.subwaytooter.util.LogCategory
|
||||
import jp.juggler.subwaytooter.util.toJsonObject
|
||||
import org.json.JSONObject
|
||||
import java.util.regex.Pattern
|
||||
|
||||
|
@ -50,7 +51,7 @@ object TootPayload {
|
|||
if(payload is String) {
|
||||
|
||||
if(payload[0] == '{') {
|
||||
val src = JSONObject(payload)
|
||||
val src = payload.toJsonObject()
|
||||
return when(event) {
|
||||
"update" ->
|
||||
// 2017/8/24 18:37 mastodon.juggler.jpでここを通った
|
||||
|
|
|
@ -3,7 +3,7 @@ package jp.juggler.subwaytooter.api.entity
|
|||
import org.json.JSONObject
|
||||
|
||||
import jp.juggler.subwaytooter.table.UserRelation
|
||||
import jp.juggler.subwaytooter.util.Utils
|
||||
import jp.juggler.subwaytooter.util.parseLong
|
||||
|
||||
class TootRelationShip(src : JSONObject) {
|
||||
|
||||
|
@ -32,7 +32,7 @@ class TootRelationShip(src : JSONObject) {
|
|||
val showing_reblogs : Int
|
||||
|
||||
init {
|
||||
this.id = Utils.optLongX(src, "id")
|
||||
this.id = src.parseLong("id") ?: - 1L
|
||||
|
||||
var ov = src.opt("following")
|
||||
if(ov is JSONObject) {
|
||||
|
|
|
@ -2,15 +2,16 @@ package jp.juggler.subwaytooter.api.entity
|
|||
|
||||
import org.json.JSONObject
|
||||
|
||||
import jp.juggler.subwaytooter.util.Utils
|
||||
import jp.juggler.subwaytooter.util.parseLong
|
||||
import jp.juggler.subwaytooter.util.parseString
|
||||
|
||||
class TootReport(
|
||||
val id : Long,
|
||||
private val action_taken : String? // The action taken in response to the report
|
||||
) :TimelineItem() {
|
||||
@Suppress("unused") private val action_taken : String? // The action taken in response to the report
|
||||
) : TimelineItem() {
|
||||
|
||||
constructor(src : JSONObject) : this(
|
||||
id = Utils.optLongX(src, "id"),
|
||||
action_taken = Utils.optStringX(src, "action_taken")
|
||||
id = src.parseLong("id") ?: - 1L,
|
||||
action_taken = src.parseString("action_taken")
|
||||
)
|
||||
}
|
||||
|
|
|
@ -22,7 +22,8 @@ import java.text.SimpleDateFormat
|
|||
import java.util.*
|
||||
|
||||
@Suppress("MemberVisibilityCanPrivate")
|
||||
class TootStatus(parser : TootParser, src : JSONObject, serviceType : ServiceType) :TimelineItem(){
|
||||
class TootStatus(parser : TootParser, src : JSONObject, serviceType : ServiceType) :
|
||||
TimelineItem() {
|
||||
|
||||
val json : JSONObject
|
||||
|
||||
|
@ -71,7 +72,7 @@ class TootStatus(parser : TootParser, src : JSONObject, serviceType : ServiceTyp
|
|||
val sensitive : Boolean
|
||||
|
||||
// The detected language for the status, if detected
|
||||
val language : String?
|
||||
private val language : String?
|
||||
|
||||
//If not empty, warning text that should be displayed before the actual content
|
||||
val spoiler_text : String?
|
||||
|
@ -89,13 +90,13 @@ class TootStatus(parser : TootParser, src : JSONObject, serviceType : ServiceTyp
|
|||
val profile_emojis : HashMap<String, NicoProfileEmoji>?
|
||||
|
||||
// The time the status was created
|
||||
val created_at : String?
|
||||
private val created_at : String?
|
||||
|
||||
// null or the ID of the status it replies to
|
||||
val in_reply_to_id : String?
|
||||
|
||||
// null or the ID of the account it replies to
|
||||
val in_reply_to_account_id : String?
|
||||
private val in_reply_to_account_id : String?
|
||||
|
||||
// null or the reblogged Status
|
||||
val reblog : TootStatus?
|
||||
|
@ -148,13 +149,14 @@ class TootStatus(parser : TootParser, src : JSONObject, serviceType : ServiceTyp
|
|||
init {
|
||||
this.json = src
|
||||
|
||||
this.uri = Utils.optStringX(src, "uri") // MSPだとuriは提供されない
|
||||
this.url = Utils.optStringX(src, "url") // 頻繁にnullになる
|
||||
this.created_at = Utils.optStringX(src, "created_at")
|
||||
this.uri = src.parseString("uri") // MSPだとuriは提供されない
|
||||
this.url = src.parseString("url") // 頻繁にnullになる
|
||||
this.created_at = src.parseString("created_at")
|
||||
|
||||
// 絵文字マップはすぐ後で使うので、最初の方で読んでおく
|
||||
this.custom_emojis = parseMapOrNull(::CustomEmoji, src.optJSONArray("emojis"), log)
|
||||
this.profile_emojis = parseMapOrNull(::NicoProfileEmoji, src.optJSONArray("profile_emojis"), log)
|
||||
this.profile_emojis =
|
||||
parseMapOrNull(::NicoProfileEmoji, src.optJSONArray("profile_emojis"), log)
|
||||
|
||||
this.account = TootAccount.parse(
|
||||
parser.context,
|
||||
|
@ -167,16 +169,17 @@ class TootStatus(parser : TootParser, src : JSONObject, serviceType : ServiceTyp
|
|||
ServiceType.MASTODON -> {
|
||||
this.host_access = parser.accessInfo.host
|
||||
|
||||
this.id = Utils.optLongX(src, "id", INVALID_ID)
|
||||
this.id = src.parseLong("id") ?: INVALID_ID
|
||||
|
||||
this.reblogs_count = Utils.optLongX(src, "reblogs_count")
|
||||
this.favourites_count = Utils.optLongX(src, "favourites_count")
|
||||
this.reblogs_count = src.parseLong("reblogs_count")
|
||||
this.favourites_count = src.parseLong("favourites_count")
|
||||
this.reblogged = src.optBoolean("reblogged")
|
||||
this.favourited = src.optBoolean("favourited")
|
||||
|
||||
this.time_created_at = parseTime(this.created_at)
|
||||
this.media_attachments = parseListOrNull(::TootAttachment, src.optJSONArray("media_attachments"), log)
|
||||
this.visibility = Utils.optStringX(src, "visibility")
|
||||
this.media_attachments =
|
||||
parseListOrNull(::TootAttachment, src.optJSONArray("media_attachments"), log)
|
||||
this.visibility = src.parseString("visibility")
|
||||
this.sensitive = src.optBoolean("sensitive")
|
||||
|
||||
}
|
||||
|
@ -188,11 +191,12 @@ class TootStatus(parser : TootParser, src : JSONObject, serviceType : ServiceTyp
|
|||
this.id = findStatusIdFromUri(uri, url)
|
||||
|
||||
|
||||
this.reblogs_count = Utils.optLongX(src, "reblogs_count")
|
||||
this.favourites_count = Utils.optLongX(src, "favourites_count")
|
||||
this.reblogs_count = src.parseLong("reblogs_count")
|
||||
this.favourites_count = src.parseLong("favourites_count")
|
||||
|
||||
this.time_created_at = TootStatus.parseTime(this.created_at)
|
||||
this.media_attachments = parseListOrNull(::TootAttachment, src.optJSONArray("media_attachments"), log)
|
||||
this.media_attachments =
|
||||
parseListOrNull(::TootAttachment, src.optJSONArray("media_attachments"), log)
|
||||
this.visibility = VISIBILITY_PUBLIC
|
||||
this.sensitive = src.optBoolean("sensitive")
|
||||
|
||||
|
@ -202,28 +206,33 @@ class TootStatus(parser : TootParser, src : JSONObject, serviceType : ServiceTyp
|
|||
this.host_access = null
|
||||
|
||||
// MSPのデータはLTLから呼んだものなので、常に投稿元タンスでのidが得られる
|
||||
this.id = Utils.optLongX(src, "id", INVALID_ID)
|
||||
this.id = src.parseLong("id") ?: INVALID_ID
|
||||
|
||||
this.time_created_at = parseTimeMSP(created_at)
|
||||
this.media_attachments = TootAttachmentMSP.parseList(src.optJSONArray("media_attachments"))
|
||||
this.media_attachments =
|
||||
TootAttachmentMSP.parseList(src.optJSONArray("media_attachments"))
|
||||
this.visibility = VISIBILITY_PUBLIC
|
||||
this.sensitive = src.optInt("sensitive", 0) != 0
|
||||
}
|
||||
}
|
||||
|
||||
this.in_reply_to_id = Utils.optStringX(src, "in_reply_to_id")
|
||||
this.in_reply_to_account_id = Utils.optStringX(src, "in_reply_to_account_id")
|
||||
this.in_reply_to_id = src.parseString("in_reply_to_id")
|
||||
this.in_reply_to_account_id = src.parseString("in_reply_to_account_id")
|
||||
this.mentions = parseListOrNull(::TootMention, src.optJSONArray("mentions"), log)
|
||||
this.tags = parseListOrNull(::TootTag, src.optJSONArray("tags"))
|
||||
this.application = parseItem(::TootApplication, src.optJSONObject("application"), log)
|
||||
this.pinned = parser.pinned || src.optBoolean("pinned")
|
||||
this.muted = src.optBoolean("muted")
|
||||
this.language = Utils.optStringX(src, "language")
|
||||
this.decoded_mentions = HTMLDecoder.decodeMentions(parser.accessInfo, this.mentions, this) ?: EMPTY_SPANNABLE
|
||||
this.language = src.parseString("language")
|
||||
this.decoded_mentions = HTMLDecoder.decodeMentions(
|
||||
parser.accessInfo,
|
||||
this.mentions,
|
||||
this
|
||||
) ?: EMPTY_SPANNABLE
|
||||
// this.decoded_tags = HTMLDecoder.decodeTags( account,status.tags );
|
||||
|
||||
// content
|
||||
this.content = Utils.optStringX(src, "content")
|
||||
this.content = src.parseString("content")
|
||||
var options = DecodeOptions(
|
||||
short = true,
|
||||
decodeEmoji = true,
|
||||
|
@ -241,11 +250,10 @@ class TootStatus(parser : TootParser, src : JSONObject, serviceType : ServiceTyp
|
|||
}
|
||||
|
||||
// spoiler_text
|
||||
this.spoiler_text = Utils.sanitizeBDI(
|
||||
reWhitespace
|
||||
.matcher(Utils.optStringX(src, "spoiler_text") ?: "")
|
||||
.replaceAll(" ")
|
||||
)
|
||||
this.spoiler_text = reWhitespace
|
||||
.matcher(src.parseString("spoiler_text") ?: "")
|
||||
.replaceAll(" ")
|
||||
.sanitizeBDI()
|
||||
|
||||
options = DecodeOptions(
|
||||
emojiMapCustom = custom_emojis,
|
||||
|
@ -265,7 +273,7 @@ class TootStatus(parser : TootParser, src : JSONObject, serviceType : ServiceTyp
|
|||
parser.accessInfo,
|
||||
this,
|
||||
media_attachments,
|
||||
Utils.optStringX(src, "enquete")
|
||||
src.parseString("enquete")
|
||||
)
|
||||
|
||||
// Pinned TL を取得した時にreblogが登場することはないので、reblogについてpinned 状態を気にする必要はない
|
||||
|
@ -330,15 +338,19 @@ class TootStatus(parser : TootParser, src : JSONObject, serviceType : ServiceTyp
|
|||
|
||||
// OStatus
|
||||
@Suppress("HasPlatformType")
|
||||
val reTootUriOS = Pattern.compile("tag:([^,]*),[^:]*:objectId=(\\d+):objectType=Status", Pattern.CASE_INSENSITIVE)
|
||||
private val reTootUriOS = Pattern.compile(
|
||||
"tag:([^,]*),[^:]*:objectId=(\\d+):objectType=Status",
|
||||
Pattern.CASE_INSENSITIVE
|
||||
)
|
||||
|
||||
// ActivityPub 1
|
||||
@Suppress("HasPlatformType")
|
||||
val reTootUriAP1 = Pattern.compile("https?://([^/]+)/users/[A-Za-z0-9_]+/statuses/(\\d+)")
|
||||
private val reTootUriAP1 =
|
||||
Pattern.compile("https?://([^/]+)/users/[A-Za-z0-9_]+/statuses/(\\d+)")
|
||||
|
||||
// ActivityPub 2
|
||||
@Suppress("HasPlatformType")
|
||||
val reTootUriAP2 = Pattern.compile("https?://([^/]+)/@[A-Za-z0-9_]+/(\\d+)")
|
||||
private val reTootUriAP2 = Pattern.compile("https?://([^/]+)/@[A-Za-z0-9_]+/(\\d+)")
|
||||
|
||||
const val INVALID_ID = - 1L
|
||||
|
||||
|
@ -411,9 +423,11 @@ class TootStatus(parser : TootParser, src : JSONObject, serviceType : ServiceTyp
|
|||
|
||||
private val tz_utc = TimeZone.getTimeZone("UTC")
|
||||
|
||||
private val reTime = Pattern.compile("\\A(\\d+)\\D+(\\d+)\\D+(\\d+)\\D+(\\d+)\\D+(\\d+)\\D+(\\d+)\\D+(\\d+)")
|
||||
private val reTime =
|
||||
Pattern.compile("\\A(\\d+)\\D+(\\d+)\\D+(\\d+)\\D+(\\d+)\\D+(\\d+)\\D+(\\d+)\\D+(\\d+)")
|
||||
|
||||
private val reMSPTime = Pattern.compile("\\A(\\d+)\\D+(\\d+)\\D+(\\d+)\\D+(\\d+)\\D+(\\d+)\\D+(\\d+)")
|
||||
private val reMSPTime =
|
||||
Pattern.compile("\\A(\\d+)\\D+(\\d+)\\D+(\\d+)\\D+(\\d+)\\D+(\\d+)\\D+(\\d+)")
|
||||
|
||||
fun parseTime(strTime : String?) : Long {
|
||||
if(strTime != null && strTime.isNotEmpty()) {
|
||||
|
@ -424,14 +438,14 @@ class TootStatus(parser : TootParser, src : JSONObject, serviceType : ServiceTyp
|
|||
} else {
|
||||
val g = GregorianCalendar(tz_utc)
|
||||
g.set(
|
||||
Utils.parse_int(m.group(1), 1),
|
||||
Utils.parse_int(m.group(2), 1) - 1,
|
||||
Utils.parse_int(m.group(3), 1),
|
||||
Utils.parse_int(m.group(4), 0),
|
||||
Utils.parse_int(m.group(5), 0),
|
||||
Utils.parse_int(m.group(6), 0)
|
||||
m.group(1).optInt() ?: 1,
|
||||
(m.group(2).optInt() ?: 1) - 1,
|
||||
m.group(3).optInt() ?: 1,
|
||||
m.group(4).optInt() ?: 0,
|
||||
m.group(5).optInt() ?: 0,
|
||||
m.group(6).optInt() ?: 0
|
||||
)
|
||||
g.set(Calendar.MILLISECOND, Utils.parse_int(m.group(7), 0))
|
||||
g.set(Calendar.MILLISECOND, m.group(7).optInt() ?: 0)
|
||||
return g.timeInMillis
|
||||
}
|
||||
} catch(ex : Throwable) { // ParseException, ArrayIndexOutOfBoundsException
|
||||
|
@ -452,12 +466,12 @@ class TootStatus(parser : TootParser, src : JSONObject, serviceType : ServiceTyp
|
|||
} else {
|
||||
val g = GregorianCalendar(tz_utc)
|
||||
g.set(
|
||||
Utils.parse_int(m.group(1), 1),
|
||||
Utils.parse_int(m.group(2), 1) - 1,
|
||||
Utils.parse_int(m.group(3), 1),
|
||||
Utils.parse_int(m.group(4), 0),
|
||||
Utils.parse_int(m.group(5), 0),
|
||||
Utils.parse_int(m.group(6), 0)
|
||||
m.group(1).optInt() ?: 1,
|
||||
(m.group(2).optInt() ?: 1) - 1,
|
||||
m.group(3).optInt() ?: 1,
|
||||
m.group(4).optInt() ?: 0,
|
||||
m.group(5).optInt() ?: 0,
|
||||
m.group(6).optInt() ?: 0
|
||||
)
|
||||
g.set(Calendar.MILLISECOND, 500)
|
||||
return g.timeInMillis
|
||||
|
@ -484,22 +498,38 @@ class TootStatus(parser : TootParser, src : JSONObject, serviceType : ServiceTyp
|
|||
|
||||
delta < 60000L -> {
|
||||
val v = (delta / 1000L).toInt()
|
||||
return context.getString(if(v > 1) R.string.relative_time_second_2 else R.string.relative_time_second_1, v, sign)
|
||||
return context.getString(
|
||||
if(v > 1) R.string.relative_time_second_2 else R.string.relative_time_second_1,
|
||||
v,
|
||||
sign
|
||||
)
|
||||
}
|
||||
|
||||
delta < 3600000L -> {
|
||||
val v = (delta / 60000L).toInt()
|
||||
return context.getString(if(v > 1) R.string.relative_time_minute_2 else R.string.relative_time_minute_1, v, sign)
|
||||
return context.getString(
|
||||
if(v > 1) R.string.relative_time_minute_2 else R.string.relative_time_minute_1,
|
||||
v,
|
||||
sign
|
||||
)
|
||||
}
|
||||
|
||||
delta < 86400000L -> {
|
||||
val v = (delta / 3600000L).toInt()
|
||||
return context.getString(if(v > 1) R.string.relative_time_hour_2 else R.string.relative_time_hour_1, v, sign)
|
||||
return context.getString(
|
||||
if(v > 1) R.string.relative_time_hour_2 else R.string.relative_time_hour_1,
|
||||
v,
|
||||
sign
|
||||
)
|
||||
}
|
||||
|
||||
delta < 40 * 86400000L -> {
|
||||
val v = (delta / 86400000L).toInt()
|
||||
return context.getString(if(v > 1) R.string.relative_time_day_2 else R.string.relative_time_day_1, v, sign)
|
||||
return context.getString(
|
||||
if(v > 1) R.string.relative_time_day_2 else R.string.relative_time_day_1,
|
||||
v,
|
||||
sign
|
||||
)
|
||||
}
|
||||
|
||||
else -> {
|
||||
|
@ -533,7 +563,10 @@ class TootStatus(parser : TootParser, src : JSONObject, serviceType : ServiceTyp
|
|||
throw IndexOutOfBoundsException("visibility not in range")
|
||||
}
|
||||
|
||||
fun isVisibilitySpoilRequired(current_visibility : String?, max_visibility : String?) : Boolean {
|
||||
fun isVisibilitySpoilRequired(
|
||||
current_visibility : String?,
|
||||
max_visibility : String?
|
||||
) : Boolean {
|
||||
return try {
|
||||
val cvi = parseVisibility(current_visibility)
|
||||
val mvi = parseVisibility(max_visibility)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package jp.juggler.subwaytooter.api.entity
|
||||
|
||||
import jp.juggler.subwaytooter.util.Utils
|
||||
import jp.juggler.subwaytooter.util.parseString
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
|
||||
|
@ -14,7 +14,7 @@ class TootTag(
|
|||
|
||||
constructor(src : JSONObject):this(
|
||||
name = src.notEmptyOrThrow("name"),
|
||||
url = Utils.optStringX(src,"url")
|
||||
url = src.parseString("url")
|
||||
)
|
||||
|
||||
companion object {
|
||||
|
@ -23,7 +23,7 @@ class TootTag(
|
|||
val result = ArrayList<TootTag>()
|
||||
if(array != null) {
|
||||
for( i in 0 until array.length() ){
|
||||
val sv = Utils.optStringX(array, i)
|
||||
val sv = array.parseString( i)
|
||||
if( sv?.isNotEmpty() == true ) {
|
||||
result.add(TootTag(name = sv))
|
||||
}
|
||||
|
|
|
@ -6,6 +6,8 @@ import android.support.v7.app.AppCompatActivity
|
|||
import android.view.Gravity
|
||||
import android.view.View
|
||||
import android.widget.Button
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
|
||||
import java.util.ArrayList
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
|
@ -13,12 +15,10 @@ import java.util.concurrent.atomic.AtomicBoolean
|
|||
import jp.juggler.subwaytooter.R
|
||||
import jp.juggler.subwaytooter.table.AcctColor
|
||||
import jp.juggler.subwaytooter.table.SavedAccount
|
||||
import jp.juggler.subwaytooter.util.Utils
|
||||
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import jp.juggler.subwaytooter.util.DialogInterfaceCallback
|
||||
import jp.juggler.subwaytooter.util.SavedAccountCallback
|
||||
import jp.juggler.subwaytooter.util.showToast
|
||||
|
||||
|
||||
object AccountPicker {
|
||||
|
||||
|
@ -39,7 +39,7 @@ object AccountPicker {
|
|||
}()
|
||||
|
||||
if(account_list.isEmpty()) {
|
||||
Utils.showToast(activity, false, R.string.account_empty)
|
||||
showToast(activity, false, R.string.account_empty)
|
||||
return
|
||||
} else if(! bAllowPseudo) {
|
||||
val tmp_list = ArrayList<SavedAccount>()
|
||||
|
@ -50,7 +50,7 @@ object AccountPicker {
|
|||
account_list.clear()
|
||||
account_list.addAll(tmp_list)
|
||||
if(account_list.isEmpty()) {
|
||||
Utils.showToast(activity, false, R.string.not_available_for_pseudo_account)
|
||||
showToast(activity, false, R.string.not_available_for_pseudo_account)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,14 +17,13 @@ import jp.juggler.subwaytooter.App1
|
|||
import jp.juggler.subwaytooter.R
|
||||
import jp.juggler.subwaytooter.api.entity.TootStatus
|
||||
import jp.juggler.subwaytooter.table.PostDraft
|
||||
import jp.juggler.subwaytooter.util.JSONObjectCallback
|
||||
import jp.juggler.subwaytooter.util.Utils
|
||||
|
||||
import jp.juggler.subwaytooter.util.showToast
|
||||
import org.json.JSONObject
|
||||
|
||||
class DlgDraftPicker : AdapterView.OnItemClickListener, AdapterView.OnItemLongClickListener, DialogInterface.OnDismissListener {
|
||||
|
||||
private lateinit var activity : ActPost
|
||||
private lateinit var callback : JSONObjectCallback
|
||||
private lateinit var callback : (draft : JSONObject) -> Unit
|
||||
private lateinit var lvDraft : ListView
|
||||
private lateinit var adapter : MyAdapter
|
||||
private lateinit var dialog : AlertDialog
|
||||
|
@ -46,7 +45,7 @@ class DlgDraftPicker : AdapterView.OnItemClickListener, AdapterView.OnItemLongCl
|
|||
|
||||
val draft = getPostDraft(position)
|
||||
if(draft != null) {
|
||||
Utils.showToast(activity, false, R.string.draft_deleted)
|
||||
showToast(activity, false, R.string.draft_deleted)
|
||||
draft.delete()
|
||||
reload()
|
||||
return true
|
||||
|
@ -65,7 +64,7 @@ class DlgDraftPicker : AdapterView.OnItemClickListener, AdapterView.OnItemLongCl
|
|||
}
|
||||
|
||||
@SuppressLint("InflateParams")
|
||||
fun open(_activity : ActPost, _callback : JSONObjectCallback) {
|
||||
fun open(_activity : ActPost, _callback : (draft : JSONObject) -> Unit) {
|
||||
this.activity = _activity
|
||||
this.callback = _callback
|
||||
|
||||
|
@ -114,7 +113,7 @@ class DlgDraftPicker : AdapterView.OnItemClickListener, AdapterView.OnItemLongCl
|
|||
|
||||
if(cursor == null) {
|
||||
// load failed.
|
||||
Utils.showToast(activity, true, "failed to loading drafts.")
|
||||
showToast(activity, true, "failed to loading drafts.")
|
||||
} else {
|
||||
this@DlgDraftPicker.cursor = cursor
|
||||
colIdx = PostDraft.ColIdx(cursor)
|
||||
|
|
|
@ -2,7 +2,6 @@ package jp.juggler.subwaytooter.dialog
|
|||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Dialog
|
||||
import android.net.Uri
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.Window
|
||||
|
@ -34,7 +33,8 @@ import jp.juggler.subwaytooter.api.entity.parseList
|
|||
import jp.juggler.subwaytooter.table.AcctColor
|
||||
import jp.juggler.subwaytooter.table.SavedAccount
|
||||
import jp.juggler.subwaytooter.util.NetworkEmojiInvalidator
|
||||
import jp.juggler.subwaytooter.util.Utils
|
||||
import jp.juggler.subwaytooter.util.encodePercent
|
||||
import jp.juggler.subwaytooter.util.showToast
|
||||
import jp.juggler.subwaytooter.view.MyListView
|
||||
import jp.juggler.subwaytooter.view.MyNetworkImageView
|
||||
|
||||
|
@ -186,7 +186,7 @@ class DlgListMember(
|
|||
override fun background(client : TootApiClient) : TootApiResult? {
|
||||
// リストに追加したいアカウントの自タンスでのアカウントIDを取得する
|
||||
local_who = null
|
||||
var result = client.request("/api/v1/search?resolve=true&q=" + Uri.encode(target_user_full_acct))
|
||||
var result = client.request("/api/v1/search?resolve=true&q=" + target_user_full_acct.encodePercent())
|
||||
val jsonObject = result?.jsonObject ?: return result
|
||||
|
||||
|
||||
|
@ -237,7 +237,7 @@ class DlgListMember(
|
|||
|
||||
val error = result.error
|
||||
if( error?.isNotEmpty() == true && result.response?.code() == 404 ) {
|
||||
Utils.showToast(activity, true, result.error)
|
||||
showToast(activity, true, result.error)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -260,14 +260,14 @@ class DlgListMember(
|
|||
DlgTextInput.show(activity, activity.getString(R.string.list_create), null, object : DlgTextInput.Callback {
|
||||
|
||||
override fun onEmptyError() {
|
||||
Utils.showToast(activity, false, R.string.list_name_empty)
|
||||
showToast(activity, false, R.string.list_name_empty)
|
||||
}
|
||||
|
||||
override fun onOK(dialog : Dialog, text : String) {
|
||||
val list_owner = this@DlgListMember.list_owner
|
||||
|
||||
if(list_owner == null) {
|
||||
Utils.showToast(activity, false, "list owner is not selected.")
|
||||
showToast(activity, false, "list owner is not selected.")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -375,13 +375,13 @@ class DlgListMember(
|
|||
|
||||
val list_owner = this@DlgListMember.list_owner
|
||||
if(list_owner == null) {
|
||||
Utils.showToast(activity, false, "list owner is not selected")
|
||||
showToast(activity, false, "list owner is not selected")
|
||||
return
|
||||
}
|
||||
|
||||
val local_who = this@DlgListMember.local_who
|
||||
if(local_who == null) {
|
||||
Utils.showToast(activity, false, "target user is not synchronized")
|
||||
showToast(activity, false, "target user is not synchronized")
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ import jp.juggler.subwaytooter.ActMain
|
|||
import jp.juggler.subwaytooter.App1
|
||||
import jp.juggler.subwaytooter.R
|
||||
import jp.juggler.subwaytooter.util.LogCategory
|
||||
import jp.juggler.subwaytooter.util.Utils
|
||||
import jp.juggler.subwaytooter.util.showToast
|
||||
|
||||
import net.glxn.qrgen.android.QRCode
|
||||
|
||||
|
@ -34,7 +34,7 @@ object DlgQRCode {
|
|||
QRCode.from(url).withSize(size, size).bitmap()
|
||||
} catch(ex : Throwable) {
|
||||
log.trace(ex)
|
||||
Utils.showToast(activity, ex, "makeQrCode failed.")
|
||||
showToast(activity, ex, "makeQrCode failed.")
|
||||
null
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ import jp.juggler.subwaytooter.view.NetworkEmojiView
|
|||
class EmojiPicker(
|
||||
private val activity : Activity,
|
||||
private val instance : String?,
|
||||
private val onEmojiPicked: (name : String,instance:String?,bInstanceHasCustomEmoji:Boolean)->Unit
|
||||
private val onEmojiPicked : (name : String, instance : String?, bInstanceHasCustomEmoji : Boolean) -> Unit
|
||||
// onEmojiPickedのinstance引数は通常の絵文字ならnull、カスタム絵文字なら非null、
|
||||
) : View.OnClickListener {
|
||||
|
||||
|
@ -43,7 +43,7 @@ class EmojiPicker(
|
|||
|
||||
const val CATEGORY_RECENT = - 2
|
||||
const val CATEGORY_CUSTOM = - 1
|
||||
|
||||
|
||||
internal val tone_list = arrayOf(
|
||||
SkinTone.create("_light_skin_tone", "_tone1"),
|
||||
SkinTone.create("_medium_light_skin_tone", "_tone2"),
|
||||
|
@ -54,15 +54,15 @@ class EmojiPicker(
|
|||
}
|
||||
|
||||
private val viewRoot : View
|
||||
|
||||
|
||||
private val pager_adapter : EmojiPickerPagerAdapter
|
||||
|
||||
private val page_list = ArrayList<EmojiPickerPage>()
|
||||
|
||||
|
||||
private val pager : ViewPager
|
||||
|
||||
private val dialog : Dialog
|
||||
|
||||
|
||||
private val pager_strip : PagerSlidingTabStrip
|
||||
|
||||
private val ibSkinTone : Array<ImageButton>
|
||||
|
@ -74,15 +74,15 @@ class EmojiPicker(
|
|||
private val custom_list = ArrayList<EmojiItem>()
|
||||
|
||||
private val emoji_url_map = HashMap<String, String>()
|
||||
|
||||
|
||||
private val recent_page_idx : Int
|
||||
|
||||
|
||||
private val custom_page_idx : Int
|
||||
|
||||
class SkinTone(val suffix_list : Array<out String>){
|
||||
class SkinTone(val suffix_list : Array<out String>) {
|
||||
companion object {
|
||||
fun create( vararg suffix_list : String):SkinTone{
|
||||
return SkinTone( suffix_list )
|
||||
fun create(vararg suffix_list : String) : SkinTone {
|
||||
return SkinTone(suffix_list)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -94,15 +94,15 @@ class EmojiPicker(
|
|||
// recentをロードする
|
||||
val pref = App1.pref
|
||||
val sv = Pref.spEmojiPickerRecent(pref)
|
||||
if( sv.isNotEmpty() ) {
|
||||
if(sv.isNotEmpty()) {
|
||||
try {
|
||||
val array = JSONArray(sv)
|
||||
for( i in 0 until array.length() ){
|
||||
val array = sv.toJsonArray()
|
||||
for(i in 0 until array.length()) {
|
||||
val item = array.optJSONObject(i)
|
||||
val c1 = Utils.optStringX(item, "name")
|
||||
val c2 = Utils.optStringX(item, "instance")
|
||||
if(c1 != null && c1.isNotEmpty() ) {
|
||||
recent_list.add(EmojiItem(c1, c2))
|
||||
val name = item.parseString("name")
|
||||
if(name?.isNotEmpty() == true) {
|
||||
val instance = item.parseString("instance")
|
||||
recent_list.add(EmojiItem(name, instance))
|
||||
}
|
||||
}
|
||||
} catch(ex : Throwable) {
|
||||
|
@ -116,13 +116,43 @@ class EmojiPicker(
|
|||
page_list.add(EmojiPickerPage(CATEGORY_RECENT, R.string.emoji_category_recent))
|
||||
this.custom_page_idx = page_list.size
|
||||
page_list.add(EmojiPickerPage(CATEGORY_CUSTOM, R.string.emoji_category_custom))
|
||||
page_list.add(EmojiPickerPage(EmojiMap201709.CATEGORY_PEOPLE, R.string.emoji_category_people))
|
||||
page_list.add(EmojiPickerPage(EmojiMap201709.CATEGORY_NATURE, R.string.emoji_category_nature))
|
||||
page_list.add(
|
||||
EmojiPickerPage(
|
||||
EmojiMap201709.CATEGORY_PEOPLE,
|
||||
R.string.emoji_category_people
|
||||
)
|
||||
)
|
||||
page_list.add(
|
||||
EmojiPickerPage(
|
||||
EmojiMap201709.CATEGORY_NATURE,
|
||||
R.string.emoji_category_nature
|
||||
)
|
||||
)
|
||||
page_list.add(EmojiPickerPage(EmojiMap201709.CATEGORY_FOODS, R.string.emoji_category_foods))
|
||||
page_list.add(EmojiPickerPage(EmojiMap201709.CATEGORY_ACTIVITY, R.string.emoji_category_activity))
|
||||
page_list.add(EmojiPickerPage(EmojiMap201709.CATEGORY_PLACES, R.string.emoji_category_places))
|
||||
page_list.add(EmojiPickerPage(EmojiMap201709.CATEGORY_OBJECTS, R.string.emoji_category_objects))
|
||||
page_list.add(EmojiPickerPage(EmojiMap201709.CATEGORY_SYMBOLS, R.string.emoji_category_symbols))
|
||||
page_list.add(
|
||||
EmojiPickerPage(
|
||||
EmojiMap201709.CATEGORY_ACTIVITY,
|
||||
R.string.emoji_category_activity
|
||||
)
|
||||
)
|
||||
page_list.add(
|
||||
EmojiPickerPage(
|
||||
EmojiMap201709.CATEGORY_PLACES,
|
||||
R.string.emoji_category_places
|
||||
)
|
||||
)
|
||||
page_list.add(
|
||||
EmojiPickerPage(
|
||||
EmojiMap201709.CATEGORY_OBJECTS,
|
||||
R.string.emoji_category_objects
|
||||
)
|
||||
)
|
||||
page_list.add(
|
||||
EmojiPickerPage(
|
||||
EmojiMap201709.CATEGORY_SYMBOLS,
|
||||
R.string.emoji_category_symbols
|
||||
)
|
||||
)
|
||||
page_list.add(EmojiPickerPage(EmojiMap201709.CATEGORY_FLAGS, R.string.emoji_category_flags))
|
||||
|
||||
this.viewRoot = activity.layoutInflater.inflate(R.layout.dlg_picker_emoji, null, false)
|
||||
|
@ -143,9 +173,9 @@ class EmojiPicker(
|
|||
pager_strip.setViewPager(pager)
|
||||
|
||||
// カスタム絵文字をロードする
|
||||
if( instance!= null && instance.isNotEmpty() ) {
|
||||
if(instance != null && instance.isNotEmpty()) {
|
||||
setCustomEmojiList(
|
||||
App1.custom_emoji_lister.getList(instance){
|
||||
App1.custom_emoji_lister.getList(instance) {
|
||||
setCustomEmojiList(it) // ロード完了時に呼ばれる
|
||||
}
|
||||
)
|
||||
|
@ -159,8 +189,7 @@ class EmojiPicker(
|
|||
w?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)
|
||||
}
|
||||
|
||||
|
||||
var bInstanceHasCustomEmoji = false
|
||||
private var bInstanceHasCustomEmoji = false
|
||||
|
||||
private fun setCustomEmojiList(list : ArrayList<CustomEmoji>?) {
|
||||
if(list == null) return
|
||||
|
@ -168,7 +197,7 @@ class EmojiPicker(
|
|||
custom_list.clear()
|
||||
for(emoji in list) {
|
||||
custom_list.add(EmojiItem(emoji.shortcode, instance))
|
||||
emoji_url_map.put(emoji.shortcode, emoji.url)
|
||||
emoji_url_map[emoji.shortcode] = emoji.url
|
||||
}
|
||||
pager_adapter.getPageViewHolder(custom_page_idx)?.notifyDataSetChanged()
|
||||
pager_adapter.getPageViewHolder(recent_page_idx)?.notifyDataSetChanged()
|
||||
|
@ -224,7 +253,6 @@ class EmojiPicker(
|
|||
showSkinTone()
|
||||
}
|
||||
|
||||
|
||||
internal inner class EmojiPickerPage(category_id : Int, title_id : Int) {
|
||||
val title : String
|
||||
val emoji_list : ArrayList<EmojiItem>
|
||||
|
@ -252,7 +280,9 @@ class EmojiPicker(
|
|||
|
||||
}
|
||||
|
||||
inner class EmojiPickerPageViewHolder(activity : Activity, root : View) : BaseAdapter(), AdapterView.OnItemClickListener {
|
||||
inner class EmojiPickerPageViewHolder(activity : Activity, root : View) : BaseAdapter(),
|
||||
AdapterView.OnItemClickListener {
|
||||
|
||||
private val gridView : GridView
|
||||
private val wh : Int
|
||||
|
||||
|
@ -331,18 +361,18 @@ class EmojiPicker(
|
|||
}
|
||||
|
||||
override fun onItemClick(adapterView : AdapterView<*>, view : View, idx : Int, l : Long) {
|
||||
|
||||
|
||||
val page = this.page ?: return
|
||||
|
||||
|
||||
val item = page.emoji_list[idx]
|
||||
val name = item.name
|
||||
if( item.instance != null && item.instance.isNotEmpty() ) {
|
||||
if(item.instance != null && item.instance.isNotEmpty()) {
|
||||
// カスタム絵文字
|
||||
selected(name, item.instance)
|
||||
}else{
|
||||
} else {
|
||||
EmojiMap201709.sShortNameToImageId[name] ?: return
|
||||
// 普通の絵文字
|
||||
selected( if(selected_tone != 0) applySkinTone(name) else name, null)
|
||||
selected(if(selected_tone != 0) applySkinTone(name) else name, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -355,9 +385,9 @@ class EmojiPicker(
|
|||
val pref = App1.pref
|
||||
val list = ArrayList<JSONObject>()
|
||||
val sv = Pref.spEmojiPickerRecent(pref)
|
||||
if( sv.isNotEmpty() ) {
|
||||
if(sv.isNotEmpty()) {
|
||||
try {
|
||||
val array = JSONArray(sv)
|
||||
val array = sv.toJsonArray()
|
||||
var i = 0
|
||||
val ie = array.length()
|
||||
while(i < ie) {
|
||||
|
@ -372,16 +402,16 @@ class EmojiPicker(
|
|||
|
||||
// 選択された絵文字と同じ項目を除去
|
||||
// 項目が増えすぎたら減らす
|
||||
run{
|
||||
run {
|
||||
val it = list.iterator()
|
||||
var nCount = 0
|
||||
while( it.hasNext()) {
|
||||
while(it.hasNext()) {
|
||||
val item = it.next()
|
||||
if(name == Utils.optStringX(item, "name")
|
||||
&& instance == Utils.optStringX(item, "instance")
|
||||
) {
|
||||
if(name == item.parseString( "name")
|
||||
&& instance == item.parseString( "instance")
|
||||
) {
|
||||
it.remove()
|
||||
}else if( ++nCount >= 256){
|
||||
} else if(++ nCount >= 256) {
|
||||
it.remove()
|
||||
}
|
||||
}
|
||||
|
@ -407,7 +437,7 @@ class EmojiPicker(
|
|||
|
||||
}
|
||||
|
||||
onEmojiPicked(name,instance,bInstanceHasCustomEmoji)
|
||||
onEmojiPicked(name, instance, bInstanceHasCustomEmoji)
|
||||
}
|
||||
|
||||
internal inner class EmojiPickerPagerAdapter : PagerAdapter() {
|
||||
|
@ -454,7 +484,7 @@ class EmojiPicker(
|
|||
}
|
||||
|
||||
override fun destroyItem(container : ViewGroup, page_idx : Int, obj : Any) {
|
||||
if( obj is View ){
|
||||
if(obj is View) {
|
||||
container.removeView(obj)
|
||||
//
|
||||
val holder = holder_list.get(page_idx)
|
||||
|
|
|
@ -19,7 +19,7 @@ import java.util.ArrayList
|
|||
|
||||
import jp.juggler.subwaytooter.R
|
||||
import jp.juggler.subwaytooter.util.LogCategory
|
||||
import jp.juggler.subwaytooter.util.Utils
|
||||
import jp.juggler.subwaytooter.util.showToast
|
||||
|
||||
object LoginForm {
|
||||
private val log = LogCategory("LoginForm")
|
||||
|
@ -65,10 +65,10 @@ object LoginForm {
|
|||
val instance = etInstance.text.toString().trim { it <= ' ' }
|
||||
|
||||
if( instance.isEmpty() ) {
|
||||
Utils.showToast(activity, true, R.string.instance_not_specified)
|
||||
showToast(activity, true, R.string.instance_not_specified)
|
||||
return@OnClickListener
|
||||
} else if(instance.contains("/") || instance.contains("@")) {
|
||||
Utils.showToast(activity, true, R.string.instance_not_need_slash)
|
||||
showToast(activity, true, R.string.instance_not_need_slash)
|
||||
return@OnClickListener
|
||||
}
|
||||
onClickOk(dialog, instance, cbPseudoAccount.isChecked, cbInputAccessToken.isChecked)
|
||||
|
|
|
@ -11,8 +11,7 @@ import android.widget.TextView
|
|||
import jp.juggler.subwaytooter.R
|
||||
import jp.juggler.subwaytooter.api.entity.TootAccount
|
||||
import jp.juggler.subwaytooter.api.entity.TootStatus
|
||||
import jp.juggler.subwaytooter.util.Utils
|
||||
|
||||
import jp.juggler.subwaytooter.util.showToast
|
||||
|
||||
object ReportForm {
|
||||
|
||||
|
@ -37,7 +36,7 @@ object ReportForm {
|
|||
view.findViewById<View>(R.id.btnOk).setOnClickListener(View.OnClickListener {
|
||||
val comment = etComment.text.toString().trim { it <= ' ' }
|
||||
if(comment.isEmpty()) {
|
||||
Utils.showToast(activity, true, R.string.comment_empty)
|
||||
showToast(activity, true, R.string.comment_empty)
|
||||
return@OnClickListener
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ import jp.juggler.subwaytooter.util.LinkClickContext
|
|||
typealias MyClickableSpanClickCallback = (widget : View, span : MyClickableSpan)->Unit
|
||||
|
||||
class MyClickableSpan(
|
||||
val lcc : LinkClickContext,
|
||||
// val lcc : LinkClickContext,
|
||||
val text : String,
|
||||
val url : String,
|
||||
ac : AcctColor?,
|
||||
|
|
|
@ -6,12 +6,12 @@ import android.database.sqlite.SQLiteDatabase
|
|||
|
||||
import jp.juggler.subwaytooter.App1
|
||||
import jp.juggler.subwaytooter.util.LogCategory
|
||||
import jp.juggler.subwaytooter.util.Utils
|
||||
import android.support.v4.util.LruCache
|
||||
import android.text.Spannable
|
||||
import android.text.SpannableStringBuilder
|
||||
import android.text.style.BackgroundColorSpan
|
||||
import android.text.style.ForegroundColorSpan
|
||||
import jp.juggler.subwaytooter.util.sanitizeBDI
|
||||
|
||||
import java.util.Locale
|
||||
|
||||
|
@ -23,7 +23,13 @@ class AcctColor {
|
|||
var nickname : String? = null
|
||||
var notification_sound : String? = null
|
||||
|
||||
constructor(acct : String, nickname : String, color_fg : Int, color_bg : Int, notification_sound : String? ) {
|
||||
constructor(
|
||||
acct : String,
|
||||
nickname : String,
|
||||
color_fg : Int,
|
||||
color_bg : Int,
|
||||
notification_sound : String?
|
||||
) {
|
||||
this.acct = acct
|
||||
this.nickname = nickname
|
||||
this.color_fg = color_fg
|
||||
|
@ -46,7 +52,10 @@ class AcctColor {
|
|||
cv.put(COL_COLOR_FG, color_fg)
|
||||
cv.put(COL_COLOR_BG, color_bg)
|
||||
cv.put(COL_NICKNAME, if(nickname == null) "" else nickname)
|
||||
cv.put(COL_NOTIFICATION_SOUND, if(notification_sound == null) "" else notification_sound)
|
||||
cv.put(
|
||||
COL_NOTIFICATION_SOUND,
|
||||
if(notification_sound == null) "" else notification_sound
|
||||
)
|
||||
App1.database.replace(table, null, cv)
|
||||
mMemoryCache.remove(acct)
|
||||
} catch(ex : Throwable) {
|
||||
|
@ -80,7 +89,6 @@ class AcctColor {
|
|||
|
||||
private val mMemoryCache = LruCache<String, AcctColor>(2048)
|
||||
|
||||
|
||||
fun onDBCreate(db : SQLiteDatabase) {
|
||||
log.d("onDBCreate!")
|
||||
db.execSQL(
|
||||
|
@ -118,8 +126,6 @@ class AcctColor {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
fun load(acctArg : String) : AcctColor {
|
||||
val acct = acctArg.toLowerCase(Locale.ENGLISH)
|
||||
val cached : AcctColor? = mMemoryCache.get(acct)
|
||||
|
@ -129,10 +135,10 @@ class AcctColor {
|
|||
val where_arg = load_where_arg.get()
|
||||
where_arg[0] = acct
|
||||
App1.database.query(table, null, load_where, where_arg, null, null, null)
|
||||
.use{cursor->
|
||||
.use { cursor ->
|
||||
if(cursor.moveToNext()) {
|
||||
var idx : Int
|
||||
|
||||
|
||||
val ac = AcctColor(acct)
|
||||
|
||||
idx = cursor.getColumnIndex(COL_COLOR_FG)
|
||||
|
@ -145,7 +151,8 @@ class AcctColor {
|
|||
ac.nickname = if(cursor.isNull(idx)) null else cursor.getString(idx)
|
||||
|
||||
idx = cursor.getColumnIndex(COL_NOTIFICATION_SOUND)
|
||||
ac.notification_sound = if(cursor.isNull(idx)) null else cursor.getString(idx)
|
||||
ac.notification_sound =
|
||||
if(cursor.isNull(idx)) null else cursor.getString(idx)
|
||||
|
||||
mMemoryCache.put(acct, ac)
|
||||
return ac
|
||||
|
@ -157,7 +164,12 @@ class AcctColor {
|
|||
log.e(ex, "load failed.")
|
||||
}
|
||||
|
||||
log.d("lruCache size=%s,hit=%s,miss=%s", mMemoryCache.size(), mMemoryCache.hitCount(), mMemoryCache.missCount())
|
||||
log.d(
|
||||
"lruCache size=%s,hit=%s,miss=%s",
|
||||
mMemoryCache.size(),
|
||||
mMemoryCache.hitCount(),
|
||||
mMemoryCache.missCount()
|
||||
)
|
||||
val ac = AcctColor(acct)
|
||||
mMemoryCache.put(acct, ac)
|
||||
return ac
|
||||
|
@ -166,13 +178,13 @@ class AcctColor {
|
|||
fun getNickname(acct : String) : String {
|
||||
val ac = load(acct)
|
||||
val nickname = ac.nickname
|
||||
return if( nickname != null && nickname.isNotEmpty() ) Utils.sanitizeBDI( nickname) else acct
|
||||
return if(nickname != null && nickname.isNotEmpty()) nickname.sanitizeBDI() else acct
|
||||
}
|
||||
|
||||
fun getNotificationSound(acct : String) : String? {
|
||||
val ac = load(acct)
|
||||
val notification_sound = ac.notification_sound
|
||||
return if( notification_sound != null && notification_sound.isNotEmpty() ) notification_sound else null
|
||||
return if(notification_sound != null && notification_sound.isNotEmpty()) notification_sound else null
|
||||
}
|
||||
|
||||
fun hasNickname(ac : AcctColor?) : Boolean {
|
||||
|
@ -192,20 +204,39 @@ class AcctColor {
|
|||
mMemoryCache.evictAll()
|
||||
}
|
||||
|
||||
fun getStringWithNickname(context : Context, string_id : Int, acct : String) : CharSequence {
|
||||
fun getStringWithNickname(
|
||||
context : Context,
|
||||
string_id : Int,
|
||||
acct : String
|
||||
) : CharSequence {
|
||||
val ac = load(acct)
|
||||
val nickname = ac.nickname
|
||||
val name = if(nickname ==null || nickname.isEmpty()) acct else Utils.sanitizeBDI(nickname)
|
||||
val sb = SpannableStringBuilder(context.getString(string_id, String(charArrayOf(CHAR_REPLACE))))
|
||||
val name = if(nickname == null || nickname.isEmpty()) acct else nickname.sanitizeBDI()
|
||||
val sb = SpannableStringBuilder(
|
||||
context.getString(
|
||||
string_id,
|
||||
String(charArrayOf(CHAR_REPLACE))
|
||||
)
|
||||
)
|
||||
for(i in sb.length - 1 downTo 0) {
|
||||
val c = sb[i]
|
||||
if(c != CHAR_REPLACE) continue
|
||||
sb.replace(i, i + 1, name)
|
||||
if(ac.color_fg != 0) {
|
||||
sb.setSpan(ForegroundColorSpan(ac.color_fg), i, i + name.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
sb.setSpan(
|
||||
ForegroundColorSpan(ac.color_fg),
|
||||
i,
|
||||
i + name.length,
|
||||
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
)
|
||||
}
|
||||
if(ac.color_bg != 0) {
|
||||
sb.setSpan(BackgroundColorSpan(ac.color_bg), i, i + name.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
sb.setSpan(
|
||||
BackgroundColorSpan(ac.color_bg),
|
||||
i,
|
||||
i + name.length,
|
||||
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
)
|
||||
}
|
||||
}
|
||||
return sb
|
||||
|
|
|
@ -7,6 +7,7 @@ import org.json.JSONObject
|
|||
|
||||
import jp.juggler.subwaytooter.App1
|
||||
import jp.juggler.subwaytooter.util.LogCategory
|
||||
import jp.juggler.subwaytooter.util.toJsonObject
|
||||
|
||||
object ClientInfo {
|
||||
private val log = LogCategory("ClientInfo")
|
||||
|
@ -41,9 +42,8 @@ object ClientInfo {
|
|||
App1.database.query(table, null, "h=? and cn=?", arrayOf(instance, client_name), null, null, null)
|
||||
.use { cursor ->
|
||||
if(cursor.moveToFirst()) {
|
||||
return JSONObject(cursor.getString(cursor.getColumnIndex(COL_RESULT)))
|
||||
return cursor.getString(cursor.getColumnIndex(COL_RESULT)).toJsonObject()
|
||||
}
|
||||
|
||||
}
|
||||
} catch(ex : Throwable) {
|
||||
log.e(ex, "load failed.")
|
||||
|
|
|
@ -4,14 +4,11 @@ import android.content.ContentValues
|
|||
import android.database.Cursor
|
||||
import android.database.sqlite.SQLiteDatabase
|
||||
|
||||
import org.json.JSONException
|
||||
import org.json.JSONObject
|
||||
|
||||
import jp.juggler.subwaytooter.App1
|
||||
import jp.juggler.subwaytooter.api.entity.notEmptyOrThrow
|
||||
import jp.juggler.subwaytooter.util.LogCategory
|
||||
import jp.juggler.subwaytooter.util.Utils
|
||||
import jp.juggler.subwaytooter.util.WordTrieTree
|
||||
import jp.juggler.subwaytooter.util.*
|
||||
|
||||
class HighlightWord {
|
||||
|
||||
|
@ -120,12 +117,12 @@ class HighlightWord {
|
|||
}
|
||||
|
||||
constructor(src : JSONObject) {
|
||||
this.id = Utils.optLongX(src, COL_ID)
|
||||
this.id = src.parseLong( COL_ID) ?: -1L
|
||||
this.name = src.notEmptyOrThrow(COL_NAME)
|
||||
this.color_bg = src.optInt(COL_COLOR_BG)
|
||||
this.color_fg = src.optInt(COL_COLOR_FG)
|
||||
this.sound_type = src.optInt(COL_SOUND_TYPE)
|
||||
this.sound_uri = Utils.optStringX(src, COL_SOUND_URI)
|
||||
this.sound_uri = src.parseString( COL_SOUND_URI)
|
||||
}
|
||||
|
||||
constructor(name : String) {
|
||||
|
|
|
@ -16,8 +16,7 @@ object MutedApp {
|
|||
const val table = "app_mute"
|
||||
const val COL_ID = "_id"
|
||||
const val COL_NAME = "name"
|
||||
private val COL_TIME_SAVE = "time_save"
|
||||
|
||||
private const val COL_TIME_SAVE = "time_save"
|
||||
|
||||
fun onDBCreate(db : SQLiteDatabase) {
|
||||
log.d("onDBCreate!")
|
||||
|
|
|
@ -12,7 +12,8 @@ import java.util.Collections
|
|||
|
||||
import jp.juggler.subwaytooter.App1
|
||||
import jp.juggler.subwaytooter.util.LogCategory
|
||||
import jp.juggler.subwaytooter.util.Utils
|
||||
import jp.juggler.subwaytooter.util.digestSHA256
|
||||
import jp.juggler.subwaytooter.util.toJsonObject
|
||||
|
||||
class PostDraft {
|
||||
|
||||
|
@ -49,11 +50,11 @@ class PostDraft {
|
|||
|
||||
private val log = LogCategory("PostDraft")
|
||||
|
||||
private val table = "post_draft"
|
||||
private val COL_ID = BaseColumns._ID
|
||||
private val COL_TIME_SAVE = "time_save"
|
||||
private val COL_JSON = "json"
|
||||
private val COL_HASH = "hash"
|
||||
private const val table = "post_draft"
|
||||
private const val COL_ID = BaseColumns._ID
|
||||
private const val COL_TIME_SAVE = "time_save"
|
||||
private const val COL_JSON = "json"
|
||||
private const val COL_HASH = "hash"
|
||||
|
||||
fun onDBCreate(db : SQLiteDatabase) {
|
||||
log.d("onDBCreate!")
|
||||
|
@ -110,7 +111,7 @@ class PostDraft {
|
|||
sb.append("=")
|
||||
sb.append(v)
|
||||
}
|
||||
val hash = Utils.digestSHA256(sb.toString())
|
||||
val hash = sb.toString().digestSHA256()
|
||||
|
||||
// save to db
|
||||
val cv = ContentValues()
|
||||
|
@ -126,17 +127,13 @@ class PostDraft {
|
|||
|
||||
fun hasDraft() : Boolean {
|
||||
try {
|
||||
val cursor = App1.database.query(table, arrayOf("count(*)"), null, null, null, null, null)
|
||||
if(cursor != null) {
|
||||
try {
|
||||
App1.database.query(table, arrayOf("count(*)"), null, null, null, null, null)
|
||||
.use { cursor ->
|
||||
if(cursor.moveToNext()) {
|
||||
val count = cursor.getInt(0)
|
||||
return count > 0
|
||||
}
|
||||
} finally {
|
||||
cursor.close()
|
||||
}
|
||||
}
|
||||
} catch(ex : Throwable) {
|
||||
log.trace(ex)
|
||||
log.e(ex, "hasDraft failed.")
|
||||
|
@ -147,7 +144,15 @@ class PostDraft {
|
|||
|
||||
fun createCursor() : Cursor? {
|
||||
try {
|
||||
return App1.database.query(table, null, null, null, null, null, COL_TIME_SAVE + " desc")
|
||||
return App1.database.query(
|
||||
table,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
COL_TIME_SAVE + " desc"
|
||||
)
|
||||
} catch(ex : Throwable) {
|
||||
log.trace(ex)
|
||||
log.e(ex, "createCursor failed.")
|
||||
|
@ -168,7 +173,7 @@ class PostDraft {
|
|||
dst.id = cursor.getLong(colIdx.idx_id)
|
||||
dst.time_save = cursor.getLong(colIdx.idx_time_save)
|
||||
try {
|
||||
dst.json = JSONObject(cursor.getString(colIdx.idx_json))
|
||||
dst.json = cursor.getString(colIdx.idx_json).toJsonObject()
|
||||
} catch(ex : Throwable) {
|
||||
log.trace(ex)
|
||||
dst.json = JSONObject()
|
||||
|
|
|
@ -19,7 +19,8 @@ import jp.juggler.subwaytooter.api.entity.TootAccount
|
|||
import jp.juggler.subwaytooter.api.entity.TootInstance
|
||||
import jp.juggler.subwaytooter.util.LinkClickContext
|
||||
import jp.juggler.subwaytooter.util.LogCategory
|
||||
import jp.juggler.subwaytooter.util.Utils
|
||||
import jp.juggler.subwaytooter.util.parseString
|
||||
import jp.juggler.subwaytooter.util.toJsonObject
|
||||
|
||||
class SavedAccount(
|
||||
val db_id : Long,
|
||||
|
@ -92,7 +93,7 @@ class SavedAccount(
|
|||
cursor.getString(cursor.getColumnIndex(COL_HOST)) // host
|
||||
) {
|
||||
|
||||
val jsonAccount = JSONObject(cursor.getString(cursor.getColumnIndex(COL_ACCOUNT)))
|
||||
val jsonAccount = cursor.getString(cursor.getColumnIndex(COL_ACCOUNT)).toJsonObject()
|
||||
|
||||
val loginAccount = TootAccount.parse(
|
||||
context,
|
||||
|
@ -133,7 +134,7 @@ class SavedAccount(
|
|||
|
||||
this.register_time = cursor.getLong(cursor.getColumnIndex(COL_REGISTER_TIME))
|
||||
|
||||
this.token_info = JSONObject(cursor.getString(cursor.getColumnIndex(COL_TOKEN)))
|
||||
this.token_info = cursor.getString(cursor.getColumnIndex(COL_TOKEN)).toJsonObject()
|
||||
|
||||
this.sound_uri = cursor.getString(cursor.getColumnIndex(COL_SOUND_URI))
|
||||
}
|
||||
|
@ -561,7 +562,7 @@ class SavedAccount(
|
|||
|
||||
}
|
||||
|
||||
val REGISTER_KEY_UNREGISTERED = "unregistered"
|
||||
const val REGISTER_KEY_UNREGISTERED = "unregistered"
|
||||
|
||||
fun clearRegistrationCache() {
|
||||
val cv = ContentValues()
|
||||
|
@ -735,7 +736,7 @@ class SavedAccount(
|
|||
}
|
||||
|
||||
fun getAccessToken() : String? {
|
||||
return token_info?.let { Utils.optStringX(it, "access_token") }
|
||||
return token_info?.parseString("access_token")
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,188 @@
|
|||
package jp.juggler.subwaytooter.util
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.*
|
||||
import android.net.Uri
|
||||
import it.sephiroth.android.library.exif2.ExifInterface
|
||||
|
||||
object BitmapUtils {
|
||||
internal val log = LogCategory("BitmapUtils")
|
||||
}
|
||||
fun createResizedBitmap(
|
||||
context : Context,
|
||||
uri : Uri,
|
||||
resizeToArg : Int,
|
||||
skipIfNoNeedToResizeAndRotate : Boolean =false
|
||||
) : Bitmap? {
|
||||
var resize_to = resizeToArg
|
||||
try {
|
||||
|
||||
// EXIF回転情報の取得
|
||||
val orientation : Int? = context.contentResolver.openInputStream(uri)?.use { inStream ->
|
||||
val exif = ExifInterface()
|
||||
exif.readExif(
|
||||
inStream,
|
||||
ExifInterface.Options.OPTION_IFD_0 or ExifInterface.Options.OPTION_IFD_1 or ExifInterface.Options.OPTION_IFD_EXIF
|
||||
)
|
||||
exif.getTagIntValue(ExifInterface.TAG_ORIENTATION)
|
||||
}
|
||||
|
||||
// 画像のサイズを調べる
|
||||
val options = BitmapFactory.Options()
|
||||
options.inJustDecodeBounds = true
|
||||
options.inScaled = false
|
||||
options.outWidth = 0
|
||||
options.outHeight = 0
|
||||
context.contentResolver.openInputStream(uri)?.use { inStream ->
|
||||
BitmapFactory.decodeStream(inStream, null, options)
|
||||
}
|
||||
var src_width = options.outWidth
|
||||
var src_height = options.outHeight
|
||||
if(src_width <= 0 || src_height <= 0) {
|
||||
showToast(context, false, "could not get image bounds.")
|
||||
return null
|
||||
}
|
||||
|
||||
// 長辺
|
||||
val size = if(src_width > src_height) src_width else src_height
|
||||
|
||||
// リサイズも回転も必要がない場合
|
||||
if(skipIfNoNeedToResizeAndRotate
|
||||
&& (orientation == null || orientation == 1)
|
||||
&& (resize_to <= 0 || size <= resize_to)) {
|
||||
BitmapUtils.log.d("createOpener: no need to resize & rotate")
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
if(size > resize_to) {
|
||||
// 縮小が必要
|
||||
} else {
|
||||
// 縮小は不要
|
||||
resize_to = size
|
||||
}
|
||||
|
||||
// inSampleSizeを計算
|
||||
var bits = 0
|
||||
var x = size
|
||||
while(x > resize_to * 2) {
|
||||
++ bits
|
||||
x = x shr 1
|
||||
}
|
||||
options.inJustDecodeBounds = false
|
||||
options.inSampleSize = 1 shl bits
|
||||
|
||||
val sourceBitmap : Bitmap? =
|
||||
context.contentResolver.openInputStream(uri)?.use { inStream ->
|
||||
BitmapFactory.decodeStream(inStream, null, options)
|
||||
}
|
||||
|
||||
if(sourceBitmap == null) {
|
||||
showToast(context, false, "could not decode image.")
|
||||
return null
|
||||
}
|
||||
try {
|
||||
src_width = options.outWidth
|
||||
src_height = options.outHeight
|
||||
val scale : Float
|
||||
var dst_width : Int
|
||||
var dst_height : Int
|
||||
if(src_width >= src_height) {
|
||||
scale = resize_to / src_width.toFloat()
|
||||
dst_width = resize_to
|
||||
dst_height = (0.5f + src_height / src_width.toFloat() * resize_to).toInt()
|
||||
if(dst_height < 1) dst_height = 1
|
||||
} else {
|
||||
scale = resize_to / src_height.toFloat()
|
||||
dst_height = resize_to
|
||||
dst_width = (0.5f + src_width / src_height.toFloat() * resize_to).toInt()
|
||||
if(dst_width < 1) dst_width = 1
|
||||
}
|
||||
|
||||
val matrix = Matrix()
|
||||
matrix.reset()
|
||||
|
||||
// 画像の中心が原点に来るようにして
|
||||
matrix.postTranslate(src_width * - 0.5f, src_height * - 0.5f)
|
||||
// スケーリング
|
||||
matrix.postScale(scale, scale)
|
||||
// 回転情報があれば回転
|
||||
if(orientation != null) {
|
||||
val tmp : Int
|
||||
when(orientation) {
|
||||
|
||||
2 -> matrix.postScale(1f, - 1f) // 上下反転
|
||||
3 -> matrix.postRotate(180f) // 180度回転
|
||||
4 -> matrix.postScale(- 1f, 1f) // 左右反転
|
||||
|
||||
5 -> {
|
||||
tmp = dst_width
|
||||
|
||||
dst_width = dst_height
|
||||
dst_height = tmp
|
||||
matrix.postScale(1f, - 1f)
|
||||
matrix.postRotate(- 90f)
|
||||
}
|
||||
|
||||
6 -> {
|
||||
tmp = dst_width
|
||||
|
||||
dst_width = dst_height
|
||||
dst_height = tmp
|
||||
matrix.postRotate(90f)
|
||||
}
|
||||
|
||||
7 -> {
|
||||
tmp = dst_width
|
||||
|
||||
dst_width = dst_height
|
||||
dst_height = tmp
|
||||
matrix.postScale(1f, - 1f)
|
||||
matrix.postRotate(90f)
|
||||
}
|
||||
|
||||
8 -> {
|
||||
tmp = dst_width
|
||||
|
||||
dst_width = dst_height
|
||||
dst_height = tmp
|
||||
matrix.postRotate(- 90f)
|
||||
}
|
||||
|
||||
else -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
// 表示領域に埋まるように平行移動
|
||||
matrix.postTranslate(dst_width * 0.5f, dst_height * 0.5f)
|
||||
|
||||
// 出力用Bitmap作成
|
||||
var dst : Bitmap? =
|
||||
Bitmap.createBitmap(dst_width, dst_height, Bitmap.Config.ARGB_8888)
|
||||
try {
|
||||
return if(dst == null) {
|
||||
showToast(context, false, "bitmap creation failed.")
|
||||
null
|
||||
} else {
|
||||
val canvas = Canvas(dst)
|
||||
val paint = Paint()
|
||||
paint.isFilterBitmap = true
|
||||
canvas.drawBitmap(sourceBitmap, matrix, paint)
|
||||
BitmapUtils.log.d("createResizedBitmap: resized to %sx%s", dst_width, dst_height)
|
||||
val tmp = dst
|
||||
dst = null
|
||||
tmp
|
||||
}
|
||||
} finally {
|
||||
dst?.recycle()
|
||||
}
|
||||
} finally {
|
||||
sourceBitmap.recycle()
|
||||
}
|
||||
} catch(ex : SecurityException) {
|
||||
BitmapUtils.log.e(ex, "maybe we need pick up image again.")
|
||||
} catch(ex : Throwable) {
|
||||
BitmapUtils.log.trace(ex)
|
||||
}
|
||||
return null
|
||||
}
|
|
@ -15,21 +15,6 @@ class ChromeTabOpener(
|
|||
var allowIntercept :Boolean = true
|
||||
) {
|
||||
|
||||
fun setAccessInfo(access_info : SavedAccount?) : ChromeTabOpener {
|
||||
this.accessInfo = access_info
|
||||
return this
|
||||
}
|
||||
|
||||
fun allowIntercept( v:Boolean ): ChromeTabOpener{
|
||||
this.allowIntercept = v;
|
||||
return this
|
||||
}
|
||||
|
||||
fun setTagList(tag_list : ArrayList<String>) : ChromeTabOpener {
|
||||
this.tagList = tag_list
|
||||
return this
|
||||
}
|
||||
|
||||
fun open() {
|
||||
activity.openChromeTab(this)
|
||||
}
|
||||
|
|
|
@ -189,7 +189,7 @@ class CustomEmojiCache(internal val context : Context) {
|
|||
var item : CacheItem? = cache[request.url]
|
||||
if(item == null) {
|
||||
item = CacheItem(request.url, frames)
|
||||
cache.put(request.url, item)
|
||||
cache[request.url] = item
|
||||
} else {
|
||||
item.frames?.dispose()
|
||||
item.frames = frames
|
||||
|
|
|
@ -4,8 +4,6 @@ import android.content.Context
|
|||
import android.os.Handler
|
||||
import android.os.SystemClock
|
||||
|
||||
import org.json.JSONArray
|
||||
|
||||
import java.util.ArrayList
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import java.util.concurrent.ConcurrentLinkedQueue
|
||||
|
@ -159,7 +157,7 @@ class CustomEmojiLister(internal val context : Context) {
|
|||
var item : CacheItem? = cache[request.instance]
|
||||
if(item == null) {
|
||||
item = CacheItem(request.instance, list)
|
||||
cache.put(request.instance, item)
|
||||
cache[request.instance] = item
|
||||
} else {
|
||||
item.list = list
|
||||
item.time_update = now
|
||||
|
@ -204,7 +202,7 @@ class CustomEmojiLister(internal val context : Context) {
|
|||
|
||||
private fun decodeEmojiList(data : String, instance : String) : ArrayList<CustomEmoji>? {
|
||||
return try {
|
||||
val list = parseList(::CustomEmoji, JSONArray(data))
|
||||
val list = parseList(::CustomEmoji, data.toJsonArray() )
|
||||
list.sortWith(compareBy(String.CASE_INSENSITIVE_ORDER, { it.shortcode }))
|
||||
list
|
||||
} catch(ex : Throwable) {
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
@file:Suppress("unused")
|
||||
|
||||
package jp.juggler.subwaytooter.util
|
||||
|
||||
import org.w3c.dom.Element
|
||||
import org.w3c.dom.NamedNodeMap
|
||||
import java.io.ByteArrayInputStream
|
||||
import javax.xml.parsers.DocumentBuilder
|
||||
import javax.xml.parsers.DocumentBuilderFactory
|
||||
|
||||
object DomXmlUtils {
|
||||
val log = LogCategory("DomXmlUtils")
|
||||
|
||||
val xml_builder : DocumentBuilder by lazy {
|
||||
DocumentBuilderFactory.newInstance().newDocumentBuilder()
|
||||
}
|
||||
}
|
||||
|
||||
fun ByteArray.parseXml() : Element? {
|
||||
return try {
|
||||
DomXmlUtils.xml_builder.parse(ByteArrayInputStream(this)).documentElement
|
||||
} catch(ex : Throwable) {
|
||||
DomXmlUtils.log.trace(ex)
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun NamedNodeMap.getAttribute(name : String, defVal : String?) : String? {
|
||||
return this.getNamedItem(name)?.nodeValue ?: defVal
|
||||
}
|
|
@ -67,7 +67,7 @@ object HTMLDecoder {
|
|||
private val reEntity = Pattern.compile("&(#?)(\\w+);")
|
||||
private val entity_map = HashMap<String, Char>()
|
||||
private fun _addEntity(s : String, c : Char) {
|
||||
entity_map.put(s, c)
|
||||
entity_map[s] = c
|
||||
}
|
||||
|
||||
private fun chr(num : Int) : Char {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package jp.juggler.subwaytooter.util
|
||||
|
||||
import android.content.SharedPreferences
|
||||
import android.net.Uri
|
||||
import android.os.Handler
|
||||
import android.support.v7.app.AlertDialog
|
||||
import android.support.v7.app.AppCompatActivity
|
||||
|
@ -83,14 +82,14 @@ class PostHelper(
|
|||
var visibility = this.visibility ?: ""
|
||||
|
||||
if(content.isEmpty()) {
|
||||
Utils.showToast(activity, true, R.string.post_error_contents_empty)
|
||||
showToast(activity, true, R.string.post_error_contents_empty)
|
||||
return
|
||||
}
|
||||
|
||||
// nullはCWチェックなしを示す
|
||||
// nullじゃなくてカラならエラー
|
||||
if(spoiler_text != null && spoiler_text.isEmpty()) {
|
||||
Utils.showToast(activity, true, R.string.post_error_contents_warning_empty)
|
||||
showToast(activity, true, R.string.post_error_contents_warning_empty)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -105,19 +104,19 @@ class PostHelper(
|
|||
val item = enquete_items[n]
|
||||
if(item.isEmpty()) {
|
||||
if(n < 2) {
|
||||
Utils.showToast(activity, true, R.string.enquete_item_is_empty, n + 1)
|
||||
showToast(activity, true, R.string.enquete_item_is_empty, n + 1)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
val code_count = item.codePointCount(0, item.length)
|
||||
if(code_count > 15) {
|
||||
val over = code_count - 15
|
||||
Utils.showToast(activity, true, R.string.enquete_item_too_long, n + 1, over)
|
||||
showToast(activity, true, R.string.enquete_item_too_long, n + 1, over)
|
||||
return
|
||||
} else if(n > 0) {
|
||||
for(i in 0 until n) {
|
||||
if(item == enquete_items[i]) {
|
||||
Utils.showToast(activity, true, R.string.enquete_item_duplicate, n + 1)
|
||||
showToast(activity, true, R.string.enquete_item_duplicate, n + 1)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -239,11 +238,11 @@ class PostHelper(
|
|||
val sb = StringBuilder()
|
||||
|
||||
sb.append("status=")
|
||||
sb.append(Uri.encode(EmojiDecoder.decodeShortCode(content)))
|
||||
sb.append(EmojiDecoder.decodeShortCode(content).encodePercent())
|
||||
|
||||
if(visibility_checked != null) {
|
||||
sb.append("&visibility=")
|
||||
sb.append(Uri.encode(visibility_checked))
|
||||
sb.append(visibility_checked.encodePercent())
|
||||
}
|
||||
|
||||
if(bNSFW) {
|
||||
|
@ -252,7 +251,7 @@ class PostHelper(
|
|||
|
||||
if(spoiler_text?.isNotEmpty() == true) {
|
||||
sb.append("&spoiler_text=")
|
||||
sb.append(Uri.encode(EmojiDecoder.decodeShortCode(spoiler_text)))
|
||||
sb.append(EmojiDecoder.decodeShortCode(spoiler_text).encodePercent())
|
||||
}
|
||||
|
||||
if(in_reply_to_id != - 1L) {
|
||||
|
@ -273,11 +272,10 @@ class PostHelper(
|
|||
)
|
||||
}
|
||||
|
||||
val request_builder = Request.Builder()
|
||||
.post(request_body)
|
||||
val digest = Utils.digestSHA256(body_string + account.acct)
|
||||
val request_builder = Request.Builder().post(request_body)
|
||||
|
||||
if(digest != null && ! Pref.bpDontDuplicationCheck(pref) ) {
|
||||
if( ! Pref.bpDontDuplicationCheck(pref) ) {
|
||||
val digest = (body_string + account.acct).digestSHA256()
|
||||
request_builder.header("Idempotency-Key", digest)
|
||||
}
|
||||
|
||||
|
@ -316,7 +314,7 @@ class PostHelper(
|
|||
// 連投してIdempotency が同じだった場合もエラーにはならず、ここを通る
|
||||
callback(account, status)
|
||||
} else {
|
||||
Utils.showToast(activity, true, result.error)
|
||||
showToast(activity, true, result.error)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -624,7 +622,7 @@ class PostHelper(
|
|||
proc_text_changed.run()
|
||||
|
||||
// キーボードを再度表示する
|
||||
Handler(activity.mainLooper).post { Utils.showKeyboard(activity, et) }
|
||||
Handler(activity.mainLooper).post { et.showKeyboard() }
|
||||
|
||||
}.show()
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ class ProgressResponseBody private constructor(private val originalBody : Respon
|
|||
|
||||
@Suppress("MemberVisibilityCanPrivate")
|
||||
@Throws(IOException::class)
|
||||
fun bytes(body : ResponseBody, callback : ProgressResponseBodyCallback) : ByteArray {
|
||||
private fun bytes(body : ResponseBody, callback : ProgressResponseBodyCallback) : ByteArray {
|
||||
if(body is ProgressResponseBody) {
|
||||
body.callback = callback
|
||||
}
|
||||
|
|
|
@ -9,13 +9,13 @@ interface CurrentCallCallback {
|
|||
fun onCallCreated(call : Call)
|
||||
}
|
||||
|
||||
interface SimpleHttpClient{
|
||||
interface SimpleHttpClient {
|
||||
var currentCallCallback : CurrentCallCallback?
|
||||
fun getResponse(request: Request) : Response
|
||||
fun getWebSocket(request: Request, webSocketListener : WebSocketListener): WebSocket
|
||||
fun getResponse(request : Request) : Response
|
||||
fun getWebSocket(request : Request, webSocketListener : WebSocketListener) : WebSocket
|
||||
}
|
||||
|
||||
class SimpleHttpClientImpl(val okHttpClient:OkHttpClient): SimpleHttpClient{
|
||||
class SimpleHttpClientImpl(private val okHttpClient : OkHttpClient) : SimpleHttpClient {
|
||||
override var currentCallCallback : CurrentCallCallback? = null
|
||||
|
||||
override fun getResponse(request : Request) : Response {
|
||||
|
@ -24,8 +24,11 @@ class SimpleHttpClientImpl(val okHttpClient:OkHttpClient): SimpleHttpClient{
|
|||
return call.execute()
|
||||
}
|
||||
|
||||
override fun getWebSocket(request : Request, webSocketListener : WebSocketListener) : WebSocket {
|
||||
return okHttpClient.newWebSocket(request,webSocketListener)
|
||||
override fun getWebSocket(
|
||||
request : Request,
|
||||
webSocketListener : WebSocketListener
|
||||
) : WebSocket {
|
||||
return okHttpClient.newWebSocket(request, webSocketListener)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,159 @@
|
|||
@file:Suppress("unused")
|
||||
|
||||
package jp.juggler.subwaytooter.util
|
||||
|
||||
import android.content.Context
|
||||
import android.database.Cursor
|
||||
import android.net.Uri
|
||||
import android.os.Environment
|
||||
import android.os.storage.StorageManager
|
||||
import android.webkit.MimeTypeMap
|
||||
import java.io.File
|
||||
import java.util.ArrayList
|
||||
import java.util.HashMap
|
||||
|
||||
object StorageUtils{
|
||||
|
||||
private val log = LogCategory("StorageUtils")
|
||||
|
||||
private const val PATH_TREE = "tree"
|
||||
private const val PATH_DOCUMENT = "document"
|
||||
|
||||
internal class FileInfo(any_uri : String?) {
|
||||
|
||||
var uri : Uri? = null
|
||||
private var mime_type : String? = null
|
||||
|
||||
init {
|
||||
if(any_uri != null) {
|
||||
uri = if(any_uri.startsWith("/")) {
|
||||
Uri.fromFile(File(any_uri))
|
||||
} else {
|
||||
Uri.parse(any_uri)
|
||||
}
|
||||
val ext = MimeTypeMap.getFileExtensionFromUrl(any_uri)
|
||||
if(ext != null) {
|
||||
mime_type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(ext.toLowerCase())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getSecondaryStorageVolumesMap(context : Context) : Map<String, String> {
|
||||
val result = HashMap<String, String>()
|
||||
try {
|
||||
val sm = context.applicationContext.getSystemService(Context.STORAGE_SERVICE) as? StorageManager
|
||||
if(sm == null) {
|
||||
log.e("can't get StorageManager")
|
||||
} else {
|
||||
|
||||
// SDカードスロットのある7.0端末が手元にないから検証できない
|
||||
// if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.N ){
|
||||
// for(StorageVolume volume : sm.getStorageVolumes() ){
|
||||
// // String path = volume.getPath();
|
||||
// String state = volume.getState();
|
||||
//
|
||||
// }
|
||||
// }
|
||||
|
||||
val getVolumeList = sm.javaClass.getMethod("getVolumeList")
|
||||
val volumes = getVolumeList.invoke(sm)
|
||||
log.d("volumes type=%s", volumes.javaClass)
|
||||
|
||||
if(volumes is ArrayList<*>) {
|
||||
//
|
||||
for(volume in volumes) {
|
||||
val volume_clazz = volume.javaClass
|
||||
|
||||
val path = volume_clazz.getMethod("getPath").invoke(volume) as? String
|
||||
val state = volume_clazz.getMethod("getState").invoke(volume) as? String
|
||||
if(path != null && state == "mounted") {
|
||||
//
|
||||
val isPrimary = volume_clazz.getMethod("isPrimary").invoke(volume) as? Boolean
|
||||
if(isPrimary == true) result["primary"] = path
|
||||
//
|
||||
val uuid = volume_clazz.getMethod("getUuid").invoke(volume) as? String
|
||||
if(uuid != null) result[uuid] = path
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch(ex : Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
private fun isExternalStorageDocument(uri : Uri) : Boolean {
|
||||
return "com.android.externalstorage.documents" == uri.authority
|
||||
}
|
||||
|
||||
private fun getDocumentId(documentUri : Uri) : String {
|
||||
val paths = documentUri.pathSegments
|
||||
if(paths.size >= 2 && PATH_DOCUMENT == paths[0]) {
|
||||
// document
|
||||
return paths[1]
|
||||
}
|
||||
if(paths.size >= 4 && PATH_TREE == paths[0]
|
||||
&& PATH_DOCUMENT == paths[2]) {
|
||||
// document in tree
|
||||
return paths[3]
|
||||
}
|
||||
if(paths.size >= 2 && PATH_TREE == paths[0]) {
|
||||
// tree
|
||||
return paths[1]
|
||||
}
|
||||
throw IllegalArgumentException("Invalid URI: " + documentUri)
|
||||
}
|
||||
|
||||
fun getFile(context : Context, path : String) : File? {
|
||||
try {
|
||||
if(path.startsWith("/")) return File(path)
|
||||
val uri = Uri.parse(path)
|
||||
if("file" == uri.scheme) return File(uri.path)
|
||||
|
||||
// if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT )
|
||||
run {
|
||||
if(isExternalStorageDocument(uri)) {
|
||||
try {
|
||||
val docId = getDocumentId(uri)
|
||||
val split = docId.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
|
||||
if(split.size >= 2) {
|
||||
val uuid = split[0]
|
||||
if("primary".equals(uuid, ignoreCase = true)) {
|
||||
return File(Environment.getExternalStorageDirectory().toString() + "/" + split[1])
|
||||
} else {
|
||||
val volume_map = getSecondaryStorageVolumesMap(context)
|
||||
val volume_path = volume_map[uuid]
|
||||
if(volume_path != null) {
|
||||
return File(volume_path + "/" + split[1])
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch(ex : Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
// MediaStore Uri
|
||||
context.contentResolver.query(uri, null, null, null, null).use { cursor ->
|
||||
if(cursor.moveToFirst()) {
|
||||
val col_count = cursor.columnCount
|
||||
for(i in 0 until col_count) {
|
||||
val type = cursor.getType(i)
|
||||
if(type != Cursor.FIELD_TYPE_STRING) continue
|
||||
val name = cursor.getColumnName(i)
|
||||
val value = if(cursor.isNull(i)) null else cursor.getString(i)
|
||||
if(value != null && value.isNotEmpty() && "filePath" == name) return File(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch(ex : Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
}
|
|
@ -19,7 +19,8 @@ class TaskList {
|
|||
|
||||
private lateinit var _list : LinkedList<JSONObject>
|
||||
|
||||
@Synchronized private fun prepareList(context : Context) : LinkedList<JSONObject> {
|
||||
@Synchronized
|
||||
private fun prepareList(context : Context) : LinkedList<JSONObject> {
|
||||
if(! ::_list.isInitialized) {
|
||||
_list = LinkedList()
|
||||
|
||||
|
@ -27,7 +28,7 @@ class TaskList {
|
|||
context.openFileInput(FILE_TASK_LIST).use { inputStream ->
|
||||
val bao = ByteArrayOutputStream()
|
||||
IOUtils.copy(inputStream, bao)
|
||||
val array = JSONArray(Utils.decodeUTF8(bao.toByteArray()))
|
||||
val array = bao.toByteArray().decodeUTF8().toJsonArray()
|
||||
var i = 0
|
||||
val ie = array.length()
|
||||
while(i < ie) {
|
||||
|
@ -47,7 +48,8 @@ class TaskList {
|
|||
return _list
|
||||
}
|
||||
|
||||
@Synchronized private fun saveArray(context : Context) {
|
||||
@Synchronized
|
||||
private fun saveArray(context : Context) {
|
||||
val list = prepareList(context)
|
||||
try {
|
||||
log.d("saveArray size=%s", list.size)
|
||||
|
@ -55,10 +57,9 @@ class TaskList {
|
|||
for(item in list) {
|
||||
array.put(item)
|
||||
}
|
||||
val data = Utils.encodeUTF8(array.toString())
|
||||
context.openFileOutput(FILE_TASK_LIST, Context.MODE_PRIVATE).use { outStream ->
|
||||
IOUtils.write(data, outStream)
|
||||
}
|
||||
val data = array.toString().encodeUTF8()
|
||||
context.openFileOutput(FILE_TASK_LIST, Context.MODE_PRIVATE)
|
||||
.use { IOUtils.write(data, it) }
|
||||
} catch(ex : Throwable) {
|
||||
log.trace(ex)
|
||||
log.e(ex, "TaskList: saveArray failed.size=%s", list.size)
|
||||
|
|
|
@ -5,7 +5,6 @@ import jp.juggler.subwaytooter.api.TootApiResult
|
|||
import jp.juggler.subwaytooter.api.entity.TootAccount
|
||||
import jp.juggler.subwaytooter.api.entity.TootStatus
|
||||
import jp.juggler.subwaytooter.table.SavedAccount
|
||||
import org.json.JSONObject
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// callback (that returns Unit)
|
||||
|
@ -20,13 +19,6 @@ typealias SavedAccountCallback = (ai : SavedAccount) -> Unit
|
|||
|
||||
typealias DialogInterfaceCallback = (dialog: DialogInterface) -> Unit
|
||||
|
||||
typealias JSONObjectCallback = (draft : JSONObject) -> Unit
|
||||
|
||||
typealias PostCompleteCallback = (target_account : SavedAccount, status : TootStatus) -> Unit
|
||||
|
||||
typealias ProgressResponseBodyCallback = (bytesRead : Long, bytesTotal : Long)->Unit
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// checker (that returns not Unit)
|
||||
|
||||
typealias BooleanChecker = ()->Boolean
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -6,26 +6,27 @@ import android.support.annotation.NonNull;
|
|||
// Kotlin は wait/notify をサポートしてない
|
||||
// しかしConcurrent ライブラリには notify() を直接表現できるクラスがない
|
||||
// 仕方がないのでJavaコード経由でwait/notifyを呼び出す
|
||||
|
||||
@SuppressWarnings("SynchronizationOnLocalVariableOrMethodParameter")
|
||||
public class WaitNotifyHelper {
|
||||
|
||||
public static void waitEx( @NonNull Object obj, long ms) {
|
||||
|
||||
try {
|
||||
public static void waitEx( @NonNull Object obj, long ms ){
|
||||
try{
|
||||
synchronized( obj ){
|
||||
obj.wait(ms);
|
||||
obj.wait( ms );
|
||||
}
|
||||
} catch(InterruptedException ignored){
|
||||
}catch( InterruptedException ignored ){
|
||||
}
|
||||
}
|
||||
public static void notifyEx(@NonNull Object obj) {
|
||||
try {
|
||||
|
||||
public static void notifyEx( @NonNull Object obj ){
|
||||
try{
|
||||
synchronized( obj ){
|
||||
obj.notify();
|
||||
}
|
||||
} catch(Throwable ignored){
|
||||
}catch( Throwable ignored ){
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -13,9 +13,10 @@ import android.view.VelocityTracker
|
|||
import android.view.View
|
||||
|
||||
import jp.juggler.subwaytooter.util.LogCategory
|
||||
import jp.juggler.subwaytooter.util.Utils
|
||||
import jp.juggler.subwaytooter.util.runOnMainLooper
|
||||
|
||||
class PinchBitmapView(context : Context, attrs : AttributeSet?, defStyle : Int) : View(context, attrs, defStyle) {
|
||||
class PinchBitmapView(context : Context, attrs : AttributeSet?, defStyle : Int) :
|
||||
View(context, attrs, defStyle) {
|
||||
|
||||
companion object {
|
||||
|
||||
|
@ -125,6 +126,7 @@ class PinchBitmapView(context : Context, attrs : AttributeSet?, defStyle : Int)
|
|||
|
||||
// ページめくり操作のコールバック
|
||||
interface Callback {
|
||||
|
||||
fun onSwipe(delta : Int)
|
||||
|
||||
fun onMove(bitmap_w : Float, bitmap_h : Float, tx : Float, ty : Float, scale : Float)
|
||||
|
@ -319,15 +321,17 @@ class PinchBitmapView(context : Context, attrs : AttributeSet?, defStyle : Int)
|
|||
|
||||
} else if(image_move_x >= drag_width || image_move_y >= drag_width * 5f) {
|
||||
// 「画像を動かした」かどうかの最終チェック
|
||||
log.d("image was moved. not paging action. %f %f ", image_move_x / drag_width, image_move_y / drag_width
|
||||
log.d(
|
||||
"image was moved. not paging action. %f %f ",
|
||||
image_move_x / drag_width,
|
||||
image_move_y / drag_width
|
||||
)
|
||||
} else {
|
||||
log.d("paging! %f %f %f", image_move_x / drag_width, image_move_y / drag_width, xv
|
||||
log.d(
|
||||
"paging! %f %f %f", image_move_x / drag_width, image_move_y / drag_width, xv
|
||||
)
|
||||
|
||||
Utils.runOnMainThread {
|
||||
callback?.onSwipe(if(xv >= 0f) - 1 else 1)
|
||||
}
|
||||
runOnMainLooper { callback?.onSwipe(if(xv >= 0f) - 1 else 1) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -422,7 +426,11 @@ class PinchBitmapView(context : Context, attrs : AttributeSet?, defStyle : Int)
|
|||
getCoordinateOnImage(avg_on_image1, pos.avg)
|
||||
|
||||
// ズーム率を変更する
|
||||
current_scale = clip(scale_min, scale_max, start_image_scale * pos.max_radius / start_pos.max_radius)
|
||||
current_scale = clip(
|
||||
scale_min,
|
||||
scale_max,
|
||||
start_image_scale * pos.max_radius / start_pos.max_radius
|
||||
)
|
||||
|
||||
// 再び調べる
|
||||
getCoordinateOnImage(avg_on_image2, pos.avg)
|
||||
|
@ -445,13 +453,14 @@ class PinchBitmapView(context : Context, attrs : AttributeSet?, defStyle : Int)
|
|||
}
|
||||
|
||||
// 画像の表示位置を更新
|
||||
current_trans_x = clipTranslate(view_w, bitmap_w, current_scale, start_image_trans_x + move_x)
|
||||
current_trans_y = clipTranslate(view_h, bitmap_h, current_scale, start_image_trans_y + move_y)
|
||||
current_trans_x =
|
||||
clipTranslate(view_w, bitmap_w, current_scale, start_image_trans_x + move_x)
|
||||
current_trans_y =
|
||||
clipTranslate(view_h, bitmap_h, current_scale, start_image_trans_y + move_y)
|
||||
}
|
||||
|
||||
callback?.onMove(bitmap_w, bitmap_h, current_trans_x, current_trans_y, current_scale)
|
||||
invalidate()
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -115,7 +115,6 @@
|
|||
android:id="@+id/ivBackground"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:scaleType="centerCrop"
|
||||
/>
|
||||
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
@file:Suppress(
|
||||
"USELESS_CAST", "unused", "DEPRECATED_IDENTITY_EQUALS", "UNUSED_VARIABLE",
|
||||
"UNUSED_VALUE", "ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE", "VARIABLE_WITH_REDUNDANT_INITIALIZER",
|
||||
"ReplaceCallWithComparison"
|
||||
)
|
||||
|
||||
package jp.juggler.subwaytooter
|
||||
|
||||
import android.view.View
|
||||
|
@ -5,9 +11,10 @@ import org.junit.Test
|
|||
|
||||
import org.junit.Assert.*
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
//import kotlin.test.*
|
||||
|
||||
typealias TestLambdaCallback = (x:Int)->Int
|
||||
typealias TestLambdaCallback = (x : Int) -> Int
|
||||
|
||||
class TestKotlinFeature {
|
||||
|
||||
|
@ -15,7 +22,7 @@ class TestKotlinFeature {
|
|||
private val CODE_A2 = 2
|
||||
|
||||
@Test
|
||||
fun WhenExpression() {
|
||||
fun testWhenExpression() {
|
||||
|
||||
// ifを式として扱えるように、whenも式として扱える
|
||||
run {
|
||||
|
@ -113,8 +120,8 @@ class TestKotlinFeature {
|
|||
assertEquals(true, 10 !in range2)
|
||||
|
||||
// ==,=== 演算子とプリミティブ型
|
||||
val long10 : Long = 10L
|
||||
val int10 : Int = 10
|
||||
val long10 = 10L
|
||||
val int10 = 10
|
||||
val int10b : Int = generate10A()
|
||||
val int10c : Int = generate10B()
|
||||
|
||||
|
@ -154,8 +161,8 @@ class TestKotlinFeature {
|
|||
assertEquals(true, int10 as Any === generate10A() as Any)
|
||||
|
||||
// valueOfは-128..127は内部でキャッシュを行うから、値によっては同じアドレスだったり異なったりする
|
||||
var intA : Int = 0
|
||||
var intB : Int = 0
|
||||
var intA = 0
|
||||
var intB = 0
|
||||
for(i in 126 .. 127) {
|
||||
println("i=$i")
|
||||
intA = i
|
||||
|
@ -215,8 +222,8 @@ class TestKotlinFeature {
|
|||
assertEquals(0, if(nullableNull != null) 1 else 0)
|
||||
}
|
||||
|
||||
interface MyKotlinInterface{
|
||||
fun method(x:Int) :Int
|
||||
interface MyKotlinInterface {
|
||||
fun method(x : Int) : Int
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -224,74 +231,76 @@ class TestKotlinFeature {
|
|||
|
||||
// 定義例(文脈あり)
|
||||
Thread({ println("SAM 1") }).start()
|
||||
Thread{ println("SAM 2") }.start()
|
||||
Thread { println("SAM 2") }.start()
|
||||
|
||||
// 定義例(文脈不明)
|
||||
val a = Runnable({ println("SAM a") })
|
||||
|
||||
// 参照型の定義
|
||||
val ref: Runnable = a
|
||||
val ref : Runnable = a
|
||||
|
||||
// 参照型の呼び出し
|
||||
ref.run()
|
||||
|
||||
// Nullableな参照型の定義
|
||||
val refNullable : Runnable? = a
|
||||
if( refNullable != null) {
|
||||
if(refNullable != null) {
|
||||
// 呼び出し
|
||||
Thread(refNullable).start()
|
||||
}
|
||||
|
||||
View.OnClickListener{ _ ->
|
||||
View.OnClickListener { _ ->
|
||||
println("clicked")
|
||||
}.onClick( null)
|
||||
}.onClick(null)
|
||||
|
||||
// kotlinで定義したインタフェースに対してSAMコンストラクタを使えるか?
|
||||
// ダメでした
|
||||
// val ki = MyKotlinInterface{
|
||||
// it * it
|
||||
// }
|
||||
// val ki = MyKotlinInterface{
|
||||
// it * it
|
||||
// }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testLambda() {
|
||||
// 定義例(文脈あり)
|
||||
thread(start=true){println("testLambda")}
|
||||
println( 10.let{ x-> x*x})
|
||||
10.let{ println(it)}
|
||||
thread(start = true) { println("testLambda") }
|
||||
println(10.let { x -> x * x })
|
||||
10.let { println(it) }
|
||||
// 定義例(文脈不明)
|
||||
val a = { println("testLambda") }
|
||||
|
||||
// 参照型の定義
|
||||
val ref: (x:Int)->Int = { it* it }
|
||||
val ref : (x : Int) -> Int = { it * it }
|
||||
|
||||
// 参照型の呼び出し
|
||||
println( ref(10))
|
||||
println(ref(10))
|
||||
|
||||
// 参照型の定義(Nullable)
|
||||
val refNullable : TestLambdaCallback? = { it * it }
|
||||
if( refNullable != null ){
|
||||
if(refNullable != null) {
|
||||
refNullable(10)
|
||||
}
|
||||
|
||||
}
|
||||
@Test fun testAnonymousFunction(){
|
||||
|
||||
@Test
|
||||
fun testAnonymousFunction() {
|
||||
// 定義例(文脈あり)
|
||||
thread( start=true,block=fun(){ println("testAnonymousFunction")})
|
||||
println( 10.let(fun(x:Int)=x*x))
|
||||
10.let{ println(it)}
|
||||
thread(start = true, block = fun() { println("testAnonymousFunction") })
|
||||
println(10.let(fun(x : Int) = x * x))
|
||||
10.let { println(it) }
|
||||
// 定義例(文脈不明)
|
||||
val a = fun(x:Int)=x*x
|
||||
val a = fun(x : Int) = x * x
|
||||
|
||||
// 参照型の定義
|
||||
val ref: (x:Int)->Int = a
|
||||
val ref : (x : Int) -> Int = a
|
||||
|
||||
// 参照型の呼び出し
|
||||
println( ref(10))
|
||||
println(ref(10))
|
||||
|
||||
// 参照型の定義(Nullable)
|
||||
val refNullable : TestLambdaCallback? = fun(i:Int)=i*i
|
||||
if( refNullable != null ){
|
||||
val refNullable : TestLambdaCallback? = fun(i : Int) = i * i
|
||||
if(refNullable != null) {
|
||||
refNullable(10)
|
||||
}
|
||||
}
|
||||
|
@ -300,102 +309,106 @@ class TestKotlinFeature {
|
|||
fun testObjectExpression() {
|
||||
// 定義例(文脈あり)
|
||||
abstract class Base {
|
||||
abstract fun method(x:Int) :Int
|
||||
|
||||
abstract fun method(x : Int) : Int
|
||||
}
|
||||
val a = object: Base() {
|
||||
override fun method(x:Int) :Int{
|
||||
return x*x
|
||||
|
||||
val a = object : Base() {
|
||||
override fun method(x : Int) : Int {
|
||||
return x * x
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 定義例(文脈の有無で変化しない)
|
||||
// 参照型の定義
|
||||
val ref: Base = a
|
||||
val ref : Base = a
|
||||
|
||||
// 参照型の定義(Nullable)
|
||||
val refNullable : Base? = a
|
||||
if( refNullable != null ){
|
||||
if(refNullable != null) {
|
||||
val v = refNullable.method(10)
|
||||
println("OE v=$v")
|
||||
}
|
||||
|
||||
|
||||
fun caller( b : Base){
|
||||
fun caller(b : Base) {
|
||||
val v = b.method(10)
|
||||
println("OE b $v")
|
||||
}
|
||||
caller(object:Base(){
|
||||
caller(object : Base() {
|
||||
override fun method(x : Int) : Int {
|
||||
return x* x* x
|
||||
return x * x * x
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
private fun member(x :Int ) = x * x
|
||||
private fun member(x : Int) = x * x
|
||||
@Test
|
||||
fun testMemberReference() {
|
||||
fun caller( a :( receiver:TestKotlinFeature, x:Int)->Int){
|
||||
val v = a(this,10)
|
||||
fun caller(a : (receiver : TestKotlinFeature, x : Int) -> Int) {
|
||||
val v = a(this, 10)
|
||||
println("testMemberReference caller $v")
|
||||
}
|
||||
caller( TestKotlinFeature::member)
|
||||
caller(TestKotlinFeature::member)
|
||||
|
||||
val b = TestKotlinFeature::member
|
||||
val a : ( receiver:TestKotlinFeature, x:Int)->Int = TestKotlinFeature::member
|
||||
val a : (receiver : TestKotlinFeature, x : Int) -> Int = TestKotlinFeature::member
|
||||
}
|
||||
|
||||
fun methodNotInline( callback: (x:Int) ->Int):Int{
|
||||
fun methodNotInline(callback : (x : Int) -> Int) : Int {
|
||||
return callback(3)
|
||||
}
|
||||
inline fun methodInline( callback: (x:Int) ->Int):Int{
|
||||
|
||||
inline fun methodInline(callback : (x : Int) -> Int) : Int {
|
||||
return callback(5)
|
||||
}
|
||||
|
||||
@Test fun testReturn() {
|
||||
@Test
|
||||
fun testReturn() {
|
||||
|
||||
// loop@ for( i in 1..2) {
|
||||
// // 関数の引数以外の場所で定義したラムダ式
|
||||
// var x = { x : Int ->
|
||||
// break // コンパイルエラー
|
||||
// break@loop // コンパイルエラー
|
||||
// // return // コンパイルエラー
|
||||
// x * x
|
||||
// }(10)
|
||||
// println("testReturn A:$x")
|
||||
//
|
||||
// // 非インライン関数の引数として定義したラムダ式
|
||||
// x = methodNotInline { x : Int ->
|
||||
// break // コンパイルエラー
|
||||
// break@loop // コンパイルエラー
|
||||
//
|
||||
// // return // コンパイルエラー
|
||||
//
|
||||
// return@methodNotInline x * x
|
||||
// }
|
||||
// println("testReturn B:$x")
|
||||
//
|
||||
// // インライン関数の引数として定義したラムダ式
|
||||
// methodInline { x : Int ->
|
||||
// break // コンパイルエラー
|
||||
// break@loop // コンパイルエラー
|
||||
//
|
||||
// return 10 // できる
|
||||
//
|
||||
// return@methodInline 10 // できる
|
||||
// }
|
||||
// }
|
||||
// loop@ for( i in 1..2) {
|
||||
// // 関数の引数以外の場所で定義したラムダ式
|
||||
// var x = { x : Int ->
|
||||
// break // コンパイルエラー
|
||||
// break@loop // コンパイルエラー
|
||||
// // return // コンパイルエラー
|
||||
// x * x
|
||||
// }(10)
|
||||
// println("testReturn A:$x")
|
||||
//
|
||||
// // 非インライン関数の引数として定義したラムダ式
|
||||
// x = methodNotInline { x : Int ->
|
||||
// break // コンパイルエラー
|
||||
// break@loop // コンパイルエラー
|
||||
//
|
||||
// // return // コンパイルエラー
|
||||
//
|
||||
// return@methodNotInline x * x
|
||||
// }
|
||||
// println("testReturn B:$x")
|
||||
//
|
||||
// // インライン関数の引数として定義したラムダ式
|
||||
// methodInline { x : Int ->
|
||||
// break // コンパイルエラー
|
||||
// break@loop // コンパイルエラー
|
||||
//
|
||||
// return 10 // できる
|
||||
//
|
||||
// return@methodInline 10 // できる
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
private fun <A,B> A.letNotInline( code: (A)->B ) :B {
|
||||
private fun <A, B> A.letNotInline(code : (A) -> B) : B {
|
||||
return code(this)
|
||||
}
|
||||
|
||||
@Test fun testInline0(){
|
||||
var result :Int
|
||||
|
||||
@Test
|
||||
fun testInline0() {
|
||||
var result : Int
|
||||
val n = 11
|
||||
for( i in 1..10) {
|
||||
for(i in 1 .. 10) {
|
||||
println(n.letNotInline { v ->
|
||||
val rv = v * i
|
||||
result = rv
|
||||
|
@ -403,11 +416,12 @@ class TestKotlinFeature {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun testInline1(){
|
||||
var result :Int
|
||||
|
||||
@Test
|
||||
fun testInline1() {
|
||||
var result : Int
|
||||
val n = 12
|
||||
for( i in 1..10) {
|
||||
for(i in 1 .. 10) {
|
||||
println(n.let { v ->
|
||||
val rv = v * i
|
||||
result = rv
|
||||
|
@ -416,61 +430,65 @@ class TestKotlinFeature {
|
|||
}
|
||||
}
|
||||
|
||||
@Test fun testInline2(){
|
||||
var result :Int
|
||||
@Test
|
||||
fun testInline2() {
|
||||
var result : Int
|
||||
val n = 13
|
||||
for( i in 1..10) {
|
||||
for(i in 1 .. 10) {
|
||||
val rv = n * i
|
||||
result = rv
|
||||
println(rv)
|
||||
}
|
||||
}
|
||||
@Test fun testRawArray(){
|
||||
|
||||
@Test
|
||||
fun testRawArray() {
|
||||
// サイズを指定して生成
|
||||
val a = IntArray(4)
|
||||
for(i in 0 until a.size){
|
||||
a[i]=i*2
|
||||
for(i in 0 until a.size) {
|
||||
a[i] = i * 2
|
||||
}
|
||||
println( a.joinToString(","))
|
||||
println(a.joinToString(","))
|
||||
|
||||
// サイズと初期化ラムダを指定して生成
|
||||
val b = IntArray(4){ index -> index *3 }
|
||||
println( b.joinToString(","))
|
||||
|
||||
val b = IntArray(4) { index -> index * 3 }
|
||||
println(b.joinToString(","))
|
||||
|
||||
// 可変長引数で初期化するライブラリ関数
|
||||
var b2 = intArrayOf( 0,1,2,3)
|
||||
var b2 = intArrayOf(0, 1, 2, 3)
|
||||
|
||||
// 参照型の配列だと初期化ラムダが必須
|
||||
val c = Array<CharSequence>(4){ (it*4).toString() }
|
||||
println( c.joinToString(","))
|
||||
val d = Array<CharSequence?>(4){ if( it%2 == 0 ) null else (it*5).toString() }
|
||||
println( d.joinToString(","))
|
||||
val c = Array<CharSequence>(4) { (it * 4).toString() }
|
||||
println(c.joinToString(","))
|
||||
val d = Array<CharSequence?>(4) { if(it % 2 == 0) null else (it * 5).toString() }
|
||||
println(d.joinToString(","))
|
||||
|
||||
// ラムダ式の戻り値の型から配列の型パラメータが推測される
|
||||
val e = Array(4){ if( it%2 == 0 ) null else (it*6).toString() }
|
||||
println( e.joinToString(","))
|
||||
|
||||
val e = Array(4) { if(it % 2 == 0) null else (it * 6).toString() }
|
||||
println(e.joinToString(","))
|
||||
|
||||
// 可変長引数で初期化するライブラリ関数
|
||||
var e2 = arrayOf( null,1,null,2)
|
||||
var e2 = arrayOf(null, 1, null, 2)
|
||||
|
||||
}
|
||||
|
||||
@Test fun testOutProjectedType(){
|
||||
fun foo( args: Array<out Number>){
|
||||
@Test
|
||||
fun testOutProjectedType() {
|
||||
fun foo(args : Array<out Number>) {
|
||||
val sb = StringBuilder()
|
||||
for(s in args){
|
||||
for(s in args) {
|
||||
if(sb.isNotEmpty()) sb.append(',')
|
||||
sb
|
||||
.append(s.toString())
|
||||
.append('#')
|
||||
.append( s.javaClass.simpleName)
|
||||
.append(s.javaClass.simpleName)
|
||||
|
||||
}
|
||||
println(sb)
|
||||
println(args.contains(6)) // 禁止されていない。inポジションって何だ…?
|
||||
// arggs[0]=6 //禁止されている
|
||||
}
|
||||
foo( arrayOf(1,2,3))
|
||||
foo( arrayOf(1f,2f,3f))
|
||||
foo(arrayOf(1, 2, 3))
|
||||
foo(arrayOf(1f, 2f, 3f))
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue