Compare commits
5 Commits
77d780881c
...
78e315e6ea
Author | SHA1 | Date |
---|---|---|
tateisu | 78e315e6ea | |
tateisu | 9cf5a36ff4 | |
tateisu | 18f9aa1c15 | |
tateisu | d842bd5ca1 | |
tateisu | 2ed3ebe0f1 |
|
@ -1,3 +1,84 @@
|
||||||
|
#####################################
|
||||||
|
# https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||||
|
|
||||||
|
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
||||||
|
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||||
|
|
||||||
|
# User-specific stuff
|
||||||
|
.idea/**/workspace.xml
|
||||||
|
.idea/**/tasks.xml
|
||||||
|
.idea/**/usage.statistics.xml
|
||||||
|
.idea/**/dictionaries
|
||||||
|
.idea/**/shelf
|
||||||
|
|
||||||
|
# AWS User-specific
|
||||||
|
.idea/**/aws.xml
|
||||||
|
|
||||||
|
# Generated files
|
||||||
|
.idea/**/contentModel.xml
|
||||||
|
|
||||||
|
# Sensitive or high-churn files
|
||||||
|
.idea/**/dataSources/
|
||||||
|
.idea/**/dataSources.ids
|
||||||
|
.idea/**/dataSources.local.xml
|
||||||
|
.idea/**/sqlDataSources.xml
|
||||||
|
.idea/**/dynamic.xml
|
||||||
|
.idea/**/uiDesigner.xml
|
||||||
|
.idea/**/dbnavigator.xml
|
||||||
|
|
||||||
|
# Gradle
|
||||||
|
.idea/**/gradle.xml
|
||||||
|
.idea/**/libraries
|
||||||
|
|
||||||
|
# Gradle and Maven with auto-import
|
||||||
|
# When using Gradle or Maven with auto-import, you should exclude module files,
|
||||||
|
# since they will be recreated, and may cause churn. Uncomment if using
|
||||||
|
# auto-import.
|
||||||
|
# .idea/artifacts
|
||||||
|
# .idea/compiler.xml
|
||||||
|
# .idea/jarRepositories.xml
|
||||||
|
# .idea/modules.xml
|
||||||
|
# .idea/*.iml
|
||||||
|
# .idea/modules
|
||||||
|
# *.iml
|
||||||
|
# *.ipr
|
||||||
|
|
||||||
|
# CMake
|
||||||
|
cmake-build-*/
|
||||||
|
|
||||||
|
# Mongo Explorer plugin
|
||||||
|
.idea/**/mongoSettings.xml
|
||||||
|
|
||||||
|
# File-based project format
|
||||||
|
*.iws
|
||||||
|
|
||||||
|
# IntelliJ
|
||||||
|
out/
|
||||||
|
|
||||||
|
# mpeltonen/sbt-idea plugin
|
||||||
|
.idea_modules/
|
||||||
|
|
||||||
|
# JIRA plugin
|
||||||
|
atlassian-ide-plugin.xml
|
||||||
|
|
||||||
|
# Cursive Clojure plugin
|
||||||
|
.idea/replstate.xml
|
||||||
|
|
||||||
|
# SonarLint plugin
|
||||||
|
.idea/sonarlint/
|
||||||
|
|
||||||
|
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||||
|
com_crashlytics_export_strings.xml
|
||||||
|
crashlytics.properties
|
||||||
|
crashlytics-build.properties
|
||||||
|
fabric.properties
|
||||||
|
|
||||||
|
# Editor-based Rest Client
|
||||||
|
.idea/httpRequests
|
||||||
|
|
||||||
|
# Android studio 3.1+ serialized cache file
|
||||||
|
.idea/caches/build_file_checksums.ser
|
||||||
|
|
||||||
#####################################
|
#####################################
|
||||||
# https://github.com/github/gitignore/blob/master/Android.gitignore
|
# https://github.com/github/gitignore/blob/master/Android.gitignore
|
||||||
# Built application files
|
# Built application files
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectMigrations">
|
||||||
|
<option name="MigrateToGradleLocalJavaHome">
|
||||||
|
<set>
|
||||||
|
<option value="$PROJECT_DIR$" />
|
||||||
|
</set>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
|
@ -164,7 +164,7 @@ dependencies {
|
||||||
implementation(project(":anko"))
|
implementation(project(":anko"))
|
||||||
implementation(fileTree(mapOf("dir" to "src/main/libs", "include" to arrayOf("*.aar"))))
|
implementation(fileTree(mapOf("dir" to "src/main/libs", "include" to arrayOf("*.aar"))))
|
||||||
|
|
||||||
"fcmImplementation"("com.google.firebase:firebase-messaging:23.3.1")
|
"fcmImplementation"("com.google.firebase:firebase-messaging:23.4.0")
|
||||||
"fcmImplementation"("org.jetbrains.kotlinx:kotlinx-coroutines-play-services:${Vers.kotlinxCoroutinesVersion}")
|
"fcmImplementation"("org.jetbrains.kotlinx:kotlinx-coroutines-play-services:${Vers.kotlinxCoroutinesVersion}")
|
||||||
|
|
||||||
// implementation "org.conscrypt:conscrypt-android:$conscryptVersion"
|
// implementation "org.conscrypt:conscrypt-android:$conscryptVersion"
|
||||||
|
@ -272,25 +272,26 @@ tasks.register<Detekt>("detektAll") {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
// val kotlinFiles = "**/*.kt"
|
|
||||||
// include(kotlinFiles)
|
|
||||||
|
|
||||||
val resourceFiles = "**/resources/**"
|
val resourceFiles = "**/resources/**"
|
||||||
val buildFiles = "**/build/**"
|
val buildFiles = "**/build/**"
|
||||||
exclude(resourceFiles, buildFiles)
|
exclude(resourceFiles, buildFiles)
|
||||||
reports {
|
reports {
|
||||||
val buildDir = layout.buildDirectory
|
fun reportLocationByExt(ext: String) =
|
||||||
|
layout.buildDirectory
|
||||||
xml.required.set(false)
|
.file("reports/detekt/st-${name}.$ext")
|
||||||
xml.outputLocation.set(file("$buildDir/reports/detekt/st-${name}.xml"))
|
.get()
|
||||||
|
.asFile
|
||||||
html.required.set(true)
|
|
||||||
html.outputLocation.set(file("$buildDir/reports/detekt/st-${name}.html"))
|
|
||||||
|
|
||||||
txt.required.set(true)
|
txt.required.set(true)
|
||||||
txt.outputLocation.set(file("$buildDir/reports/detekt/st-${name}.txt"))
|
txt.outputLocation.set(reportLocationByExt("txt"))
|
||||||
|
|
||||||
sarif.required.set(true)
|
html.required.set(true)
|
||||||
sarif.outputLocation.set(file("$buildDir/reports/detekt/st-${name}.sarif"))
|
html.outputLocation.set(reportLocationByExt("html"))
|
||||||
|
|
||||||
|
xml.required.set(false)
|
||||||
|
xml.outputLocation.set(reportLocationByExt("xml"))
|
||||||
|
|
||||||
|
sarif.required.set(false)
|
||||||
|
sarif.outputLocation.set(reportLocationByExt("sarif"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,13 @@ import android.view.View.FOCUS_FORWARD
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.view.Window
|
import android.view.Window
|
||||||
import android.view.inputmethod.EditorInfo
|
import android.view.inputmethod.EditorInfo
|
||||||
import android.widget.*
|
import android.widget.AdapterView
|
||||||
|
import android.widget.ArrayAdapter
|
||||||
|
import android.widget.BaseAdapter
|
||||||
|
import android.widget.CompoundButton
|
||||||
|
import android.widget.FrameLayout
|
||||||
|
import android.widget.Spinner
|
||||||
|
import android.widget.TextView
|
||||||
import android.widget.TextView.OnEditorActionListener
|
import android.widget.TextView.OnEditorActionListener
|
||||||
import androidx.annotation.ColorInt
|
import androidx.annotation.ColorInt
|
||||||
import androidx.annotation.WorkerThread
|
import androidx.annotation.WorkerThread
|
||||||
|
@ -53,22 +59,35 @@ import jp.juggler.subwaytooter.util.CustomShare
|
||||||
import jp.juggler.subwaytooter.util.CustomShareTarget
|
import jp.juggler.subwaytooter.util.CustomShareTarget
|
||||||
import jp.juggler.subwaytooter.util.cn
|
import jp.juggler.subwaytooter.util.cn
|
||||||
import jp.juggler.subwaytooter.view.MyTextView
|
import jp.juggler.subwaytooter.view.MyTextView
|
||||||
import jp.juggler.util.*
|
import jp.juggler.util.backPressed
|
||||||
import jp.juggler.util.coroutine.launchAndShowError
|
import jp.juggler.util.coroutine.launchAndShowError
|
||||||
import jp.juggler.util.coroutine.launchProgress
|
import jp.juggler.util.coroutine.launchProgress
|
||||||
import jp.juggler.util.data.*
|
import jp.juggler.util.data.cast
|
||||||
|
import jp.juggler.util.data.defaultLocale
|
||||||
|
import jp.juggler.util.data.handleGetContentResult
|
||||||
|
import jp.juggler.util.data.intentOpenDocument
|
||||||
|
import jp.juggler.util.data.notEmpty
|
||||||
|
import jp.juggler.util.data.notZero
|
||||||
|
import jp.juggler.util.getPackageInfoCompat
|
||||||
import jp.juggler.util.log.LogCategory
|
import jp.juggler.util.log.LogCategory
|
||||||
import jp.juggler.util.log.dialogOrToast
|
import jp.juggler.util.log.dialogOrToast
|
||||||
import jp.juggler.util.log.showToast
|
import jp.juggler.util.log.showToast
|
||||||
import jp.juggler.util.log.withCaption
|
import jp.juggler.util.log.withCaption
|
||||||
import jp.juggler.util.ui.*
|
import jp.juggler.util.queryIntentActivitiesCompat
|
||||||
|
import jp.juggler.util.ui.ActivityResultHandler
|
||||||
|
import jp.juggler.util.ui.attrColor
|
||||||
|
import jp.juggler.util.ui.hideKeyboard
|
||||||
|
import jp.juggler.util.ui.isEnabledAlpha
|
||||||
|
import jp.juggler.util.ui.isNotOk
|
||||||
|
import jp.juggler.util.ui.launch
|
||||||
|
import jp.juggler.util.ui.vg
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileInputStream
|
import java.io.FileInputStream
|
||||||
import java.io.FileOutputStream
|
import java.io.FileOutputStream
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.io.OutputStreamWriter
|
import java.io.OutputStreamWriter
|
||||||
import java.text.NumberFormat
|
import java.text.NumberFormat
|
||||||
import java.util.*
|
import java.util.TimeZone
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
import java.util.zip.ZipEntry
|
import java.util.zip.ZipEntry
|
||||||
|
@ -175,7 +194,7 @@ class ActAppSetting : AppCompatActivity(), ColorPickerDialogListener, View.OnCli
|
||||||
if (savedInstanceState != null) {
|
if (savedInstanceState != null) {
|
||||||
try {
|
try {
|
||||||
savedInstanceState.getString(STATE_CHOOSE_INTENT_TARGET)?.let { target ->
|
savedInstanceState.getString(STATE_CHOOSE_INTENT_TARGET)?.let { target ->
|
||||||
customShareTarget = CustomShareTarget.values().firstOrNull { it.name == target }
|
customShareTarget = CustomShareTarget.entries.find { it.name == target }
|
||||||
}
|
}
|
||||||
} catch (ex: Throwable) {
|
} catch (ex: Throwable) {
|
||||||
log.e(ex, "can't restore customShareTarget.")
|
log.e(ex, "can't restore customShareTarget.")
|
||||||
|
|
|
@ -79,12 +79,14 @@ class ActCallback : AppCompatActivity() {
|
||||||
sharedIntent.set(intent)
|
sharedIntent.set(intent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
forbidUriFromApp(intent) -> {
|
forbidUriFromApp(intent) -> {
|
||||||
// last_uriをクリアする
|
// last_uriをクリアする
|
||||||
lastUri.set(null)
|
lastUri.set(null)
|
||||||
// ダイアログを閉じるまで画面遷移しない
|
// ダイアログを閉じるまで画面遷移しない
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
val uri = intent.data
|
val uri = intent.data
|
||||||
if (uri != null) {
|
if (uri != null) {
|
||||||
|
@ -123,7 +125,7 @@ class ActCallback : AppCompatActivity() {
|
||||||
val type = src.type
|
val type = src.type
|
||||||
|
|
||||||
if (type.isMediaMimeType()) {
|
if (type.isMediaMimeType()) {
|
||||||
when (action){
|
when (action) {
|
||||||
Intent.ACTION_VIEW -> {
|
Intent.ACTION_VIEW -> {
|
||||||
src.data?.let { uriOriginal ->
|
src.data?.let { uriOriginal ->
|
||||||
try {
|
try {
|
||||||
|
@ -137,6 +139,7 @@ class ActCallback : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Intent.ACTION_SEND -> {
|
Intent.ACTION_SEND -> {
|
||||||
var uri = src.getStreamUriExtra()
|
var uri = src.getStreamUriExtra()
|
||||||
?: return src // text/plainの場合
|
?: return src // text/plainの場合
|
||||||
|
@ -152,7 +155,8 @@ class ActCallback : AppCompatActivity() {
|
||||||
log.e(ex, "remake failed. src=$src")
|
log.e(ex, "remake failed. src=$src")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Intent.ACTION_SEND_MULTIPLE -> {
|
|
||||||
|
Intent.ACTION_SEND_MULTIPLE -> {
|
||||||
val listUri = src.getStreamUriListExtra()
|
val listUri = src.getStreamUriListExtra()
|
||||||
?: return null
|
?: return null
|
||||||
val listDst = ArrayList<Uri>()
|
val listDst = ArrayList<Uri>()
|
||||||
|
|
|
@ -247,7 +247,7 @@ class ActKeywordFilter : AppCompatActivity() {
|
||||||
|
|
||||||
if (result?.response?.code == 404) {
|
if (result?.response?.code == 404) {
|
||||||
// try v1
|
// try v1
|
||||||
result = client.request("${ApiPath.PATH_FILTERS}/$filterId")
|
result = client.request("${ApiPath.PATH_FILTERS_V1}/$filterId")
|
||||||
result?.jsonObject?.let {
|
result?.jsonObject?.let {
|
||||||
try {
|
try {
|
||||||
resultFilter = TootFilter(it)
|
resultFilter = TootFilter(it)
|
||||||
|
@ -391,12 +391,12 @@ class ActKeywordFilter : AppCompatActivity() {
|
||||||
return runApiTask(account) { client ->
|
return runApiTask(account) { client ->
|
||||||
if (filterId == null) {
|
if (filterId == null) {
|
||||||
client.request(
|
client.request(
|
||||||
ApiPath.PATH_FILTERS,
|
ApiPath.PATH_FILTERS_V1,
|
||||||
params.toPostRequestBuilder()
|
params.toPostRequestBuilder()
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
client.request(
|
client.request(
|
||||||
"${ApiPath.PATH_FILTERS}/$filterId",
|
"${ApiPath.PATH_FILTERS_V1}/$filterId",
|
||||||
params.toRequestBody().toPut()
|
params.toRequestBody().toPut()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,11 +7,17 @@ import jp.juggler.subwaytooter.R
|
||||||
import jp.juggler.subwaytooter.actmain.addColumn
|
import jp.juggler.subwaytooter.actmain.addColumn
|
||||||
import jp.juggler.subwaytooter.actmain.reloadAccountSetting
|
import jp.juggler.subwaytooter.actmain.reloadAccountSetting
|
||||||
import jp.juggler.subwaytooter.actmain.showColumnMatchAccount
|
import jp.juggler.subwaytooter.actmain.showColumnMatchAccount
|
||||||
import jp.juggler.subwaytooter.api.*
|
import jp.juggler.subwaytooter.api.ApiTask
|
||||||
|
import jp.juggler.subwaytooter.api.TootApiClient
|
||||||
|
import jp.juggler.subwaytooter.api.TootApiResult
|
||||||
|
import jp.juggler.subwaytooter.api.TootParser
|
||||||
import jp.juggler.subwaytooter.api.entity.Acct
|
import jp.juggler.subwaytooter.api.entity.Acct
|
||||||
import jp.juggler.subwaytooter.api.entity.EntityId
|
import jp.juggler.subwaytooter.api.entity.EntityId
|
||||||
import jp.juggler.subwaytooter.api.entity.TootStatus
|
import jp.juggler.subwaytooter.api.entity.TootStatus
|
||||||
import jp.juggler.subwaytooter.api.entity.TootVisibility
|
import jp.juggler.subwaytooter.api.entity.TootVisibility
|
||||||
|
import jp.juggler.subwaytooter.api.errorApiResult
|
||||||
|
import jp.juggler.subwaytooter.api.runApiTask2
|
||||||
|
import jp.juggler.subwaytooter.api.syncStatus
|
||||||
import jp.juggler.subwaytooter.column.ColumnType
|
import jp.juggler.subwaytooter.column.ColumnType
|
||||||
import jp.juggler.subwaytooter.column.findStatus
|
import jp.juggler.subwaytooter.column.findStatus
|
||||||
import jp.juggler.subwaytooter.dialog.DlgConfirm.confirm
|
import jp.juggler.subwaytooter.dialog.DlgConfirm.confirm
|
||||||
|
@ -27,6 +33,7 @@ import jp.juggler.util.coroutine.launchMain
|
||||||
import jp.juggler.util.data.JsonObject
|
import jp.juggler.util.data.JsonObject
|
||||||
import jp.juggler.util.log.showToast
|
import jp.juggler.util.log.showToast
|
||||||
import jp.juggler.util.network.toPostRequestBuilder
|
import jp.juggler.util.network.toPostRequestBuilder
|
||||||
|
import kotlinx.coroutines.CancellationException
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
|
|
||||||
private class BoostImpl(
|
private class BoostImpl(
|
||||||
|
@ -47,8 +54,6 @@ private class BoostImpl(
|
||||||
private val isPrivateToot = accessInfo.isMastodon &&
|
private val isPrivateToot = accessInfo.isMastodon &&
|
||||||
statusArg.visibility == TootVisibility.PrivateFollowers
|
statusArg.visibility == TootVisibility.PrivateFollowers
|
||||||
|
|
||||||
private var bConfirmed = false
|
|
||||||
|
|
||||||
private fun preCheck(): Boolean {
|
private fun preCheck(): Boolean {
|
||||||
|
|
||||||
// アカウントからステータスにブースト操作を行っているなら、何もしない
|
// アカウントからステータスにブースト操作を行っているなら、何もしない
|
||||||
|
@ -73,15 +78,15 @@ private class BoostImpl(
|
||||||
} else {
|
} else {
|
||||||
val (result, status) = client.syncStatus(accessInfo, statusArg)
|
val (result, status) = client.syncStatus(accessInfo, statusArg)
|
||||||
when {
|
when {
|
||||||
|
result == null -> throw CancellationException()
|
||||||
status == null -> errorApiResult(result)
|
status == null -> errorApiResult(result)
|
||||||
status.reblogged -> errorApiResult(getString(R.string.already_boosted))
|
status.reblogged -> errorApiResult(R.string.already_boosted)
|
||||||
else -> status
|
else -> status
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ブースト結果をUIに反映させる
|
// ブースト結果をUIに反映させる
|
||||||
private fun after(result: TootApiResult?, newStatus: TootStatus?, unrenoteId: EntityId?) {
|
private fun after(newStatus: TootStatus?, unrenoteId: EntityId?) {
|
||||||
result ?: return // cancelled.
|
|
||||||
when {
|
when {
|
||||||
// Misskeyでunrenoteに成功した
|
// Misskeyでunrenoteに成功した
|
||||||
unrenoteId != null -> {
|
unrenoteId != null -> {
|
||||||
|
@ -143,36 +148,20 @@ private class BoostImpl(
|
||||||
}
|
}
|
||||||
callback()
|
callback()
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> activity.showToast(true, result.error)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun boostApi(client: TootApiClient, targetStatus: TootStatus): TootApiResult? =
|
suspend fun boostApi(client: TootApiClient, targetStatus: TootStatus): TootApiResult =
|
||||||
if (accessInfo.isMisskey) {
|
when {
|
||||||
if (!bSet) {
|
accessInfo.isMisskey -> when {
|
||||||
val myRenoteId = targetStatus.myRenoteId ?: errorApiResult("missing renote id.")
|
// misskey, create renote
|
||||||
|
bSet -> client.requestOrThrow(
|
||||||
client.request(
|
|
||||||
"/api/notes/delete",
|
|
||||||
accessInfo.putMisskeyApiToken().apply {
|
|
||||||
put("noteId", myRenoteId.toString())
|
|
||||||
put("renoteId", targetStatus.id.toString())
|
|
||||||
}.toPostRequestBuilder()
|
|
||||||
)?.also {
|
|
||||||
if (it.response?.code == 204) {
|
|
||||||
resultUnrenoteId = myRenoteId
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
client.request(
|
|
||||||
"/api/notes/create",
|
"/api/notes/create",
|
||||||
accessInfo.putMisskeyApiToken().apply {
|
accessInfo.putMisskeyApiToken().apply {
|
||||||
put("renoteId", targetStatus.id.toString())
|
put("renoteId", targetStatus.id.toString())
|
||||||
}.toPostRequestBuilder()
|
}.toPostRequestBuilder()
|
||||||
)?.also { result ->
|
).apply {
|
||||||
val jsonObject = result.jsonObject
|
jsonObject?.let { jsonObject ->
|
||||||
if (jsonObject != null) {
|
|
||||||
val outerStatus =
|
val outerStatus =
|
||||||
parser.status(jsonObject.jsonObject("createdNote") ?: jsonObject)
|
parser.status(jsonObject.jsonObject("createdNote") ?: jsonObject)
|
||||||
val innerStatus = outerStatus?.reblog ?: outerStatus
|
val innerStatus = outerStatus?.reblog ?: outerStatus
|
||||||
|
@ -184,73 +173,84 @@ private class BoostImpl(
|
||||||
resultStatus = innerStatus
|
resultStatus = innerStatus
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
// misskey, delete renote
|
||||||
} else {
|
else -> {
|
||||||
val b = JsonObject().apply {
|
val myRenoteId = targetStatus.myRenoteId
|
||||||
if (visibility != null) put("visibility", visibility.strMastodon)
|
?: error("missing renote id.")
|
||||||
}.toPostRequestBuilder()
|
|
||||||
|
|
||||||
client.request(
|
client.requestOrThrow(
|
||||||
|
"/api/notes/delete",
|
||||||
|
accessInfo.putMisskeyApiToken().apply {
|
||||||
|
put("noteId", myRenoteId.toString())
|
||||||
|
put("renoteId", targetStatus.id.toString())
|
||||||
|
}.toPostRequestBuilder()
|
||||||
|
).apply {
|
||||||
|
if (response?.code == 204) {
|
||||||
|
resultUnrenoteId = myRenoteId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// mastodon, reblog or unreblog
|
||||||
|
else -> client.requestOrThrow(
|
||||||
"/api/v1/statuses/${targetStatus.id}/${if (bSet) "reblog" else "unreblog"}",
|
"/api/v1/statuses/${targetStatus.id}/${if (bSet) "reblog" else "unreblog"}",
|
||||||
b
|
requestBuilder = JsonObject().apply {
|
||||||
)?.also { result ->
|
if (visibility != null) put("visibility", visibility.strMastodon)
|
||||||
|
}.toPostRequestBuilder()
|
||||||
|
).apply {
|
||||||
// reblogはreblogを表すStatusを返す
|
// reblogはreblogを表すStatusを返す
|
||||||
// unreblogはreblogしたStatusを返す
|
// unreblogはreblogしたStatusを返す
|
||||||
val s = parser.status(result.jsonObject)
|
val s = parser.status(jsonObject)
|
||||||
resultStatus = s?.reblog ?: s
|
resultStatus = s?.reblog ?: s
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun run() {
|
fun run() = activity.launchAndShowError {
|
||||||
activity.launchAndShowError {
|
if (!preCheck()) return@launchAndShowError
|
||||||
if (!preCheck()) return@launchAndShowError
|
|
||||||
|
|
||||||
if (!bConfirmed) {
|
val confirmMessage = activity.getString(
|
||||||
activity.confirm(
|
when {
|
||||||
activity.getString(
|
!bSet -> R.string.confirm_unboost_from
|
||||||
when {
|
isPrivateToot -> R.string.confirm_boost_private_from
|
||||||
!bSet -> R.string.confirm_unboost_from
|
visibility == TootVisibility.PrivateFollowers ->
|
||||||
isPrivateToot -> R.string.confirm_boost_private_from
|
R.string.confirm_private_boost_from
|
||||||
visibility == TootVisibility.PrivateFollowers -> R.string.confirm_private_boost_from
|
|
||||||
else -> R.string.confirm_boost_from
|
else -> R.string.confirm_boost_from
|
||||||
},
|
},
|
||||||
daoAcctColor.getNickname(accessInfo)
|
daoAcctColor.getNickname(accessInfo)
|
||||||
),
|
)
|
||||||
when (bSet) {
|
|
||||||
true -> accessInfo.confirmBoost
|
activity.confirm(
|
||||||
else -> accessInfo.confirmUnboost
|
message = confirmMessage,
|
||||||
}
|
isConfirmEnabled = when (bSet) {
|
||||||
) { newConfirmEnabled ->
|
true -> accessInfo.confirmBoost
|
||||||
when (bSet) {
|
else -> accessInfo.confirmUnboost
|
||||||
true -> accessInfo.confirmBoost = newConfirmEnabled
|
|
||||||
else -> accessInfo.confirmUnboost = newConfirmEnabled
|
|
||||||
}
|
|
||||||
daoSavedAccount.save(accessInfo)
|
|
||||||
activity.reloadAccountSetting(accessInfo)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
) { newConfirmEnabled ->
|
||||||
|
when (bSet) {
|
||||||
|
true -> accessInfo.confirmBoost = newConfirmEnabled
|
||||||
|
else -> accessInfo.confirmUnboost = newConfirmEnabled
|
||||||
|
}
|
||||||
|
daoSavedAccount.save(accessInfo)
|
||||||
|
activity.reloadAccountSetting(accessInfo)
|
||||||
|
}
|
||||||
|
|
||||||
// ブースト表示を更新中にする
|
// ブースト表示を更新中にする
|
||||||
activity.appState.setBusyBoost(accessInfo, statusArg)
|
activity.appState.setBusyBoost(accessInfo, statusArg)
|
||||||
activity.showColumnMatchAccount(accessInfo)
|
activity.showColumnMatchAccount(accessInfo)
|
||||||
|
try {
|
||||||
val result =
|
activity.runApiTask2(
|
||||||
activity.runApiTask(
|
accessInfo = accessInfo,
|
||||||
accessInfo,
|
progressStyle = ApiTask.PROGRESS_NONE
|
||||||
progressStyle = ApiTask.PROGRESS_NONE
|
) { client ->
|
||||||
) { client ->
|
boostApi(client, syncStatus(client))
|
||||||
try {
|
}
|
||||||
val targetStatus = syncStatus(client)
|
// カラムデータの書き換え
|
||||||
boostApi(client, targetStatus)
|
after(resultStatus, resultUnrenoteId)
|
||||||
} catch (ex: TootApiResultException) {
|
} finally {
|
||||||
ex.result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 更新中状態をリセット
|
// 更新中状態をリセット
|
||||||
activity.appState.resetBusyBoost(accessInfo, statusArg)
|
activity.appState.resetBusyBoost(accessInfo, statusArg)
|
||||||
// カラムデータの書き換え
|
// 失敗やキャンセルの場合でも表示を更新する
|
||||||
after(result, resultStatus, resultUnrenoteId)
|
|
||||||
// result == null の場合でも更新中表示の解除が必要になる
|
|
||||||
activity.showColumnMatchAccount(accessInfo)
|
activity.showColumnMatchAccount(accessInfo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,15 +3,19 @@ package jp.juggler.subwaytooter.action
|
||||||
import jp.juggler.subwaytooter.ActKeywordFilter
|
import jp.juggler.subwaytooter.ActKeywordFilter
|
||||||
import jp.juggler.subwaytooter.ActMain
|
import jp.juggler.subwaytooter.ActMain
|
||||||
import jp.juggler.subwaytooter.R
|
import jp.juggler.subwaytooter.R
|
||||||
|
import jp.juggler.subwaytooter.api.ApiPath
|
||||||
|
import jp.juggler.subwaytooter.api.TootApiClient
|
||||||
|
import jp.juggler.subwaytooter.api.TootApiResult
|
||||||
|
import jp.juggler.subwaytooter.api.TootApiResultException
|
||||||
|
import jp.juggler.subwaytooter.api.entity.EntityId
|
||||||
import jp.juggler.subwaytooter.api.entity.TootFilter
|
import jp.juggler.subwaytooter.api.entity.TootFilter
|
||||||
import jp.juggler.subwaytooter.api.runApiTask
|
import jp.juggler.subwaytooter.api.runApiTask2
|
||||||
import jp.juggler.subwaytooter.column.onFilterDeleted
|
import jp.juggler.subwaytooter.column.onFilterDeleted
|
||||||
import jp.juggler.subwaytooter.dialog.DlgConfirm.confirm
|
import jp.juggler.subwaytooter.dialog.DlgConfirm.confirm
|
||||||
import jp.juggler.subwaytooter.dialog.actionsDialog
|
import jp.juggler.subwaytooter.dialog.actionsDialog
|
||||||
import jp.juggler.subwaytooter.table.SavedAccount
|
import jp.juggler.subwaytooter.table.SavedAccount
|
||||||
import jp.juggler.util.coroutine.launchAndShowError
|
import jp.juggler.util.coroutine.launchAndShowError
|
||||||
import jp.juggler.util.log.showToast
|
import jp.juggler.util.log.showToast
|
||||||
import okhttp3.Request
|
|
||||||
|
|
||||||
// private val log = LogCategory("Action_Filter")
|
// private val log = LogCategory("Action_Filter")
|
||||||
|
|
||||||
|
@ -30,39 +34,56 @@ fun ActMain.openFilterMenu(accessInfo: SavedAccount, item: TootFilter?) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun ActMain.filterDelete(
|
suspend fun TootApiClient.filterDelete(filterId: EntityId): TootApiResult {
|
||||||
accessInfo: SavedAccount,
|
for (path in arrayOf(
|
||||||
filter: TootFilter,
|
"/api/v2/filters/${filterId}",
|
||||||
bConfirmed: Boolean = false,
|
"/api/v1/filters/${filterId}",
|
||||||
) {
|
)) {
|
||||||
launchAndShowError {
|
try {
|
||||||
if (!bConfirmed) {
|
return requestOrThrow(path = path)
|
||||||
confirm(R.string.filter_delete_confirm, filter.displayString)
|
} catch (ex: TootApiResultException) {
|
||||||
}
|
when (ex.result?.response?.code) {
|
||||||
|
404 -> continue
|
||||||
var resultFilterList: List<TootFilter>? = null
|
else -> throw ex
|
||||||
runApiTask(accessInfo) { client ->
|
|
||||||
var result =
|
|
||||||
client.request("/api/v1/filters/${filter.id}", Request.Builder().delete())
|
|
||||||
if (result != null && result.error == null) {
|
|
||||||
result = client.request("/api/v1/filters")
|
|
||||||
val jsonArray = result?.jsonArray
|
|
||||||
if (jsonArray != null) resultFilterList = TootFilter.parseList(jsonArray)
|
|
||||||
}
|
|
||||||
result
|
|
||||||
}?.let { result ->
|
|
||||||
when (val filterList = resultFilterList) {
|
|
||||||
null -> showToast(false, result.error)
|
|
||||||
|
|
||||||
else -> {
|
|
||||||
showToast(false, R.string.delete_succeeded)
|
|
||||||
for (column in appState.columnList) {
|
|
||||||
if (column.accessInfo == accessInfo) {
|
|
||||||
column.onFilterDeleted(filter, filterList)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
error("missing filter APIs.")
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun TootApiClient.filterLoad(): List<TootFilter> {
|
||||||
|
for (path in arrayOf(
|
||||||
|
ApiPath.PATH_FILTERS_V2,
|
||||||
|
ApiPath.PATH_FILTERS_V1,
|
||||||
|
)) {
|
||||||
|
try {
|
||||||
|
val jsonArray = requestOrThrow(path).jsonArray
|
||||||
|
?: error("API response has no jsonArray.")
|
||||||
|
return TootFilter.parseList(jsonArray)
|
||||||
|
?: error("TootFilter.parseList returns null.")
|
||||||
|
} catch (ex: TootApiResultException) {
|
||||||
|
when (ex.result?.response?.code) {
|
||||||
|
404 -> continue
|
||||||
|
else -> throw ex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
error("missing filter APIs.")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ActMain.filterDelete(
|
||||||
|
accessInfo: SavedAccount,
|
||||||
|
filter: TootFilter,
|
||||||
|
) = launchAndShowError {
|
||||||
|
confirm(R.string.filter_delete_confirm, filter.displayString)
|
||||||
|
val newFilters = runApiTask2(accessInfo) { client ->
|
||||||
|
client.filterDelete(filter.id)
|
||||||
|
client.filterLoad()
|
||||||
|
}
|
||||||
|
showToast(false, R.string.delete_succeeded)
|
||||||
|
for (column in appState.columnList) {
|
||||||
|
if (column.accessInfo == accessInfo) {
|
||||||
|
column.onFilterDeleted(filter, newFilters)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,8 +20,24 @@ import android.widget.ListView
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.core.view.GravityCompat
|
import androidx.core.view.GravityCompat
|
||||||
import androidx.drawerlayout.widget.DrawerLayout
|
import androidx.drawerlayout.widget.DrawerLayout
|
||||||
import jp.juggler.subwaytooter.*
|
import jp.juggler.subwaytooter.ActAbout
|
||||||
import jp.juggler.subwaytooter.action.*
|
import jp.juggler.subwaytooter.ActAppSetting
|
||||||
|
import jp.juggler.subwaytooter.ActFavMute
|
||||||
|
import jp.juggler.subwaytooter.ActHighlightWordList
|
||||||
|
import jp.juggler.subwaytooter.ActMain
|
||||||
|
import jp.juggler.subwaytooter.ActMutedApp
|
||||||
|
import jp.juggler.subwaytooter.ActMutedPseudoAccount
|
||||||
|
import jp.juggler.subwaytooter.ActMutedWord
|
||||||
|
import jp.juggler.subwaytooter.ActOSSLicense
|
||||||
|
import jp.juggler.subwaytooter.ActPushMessageList
|
||||||
|
import jp.juggler.subwaytooter.App1
|
||||||
|
import jp.juggler.subwaytooter.R
|
||||||
|
import jp.juggler.subwaytooter.action.accountAdd
|
||||||
|
import jp.juggler.subwaytooter.action.accountOpenSetting
|
||||||
|
import jp.juggler.subwaytooter.action.openColumnFromUrl
|
||||||
|
import jp.juggler.subwaytooter.action.openColumnList
|
||||||
|
import jp.juggler.subwaytooter.action.serverProfileDirectoryFromSideMenu
|
||||||
|
import jp.juggler.subwaytooter.action.timeline
|
||||||
import jp.juggler.subwaytooter.api.entity.TootStatus
|
import jp.juggler.subwaytooter.api.entity.TootStatus
|
||||||
import jp.juggler.subwaytooter.column.ColumnType
|
import jp.juggler.subwaytooter.column.ColumnType
|
||||||
import jp.juggler.subwaytooter.dialog.pickAccount
|
import jp.juggler.subwaytooter.dialog.pickAccount
|
||||||
|
@ -50,7 +66,7 @@ import jp.juggler.util.ui.createColoredDrawable
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import org.jetbrains.anko.backgroundColor
|
import org.jetbrains.anko.backgroundColor
|
||||||
import java.lang.ref.WeakReference
|
import java.lang.ref.WeakReference
|
||||||
import java.util.*
|
import java.util.TimeZone
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
|
|
||||||
|
@ -71,7 +87,7 @@ class SideMenuAdapter(
|
||||||
private const val urlOlderDevices =
|
private const val urlOlderDevices =
|
||||||
"https://github.com/tateisu/SubwayTooter/discussions/192"
|
"https://github.com/tateisu/SubwayTooter/discussions/192"
|
||||||
|
|
||||||
private val itemTypeCount = ItemType.values().size
|
private val itemTypeCount = ItemType.entries.size
|
||||||
|
|
||||||
private var lastVersionView: WeakReference<TextView>? = null
|
private var lastVersionView: WeakReference<TextView>? = null
|
||||||
|
|
||||||
|
@ -487,10 +503,12 @@ class SideMenuAdapter(
|
||||||
when (itemType) {
|
when (itemType) {
|
||||||
ItemType.IT_DIVIDER ->
|
ItemType.IT_DIVIDER ->
|
||||||
viewOrInflate(view, parent, R.layout.lv_sidemenu_separator)
|
viewOrInflate(view, parent, R.layout.lv_sidemenu_separator)
|
||||||
|
|
||||||
ItemType.IT_GROUP_HEADER ->
|
ItemType.IT_GROUP_HEADER ->
|
||||||
viewOrInflate<TextView>(view, parent, R.layout.lv_sidemenu_group).apply {
|
viewOrInflate<TextView>(view, parent, R.layout.lv_sidemenu_group).apply {
|
||||||
text = actMain.getString(title)
|
text = actMain.getString(title)
|
||||||
}
|
}
|
||||||
|
|
||||||
ItemType.IT_NORMAL ->
|
ItemType.IT_NORMAL ->
|
||||||
viewOrInflate<TextView>(view, parent, R.layout.lv_sidemenu_item).apply {
|
viewOrInflate<TextView>(view, parent, R.layout.lv_sidemenu_item).apply {
|
||||||
isAllCaps = false
|
isAllCaps = false
|
||||||
|
@ -522,6 +540,7 @@ class SideMenuAdapter(
|
||||||
background = null
|
background = null
|
||||||
text = versionText
|
text = versionText
|
||||||
}
|
}
|
||||||
|
|
||||||
ItemType.IT_TIMEZONE ->
|
ItemType.IT_TIMEZONE ->
|
||||||
viewOrInflate<TextView>(view, parent, R.layout.lv_sidemenu_item).apply {
|
viewOrInflate<TextView>(view, parent, R.layout.lv_sidemenu_item).apply {
|
||||||
textSize = 14f
|
textSize = 14f
|
||||||
|
@ -529,6 +548,7 @@ class SideMenuAdapter(
|
||||||
background = null
|
background = null
|
||||||
text = getTimeZoneString(context)
|
text = getTimeZoneString(context)
|
||||||
}
|
}
|
||||||
|
|
||||||
ItemType.IT_NOTIFICATION_PERMISSION ->
|
ItemType.IT_NOTIFICATION_PERMISSION ->
|
||||||
viewOrInflate<TextView>(view, parent, R.layout.lv_sidemenu_item).apply {
|
viewOrInflate<TextView>(view, parent, R.layout.lv_sidemenu_item).apply {
|
||||||
isAllCaps = false
|
isAllCaps = false
|
||||||
|
@ -599,6 +619,7 @@ class SideMenuAdapter(
|
||||||
Pair(R.string.notification_push_distributor_disabled) {
|
Pair(R.string.notification_push_distributor_disabled) {
|
||||||
actMain.selectPushDistributor()
|
actMain.selectPushDistributor()
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -616,6 +637,7 @@ class SideMenuAdapter(
|
||||||
|
|
||||||
else -> true
|
else -> true
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> true
|
else -> true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,7 @@ object ApiPath {
|
||||||
// リストではなくオブジェクトを返すAPI
|
// リストではなくオブジェクトを返すAPI
|
||||||
const val PATH_STATUSES = "/api/v1/statuses/%s" // 1:status_id
|
const val PATH_STATUSES = "/api/v1/statuses/%s" // 1:status_id
|
||||||
|
|
||||||
const val PATH_FILTERS = "/api/v1/filters"
|
const val PATH_FILTERS_V1 = "/api/v1/filters"
|
||||||
const val PATH_FILTERS_V2 = "/api/v2/filters"
|
const val PATH_FILTERS_V2 = "/api/v2/filters"
|
||||||
|
|
||||||
const val PATH_MISSKEY_PROFILE_FOLLOWING = "/api/users/following"
|
const val PATH_MISSKEY_PROFILE_FOLLOWING = "/api/users/following"
|
||||||
|
|
|
@ -5,16 +5,42 @@ import android.net.Uri
|
||||||
import jp.juggler.subwaytooter.App1
|
import jp.juggler.subwaytooter.App1
|
||||||
import jp.juggler.subwaytooter.R
|
import jp.juggler.subwaytooter.R
|
||||||
import jp.juggler.subwaytooter.api.auth.AuthBase
|
import jp.juggler.subwaytooter.api.auth.AuthBase
|
||||||
import jp.juggler.subwaytooter.api.entity.*
|
import jp.juggler.subwaytooter.api.entity.Acct
|
||||||
|
import jp.juggler.subwaytooter.api.entity.Host
|
||||||
|
import jp.juggler.subwaytooter.api.entity.TootAccount
|
||||||
|
import jp.juggler.subwaytooter.api.entity.TootAccountRef
|
||||||
import jp.juggler.subwaytooter.api.entity.TootAccountRef.Companion.tootAccountRefOrNull
|
import jp.juggler.subwaytooter.api.entity.TootAccountRef.Companion.tootAccountRefOrNull
|
||||||
|
import jp.juggler.subwaytooter.api.entity.TootInstance
|
||||||
|
import jp.juggler.subwaytooter.api.entity.TootResults
|
||||||
|
import jp.juggler.subwaytooter.api.entity.TootStatus
|
||||||
import jp.juggler.subwaytooter.pref.PrefB
|
import jp.juggler.subwaytooter.pref.PrefB
|
||||||
import jp.juggler.subwaytooter.table.SavedAccount
|
import jp.juggler.subwaytooter.table.SavedAccount
|
||||||
import jp.juggler.subwaytooter.util.*
|
import jp.juggler.subwaytooter.util.DecodeOptions
|
||||||
import jp.juggler.util.data.*
|
import jp.juggler.subwaytooter.util.LinkHelper
|
||||||
|
import jp.juggler.subwaytooter.util.SimpleHttpClient
|
||||||
|
import jp.juggler.subwaytooter.util.SimpleHttpClientImpl
|
||||||
|
import jp.juggler.subwaytooter.util.matchHost
|
||||||
|
import jp.juggler.util.data.CharacterGroup
|
||||||
|
import jp.juggler.util.data.JsonObject
|
||||||
|
import jp.juggler.util.data.asciiRegex
|
||||||
|
import jp.juggler.util.data.decodeJsonArray
|
||||||
|
import jp.juggler.util.data.decodeJsonObject
|
||||||
|
import jp.juggler.util.data.decodePercent
|
||||||
|
import jp.juggler.util.data.decodeUTF8
|
||||||
|
import jp.juggler.util.data.encodePercent
|
||||||
|
import jp.juggler.util.data.groupEx
|
||||||
|
import jp.juggler.util.data.letNotEmpty
|
||||||
|
import jp.juggler.util.data.notEmpty
|
||||||
import jp.juggler.util.log.LogCategory
|
import jp.juggler.util.log.LogCategory
|
||||||
import jp.juggler.util.log.withCaption
|
import jp.juggler.util.log.withCaption
|
||||||
import jp.juggler.util.network.toPostRequestBuilder
|
import jp.juggler.util.network.toPostRequestBuilder
|
||||||
import okhttp3.*
|
import kotlinx.coroutines.CancellationException
|
||||||
|
import okhttp3.Call
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
|
import okhttp3.Request
|
||||||
|
import okhttp3.Response
|
||||||
|
import okhttp3.WebSocket
|
||||||
|
import okhttp3.WebSocketListener
|
||||||
import okhttp3.internal.closeQuietly
|
import okhttp3.internal.closeQuietly
|
||||||
|
|
||||||
class TootApiClient(
|
class TootApiClient(
|
||||||
|
@ -358,7 +384,7 @@ class TootApiClient(
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// fun request(
|
// fun request(
|
||||||
// path: String,
|
// path: String,
|
||||||
// request_builder: Request.Builder = Request.Builder()
|
// request_builder: Request.Builder = Request.Builder()
|
||||||
// ): TootApiResult? {
|
// ): TootApiResult? {
|
||||||
|
@ -391,6 +417,26 @@ class TootApiClient(
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
|
|
||||||
|
/**
|
||||||
|
* requestと同じだがキャンセルやエラー発生時に例外を投げる
|
||||||
|
*/
|
||||||
|
suspend fun requestOrThrow(
|
||||||
|
path: String,
|
||||||
|
requestBuilder: Request.Builder = Request.Builder(),
|
||||||
|
forceAccessToken: String? = null,
|
||||||
|
): TootApiResult {
|
||||||
|
val result = request(
|
||||||
|
path = path,
|
||||||
|
requestBuilder = requestBuilder,
|
||||||
|
forceAccessToken = forceAccessToken,
|
||||||
|
)
|
||||||
|
when {
|
||||||
|
result == null -> throw CancellationException()
|
||||||
|
!result.error.isNullOrBlank() -> errorApiResult(result)
|
||||||
|
else -> return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun request(
|
suspend fun request(
|
||||||
path: String,
|
path: String,
|
||||||
requestBuilder: Request.Builder = Request.Builder(),
|
requestBuilder: Request.Builder = Request.Builder(),
|
||||||
|
|
|
@ -1,12 +1,18 @@
|
||||||
package jp.juggler.subwaytooter.api
|
package jp.juggler.subwaytooter.api
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.annotation.StringRes
|
||||||
|
|
||||||
class TootApiResultException(val result: TootApiResult?) :
|
class TootApiResultException(val result: TootApiResult?) :
|
||||||
Exception(result?.error ?: "cancelled.") {
|
Exception(result?.error ?: "cancelled.") {
|
||||||
constructor(error: String) : this(TootApiResult(error))
|
constructor(error: String) : this(TootApiResult(error))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun errorApiResult(result: TootApiResult?): Nothing =
|
fun errorApiResult(result: TootApiResult): Nothing =
|
||||||
throw TootApiResultException(result)
|
throw TootApiResultException(result)
|
||||||
|
|
||||||
fun errorApiResult(error: String): Nothing =
|
fun errorApiResult(error: String): Nothing =
|
||||||
throw TootApiResultException(error)
|
throw TootApiResultException(error)
|
||||||
|
|
||||||
|
fun Context.errorApiResult(@StringRes stringId: Int, vararg args: Any?): Nothing =
|
||||||
|
errorApiResult(getString(stringId, *args))
|
||||||
|
|
|
@ -2,7 +2,13 @@ package jp.juggler.subwaytooter.api.entity
|
||||||
|
|
||||||
import jp.juggler.subwaytooter.api.TootParser
|
import jp.juggler.subwaytooter.api.TootParser
|
||||||
import jp.juggler.subwaytooter.pref.PrefB
|
import jp.juggler.subwaytooter.pref.PrefB
|
||||||
import jp.juggler.util.data.*
|
import jp.juggler.util.data.JsonObject
|
||||||
|
import jp.juggler.util.data.addTo
|
||||||
|
import jp.juggler.util.data.buildJsonObject
|
||||||
|
import jp.juggler.util.data.clip
|
||||||
|
import jp.juggler.util.data.mayUri
|
||||||
|
import jp.juggler.util.data.notBlank
|
||||||
|
import jp.juggler.util.data.notEmpty
|
||||||
|
|
||||||
@Suppress("LongParameterList")
|
@Suppress("LongParameterList")
|
||||||
class TootAttachment private constructor(
|
class TootAttachment private constructor(
|
||||||
|
@ -92,7 +98,7 @@ class TootAttachment private constructor(
|
||||||
private val ext_audio = arrayOf(".mpga", ".mp3", ".aac", ".ogg")
|
private val ext_audio = arrayOf(".mpga", ".mp3", ".aac", ".ogg")
|
||||||
|
|
||||||
private fun parseType(src: String?) =
|
private fun parseType(src: String?) =
|
||||||
TootAttachmentType.values().find { it.id == src }
|
TootAttachmentType.entries.find { it.id == src }
|
||||||
|
|
||||||
private fun guessMediaTypeByUrl(src: String?): TootAttachmentType? {
|
private fun guessMediaTypeByUrl(src: String?): TootAttachmentType? {
|
||||||
val uri = src.mayUri() ?: return null
|
val uri = src.mayUri() ?: return null
|
||||||
|
@ -104,6 +110,9 @@ class TootAttachment private constructor(
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* アプリ内でencodeJson()した情報をデコードする
|
||||||
|
*/
|
||||||
fun tootAttachmentJson(
|
fun tootAttachmentJson(
|
||||||
src: JsonObject,
|
src: JsonObject,
|
||||||
): TootAttachment {
|
): TootAttachment {
|
||||||
|
@ -147,8 +156,9 @@ class TootAttachment private constructor(
|
||||||
else -> TootAttachmentType.Unknown
|
else -> TootAttachmentType.Unknown
|
||||||
}
|
}
|
||||||
val url = src.string("url")
|
val url = src.string("url")
|
||||||
val description = src.string("comment")?.notBlank()
|
val description = (src.string("comment")?.notBlank()
|
||||||
?: src.string("name")?.notBlank()
|
?: src.string("name")?.notBlank())
|
||||||
|
?.takeIf { it != "null" }
|
||||||
return TootAttachment(
|
return TootAttachment(
|
||||||
blurhash = null,
|
blurhash = null,
|
||||||
description = description,
|
description = description,
|
||||||
|
@ -189,7 +199,7 @@ class TootAttachment private constructor(
|
||||||
|
|
||||||
return TootAttachment(
|
return TootAttachment(
|
||||||
blurhash = src.string("blurhash"),
|
blurhash = src.string("blurhash"),
|
||||||
description = src.string("name"),
|
description = src.string("name")?.notBlank()?.takeIf { it != "null" },
|
||||||
focusX = parseFocusValue(focus, "x"),
|
focusX = parseFocusValue(focus, "x"),
|
||||||
focusY = parseFocusValue(focus, "y"),
|
focusY = parseFocusValue(focus, "y"),
|
||||||
id = EntityId.DEFAULT,
|
id = EntityId.DEFAULT,
|
||||||
|
@ -219,7 +229,8 @@ class TootAttachment private constructor(
|
||||||
|
|
||||||
return TootAttachment(
|
return TootAttachment(
|
||||||
blurhash = src.string("blurhash"),
|
blurhash = src.string("blurhash"),
|
||||||
description = src.string("description"),
|
description = src.string("description")
|
||||||
|
?.notBlank()?.takeIf { it != "null" },
|
||||||
focusX = parseFocusValue(focus, "x"),
|
focusX = parseFocusValue(focus, "x"),
|
||||||
focusY = parseFocusValue(focus, "y"),
|
focusY = parseFocusValue(focus, "y"),
|
||||||
id = EntityId.mayDefault(src.string("id")),
|
id = EntityId.mayDefault(src.string("id")),
|
||||||
|
|
|
@ -23,13 +23,12 @@ enum class TootFilterContext(
|
||||||
companion object {
|
companion object {
|
||||||
private val log = LogCategory("TootFilterContext")
|
private val log = LogCategory("TootFilterContext")
|
||||||
|
|
||||||
private val valuesCache = values()
|
private val apiNameMap = entries.associateBy { it.apiName }
|
||||||
private val apiNameMap = valuesCache.associateBy { it.apiName }
|
|
||||||
|
|
||||||
fun parseBits(src: JsonArray?): Int =
|
fun parseBits(src: JsonArray?): Int =
|
||||||
src?.stringList()?.mapNotNull { apiNameMap[it]?.bit }?.sum() ?: 0
|
src?.stringList()?.mapNotNull { apiNameMap[it]?.bit }?.sum() ?: 0
|
||||||
|
|
||||||
fun bitsToNames(mask: Int) =
|
fun bitsToNames(mask: Int) =
|
||||||
valuesCache.filter { it.bit.and(mask) != 0 }.map { it.caption_id }
|
entries.filter { it.bit.and(mask) != 0 }.map { it.caption_id }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,6 +68,7 @@ enum class TootVisibility(
|
||||||
LocalPublic, LocalHome -> true
|
LocalPublic, LocalHome -> true
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> when (this) {
|
else -> when (this) {
|
||||||
Public, UnlistedHome -> true
|
Public, UnlistedHome -> true
|
||||||
else -> false
|
else -> false
|
||||||
|
@ -109,47 +110,35 @@ enum class TootVisibility(
|
||||||
companion object {
|
companion object {
|
||||||
private val log = LogCategory("TootVisivbility")
|
private val log = LogCategory("TootVisivbility")
|
||||||
|
|
||||||
fun parseMastodon(a: String?): TootVisibility? {
|
fun parseMastodon(a: String?) =
|
||||||
for (v in values()) {
|
entries.find { it.strMastodon == a }
|
||||||
if (v.strMastodon == a) return v
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
fun parseMisskey(a: String?, localOnly: Boolean = false): TootVisibility? {
|
fun parseMisskey(a: String?, localOnly: Boolean = false): TootVisibility? {
|
||||||
for (v in values()) {
|
entries.find { it.strMisskey == a }?.let { v ->
|
||||||
if (v.strMisskey == a) {
|
if (localOnly) {
|
||||||
if (localOnly) {
|
when (v) {
|
||||||
when (v) {
|
Public -> return LocalPublic
|
||||||
Public -> return LocalPublic
|
UnlistedHome -> return LocalHome
|
||||||
UnlistedHome -> return LocalHome
|
PrivateFollowers -> return LocalFollowers
|
||||||
PrivateFollowers -> return LocalFollowers
|
|
||||||
|
|
||||||
else -> {
|
else -> Unit
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return v
|
|
||||||
}
|
}
|
||||||
|
return v
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
fun fromId(id: Int): TootVisibility? {
|
fun fromId(id: Int) = entries.find { it.id == id }
|
||||||
for (v in values()) {
|
|
||||||
if (v.id == id) return v
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
fun parseSavedVisibility(sv: String?): TootVisibility? {
|
fun parseSavedVisibility(sv: String?): TootVisibility? {
|
||||||
sv ?: return null
|
sv ?: return null
|
||||||
|
|
||||||
// 新しい方式ではenumのidの文字列表現
|
// 新しい方式ではenumのidの文字列表現
|
||||||
values().find { it.id.toString() == sv }?.let { return it }
|
entries.find { it.id.toString() == sv }?.let { return it }
|
||||||
|
|
||||||
// 古い方式ではマストドンの公開範囲文字列かweb_setting
|
// 古い方式ではマストドンの公開範囲文字列かweb_setting
|
||||||
values().find { it.strMastodon == sv }?.let { return it }
|
entries.find { it.strMastodon == sv }?.let { return it }
|
||||||
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,7 +70,7 @@ enum class SettingType(val id: Int) {
|
||||||
;
|
;
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val map = values().associateBy { it.id }
|
val map = entries.associateBy { it.id }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -367,7 +367,7 @@ val appSettingRoot = AppSettingItem(null, SettingType.Section, R.string.app_sett
|
||||||
spinnerSimple(
|
spinnerSimple(
|
||||||
PrefI.ipAdditionalButtonsPosition,
|
PrefI.ipAdditionalButtonsPosition,
|
||||||
R.string.additional_buttons_position,
|
R.string.additional_buttons_position,
|
||||||
*(AdditionalButtonsPosition.values().sortedBy { it.idx }.map { it.captionId }
|
*(AdditionalButtonsPosition.entries.sortedBy { it.idx }.map { it.captionId }
|
||||||
.toIntArray())
|
.toIntArray())
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -380,7 +380,7 @@ val appSettingRoot = AppSettingItem(null, SettingType.Section, R.string.app_sett
|
||||||
}
|
}
|
||||||
|
|
||||||
section(R.string.translate_or_custom_share) {
|
section(R.string.translate_or_custom_share) {
|
||||||
CustomShareTarget.values().forEach { target ->
|
for (target in CustomShareTarget.entries) {
|
||||||
item(
|
item(
|
||||||
SettingType.TextWithSelector,
|
SettingType.TextWithSelector,
|
||||||
target.pref,
|
target.pref,
|
||||||
|
@ -498,7 +498,7 @@ val appSettingRoot = AppSettingItem(null, SettingType.Section, R.string.app_sett
|
||||||
sw(PrefB.bpUseInternalMediaViewer, R.string.use_internal_media_viewer)
|
sw(PrefB.bpUseInternalMediaViewer, R.string.use_internal_media_viewer)
|
||||||
|
|
||||||
spinner(PrefI.ipMediaBackground, R.string.background_pattern) {
|
spinner(PrefI.ipMediaBackground, R.string.background_pattern) {
|
||||||
MediaBackgroundDrawable.Kind.values()
|
MediaBackgroundDrawable.Kind.entries
|
||||||
.filter { it.isMediaBackground }
|
.filter { it.isMediaBackground }
|
||||||
.map { it.name }
|
.map { it.name }
|
||||||
}
|
}
|
||||||
|
|
|
@ -326,7 +326,7 @@ object ColumnEncoder {
|
||||||
-> {
|
-> {
|
||||||
profileId = EntityId.mayNull(src.string(KEY_PROFILE_ID))
|
profileId = EntityId.mayNull(src.string(KEY_PROFILE_ID))
|
||||||
val tabId = src.optInt(KEY_PROFILE_TAB)
|
val tabId = src.optInt(KEY_PROFILE_TAB)
|
||||||
profileTab = ProfileTab.values().find { it.id == tabId } ?: ProfileTab.Status
|
profileTab = ProfileTab.entries.find { it.id == tabId } ?: ProfileTab.Status
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnType.LIST_MEMBER,
|
ColumnType.LIST_MEMBER,
|
||||||
|
|
|
@ -8,7 +8,6 @@ import jp.juggler.subwaytooter.api.TootApiClient
|
||||||
import jp.juggler.subwaytooter.api.entity.*
|
import jp.juggler.subwaytooter.api.entity.*
|
||||||
import jp.juggler.subwaytooter.api.runApiTask
|
import jp.juggler.subwaytooter.api.runApiTask
|
||||||
import jp.juggler.subwaytooter.table.*
|
import jp.juggler.subwaytooter.table.*
|
||||||
import jp.juggler.util.*
|
|
||||||
import jp.juggler.util.coroutine.launchMain
|
import jp.juggler.util.coroutine.launchMain
|
||||||
import jp.juggler.util.data.WordTrieTree
|
import jp.juggler.util.data.WordTrieTree
|
||||||
import jp.juggler.util.log.LogCategory
|
import jp.juggler.util.log.LogCategory
|
||||||
|
@ -433,7 +432,7 @@ suspend fun Column.loadFilter2(client: TootApiClient): List<TootFilter>? {
|
||||||
if (getFilterContext() == null) return null
|
if (getFilterContext() == null) return null
|
||||||
var result = client.request(ApiPath.PATH_FILTERS_V2)
|
var result = client.request(ApiPath.PATH_FILTERS_V2)
|
||||||
if (result?.response?.code == 404) {
|
if (result?.response?.code == 404) {
|
||||||
result = client.request(ApiPath.PATH_FILTERS)
|
result = client.request(ApiPath.PATH_FILTERS_V1)
|
||||||
}
|
}
|
||||||
|
|
||||||
val jsonArray = result?.jsonArray ?: return null
|
val jsonArray = result?.jsonArray ?: return null
|
||||||
|
@ -511,7 +510,7 @@ fun reloadFilter(context: Context, accessInfo: SavedAccount) {
|
||||||
) { client ->
|
) { client ->
|
||||||
var result = client.request(ApiPath.PATH_FILTERS_V2)
|
var result = client.request(ApiPath.PATH_FILTERS_V2)
|
||||||
if (result?.response?.code == 404) {
|
if (result?.response?.code == 404) {
|
||||||
result = client.request(ApiPath.PATH_FILTERS)
|
result = client.request(ApiPath.PATH_FILTERS_V1)
|
||||||
}
|
}
|
||||||
result?.jsonArray?.let {
|
result?.jsonArray?.let {
|
||||||
resultList = TootFilter.parseList(it)
|
resultList = TootFilter.parseList(it)
|
||||||
|
|
|
@ -10,7 +10,6 @@ import jp.juggler.subwaytooter.columnviewholder.scrollToTop
|
||||||
import jp.juggler.subwaytooter.notification.injectData
|
import jp.juggler.subwaytooter.notification.injectData
|
||||||
import jp.juggler.subwaytooter.pref.PrefB
|
import jp.juggler.subwaytooter.pref.PrefB
|
||||||
import jp.juggler.subwaytooter.util.OpenSticker
|
import jp.juggler.subwaytooter.util.OpenSticker
|
||||||
import jp.juggler.util.*
|
|
||||||
import jp.juggler.util.coroutine.runOnMainLooper
|
import jp.juggler.util.coroutine.runOnMainLooper
|
||||||
import jp.juggler.util.coroutine.runOnMainLooperDelayed
|
import jp.juggler.util.coroutine.runOnMainLooperDelayed
|
||||||
import jp.juggler.util.data.JsonArray
|
import jp.juggler.util.data.JsonArray
|
||||||
|
@ -902,7 +901,7 @@ class ColumnTask_Loading(
|
||||||
suspend fun getFilterList(client: TootApiClient): TootApiResult? {
|
suspend fun getFilterList(client: TootApiClient): TootApiResult? {
|
||||||
var result = client.request(ApiPath.PATH_FILTERS_V2)
|
var result = client.request(ApiPath.PATH_FILTERS_V2)
|
||||||
if (result?.response?.code == 404) {
|
if (result?.response?.code == 404) {
|
||||||
result = client.request(ApiPath.PATH_FILTERS)
|
result = client.request(ApiPath.PATH_FILTERS_V1)
|
||||||
}
|
}
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
val src = TootFilter.parseList(result.jsonArray)
|
val src = TootFilter.parseList(result.jsonArray)
|
||||||
|
|
|
@ -6,9 +6,22 @@ import jp.juggler.subwaytooter.R
|
||||||
import jp.juggler.subwaytooter.api.ApiPath
|
import jp.juggler.subwaytooter.api.ApiPath
|
||||||
import jp.juggler.subwaytooter.api.TootApiClient
|
import jp.juggler.subwaytooter.api.TootApiClient
|
||||||
import jp.juggler.subwaytooter.api.TootApiResult
|
import jp.juggler.subwaytooter.api.TootApiResult
|
||||||
import jp.juggler.subwaytooter.api.entity.*
|
import jp.juggler.subwaytooter.api.entity.Acct
|
||||||
|
import jp.juggler.subwaytooter.api.entity.Host
|
||||||
|
import jp.juggler.subwaytooter.api.entity.TimelineItem
|
||||||
import jp.juggler.subwaytooter.api.entity.TootAccountRef.Companion.tootAccountRef
|
import jp.juggler.subwaytooter.api.entity.TootAccountRef.Companion.tootAccountRef
|
||||||
import jp.juggler.subwaytooter.api.finder.*
|
import jp.juggler.subwaytooter.api.entity.TootInstance
|
||||||
|
import jp.juggler.subwaytooter.api.entity.TootMessageHolder
|
||||||
|
import jp.juggler.subwaytooter.api.entity.TootNotification
|
||||||
|
import jp.juggler.subwaytooter.api.entity.TootStatus
|
||||||
|
import jp.juggler.subwaytooter.api.finder.mastodonFollowSuggestion2ListParser
|
||||||
|
import jp.juggler.subwaytooter.api.finder.misskey11FollowersParser
|
||||||
|
import jp.juggler.subwaytooter.api.finder.misskey11FollowingParser
|
||||||
|
import jp.juggler.subwaytooter.api.finder.misskeyArrayFinderUsers
|
||||||
|
import jp.juggler.subwaytooter.api.finder.misskeyCustomParserBlocks
|
||||||
|
import jp.juggler.subwaytooter.api.finder.misskeyCustomParserFavorites
|
||||||
|
import jp.juggler.subwaytooter.api.finder.misskeyCustomParserFollowRequest
|
||||||
|
import jp.juggler.subwaytooter.api.finder.misskeyCustomParserMutes
|
||||||
import jp.juggler.subwaytooter.search.MspHelper.loadingMSP
|
import jp.juggler.subwaytooter.search.MspHelper.loadingMSP
|
||||||
import jp.juggler.subwaytooter.search.MspHelper.refreshMSP
|
import jp.juggler.subwaytooter.search.MspHelper.refreshMSP
|
||||||
import jp.juggler.subwaytooter.search.NotestockHelper.loadingNotestock
|
import jp.juggler.subwaytooter.search.NotestockHelper.loadingNotestock
|
||||||
|
@ -17,10 +30,16 @@ import jp.juggler.subwaytooter.search.TootsearchHelper.loadingTootsearch
|
||||||
import jp.juggler.subwaytooter.search.TootsearchHelper.refreshTootsearch
|
import jp.juggler.subwaytooter.search.TootsearchHelper.refreshTootsearch
|
||||||
import jp.juggler.subwaytooter.streaming.StreamSpec
|
import jp.juggler.subwaytooter.streaming.StreamSpec
|
||||||
import jp.juggler.subwaytooter.table.daoAcctColor
|
import jp.juggler.subwaytooter.table.daoAcctColor
|
||||||
import jp.juggler.util.*
|
import jp.juggler.util.data.JsonArray
|
||||||
import jp.juggler.util.data.*
|
import jp.juggler.util.data.JsonObject
|
||||||
|
import jp.juggler.util.data.appendIf
|
||||||
|
import jp.juggler.util.data.ellipsizeDot3
|
||||||
|
import jp.juggler.util.data.jsonArrayOf
|
||||||
|
import jp.juggler.util.data.jsonObjectOf
|
||||||
|
import jp.juggler.util.data.notEmpty
|
||||||
|
import jp.juggler.util.data.toJsonArray
|
||||||
import jp.juggler.util.log.LogCategory
|
import jp.juggler.util.log.LogCategory
|
||||||
import java.util.*
|
import java.util.Locale
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
|
|
||||||
|
@ -1128,6 +1147,7 @@ enum class ColumnType(
|
||||||
arrayFinder = misskeyArrayFinderUsers,
|
arrayFinder = misskeyArrayFinderUsers,
|
||||||
listParser = misskeyCustomParserMutes
|
listParser = misskeyCustomParserMutes
|
||||||
)
|
)
|
||||||
|
|
||||||
else -> getAccountList(client, ApiPath.PATH_MUTES)
|
else -> getAccountList(client, ApiPath.PATH_MUTES)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1142,6 +1162,7 @@ enum class ColumnType(
|
||||||
arrayFinder = misskeyArrayFinderUsers,
|
arrayFinder = misskeyArrayFinderUsers,
|
||||||
listParser = misskeyCustomParserMutes
|
listParser = misskeyCustomParserMutes
|
||||||
)
|
)
|
||||||
|
|
||||||
else -> getAccountList(
|
else -> getAccountList(
|
||||||
client,
|
client,
|
||||||
ApiPath.PATH_MUTES,
|
ApiPath.PATH_MUTES,
|
||||||
|
@ -1750,6 +1771,7 @@ enum class ColumnType(
|
||||||
ApiPath.PATH_FOLLOW_SUGGESTION2,
|
ApiPath.PATH_FOLLOW_SUGGESTION2,
|
||||||
listParser = mastodonFollowSuggestion2ListParser,
|
listParser = mastodonFollowSuggestion2ListParser,
|
||||||
)
|
)
|
||||||
|
|
||||||
else ->
|
else ->
|
||||||
getAccountList(client, ApiPath.PATH_FOLLOW_SUGGESTION)
|
getAccountList(client, ApiPath.PATH_FOLLOW_SUGGESTION)
|
||||||
}
|
}
|
||||||
|
@ -1773,6 +1795,7 @@ enum class ColumnType(
|
||||||
ApiPath.PATH_FOLLOW_SUGGESTION2,
|
ApiPath.PATH_FOLLOW_SUGGESTION2,
|
||||||
listParser = mastodonFollowSuggestion2ListParser,
|
listParser = mastodonFollowSuggestion2ListParser,
|
||||||
)
|
)
|
||||||
|
|
||||||
else ->
|
else ->
|
||||||
getAccountList(client, ApiPath.PATH_FOLLOW_SUGGESTION)
|
getAccountList(client, ApiPath.PATH_FOLLOW_SUGGESTION)
|
||||||
}
|
}
|
||||||
|
@ -1798,6 +1821,7 @@ enum class ColumnType(
|
||||||
listParser = mastodonFollowSuggestion2ListParser,
|
listParser = mastodonFollowSuggestion2ListParser,
|
||||||
mastodonFilterByIdRange = false
|
mastodonFilterByIdRange = false
|
||||||
)
|
)
|
||||||
|
|
||||||
else ->
|
else ->
|
||||||
getAccountList(
|
getAccountList(
|
||||||
client,
|
client,
|
||||||
|
@ -2088,7 +2112,7 @@ enum class ColumnType(
|
||||||
fun dump() {
|
fun dump() {
|
||||||
var min = Int.MAX_VALUE
|
var min = Int.MAX_VALUE
|
||||||
var max = Int.MIN_VALUE
|
var max = Int.MIN_VALUE
|
||||||
values().forEach {
|
for (it in entries) {
|
||||||
val id = it.id
|
val id = it.id
|
||||||
min = min(min, id)
|
min = min(min, id)
|
||||||
max = max(max, id)
|
max = max(max, id)
|
||||||
|
|
|
@ -3,7 +3,9 @@ package jp.juggler.subwaytooter.dialog
|
||||||
import android.app.Dialog
|
import android.app.Dialog
|
||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
import android.view.inputmethod.EditorInfo
|
import android.view.inputmethod.EditorInfo
|
||||||
import android.widget.*
|
import android.widget.ArrayAdapter
|
||||||
|
import android.widget.Filter
|
||||||
|
import android.widget.TextView
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.widget.addTextChangedListener
|
import androidx.core.widget.addTextChangedListener
|
||||||
|
@ -21,15 +23,23 @@ import jp.juggler.util.coroutine.launchAndShowError
|
||||||
import jp.juggler.util.coroutine.launchMain
|
import jp.juggler.util.coroutine.launchMain
|
||||||
import jp.juggler.util.data.notBlank
|
import jp.juggler.util.data.notBlank
|
||||||
import jp.juggler.util.data.notEmpty
|
import jp.juggler.util.data.notEmpty
|
||||||
import jp.juggler.util.log.*
|
import jp.juggler.util.log.LogCategory
|
||||||
import jp.juggler.util.ui.*
|
import jp.juggler.util.log.showToast
|
||||||
|
import jp.juggler.util.ui.ProgressDialogEx
|
||||||
|
import jp.juggler.util.ui.attrColor
|
||||||
|
import jp.juggler.util.ui.dismissSafe
|
||||||
|
import jp.juggler.util.ui.hideKeyboard
|
||||||
|
import jp.juggler.util.ui.invisible
|
||||||
|
import jp.juggler.util.ui.isEnabledAlpha
|
||||||
|
import jp.juggler.util.ui.vg
|
||||||
|
import jp.juggler.util.ui.visible
|
||||||
|
import jp.juggler.util.ui.visibleOrInvisible
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import org.jetbrains.anko.textColor
|
import org.jetbrains.anko.textColor
|
||||||
import org.jetbrains.anko.textResource
|
import org.jetbrains.anko.textResource
|
||||||
import java.io.BufferedReader
|
import java.io.BufferedReader
|
||||||
import java.io.InputStreamReader
|
import java.io.InputStreamReader
|
||||||
import java.net.IDN
|
import java.net.IDN
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
class LoginForm(
|
class LoginForm(
|
||||||
val activity: AppCompatActivity,
|
val activity: AppCompatActivity,
|
||||||
|
@ -62,7 +72,8 @@ class LoginForm(
|
||||||
) {
|
) {
|
||||||
Login(R.string.existing_account, R.string.existing_account_desc),
|
Login(R.string.existing_account, R.string.existing_account_desc),
|
||||||
Pseudo(R.string.pseudo_account, R.string.pseudo_account_desc),
|
Pseudo(R.string.pseudo_account, R.string.pseudo_account_desc),
|
||||||
// Create(2, R.string.create_account, R.string.create_account_desc),
|
|
||||||
|
// Create(2, R.string.create_account, R.string.create_account_desc),
|
||||||
Token(R.string.input_access_token, R.string.input_access_token_desc),
|
Token(R.string.input_access_token, R.string.input_access_token_desc),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,7 +87,7 @@ class LoginForm(
|
||||||
private var targetServerInfo: TootInstance? = null
|
private var targetServerInfo: TootInstance? = null
|
||||||
|
|
||||||
init {
|
init {
|
||||||
for (a in Action.values()) {
|
for (a in Action.entries) {
|
||||||
val subViews =
|
val subViews =
|
||||||
LvAuthTypeBinding.inflate(activity.layoutInflater, views.llPageAuthType, true)
|
LvAuthTypeBinding.inflate(activity.layoutInflater, views.llPageAuthType, true)
|
||||||
subViews.btnAuthType.textResource = a.idName
|
subViews.btnAuthType.textResource = a.idName
|
||||||
|
|
|
@ -1,10 +1,15 @@
|
||||||
package jp.juggler.subwaytooter.drawable
|
package jp.juggler.subwaytooter.drawable
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.*
|
import android.graphics.Canvas
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.graphics.ColorFilter
|
||||||
|
import android.graphics.Paint
|
||||||
|
import android.graphics.PixelFormat
|
||||||
|
import android.graphics.Rect
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import jp.juggler.subwaytooter.R
|
import jp.juggler.subwaytooter.R
|
||||||
import jp.juggler.util.ui.*
|
import jp.juggler.util.ui.attrColor
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
|
|
||||||
class MediaBackgroundDrawable(
|
class MediaBackgroundDrawable(
|
||||||
|
@ -33,10 +38,10 @@ class MediaBackgroundDrawable(
|
||||||
|
|
||||||
;
|
;
|
||||||
|
|
||||||
fun toIndex() = values().indexOf(this)
|
fun toIndex() = entries.indexOf(this)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun fromIndex(idx: Int) = values().elementAtOrNull(idx) ?: BlackTile
|
fun fromIndex(idx: Int) = entries.elementAtOrNull(idx) ?: BlackTile
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,9 +20,7 @@ class EmojiMapLoader(
|
||||||
private val assetsSet = appContext.assets.list("")!!.toSet()
|
private val assetsSet = appContext.assets.list("")!!.toSet()
|
||||||
private val resources = appContext.resources!!
|
private val resources = appContext.resources!!
|
||||||
|
|
||||||
private val categoryNameMap = HashMap<String, EmojiCategory>().apply {
|
private val categoryNameMap = EmojiCategory.entries.associateBy { it.name }
|
||||||
EmojiCategory.values().forEach { put(it.name, it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
private var lastEmoji: UnicodeEmoji? = null
|
private var lastEmoji: UnicodeEmoji? = null
|
||||||
private var lastCategory: EmojiCategory? = null
|
private var lastCategory: EmojiCategory? = null
|
||||||
|
@ -60,28 +58,34 @@ class EmojiMapLoader(
|
||||||
if (!assetsSet.contains(line)) error("missing assets.")
|
if (!assetsSet.contains(line)) error("missing assets.")
|
||||||
lastEmoji = UnicodeEmoji(assetsName = line)
|
lastEmoji = UnicodeEmoji(assetsName = line)
|
||||||
}
|
}
|
||||||
|
|
||||||
"drawable" -> {
|
"drawable" -> {
|
||||||
val drawableId = getDrawableId(line) ?: error("missing drawable.")
|
val drawableId = getDrawableId(line) ?: error("missing drawable.")
|
||||||
lastEmoji = UnicodeEmoji(drawableId = drawableId)
|
lastEmoji = UnicodeEmoji(drawableId = drawableId)
|
||||||
}
|
}
|
||||||
|
|
||||||
"un" -> {
|
"un" -> {
|
||||||
val emoji = lastEmoji ?: error("missing lastEmoji.")
|
val emoji = lastEmoji ?: error("missing lastEmoji.")
|
||||||
addCode(emoji, line)
|
addCode(emoji, line)
|
||||||
emoji.unifiedCode = line
|
emoji.unifiedCode = line
|
||||||
}
|
}
|
||||||
|
|
||||||
"u" -> {
|
"u" -> {
|
||||||
val emoji = lastEmoji ?: error("missing lastEmoji.")
|
val emoji = lastEmoji ?: error("missing lastEmoji.")
|
||||||
addCode(emoji, line)
|
addCode(emoji, line)
|
||||||
}
|
}
|
||||||
|
|
||||||
"sn" -> {
|
"sn" -> {
|
||||||
val emoji = lastEmoji ?: error("missing lastEmoji.")
|
val emoji = lastEmoji ?: error("missing lastEmoji.")
|
||||||
addName(emoji, line)
|
addName(emoji, line)
|
||||||
emoji.unifiedName = line
|
emoji.unifiedName = line
|
||||||
}
|
}
|
||||||
|
|
||||||
"s" -> {
|
"s" -> {
|
||||||
val emoji = lastEmoji ?: error("missing lastEmoji.")
|
val emoji = lastEmoji ?: error("missing lastEmoji.")
|
||||||
addName(emoji, line)
|
addName(emoji, line)
|
||||||
}
|
}
|
||||||
|
|
||||||
"t" -> {
|
"t" -> {
|
||||||
val cols = line.split(",", limit = 3)
|
val cols = line.split(",", limit = 3)
|
||||||
if (cols.size != 3) error("invalid tone spec. line=$lno $line")
|
if (cols.size != 3) error("invalid tone spec. line=$lno $line")
|
||||||
|
@ -99,6 +103,7 @@ class EmojiMapLoader(
|
||||||
lastCategory = categoryNameMap[line]
|
lastCategory = categoryNameMap[line]
|
||||||
?: error("missing category name.")
|
?: error("missing category name.")
|
||||||
}
|
}
|
||||||
|
|
||||||
"c" -> {
|
"c" -> {
|
||||||
val category = lastCategory
|
val category = lastCategory
|
||||||
?: error("missing lastCategory.")
|
?: error("missing lastCategory.")
|
||||||
|
@ -110,6 +115,7 @@ class EmojiMapLoader(
|
||||||
category.emojiList.add(emoji)
|
category.emojiList.add(emoji)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> error("unknown header $head")
|
else -> error("unknown header $head")
|
||||||
}
|
}
|
||||||
} catch (ex: Throwable) {
|
} catch (ex: Throwable) {
|
||||||
|
|
|
@ -76,7 +76,7 @@ enum class AdditionalButtonsPosition(
|
||||||
;
|
;
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun fromIndex(i: Int) = values().find { it.idx == i } ?: Top
|
fun fromIndex(i: Int) = entries.find { it.idx == i } ?: Top
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -821,7 +821,7 @@ class StatusButtonsViewHolder(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun AnkoFlexboxLayout.additionalButtons() {
|
private fun AnkoFlexboxLayout.additionalButtons() {
|
||||||
btnCustomShares = CustomShareTarget.values().map { target ->
|
btnCustomShares = CustomShareTarget.entries.map { target ->
|
||||||
imageButton {
|
imageButton {
|
||||||
background = ContextCompat.getDrawable(
|
background = ContextCompat.getDrawable(
|
||||||
context,
|
context,
|
||||||
|
|
|
@ -256,10 +256,9 @@ class NotificationChannelsInitializer : Initializer<Boolean> {
|
||||||
|
|
||||||
override fun create(context: Context): Boolean {
|
override fun create(context: Context): Boolean {
|
||||||
context.run {
|
context.run {
|
||||||
val list = NotificationChannels.values()
|
|
||||||
log.i("createNotificationChannel(s) size=${list.size}")
|
|
||||||
val notificationManager = NotificationManagerCompat.from(this)
|
val notificationManager = NotificationManagerCompat.from(this)
|
||||||
for (nc in list) {
|
log.i("createNotificationChannel(s) size=${NotificationChannels.entries.size}")
|
||||||
|
for (nc in NotificationChannels.entries) {
|
||||||
val channel = NotificationChannel(
|
val channel = NotificationChannel(
|
||||||
nc.id,
|
nc.id,
|
||||||
getString(nc.titleId),
|
getString(nc.titleId),
|
||||||
|
|
|
@ -10,9 +10,4 @@ enum class PollingState(val desc: String) {
|
||||||
CheckServerInformation("check server information"),
|
CheckServerInformation("check server information"),
|
||||||
CheckPushSubscription("check push subscription"),
|
CheckPushSubscription("check push subscription"),
|
||||||
CheckNotifications("check notifications"),
|
CheckNotifications("check notifications"),
|
||||||
;
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
val valuesCache = values()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,17 @@ import android.app.ActivityManager
|
||||||
import android.app.PendingIntent
|
import android.app.PendingIntent
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import androidx.work.*
|
import androidx.work.Constraints
|
||||||
|
import androidx.work.CoroutineWorker
|
||||||
|
import androidx.work.Data
|
||||||
|
import androidx.work.ExistingPeriodicWorkPolicy
|
||||||
|
import androidx.work.ForegroundInfo
|
||||||
|
import androidx.work.NetworkType
|
||||||
|
import androidx.work.PeriodicWorkRequest
|
||||||
|
import androidx.work.PeriodicWorkRequestBuilder
|
||||||
|
import androidx.work.WorkManager
|
||||||
|
import androidx.work.WorkerParameters
|
||||||
|
import androidx.work.await
|
||||||
import jp.juggler.subwaytooter.ActMain
|
import jp.juggler.subwaytooter.ActMain
|
||||||
import jp.juggler.subwaytooter.App1
|
import jp.juggler.subwaytooter.App1
|
||||||
import jp.juggler.subwaytooter.R
|
import jp.juggler.subwaytooter.R
|
||||||
|
@ -114,7 +124,7 @@ class PollingWorker2(
|
||||||
|
|
||||||
private fun stateMapToString(map: Map<PollingState, List<String>>) =
|
private fun stateMapToString(map: Map<PollingState, List<String>>) =
|
||||||
StringBuilder().apply {
|
StringBuilder().apply {
|
||||||
for (state in PollingState.valuesCache) {
|
for (state in PollingState.entries) {
|
||||||
val list = map[state] ?: continue
|
val list = map[state] ?: continue
|
||||||
if (isNotEmpty()) append(" |")
|
if (isNotEmpty()) append(" |")
|
||||||
append(state.desc)
|
append(state.desc)
|
||||||
|
|
|
@ -9,7 +9,6 @@ enum class TrackingType(
|
||||||
NotReply("notReply", PullNotification.TRACKING_NAME_DEFAULT);
|
NotReply("notReply", PullNotification.TRACKING_NAME_DEFAULT);
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val valuesCache = values()
|
fun parseStr(str: String?) = entries.firstOrNull { it.str == str } ?: All
|
||||||
fun parseStr(str: String?) = valuesCache.firstOrNull { it.str == str } ?: All
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,99 +7,102 @@ import jp.juggler.subwaytooter.api.entity.TootNotification
|
||||||
import jp.juggler.subwaytooter.table.PushMessage
|
import jp.juggler.subwaytooter.table.PushMessage
|
||||||
import jp.juggler.util.log.LogCategory
|
import jp.juggler.util.log.LogCategory
|
||||||
|
|
||||||
private val log = LogCategory("NotificationIconAndColor")
|
private val log = LogCategory("PushMessageIconColor")
|
||||||
|
|
||||||
enum class PushMessageIconColor(
|
enum class PushMessageIconColor(
|
||||||
@ColorRes val colorRes: Int,
|
@ColorRes val colorRes: Int,
|
||||||
@DrawableRes val iconId: Int,
|
@DrawableRes val iconId: Int,
|
||||||
val keys: Array<String>,
|
val keys: Set<String>,
|
||||||
) {
|
) {
|
||||||
Favourite(
|
Favourite(
|
||||||
0,
|
0,
|
||||||
R.drawable.ic_star_outline,
|
R.drawable.ic_star_outline,
|
||||||
arrayOf("favourite"),
|
setOf("favourite"),
|
||||||
),
|
),
|
||||||
Mention(
|
Mention(
|
||||||
0,
|
0,
|
||||||
R.drawable.outline_alternate_email_24,
|
R.drawable.outline_alternate_email_24,
|
||||||
arrayOf("mention"),
|
setOf("mention"),
|
||||||
),
|
),
|
||||||
Reply(
|
Reply(
|
||||||
0,
|
0,
|
||||||
R.drawable.ic_reply,
|
R.drawable.ic_reply,
|
||||||
arrayOf("reply")
|
setOf("reply")
|
||||||
),
|
),
|
||||||
Reblog(
|
Reblog(
|
||||||
0,
|
0,
|
||||||
R.drawable.ic_repeat,
|
R.drawable.ic_repeat,
|
||||||
arrayOf("reblog", "renote"),
|
setOf("reblog", "renote"),
|
||||||
),
|
),
|
||||||
Quote(
|
Quote(
|
||||||
0,
|
0,
|
||||||
R.drawable.ic_quote,
|
R.drawable.ic_quote,
|
||||||
arrayOf("quote"),
|
setOf("quote"),
|
||||||
),
|
),
|
||||||
Follow(
|
Follow(
|
||||||
0,
|
0,
|
||||||
R.drawable.ic_person_add,
|
R.drawable.ic_person_add,
|
||||||
arrayOf("follow", "followRequestAccepted")
|
setOf("follow", "followRequestAccepted")
|
||||||
),
|
),
|
||||||
Unfollow(
|
Unfollow(
|
||||||
0,
|
0,
|
||||||
R.drawable.ic_follow_cross,
|
R.drawable.ic_follow_cross,
|
||||||
arrayOf("unfollow")
|
setOf("unfollow")
|
||||||
),
|
),
|
||||||
Reaction(
|
Reaction(
|
||||||
0,
|
0,
|
||||||
R.drawable.outline_add_reaction_24,
|
R.drawable.outline_add_reaction_24,
|
||||||
arrayOf("reaction", "emoji_reaction", "pleroma:emoji_reaction")
|
setOf("reaction", "emoji_reaction", "pleroma:emoji_reaction")
|
||||||
),
|
),
|
||||||
FollowRequest(
|
FollowRequest(
|
||||||
R.color.colorNotificationAccentFollowRequest,
|
R.color.colorNotificationAccentFollowRequest,
|
||||||
R.drawable.ic_follow_wait,
|
R.drawable.ic_follow_wait,
|
||||||
arrayOf("follow_request", "receiveFollowRequest"),
|
setOf("follow_request", "receiveFollowRequest"),
|
||||||
),
|
),
|
||||||
Poll(
|
Poll(
|
||||||
0,
|
0,
|
||||||
R.drawable.outline_poll_24,
|
R.drawable.outline_poll_24,
|
||||||
arrayOf("pollVote", "poll_vote", "poll"),
|
setOf("pollVote", "poll_vote", "poll"),
|
||||||
),
|
),
|
||||||
Status(
|
Status(
|
||||||
0,
|
0,
|
||||||
R.drawable.ic_edit,
|
R.drawable.ic_edit,
|
||||||
arrayOf("status", "update", "status_reference")
|
setOf("status", "update", "status_reference")
|
||||||
),
|
),
|
||||||
AdminSignUp(
|
AdminSignUp(
|
||||||
0,
|
0,
|
||||||
R.drawable.outline_group_add_24,
|
R.drawable.outline_group_add_24,
|
||||||
arrayOf(TootNotification.TYPE_ADMIN_SIGNUP),
|
setOf(TootNotification.TYPE_ADMIN_SIGNUP),
|
||||||
),
|
),
|
||||||
AdminReport(
|
AdminReport(
|
||||||
R.color.colorNotificationAccentAdminReport,
|
R.color.colorNotificationAccentAdminReport,
|
||||||
R.drawable.ic_error,
|
R.drawable.ic_error,
|
||||||
arrayOf(TootNotification.TYPE_ADMIN_REPORT),
|
setOf(TootNotification.TYPE_ADMIN_REPORT),
|
||||||
),
|
),
|
||||||
|
|
||||||
Unknown(
|
Unknown(
|
||||||
R.color.colorNotificationAccentUnknown,
|
R.color.colorNotificationAccentUnknown,
|
||||||
R.drawable.ic_question,
|
R.drawable.ic_question,
|
||||||
arrayOf("unknown"),
|
setOf("unknown"),
|
||||||
)
|
)
|
||||||
;
|
;
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val map = buildMap {
|
val map = PushMessageIconColor.entries.map { it.keys }.flatten().toSet()
|
||||||
values().forEach {
|
.associateWith { key ->
|
||||||
for (k in it.keys) {
|
val colors = PushMessageIconColor.entries
|
||||||
val old: PushMessageIconColor? = get(k)
|
.filter { it.keys.contains(key) }
|
||||||
if (old != null) {
|
when {
|
||||||
error("NotificationIconAndColor: $k is duplicate: ${it.name} and ${old.name}")
|
colors.isEmpty() -> error("missing color fot key=$key")
|
||||||
} else {
|
colors.size > 1 -> error(
|
||||||
put(k, it)
|
"NotificationIconAndColor: duplicate key $key to ${
|
||||||
}
|
colors.joinToString(", ") { it.name }
|
||||||
|
}"
|
||||||
|
)
|
||||||
|
|
||||||
|
else -> colors.first()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -208,7 +208,7 @@ object CustomShare {
|
||||||
fun getCache(target: CustomShareTarget) = cache[target]
|
fun getCache(target: CustomShareTarget) = cache[target]
|
||||||
|
|
||||||
fun reloadCache(context: Context) {
|
fun reloadCache(context: Context) {
|
||||||
CustomShareTarget.values().forEach { target ->
|
for (target in CustomShareTarget.entries) {
|
||||||
val cn = target.customShareComponentName
|
val cn = target.customShareComponentName
|
||||||
val pair = getInfo(context, cn)
|
val pair = getInfo(context, cn)
|
||||||
cache[target] = pair
|
cache[target] = pair
|
||||||
|
|
|
@ -3,8 +3,20 @@ package jp.juggler.subwaytooter.util
|
||||||
import android.os.SystemClock
|
import android.os.SystemClock
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import jp.juggler.subwaytooter.R
|
import jp.juggler.subwaytooter.R
|
||||||
import jp.juggler.subwaytooter.api.*
|
import jp.juggler.subwaytooter.api.TootApiClient
|
||||||
import jp.juggler.subwaytooter.api.entity.*
|
import jp.juggler.subwaytooter.api.TootApiResultException
|
||||||
|
import jp.juggler.subwaytooter.api.TootParser
|
||||||
|
import jp.juggler.subwaytooter.api.entity.EntityId
|
||||||
|
import jp.juggler.subwaytooter.api.entity.InstanceCapability
|
||||||
|
import jp.juggler.subwaytooter.api.entity.InstanceType
|
||||||
|
import jp.juggler.subwaytooter.api.entity.TootAccount
|
||||||
|
import jp.juggler.subwaytooter.api.entity.TootInstance
|
||||||
|
import jp.juggler.subwaytooter.api.entity.TootPollsType
|
||||||
|
import jp.juggler.subwaytooter.api.entity.TootStatus
|
||||||
|
import jp.juggler.subwaytooter.api.entity.TootTag
|
||||||
|
import jp.juggler.subwaytooter.api.entity.TootVisibility
|
||||||
|
import jp.juggler.subwaytooter.api.errorApiResult
|
||||||
|
import jp.juggler.subwaytooter.api.runApiTask2
|
||||||
import jp.juggler.subwaytooter.dialog.DlgConfirm.confirm
|
import jp.juggler.subwaytooter.dialog.DlgConfirm.confirm
|
||||||
import jp.juggler.subwaytooter.emoji.CustomEmoji
|
import jp.juggler.subwaytooter.emoji.CustomEmoji
|
||||||
import jp.juggler.subwaytooter.getVisibilityString
|
import jp.juggler.subwaytooter.getVisibilityString
|
||||||
|
@ -14,19 +26,31 @@ import jp.juggler.subwaytooter.table.SavedAccount
|
||||||
import jp.juggler.subwaytooter.table.daoAcctColor
|
import jp.juggler.subwaytooter.table.daoAcctColor
|
||||||
import jp.juggler.subwaytooter.table.daoSavedAccount
|
import jp.juggler.subwaytooter.table.daoSavedAccount
|
||||||
import jp.juggler.subwaytooter.table.daoTagHistory
|
import jp.juggler.subwaytooter.table.daoTagHistory
|
||||||
import jp.juggler.util.*
|
|
||||||
import jp.juggler.util.coroutine.AppDispatchers
|
import jp.juggler.util.coroutine.AppDispatchers
|
||||||
import jp.juggler.util.data.*
|
import jp.juggler.util.data.JsonArray
|
||||||
import jp.juggler.util.log.*
|
import jp.juggler.util.data.JsonException
|
||||||
|
import jp.juggler.util.data.JsonObject
|
||||||
|
import jp.juggler.util.data.buildJsonArray
|
||||||
|
import jp.juggler.util.data.buildJsonObject
|
||||||
|
import jp.juggler.util.data.digestSHA256Hex
|
||||||
|
import jp.juggler.util.data.groupEx
|
||||||
|
import jp.juggler.util.data.jsonObjectOf
|
||||||
|
import jp.juggler.util.data.notEmpty
|
||||||
|
import jp.juggler.util.data.toJsonArray
|
||||||
|
import jp.juggler.util.log.LogCategory
|
||||||
|
import jp.juggler.util.log.errorString
|
||||||
|
import jp.juggler.util.log.showToast
|
||||||
import jp.juggler.util.network.MEDIA_TYPE_JSON
|
import jp.juggler.util.network.MEDIA_TYPE_JSON
|
||||||
import jp.juggler.util.network.toPostRequestBuilder
|
import jp.juggler.util.network.toPostRequestBuilder
|
||||||
import jp.juggler.util.ui.*
|
|
||||||
import kotlinx.coroutines.CancellationException
|
import kotlinx.coroutines.CancellationException
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import okhttp3.RequestBody.Companion.toRequestBody
|
import okhttp3.RequestBody.Companion.toRequestBody
|
||||||
import java.util.*
|
import java.util.Calendar
|
||||||
|
import java.util.GregorianCalendar
|
||||||
|
import java.util.Locale
|
||||||
|
import java.util.TimeZone
|
||||||
import java.util.concurrent.atomic.AtomicBoolean
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
|
|
||||||
sealed class PostResult {
|
sealed class PostResult {
|
||||||
|
@ -110,36 +134,22 @@ class PostImpl(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private var resultStatus: TootStatus? = null
|
// may null, not error
|
||||||
private var resultCredentialTmp: TootAccount? = null
|
|
||||||
private var resultScheduledStatusSucceeded = false
|
|
||||||
|
|
||||||
private suspend fun getCredential(
|
|
||||||
client: TootApiClient,
|
|
||||||
parser: TootParser,
|
|
||||||
): TootApiResult? {
|
|
||||||
return client.request("/api/v1/accounts/verify_credentials")?.also { result ->
|
|
||||||
resultCredentialTmp = parser.account(result.jsonObject)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun getWebVisibility(
|
private suspend fun getWebVisibility(
|
||||||
client: TootApiClient,
|
client: TootApiClient,
|
||||||
parser: TootParser,
|
parser: TootParser,
|
||||||
instance: TootInstance,
|
instance: TootInstance,
|
||||||
): TootVisibility? {
|
): TootVisibility? = when {
|
||||||
if (account.isMisskey || instance.versionGE(TootInstance.VERSION_1_6)) return null
|
account.isMisskey -> null
|
||||||
|
instance.versionGE(TootInstance.VERSION_1_6) -> null
|
||||||
val r2 = getCredential(client, parser)
|
else -> {
|
||||||
|
val privacy = parser.account(
|
||||||
val credentialTmp = resultCredentialTmp
|
client.requestOrThrow("/api/v1/accounts/verify_credentials")
|
||||||
?: errorApiResult(r2)
|
.jsonObject
|
||||||
|
)?.source?.privacy
|
||||||
val privacy = credentialTmp.source?.privacy
|
?: error(R.string.cant_get_web_setting_visibility)
|
||||||
?: errorApiResult(activity.getString(R.string.cant_get_web_setting_visibility))
|
TootVisibility.parseMastodon(privacy)
|
||||||
|
}
|
||||||
return TootVisibility.parseMastodon(privacy)
|
|
||||||
// may null, not error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkServerHasVisibility(
|
private fun checkServerHasVisibility(
|
||||||
|
@ -150,12 +160,7 @@ class PostImpl(
|
||||||
) {
|
) {
|
||||||
if (actual != extra || checkFun(instance)) return
|
if (actual != extra || checkFun(instance)) return
|
||||||
val strVisibility = extra.getVisibilityString(account.isMisskey)
|
val strVisibility = extra.getVisibilityString(account.isMisskey)
|
||||||
errorApiResult(
|
activity.errorApiResult(R.string.server_has_no_support_of_visibility, strVisibility)
|
||||||
activity.getString(
|
|
||||||
R.string.server_has_no_support_of_visibility,
|
|
||||||
strVisibility
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun checkVisibility(
|
private suspend fun checkVisibility(
|
||||||
|
@ -205,7 +210,6 @@ class PostImpl(
|
||||||
"/api/v1/scheduled_statuses/$scheduledId",
|
"/api/v1/scheduled_statuses/$scheduledId",
|
||||||
Request.Builder().delete()
|
Request.Builder().delete()
|
||||||
)
|
)
|
||||||
|
|
||||||
log.d("delete old scheduled status. result=$result")
|
log.d("delete old scheduled status. result=$result")
|
||||||
delay(2000L)
|
delay(2000L)
|
||||||
}
|
}
|
||||||
|
@ -271,15 +275,13 @@ class PostImpl(
|
||||||
|
|
||||||
// Misskeyの場合、NSFWするにはアップロード済みの画像を drive/files/update で更新する
|
// Misskeyの場合、NSFWするにはアップロード済みの画像を drive/files/update で更新する
|
||||||
if (bNSFW) {
|
if (bNSFW) {
|
||||||
val r = client.request(
|
client.requestOrThrow(
|
||||||
"/api/drive/files/update",
|
"/api/drive/files/update",
|
||||||
account.putMisskeyApiToken().apply {
|
account.putMisskeyApiToken().apply {
|
||||||
put("fileId", a.id.toString())
|
put("fileId", a.id.toString())
|
||||||
put("isSensitive", true)
|
put("isSensitive", true)
|
||||||
}
|
}.toPostRequestBuilder()
|
||||||
.toPostRequestBuilder()
|
|
||||||
)
|
)
|
||||||
if (r == null || r.error != null) errorApiResult(r)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (array.isNotEmpty()) json["mediaIds"] = array
|
if (array.isNotEmpty()) json["mediaIds"] = array
|
||||||
|
@ -361,7 +363,7 @@ class PostImpl(
|
||||||
|
|
||||||
if (scheduledAt != 0L) {
|
if (scheduledAt != 0L) {
|
||||||
if (!instance.versionGE(TootInstance.VERSION_2_7_0_rc1)) {
|
if (!instance.versionGE(TootInstance.VERSION_2_7_0_rc1)) {
|
||||||
errorApiResult(activity.getString(R.string.scheduled_status_requires_mastodon_2_7_0))
|
activity.errorApiResult(R.string.scheduled_status_requires_mastodon_2_7_0)
|
||||||
}
|
}
|
||||||
// UTCの日時を渡す
|
// UTCの日時を渡す
|
||||||
val c = GregorianCalendar.getInstance(TimeZone.getTimeZone("UTC"))
|
val c = GregorianCalendar.getInstance(TimeZone.getTimeZone("UTC"))
|
||||||
|
@ -505,32 +507,28 @@ class PostImpl(
|
||||||
isPosting.set(true)
|
isPosting.set(true)
|
||||||
return try {
|
return try {
|
||||||
withContext(AppDispatchers.MainImmediate) {
|
withContext(AppDispatchers.MainImmediate) {
|
||||||
activity.runApiTask(
|
val (status, scheduled) = activity.runApiTask2(
|
||||||
account,
|
accessInfo = account,
|
||||||
progressSetup = { it.setCanceledOnTouchOutside(false) },
|
progressSetup = { it.setCanceledOnTouchOutside(false) },
|
||||||
) { client ->
|
) { client ->
|
||||||
val (instance, ri) = TootInstance.get(client)
|
val instance = TootInstance.getOrThrow(client)
|
||||||
instance ?: return@runApiTask ri
|
|
||||||
|
|
||||||
if (instance.instanceType == InstanceType.Pixelfed) {
|
if (instance.instanceType == InstanceType.Pixelfed) {
|
||||||
// Pixelfedは返信に画像を添付できない
|
// Pixelfedは返信に画像を添付できない
|
||||||
if (inReplyToId != null && attachmentList != null) {
|
if (inReplyToId != null && attachmentList != null) {
|
||||||
return@runApiTask TootApiResult(getString(R.string.pixelfed_does_not_allow_reply_with_media))
|
error(R.string.pixelfed_does_not_allow_reply_with_media)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pixelfedの返信ではない投稿は画像添付が必須
|
// Pixelfedの返信ではない投稿は画像添付が必須
|
||||||
if (inReplyToId == null && attachmentList == null) {
|
if (inReplyToId == null && attachmentList == null) {
|
||||||
return@runApiTask TootApiResult(getString(R.string.pixelfed_does_not_allow_post_without_media))
|
error(R.string.pixelfed_does_not_allow_post_without_media)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val parser = TootParser(this, account)
|
val parser = TootParser(this, account)
|
||||||
|
|
||||||
this@PostImpl.visibilityChecked = try {
|
// may null
|
||||||
checkVisibility(client, parser, instance) // may null
|
this@PostImpl.visibilityChecked = checkVisibility(client, parser, instance)
|
||||||
} catch (ex: TootApiResultException) {
|
|
||||||
return@runApiTask ex.result
|
|
||||||
}
|
|
||||||
|
|
||||||
if (redraftStatusId != null) {
|
if (redraftStatusId != null) {
|
||||||
// 元の投稿を削除する
|
// 元の投稿を削除する
|
||||||
|
@ -546,10 +544,8 @@ class PostImpl(
|
||||||
} else {
|
} else {
|
||||||
encodeParamsMastodon(json, instance)
|
encodeParamsMastodon(json, instance)
|
||||||
}
|
}
|
||||||
} catch (ex: TootApiResultException) {
|
|
||||||
return@runApiTask ex.result
|
|
||||||
} catch (ex: JsonException) {
|
} catch (ex: JsonException) {
|
||||||
log.e(ex, "status encoding failed.")
|
throw IllegalStateException("encoding status failed.", ex)
|
||||||
}
|
}
|
||||||
|
|
||||||
val bodyString = json.toString()
|
val bodyString = json.toString()
|
||||||
|
@ -567,67 +563,66 @@ class PostImpl(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
when {
|
try {
|
||||||
account.isMisskey -> client.request(
|
val result = when {
|
||||||
"/api/notes/create",
|
account.isMisskey -> client.requestOrThrow(
|
||||||
createRequestBuilder()
|
"/api/notes/create",
|
||||||
)
|
createRequestBuilder()
|
||||||
|
)
|
||||||
|
|
||||||
editStatusId != null -> client.request(
|
editStatusId != null -> client.requestOrThrow(
|
||||||
"/api/v1/statuses/$editStatusId",
|
"/api/v1/statuses/$editStatusId",
|
||||||
createRequestBuilder(isPut = true)
|
createRequestBuilder(isPut = true)
|
||||||
)
|
)
|
||||||
|
|
||||||
else -> client.request(
|
else -> client.requestOrThrow(
|
||||||
"/api/v1/statuses",
|
"/api/v1/statuses",
|
||||||
createRequestBuilder()
|
createRequestBuilder()
|
||||||
)
|
)
|
||||||
}?.also { result ->
|
}
|
||||||
val jsonObject = result.jsonObject
|
val jsonObject = result.jsonObject
|
||||||
|
when {
|
||||||
|
|
||||||
if (scheduledAt != 0L && jsonObject != null) {
|
// 予約投稿完了
|
||||||
// {"id":"3","scheduled_at":"2019-01-06T07:08:00.000Z","media_attachments":[]}
|
scheduledAt != 0L && jsonObject != null -> {
|
||||||
resultScheduledStatusSucceeded = true
|
// {"id":"3","scheduled_at":"2019-01-06T07:08:00.000Z","media_attachments":[]}
|
||||||
return@runApiTask result
|
Pair(null, true)
|
||||||
} else {
|
}
|
||||||
val status = parser.status(
|
|
||||||
when {
|
|
||||||
account.isMisskey -> jsonObject?.jsonObject("createdNote")
|
|
||||||
?: jsonObject
|
|
||||||
|
|
||||||
else -> jsonObject
|
// 通常投稿完了
|
||||||
}
|
else -> {
|
||||||
)
|
val status = parser.status(
|
||||||
resultStatus = status
|
when {
|
||||||
saveStatusTag(status)
|
account.isMisskey ->
|
||||||
|
jsonObject?.jsonObject("createdNote")
|
||||||
|
?: jsonObject
|
||||||
|
|
||||||
|
else -> jsonObject
|
||||||
|
}
|
||||||
|
)?.also { saveStatusTag(it) }
|
||||||
|
Pair(status, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (ex: TootApiResultException) {
|
||||||
|
val errorMessage = ex.result?.error
|
||||||
|
when {
|
||||||
|
errorMessage.isNullOrBlank() -> error("(missing error detail)")
|
||||||
|
|
||||||
|
errorMessage.contains("HTTP 404") ->
|
||||||
|
error("$ex\n${activity.getString(R.string.post_404_desc)}")
|
||||||
|
|
||||||
|
else -> throw ex
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.let { result ->
|
}
|
||||||
if (result == null) throw CancellationException()
|
when {
|
||||||
|
scheduled -> PostResult.Scheduled(account)
|
||||||
|
|
||||||
val status = resultStatus
|
status == null ->
|
||||||
when {
|
error("can't parse status in API result.")
|
||||||
resultScheduledStatusSucceeded ->
|
|
||||||
PostResult.Scheduled(account)
|
|
||||||
|
|
||||||
// 連投してIdempotency が同じだった場合もエラーにはならず、ここを通る
|
// 連投してIdempotency が同じだった場合もエラーにはならず、ここを通る
|
||||||
status != null ->
|
else -> PostResult.Normal(account, status)
|
||||||
PostResult.Normal(account, status)
|
|
||||||
|
|
||||||
else -> {
|
|
||||||
val e = result.error
|
|
||||||
error(
|
|
||||||
when {
|
|
||||||
e.isNullOrBlank() -> "(missing error detail)"
|
|
||||||
|
|
||||||
e.contains("HTTP 404") ->
|
|
||||||
"$e\n${activity.getString(R.string.post_404_desc)}"
|
|
||||||
|
|
||||||
else -> e
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
|
|
|
@ -7,7 +7,12 @@ import jp.juggler.util.log.showError
|
||||||
import jp.juggler.util.log.showToast
|
import jp.juggler.util.log.showToast
|
||||||
import jp.juggler.util.ui.ProgressDialogEx
|
import jp.juggler.util.ui.ProgressDialogEx
|
||||||
import jp.juggler.util.ui.dismissSafe
|
import jp.juggler.util.ui.dismissSafe
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.CancellationException
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.async
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.supervisorScope
|
||||||
import kotlin.coroutines.CoroutineContext
|
import kotlin.coroutines.CoroutineContext
|
||||||
import kotlin.coroutines.EmptyCoroutineContext
|
import kotlin.coroutines.EmptyCoroutineContext
|
||||||
|
|
||||||
|
@ -68,6 +73,7 @@ fun AppCompatActivity.launchAndShowError(
|
||||||
is CancellationException -> {
|
is CancellationException -> {
|
||||||
log.w(errorCaption ?: "launchAndShowError cancelled.")
|
log.w(errorCaption ?: "launchAndShowError cancelled.")
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
log.e(ex, errorCaption ?: "launchAndShowError failed.")
|
log.e(ex, errorCaption ?: "launchAndShowError failed.")
|
||||||
showError(ex, errorCaption)
|
showError(ex, errorCaption)
|
||||||
|
|
|
@ -9,7 +9,8 @@ import android.util.Base64
|
||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
import jp.juggler.util.log.LogCategory
|
import jp.juggler.util.log.LogCategory
|
||||||
import java.security.MessageDigest
|
import java.security.MessageDigest
|
||||||
import java.util.*
|
import java.util.LinkedList
|
||||||
|
import java.util.Locale
|
||||||
import java.util.regex.Matcher
|
import java.util.regex.Matcher
|
||||||
import java.util.regex.Pattern
|
import java.util.regex.Pattern
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package jp.juggler.util.os
|
package jp.juggler.util.os
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import androidx.annotation.StringRes
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* インストゥルメントテストのContextは
|
* インストゥルメントテストのContextは
|
||||||
|
@ -8,4 +9,12 @@ import android.content.Context
|
||||||
* この場合は元のcontextを補うのがベストだろう。
|
* この場合は元のcontextを補うのがベストだろう。
|
||||||
*/
|
*/
|
||||||
val Context.applicationContextSafe: Context
|
val Context.applicationContextSafe: Context
|
||||||
get() = applicationContext ?: this
|
get() = try {
|
||||||
|
applicationContext ?: this
|
||||||
|
} catch (ex: Throwable) {
|
||||||
|
// applicationContextへのアクセスは例外を出すことがある
|
||||||
|
this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Context.error(@StringRes resId: Int, vararg args: Any?): Nothing =
|
||||||
|
error(getString(resId, *args))
|
||||||
|
|
|
@ -392,18 +392,17 @@ fun AppCompatActivity.setNavigationBack(toolbar: Toolbar) =
|
||||||
onBackPressedDispatcher.onBackPressed()
|
onBackPressedDispatcher.onBackPressed()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val Float.roundPixels get() = (this + 0.5f).toInt()
|
||||||
|
fun DisplayMetrics.dpFloat(src: Float) = (density * src)
|
||||||
|
fun DisplayMetrics.dpFloat(src: Int) = (density * src.toFloat())
|
||||||
|
fun Resources.dpFloat(src: Float) = displayMetrics.dpFloat(src)
|
||||||
|
fun Resources.dpFloat(src: Int) = displayMetrics.dpFloat(src)
|
||||||
|
fun Context.dpFloat(src: Float) = resources.dpFloat(src)
|
||||||
|
fun Context.dpFloat(src: Int) = resources.dpFloat(src)
|
||||||
|
|
||||||
val Float.roundPixels get()= (this+0.5f).toInt()
|
fun DisplayMetrics.dp(src: Float) = (density * src).roundPixels
|
||||||
fun DisplayMetrics.dpFloat(src:Float) = (density* src)
|
fun DisplayMetrics.dp(src: Int) = (density * src.toFloat()).roundPixels
|
||||||
fun DisplayMetrics.dpFloat(src:Int) = (density*src.toFloat())
|
fun Resources.dp(src: Float) = displayMetrics.dp(src)
|
||||||
fun Resources.dpFloat(src:Float) = displayMetrics.dpFloat(src)
|
fun Resources.dp(src: Int) = displayMetrics.dp(src)
|
||||||
fun Resources.dpFloat(src:Int) = displayMetrics.dpFloat(src)
|
fun Context.dp(src: Float) = resources.dp(src)
|
||||||
fun Context.dpFloat(src:Float) = resources.dpFloat(src)
|
fun Context.dp(src: Int) = resources.dp(src)
|
||||||
fun Context.dpFloat(src:Int) = resources.dpFloat(src)
|
|
||||||
|
|
||||||
fun DisplayMetrics.dp(src:Float) = (density* src).roundPixels
|
|
||||||
fun DisplayMetrics.dp(src:Int) = (density*src.toFloat()).roundPixels
|
|
||||||
fun Resources.dp(src:Float) = displayMetrics.dp(src)
|
|
||||||
fun Resources.dp(src:Int) = displayMetrics.dp(src)
|
|
||||||
fun Context.dp(src:Float) = resources.dp(src)
|
|
||||||
fun Context.dp(src:Int) = resources.dp(src)
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ object Vers {
|
||||||
const val conscryptVersion = "2.5.2"
|
const val conscryptVersion = "2.5.2"
|
||||||
const val coreKtxVersion = "1.12.0"
|
const val coreKtxVersion = "1.12.0"
|
||||||
const val desugarLibVersion = "2.0.3"
|
const val desugarLibVersion = "2.0.3"
|
||||||
const val detektVersion = "1.23.1"
|
const val detektVersion = "1.23.4"
|
||||||
const val emoji2Version = "1.4.0"
|
const val emoji2Version = "1.4.0"
|
||||||
const val glideVersion = "4.15.1"
|
const val glideVersion = "4.15.1"
|
||||||
const val junitVersion = "4.13.2"
|
const val junitVersion = "4.13.2"
|
||||||
|
|
|
@ -15,7 +15,6 @@ android {
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdk = Vers.stMinSdkVersion
|
minSdk = Vers.stMinSdkVersion
|
||||||
targetSdk = Vers.stTargetSdkVersion
|
|
||||||
|
|
||||||
testInstrumentationRunner = "android.support.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "android.support.test.runner.AndroidJUnitRunner"
|
||||||
vectorDrawables.useSupportLibrary = true
|
vectorDrawables.useSupportLibrary = true
|
||||||
|
|
|
@ -671,7 +671,7 @@ style:
|
||||||
active: true
|
active: true
|
||||||
OptionalUnit:
|
OptionalUnit:
|
||||||
active: false
|
active: false
|
||||||
OptionalWhenBraces:
|
BracesOnWhenStatements:
|
||||||
active: false
|
active: false
|
||||||
PreferToOverPairSyntax:
|
PreferToOverPairSyntax:
|
||||||
active: false
|
active: false
|
||||||
|
|
|
@ -13,7 +13,6 @@ android {
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdk = Vers.stMinSdkVersion
|
minSdk = Vers.stMinSdkVersion
|
||||||
targetSdk = Vers.stTargetSdkVersion
|
|
||||||
vectorDrawables.useSupportLibrary = true
|
vectorDrawables.useSupportLibrary = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,6 @@ android {
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdk = Vers.stMinSdkVersion
|
minSdk = Vers.stMinSdkVersion
|
||||||
targetSdk = Vers.stTargetSdkVersion
|
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
consumerProguardFiles("consumer-rules.pro")
|
consumerProguardFiles("consumer-rules.pro")
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@ android {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
packagingOptions {
|
packaging {
|
||||||
resources {
|
resources {
|
||||||
pickFirsts += listOf("META-INF/atomicfu.kotlin_module")
|
pickFirsts += listOf("META-INF/atomicfu.kotlin_module")
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue