絵文字ピッカーの改善
This commit is contained in:
parent
228a8d3338
commit
f1d3556abf
@ -39,9 +39,9 @@ android {
|
|||||||
jvmTarget = jvm_target
|
jvmTarget = jvm_target
|
||||||
useIR = true
|
useIR = true
|
||||||
freeCompilerArgs += [
|
freeCompilerArgs += [
|
||||||
"-Xopt-in=kotlin.ExperimentalStdlibApi",
|
"-opt-in=kotlin.ExperimentalStdlibApi",
|
||||||
"-Xopt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
|
"-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
|
||||||
"-Xopt-in=kotlinx.serialization.ExperimentalSerializationApi",
|
"-opt-in=kotlinx.serialization.ExperimentalSerializationApi",
|
||||||
// "-Xopt-in=androidx.compose.foundation.ExperimentalFoundationApi",
|
// "-Xopt-in=androidx.compose.foundation.ExperimentalFoundationApi",
|
||||||
// "-Xopt-in=androidx.compose.animation.ExperimentalAnimationApi",
|
// "-Xopt-in=androidx.compose.animation.ExperimentalAnimationApi",
|
||||||
]
|
]
|
||||||
@ -148,9 +148,11 @@ dependencies {
|
|||||||
|
|
||||||
implementation "androidx.core:core-ktx:1.7.0"
|
implementation "androidx.core:core-ktx:1.7.0"
|
||||||
|
|
||||||
def emojiVersion = "1.1.0"
|
def emoji2Version = "1.1.0"
|
||||||
implementation "androidx.emoji2:emoji2:$emojiVersion"
|
implementation "androidx.emoji2:emoji2:$emoji2Version"
|
||||||
implementation "androidx.emoji2:emoji2-bundled:$emojiVersion"
|
implementation "androidx.emoji2:emoji2-views:$emoji2Version"
|
||||||
|
implementation "androidx.emoji2:emoji2-views-helper:$emoji2Version"
|
||||||
|
implementation "androidx.emoji2:emoji2-bundled:$emoji2Version"
|
||||||
|
|
||||||
// DrawerLayout
|
// DrawerLayout
|
||||||
implementation "androidx.drawerlayout:drawerlayout:1.1.1"
|
implementation "androidx.drawerlayout:drawerlayout:1.1.1"
|
||||||
@ -236,6 +238,8 @@ dependencies {
|
|||||||
|
|
||||||
implementation 'com.caverock:androidsvg-aar:1.4'
|
implementation 'com.caverock:androidsvg-aar:1.4'
|
||||||
|
|
||||||
|
implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version"
|
||||||
|
|
||||||
// ViewModel
|
// ViewModel
|
||||||
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
|
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
|
||||||
|
|
||||||
|
@ -2,7 +2,9 @@ package jp.juggler.subwaytooter.action
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import jp.juggler.subwaytooter.*
|
import jp.juggler.subwaytooter.ActMain
|
||||||
|
import jp.juggler.subwaytooter.R
|
||||||
|
import jp.juggler.subwaytooter.Styler
|
||||||
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
|
||||||
@ -13,16 +15,12 @@ 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.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
|
import jp.juggler.subwaytooter.dialog.DlgConfirm.confirm
|
||||||
import jp.juggler.subwaytooter.dialog.pickAccount
|
import jp.juggler.subwaytooter.dialog.pickAccount
|
||||||
import jp.juggler.subwaytooter.table.AcctColor
|
import jp.juggler.subwaytooter.table.AcctColor
|
||||||
import jp.juggler.subwaytooter.table.SavedAccount
|
import jp.juggler.subwaytooter.table.SavedAccount
|
||||||
import jp.juggler.subwaytooter.util.emptyCallback
|
import jp.juggler.subwaytooter.util.emptyCallback
|
||||||
import jp.juggler.util.JsonObject
|
import jp.juggler.util.*
|
||||||
import jp.juggler.util.launchMain
|
|
||||||
import jp.juggler.util.showToast
|
|
||||||
import jp.juggler.util.toPostRequestBuilder
|
|
||||||
import java.util.*
|
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
|
|
||||||
private class BoostImpl(
|
private class BoostImpl(
|
||||||
@ -62,42 +60,6 @@ private class BoostImpl(
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun confirm(): Boolean {
|
|
||||||
if (bConfirmed) return true
|
|
||||||
DlgConfirm.open(
|
|
||||||
activity,
|
|
||||||
activity.getString(
|
|
||||||
when {
|
|
||||||
!bSet -> R.string.confirm_unboost_from
|
|
||||||
isPrivateToot -> R.string.confirm_boost_private_from
|
|
||||||
visibility == TootVisibility.PrivateFollowers -> R.string.confirm_private_boost_from
|
|
||||||
else -> R.string.confirm_boost_from
|
|
||||||
},
|
|
||||||
AcctColor.getNickname(accessInfo)
|
|
||||||
),
|
|
||||||
object : DlgConfirm.Callback {
|
|
||||||
override fun onOK() {
|
|
||||||
bConfirmed = true
|
|
||||||
run()
|
|
||||||
}
|
|
||||||
|
|
||||||
override var isConfirmEnabled: Boolean
|
|
||||||
get() = when (bSet) {
|
|
||||||
true -> accessInfo.confirm_boost
|
|
||||||
else -> accessInfo.confirm_unboost
|
|
||||||
}
|
|
||||||
set(value) {
|
|
||||||
when (bSet) {
|
|
||||||
true -> accessInfo.confirm_boost = value
|
|
||||||
else -> accessInfo.confirm_unboost = value
|
|
||||||
}
|
|
||||||
accessInfo.saveSetting()
|
|
||||||
activity.reloadAccountSetting(accessInfo)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun Context.syncStatus(client: TootApiClient) =
|
private suspend fun Context.syncStatus(client: TootApiClient) =
|
||||||
if (!crossAccountMode.isRemote) {
|
if (!crossAccountMode.isRemote) {
|
||||||
// 既に自タンスのステータスがある
|
// 既に自タンスのステータスがある
|
||||||
@ -234,16 +196,41 @@ private class BoostImpl(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun run() {
|
fun run() {
|
||||||
if (!preCheck()) return
|
activity.launchAndShowError {
|
||||||
if (!confirm()) return
|
if (!preCheck()) return@launchAndShowError
|
||||||
|
|
||||||
// ブースト表示を更新中にする
|
if (!bConfirmed) {
|
||||||
activity.appState.setBusyBoost(accessInfo, statusArg)
|
activity.confirm(
|
||||||
activity.showColumnMatchAccount(accessInfo)
|
activity.getString(
|
||||||
|
when {
|
||||||
|
!bSet -> R.string.confirm_unboost_from
|
||||||
|
isPrivateToot -> R.string.confirm_boost_private_from
|
||||||
|
visibility == TootVisibility.PrivateFollowers -> R.string.confirm_private_boost_from
|
||||||
|
else -> R.string.confirm_boost_from
|
||||||
|
},
|
||||||
|
AcctColor.getNickname(accessInfo)
|
||||||
|
),
|
||||||
|
when (bSet) {
|
||||||
|
true -> accessInfo.confirm_boost
|
||||||
|
else -> accessInfo.confirm_unboost
|
||||||
|
}
|
||||||
|
) { newConfirmEnabled ->
|
||||||
|
when (bSet) {
|
||||||
|
true -> accessInfo.confirm_boost = newConfirmEnabled
|
||||||
|
else -> accessInfo.confirm_unboost = newConfirmEnabled
|
||||||
|
}
|
||||||
|
accessInfo.saveSetting()
|
||||||
|
activity.reloadAccountSetting(accessInfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ブースト表示を更新中にする
|
||||||
|
activity.appState.setBusyBoost(accessInfo, statusArg)
|
||||||
|
activity.showColumnMatchAccount(accessInfo)
|
||||||
|
|
||||||
launchMain {
|
|
||||||
val result =
|
val result =
|
||||||
activity.runApiTask(accessInfo, progressStyle = ApiTask.PROGRESS_NONE) { client ->
|
activity.runApiTask(accessInfo,
|
||||||
|
progressStyle = ApiTask.PROGRESS_NONE) { client ->
|
||||||
try {
|
try {
|
||||||
val targetStatus = syncStatus(client)
|
val targetStatus = syncStatus(client)
|
||||||
boostApi(client, targetStatus)
|
boostApi(client, targetStatus)
|
||||||
|
@ -5,11 +5,11 @@ import jp.juggler.subwaytooter.ActMain
|
|||||||
import jp.juggler.subwaytooter.R
|
import jp.juggler.subwaytooter.R
|
||||||
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.runApiTask
|
||||||
import jp.juggler.subwaytooter.dialog.ActionsDialog
|
|
||||||
import jp.juggler.subwaytooter.dialog.DlgConfirm
|
|
||||||
import jp.juggler.subwaytooter.column.onFilterDeleted
|
import jp.juggler.subwaytooter.column.onFilterDeleted
|
||||||
|
import jp.juggler.subwaytooter.dialog.ActionsDialog
|
||||||
|
import jp.juggler.subwaytooter.dialog.DlgConfirm.confirm
|
||||||
import jp.juggler.subwaytooter.table.SavedAccount
|
import jp.juggler.subwaytooter.table.SavedAccount
|
||||||
import jp.juggler.util.launchMain
|
import jp.juggler.util.launchAndShowError
|
||||||
import jp.juggler.util.showToast
|
import jp.juggler.util.showToast
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
|
|
||||||
@ -31,22 +31,17 @@ fun ActMain.openFilterMenu(accessInfo: SavedAccount, item: TootFilter?) {
|
|||||||
fun ActMain.filterDelete(
|
fun ActMain.filterDelete(
|
||||||
accessInfo: SavedAccount,
|
accessInfo: SavedAccount,
|
||||||
filter: TootFilter,
|
filter: TootFilter,
|
||||||
bConfirmed: Boolean = false
|
bConfirmed: Boolean = false,
|
||||||
) {
|
) {
|
||||||
if (!bConfirmed) {
|
launchAndShowError {
|
||||||
DlgConfirm.openSimple(
|
if (!bConfirmed) {
|
||||||
this,
|
confirm(R.string.filter_delete_confirm, filter.phrase)
|
||||||
getString(R.string.filter_delete_confirm, filter.phrase)
|
|
||||||
) {
|
|
||||||
filterDelete(accessInfo, filter, bConfirmed = true)
|
|
||||||
}
|
}
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
launchMain {
|
|
||||||
var resultFilterList: ArrayList<TootFilter>? = null
|
var resultFilterList: ArrayList<TootFilter>? = null
|
||||||
runApiTask(accessInfo) { client ->
|
runApiTask(accessInfo) { client ->
|
||||||
var result = client.request("/api/v1/filters/${filter.id}", Request.Builder().delete())
|
var result =
|
||||||
|
client.request("/api/v1/filters/${filter.id}", Request.Builder().delete())
|
||||||
if (result != null && result.error == null) {
|
if (result != null && result.error == null) {
|
||||||
result = client.request("/api/v1/filters")
|
result = client.request("/api/v1/filters")
|
||||||
val jsonArray = result?.jsonArray
|
val jsonArray = result?.jsonArray
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
package jp.juggler.subwaytooter.action
|
package jp.juggler.subwaytooter.action
|
||||||
|
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import jp.juggler.subwaytooter.*
|
import jp.juggler.subwaytooter.ActMain
|
||||||
|
import jp.juggler.subwaytooter.R
|
||||||
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.*
|
||||||
@ -9,12 +10,16 @@ import jp.juggler.subwaytooter.api.entity.*
|
|||||||
import jp.juggler.subwaytooter.column.ColumnType
|
import jp.juggler.subwaytooter.column.ColumnType
|
||||||
import jp.juggler.subwaytooter.column.fireRebindAdapterItems
|
import jp.juggler.subwaytooter.column.fireRebindAdapterItems
|
||||||
import jp.juggler.subwaytooter.column.removeUser
|
import jp.juggler.subwaytooter.column.removeUser
|
||||||
import jp.juggler.subwaytooter.dialog.DlgConfirm
|
import jp.juggler.subwaytooter.dialog.DlgConfirm.confirm
|
||||||
import jp.juggler.subwaytooter.dialog.pickAccount
|
import jp.juggler.subwaytooter.dialog.pickAccount
|
||||||
import jp.juggler.subwaytooter.table.AcctColor
|
import jp.juggler.subwaytooter.table.AcctColor
|
||||||
import jp.juggler.subwaytooter.table.SavedAccount
|
import jp.juggler.subwaytooter.table.SavedAccount
|
||||||
import jp.juggler.subwaytooter.table.UserRelation
|
import jp.juggler.subwaytooter.table.UserRelation
|
||||||
import jp.juggler.util.*
|
import jp.juggler.util.*
|
||||||
|
import kotlinx.coroutines.CancellationException
|
||||||
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
|
import kotlin.coroutines.resume
|
||||||
|
import kotlin.coroutines.resumeWithException
|
||||||
|
|
||||||
fun ActMain.clickFollowInfo(
|
fun ActMain.clickFollowInfo(
|
||||||
pos: Int,
|
pos: Int,
|
||||||
@ -45,7 +50,10 @@ fun ActMain.clickFollow(
|
|||||||
relation.blocking || relation.muting ->
|
relation.blocking || relation.muting ->
|
||||||
Unit // 何もしない
|
Unit // 何もしない
|
||||||
accessInfo.isMisskey && relation.getRequested(who) && !relation.getFollowing(who) ->
|
accessInfo.isMisskey && relation.getRequested(who) && !relation.getFollowing(who) ->
|
||||||
followRequestDelete(pos, accessInfo, whoRef, callback = cancelFollowRequestCompleteCallback)
|
followRequestDelete(pos,
|
||||||
|
accessInfo,
|
||||||
|
whoRef,
|
||||||
|
callback = cancelFollowRequestCompleteCallback)
|
||||||
relation.getFollowing(who) || relation.getRequested(who) ->
|
relation.getFollowing(who) || relation.getRequested(who) ->
|
||||||
follow(pos, accessInfo, whoRef, bFollow = false, callback = unfollowCompleteCallback)
|
follow(pos, accessInfo, whoRef, bFollow = false, callback = unfollowCompleteCallback)
|
||||||
else ->
|
else ->
|
||||||
@ -53,15 +61,20 @@ fun ActMain.clickFollow(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun ActMain.clickFollowRequestAccept(accessInfo: SavedAccount, whoRef: TootAccountRef?, accept: Boolean) {
|
fun ActMain.clickFollowRequestAccept(
|
||||||
|
accessInfo: SavedAccount,
|
||||||
|
whoRef: TootAccountRef?,
|
||||||
|
accept: Boolean,
|
||||||
|
) {
|
||||||
val who = whoRef?.get() ?: return
|
val who = whoRef?.get() ?: return
|
||||||
DlgConfirm.openSimple(
|
launchAndShowError {
|
||||||
this,
|
confirm(
|
||||||
getString(
|
when {
|
||||||
if (accept) R.string.follow_accept_confirm else R.string.follow_deny_confirm,
|
accept -> R.string.follow_accept_confirm
|
||||||
|
else -> R.string.follow_deny_confirm
|
||||||
|
},
|
||||||
AcctColor.getNickname(accessInfo, who)
|
AcctColor.getNickname(accessInfo, who)
|
||||||
)
|
)
|
||||||
) {
|
|
||||||
followRequestAuthorize(accessInfo, whoRef, accept)
|
followRequestAuthorize(accessInfo, whoRef, accept)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -85,136 +98,90 @@ fun ActMain.follow(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!bConfirmMoved && bFollow && who.moved != null) {
|
launchAndShowError {
|
||||||
AlertDialog.Builder(activity)
|
if (!bConfirmMoved && bFollow && who.moved != null) {
|
||||||
.setMessage(
|
val selected = suspendCancellableCoroutine<Int> { cont ->
|
||||||
getString(
|
try {
|
||||||
R.string.jump_moved_user,
|
val dialog = AlertDialog.Builder(activity)
|
||||||
accessInfo.getFullAcct(who),
|
.setMessage(
|
||||||
accessInfo.getFullAcct(who.moved)
|
getString(
|
||||||
)
|
R.string.jump_moved_user,
|
||||||
)
|
accessInfo.getFullAcct(who),
|
||||||
.setPositiveButton(R.string.ok) { _, _ ->
|
accessInfo.getFullAcct(who.moved)
|
||||||
userProfileFromAnotherAccount(
|
)
|
||||||
pos,
|
)
|
||||||
accessInfo,
|
.setPositiveButton(R.string.ok) { _, _ ->
|
||||||
who.moved
|
cont.resume(R.string.ok)
|
||||||
)
|
}
|
||||||
|
.setNeutralButton(R.string.ignore_suggestion) { _, _ ->
|
||||||
|
cont.resume(R.string.ignore_suggestion)
|
||||||
|
|
||||||
|
}
|
||||||
|
.setNegativeButton(R.string.cancel, null)
|
||||||
|
.create()
|
||||||
|
dialog.setOnDismissListener {
|
||||||
|
if (cont.isActive) cont.resumeWithException(CancellationException())
|
||||||
|
}
|
||||||
|
cont.invokeOnCancellation { dialog.dismissSafe() }
|
||||||
|
dialog.show()
|
||||||
|
} catch (ex: Throwable) {
|
||||||
|
cont.resumeWithException(ex)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.setNeutralButton(R.string.ignore_suggestion) { _, _ ->
|
when (selected) {
|
||||||
follow(
|
R.string.ok -> {
|
||||||
pos,
|
userProfileFromAnotherAccount(
|
||||||
accessInfo,
|
pos,
|
||||||
whoRef,
|
accessInfo,
|
||||||
bFollow = bFollow,
|
who.moved
|
||||||
bConfirmMoved = true, // CHANGED
|
)
|
||||||
bConfirmed = bConfirmed,
|
return@launchAndShowError
|
||||||
callback = callback
|
}
|
||||||
)
|
R.string.ignore_suggestion -> Unit // fall thru
|
||||||
|
}
|
||||||
|
} else if (!bConfirmed) {
|
||||||
|
if (bFollow && who.locked) {
|
||||||
|
confirm(
|
||||||
|
getString(
|
||||||
|
R.string.confirm_follow_request_who_from,
|
||||||
|
whoRef.decoded_display_name,
|
||||||
|
AcctColor.getNickname(accessInfo)
|
||||||
|
),
|
||||||
|
accessInfo.confirm_follow_locked,
|
||||||
|
) { newConfirmEnabled ->
|
||||||
|
accessInfo.confirm_follow_locked = newConfirmEnabled
|
||||||
|
accessInfo.saveSetting()
|
||||||
|
activity.reloadAccountSetting(accessInfo)
|
||||||
|
}
|
||||||
|
} else if (bFollow) {
|
||||||
|
confirm(
|
||||||
|
getString(
|
||||||
|
R.string.confirm_follow_who_from,
|
||||||
|
whoRef.decoded_display_name,
|
||||||
|
AcctColor.getNickname(accessInfo)
|
||||||
|
),
|
||||||
|
accessInfo.confirm_follow
|
||||||
|
) { newConfirmEnabled ->
|
||||||
|
accessInfo.confirm_follow = newConfirmEnabled
|
||||||
|
accessInfo.saveSetting()
|
||||||
|
activity.reloadAccountSetting(accessInfo)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
confirm(
|
||||||
|
getString(
|
||||||
|
R.string.confirm_unfollow_who_from,
|
||||||
|
whoRef.decoded_display_name,
|
||||||
|
AcctColor.getNickname(accessInfo)
|
||||||
|
),
|
||||||
|
accessInfo.confirm_unfollow
|
||||||
|
) { newConfirmEnabled ->
|
||||||
|
accessInfo.confirm_unfollow = newConfirmEnabled
|
||||||
|
accessInfo.saveSetting()
|
||||||
|
activity.reloadAccountSetting(accessInfo)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.setNegativeButton(R.string.cancel, null)
|
|
||||||
.show()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!bConfirmed) {
|
|
||||||
if (bFollow && who.locked) {
|
|
||||||
DlgConfirm.open(
|
|
||||||
activity,
|
|
||||||
activity.getString(
|
|
||||||
R.string.confirm_follow_request_who_from,
|
|
||||||
whoRef.decoded_display_name,
|
|
||||||
AcctColor.getNickname(accessInfo)
|
|
||||||
),
|
|
||||||
object : DlgConfirm.Callback {
|
|
||||||
|
|
||||||
override fun onOK() {
|
|
||||||
follow(
|
|
||||||
pos,
|
|
||||||
accessInfo,
|
|
||||||
whoRef,
|
|
||||||
bFollow = bFollow,
|
|
||||||
bConfirmMoved = bConfirmMoved,
|
|
||||||
bConfirmed = true, // CHANGED
|
|
||||||
callback = callback
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override var isConfirmEnabled: Boolean
|
|
||||||
get() = accessInfo.confirm_follow_locked
|
|
||||||
set(value) {
|
|
||||||
accessInfo.confirm_follow_locked = value
|
|
||||||
accessInfo.saveSetting()
|
|
||||||
activity.reloadAccountSetting(accessInfo)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return
|
|
||||||
} else if (bFollow) {
|
|
||||||
DlgConfirm.open(
|
|
||||||
activity,
|
|
||||||
getString(
|
|
||||||
R.string.confirm_follow_who_from,
|
|
||||||
whoRef.decoded_display_name,
|
|
||||||
AcctColor.getNickname(accessInfo)
|
|
||||||
),
|
|
||||||
object : DlgConfirm.Callback {
|
|
||||||
|
|
||||||
override fun onOK() {
|
|
||||||
follow(
|
|
||||||
pos,
|
|
||||||
accessInfo,
|
|
||||||
whoRef,
|
|
||||||
bFollow = bFollow,
|
|
||||||
bConfirmMoved = bConfirmMoved,
|
|
||||||
bConfirmed = true, //CHANGED
|
|
||||||
callback = callback
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override var isConfirmEnabled: Boolean
|
|
||||||
get() = accessInfo.confirm_follow
|
|
||||||
set(value) {
|
|
||||||
accessInfo.confirm_follow = value
|
|
||||||
accessInfo.saveSetting()
|
|
||||||
activity.reloadAccountSetting(accessInfo)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
DlgConfirm.open(
|
|
||||||
activity,
|
|
||||||
getString(
|
|
||||||
R.string.confirm_unfollow_who_from,
|
|
||||||
whoRef.decoded_display_name,
|
|
||||||
AcctColor.getNickname(accessInfo)
|
|
||||||
),
|
|
||||||
object : DlgConfirm.Callback {
|
|
||||||
|
|
||||||
override fun onOK() {
|
|
||||||
follow(
|
|
||||||
pos,
|
|
||||||
accessInfo,
|
|
||||||
whoRef,
|
|
||||||
bFollow = bFollow,
|
|
||||||
bConfirmMoved = bConfirmMoved,
|
|
||||||
bConfirmed = true, // CHANGED
|
|
||||||
callback = callback
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override var isConfirmEnabled: Boolean
|
|
||||||
get() = accessInfo.confirm_unfollow
|
|
||||||
set(value) {
|
|
||||||
accessInfo.confirm_unfollow = value
|
|
||||||
accessInfo.saveSetting()
|
|
||||||
activity.reloadAccountSetting(accessInfo)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
launchMain {
|
|
||||||
var resultRelation: UserRelation? = null
|
var resultRelation: UserRelation? = null
|
||||||
runApiTask(accessInfo, progressStyle = ApiTask.PROGRESS_NONE) { client ->
|
runApiTask(accessInfo, progressStyle = ApiTask.PROGRESS_NONE) { client ->
|
||||||
val parser = TootParser(activity, accessInfo)
|
val parser = TootParser(activity, accessInfo)
|
||||||
@ -327,77 +294,43 @@ private fun ActMain.followRemote(
|
|||||||
bConfirmed: Boolean = false,
|
bConfirmed: Boolean = false,
|
||||||
callback: () -> Unit = {},
|
callback: () -> Unit = {},
|
||||||
) {
|
) {
|
||||||
val activity = this@followRemote
|
|
||||||
|
|
||||||
if (accessInfo.isMe(acct)) {
|
if (accessInfo.isMe(acct)) {
|
||||||
showToast(false, R.string.it_is_you)
|
showToast(false, R.string.it_is_you)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!bConfirmed) {
|
launchAndShowError {
|
||||||
if (locked) {
|
|
||||||
DlgConfirm.open(
|
|
||||||
activity,
|
|
||||||
getString(
|
|
||||||
R.string.confirm_follow_request_who_from,
|
|
||||||
AcctColor.getNickname(acct),
|
|
||||||
AcctColor.getNickname(accessInfo)
|
|
||||||
),
|
|
||||||
object : DlgConfirm.Callback {
|
|
||||||
override fun onOK() {
|
|
||||||
followRemote(
|
|
||||||
|
|
||||||
accessInfo,
|
if (!bConfirmed) {
|
||||||
acct,
|
if (locked) {
|
||||||
locked,
|
confirm(
|
||||||
bConfirmed = true, //CHANGE
|
getString(
|
||||||
callback = callback
|
R.string.confirm_follow_request_who_from,
|
||||||
)
|
AcctColor.getNickname(acct),
|
||||||
}
|
AcctColor.getNickname(accessInfo)
|
||||||
|
),
|
||||||
override var isConfirmEnabled: Boolean
|
accessInfo.confirm_follow_locked,
|
||||||
get() = accessInfo.confirm_follow_locked
|
) { newConfirmEnabled ->
|
||||||
set(value) {
|
accessInfo.confirm_follow_locked = newConfirmEnabled
|
||||||
accessInfo.confirm_follow_locked = value
|
accessInfo.saveSetting()
|
||||||
accessInfo.saveSetting()
|
reloadAccountSetting(accessInfo)
|
||||||
reloadAccountSetting(accessInfo)
|
}
|
||||||
}
|
} else {
|
||||||
})
|
confirm(
|
||||||
return
|
getString(
|
||||||
} else {
|
R.string.confirm_follow_who_from,
|
||||||
DlgConfirm.open(
|
AcctColor.getNickname(acct),
|
||||||
activity,
|
AcctColor.getNickname(accessInfo)
|
||||||
getString(
|
),
|
||||||
R.string.confirm_follow_who_from,
|
accessInfo.confirm_follow
|
||||||
AcctColor.getNickname(acct),
|
) { newConfirmEnabled ->
|
||||||
AcctColor.getNickname(accessInfo)
|
accessInfo.confirm_follow = newConfirmEnabled
|
||||||
),
|
accessInfo.saveSetting()
|
||||||
object : DlgConfirm.Callback {
|
reloadAccountSetting(accessInfo)
|
||||||
|
}
|
||||||
override fun onOK() {
|
}
|
||||||
followRemote(
|
|
||||||
|
|
||||||
accessInfo,
|
|
||||||
acct,
|
|
||||||
locked,
|
|
||||||
bConfirmed = true, //CHANGE
|
|
||||||
callback = callback
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override var isConfirmEnabled: Boolean
|
|
||||||
get() = accessInfo.confirm_follow
|
|
||||||
set(value) {
|
|
||||||
accessInfo.confirm_follow = value
|
|
||||||
accessInfo.saveSetting()
|
|
||||||
reloadAccountSetting(accessInfo)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
launchMain {
|
|
||||||
var resultRelation: UserRelation? = null
|
var resultRelation: UserRelation? = null
|
||||||
runApiTask(accessInfo, progressStyle = ApiTask.PROGRESS_NONE) { client ->
|
runApiTask(accessInfo, progressStyle = ApiTask.PROGRESS_NONE) { client ->
|
||||||
val parser = TootParser(this, accessInfo)
|
val parser = TootParser(this, accessInfo)
|
||||||
@ -598,27 +531,15 @@ fun ActMain.followRequestDelete(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!bConfirmed) {
|
launchAndShowError {
|
||||||
DlgConfirm.openSimple(
|
if (!bConfirmed) {
|
||||||
this,
|
confirm(
|
||||||
getString(
|
|
||||||
R.string.confirm_cancel_follow_request_who_from,
|
R.string.confirm_cancel_follow_request_who_from,
|
||||||
whoRef.decoded_display_name,
|
whoRef.decoded_display_name,
|
||||||
AcctColor.getNickname(accessInfo)
|
AcctColor.getNickname(accessInfo)
|
||||||
)
|
)
|
||||||
) {
|
|
||||||
followRequestDelete(
|
|
||||||
pos,
|
|
||||||
accessInfo,
|
|
||||||
whoRef,
|
|
||||||
bConfirmed = true, // CHANGED
|
|
||||||
callback = callback
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
launchMain {
|
|
||||||
var resultRelation: UserRelation? = null
|
var resultRelation: UserRelation? = null
|
||||||
runApiTask(accessInfo, progressStyle = ApiTask.PROGRESS_NONE) { client ->
|
runApiTask(accessInfo, progressStyle = ApiTask.PROGRESS_NONE) { client ->
|
||||||
if (!accessInfo.isMisskey) {
|
if (!accessInfo.isMisskey) {
|
||||||
@ -649,7 +570,6 @@ fun ActMain.followRequestDelete(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}?.let { result ->
|
}?.let { result ->
|
||||||
|
|
||||||
when (resultRelation) {
|
when (resultRelation) {
|
||||||
null -> showToast(false, result.error)
|
null -> showToast(false, result.error)
|
||||||
else -> {
|
else -> {
|
||||||
|
@ -14,7 +14,7 @@ import jp.juggler.subwaytooter.column.ColumnType
|
|||||||
import jp.juggler.subwaytooter.column.onListListUpdated
|
import jp.juggler.subwaytooter.column.onListListUpdated
|
||||||
import jp.juggler.subwaytooter.column.onListNameUpdated
|
import jp.juggler.subwaytooter.column.onListNameUpdated
|
||||||
import jp.juggler.subwaytooter.dialog.ActionsDialog
|
import jp.juggler.subwaytooter.dialog.ActionsDialog
|
||||||
import jp.juggler.subwaytooter.dialog.DlgConfirm
|
import jp.juggler.subwaytooter.dialog.DlgConfirm.confirm
|
||||||
import jp.juggler.subwaytooter.dialog.DlgTextInput
|
import jp.juggler.subwaytooter.dialog.DlgTextInput
|
||||||
import jp.juggler.subwaytooter.table.SavedAccount
|
import jp.juggler.subwaytooter.table.SavedAccount
|
||||||
import jp.juggler.util.*
|
import jp.juggler.util.*
|
||||||
@ -118,17 +118,10 @@ fun ActMain.listDelete(
|
|||||||
list: TootList,
|
list: TootList,
|
||||||
bConfirmed: Boolean = false,
|
bConfirmed: Boolean = false,
|
||||||
) {
|
) {
|
||||||
if (!bConfirmed) {
|
launchAndShowError {
|
||||||
DlgConfirm.openSimple(
|
if (!bConfirmed) {
|
||||||
this,
|
confirm(R.string.list_delete_confirm, list.title)
|
||||||
getString(R.string.list_delete_confirm, list.title)
|
|
||||||
) {
|
|
||||||
listDelete(accessInfo, list, bConfirmed = true)
|
|
||||||
}
|
}
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
launchMain {
|
|
||||||
runApiTask(accessInfo) { client ->
|
runApiTask(accessInfo) { client ->
|
||||||
if (accessInfo.isMisskey) {
|
if (accessInfo.isMisskey) {
|
||||||
client.request(
|
client.request(
|
||||||
|
@ -4,10 +4,13 @@ import android.app.AlertDialog
|
|||||||
import jp.juggler.subwaytooter.ActMain
|
import jp.juggler.subwaytooter.ActMain
|
||||||
import jp.juggler.subwaytooter.R
|
import jp.juggler.subwaytooter.R
|
||||||
import jp.juggler.subwaytooter.actmain.showColumnMatchAccount
|
import jp.juggler.subwaytooter.actmain.showColumnMatchAccount
|
||||||
import jp.juggler.subwaytooter.api.*
|
import jp.juggler.subwaytooter.api.TootApiResult
|
||||||
|
import jp.juggler.subwaytooter.api.TootParser
|
||||||
import jp.juggler.subwaytooter.api.entity.*
|
import jp.juggler.subwaytooter.api.entity.*
|
||||||
|
import jp.juggler.subwaytooter.api.runApiTask
|
||||||
|
import jp.juggler.subwaytooter.api.syncAccountByAcct
|
||||||
import jp.juggler.subwaytooter.column.onListMemberUpdated
|
import jp.juggler.subwaytooter.column.onListMemberUpdated
|
||||||
import jp.juggler.subwaytooter.dialog.DlgConfirm
|
import jp.juggler.subwaytooter.dialog.DlgConfirm.confirm
|
||||||
import jp.juggler.subwaytooter.table.SavedAccount
|
import jp.juggler.subwaytooter.table.SavedAccount
|
||||||
import jp.juggler.util.*
|
import jp.juggler.util.*
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
@ -23,7 +26,7 @@ fun ActMain.listMemberAdd(
|
|||||||
bFollow: Boolean = false,
|
bFollow: Boolean = false,
|
||||||
onListMemberUpdated: (willRegistered: Boolean, bSuccess: Boolean) -> Unit = { _, _ -> },
|
onListMemberUpdated: (willRegistered: Boolean, bSuccess: Boolean) -> Unit = { _, _ -> },
|
||||||
) {
|
) {
|
||||||
launchMain {
|
launchAndShowError {
|
||||||
runApiTask(accessInfo) { client ->
|
runApiTask(accessInfo) { client ->
|
||||||
val parser = TootParser(this, accessInfo)
|
val parser = TootParser(this, accessInfo)
|
||||||
|
|
||||||
@ -31,7 +34,6 @@ fun ActMain.listMemberAdd(
|
|||||||
|
|
||||||
if (accessInfo.isMisskey) {
|
if (accessInfo.isMisskey) {
|
||||||
// misskeyのリストはフォロー無関係
|
// misskeyのリストはフォロー無関係
|
||||||
|
|
||||||
client.request(
|
client.request(
|
||||||
"/api/users/lists/push",
|
"/api/users/lists/push",
|
||||||
accessInfo.putMisskeyApiToken().apply {
|
accessInfo.putMisskeyApiToken().apply {
|
||||||
@ -128,21 +130,17 @@ fun ActMain.listMemberAdd(
|
|||||||
|
|
||||||
isNotFollowed() -> {
|
isNotFollowed() -> {
|
||||||
if (!bFollow) {
|
if (!bFollow) {
|
||||||
DlgConfirm.openSimple(
|
confirm(
|
||||||
this@listMemberAdd,
|
R.string.list_retry_with_follow,
|
||||||
getString(
|
accessInfo.getFullAcct(localWho)
|
||||||
R.string.list_retry_with_follow,
|
)
|
||||||
accessInfo.getFullAcct(localWho)
|
listMemberAdd(
|
||||||
)
|
accessInfo,
|
||||||
) {
|
listId,
|
||||||
listMemberAdd(
|
localWho,
|
||||||
accessInfo,
|
bFollow = true,
|
||||||
listId,
|
onListMemberUpdated = onListMemberUpdated
|
||||||
localWho,
|
)
|
||||||
bFollow = true,
|
|
||||||
onListMemberUpdated = onListMemberUpdated
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
AlertDialog.Builder(this@listMemberAdd)
|
AlertDialog.Builder(this@listMemberAdd)
|
||||||
.setCancelable(true)
|
.setCancelable(true)
|
||||||
@ -165,7 +163,7 @@ fun ActMain.listMemberDelete(
|
|||||||
accessInfo: SavedAccount,
|
accessInfo: SavedAccount,
|
||||||
listId: EntityId,
|
listId: EntityId,
|
||||||
localWho: TootAccount,
|
localWho: TootAccount,
|
||||||
onListMemberDeleted: (willRegistered: Boolean, bSuccess: Boolean) -> Unit = { _, _ -> },
|
onListMemberDeleted: (willRegistered: Boolean, bSuccess: Boolean) -> Unit = { _, _ -> },
|
||||||
) {
|
) {
|
||||||
launchMain {
|
launchMain {
|
||||||
runApiTask(accessInfo) { client ->
|
runApiTask(accessInfo) { client ->
|
||||||
|
@ -1,14 +1,20 @@
|
|||||||
package jp.juggler.subwaytooter.action
|
package jp.juggler.subwaytooter.action
|
||||||
|
|
||||||
import androidx.appcompat.app.AlertDialog
|
import jp.juggler.subwaytooter.ActMain
|
||||||
import jp.juggler.subwaytooter.*
|
import jp.juggler.subwaytooter.R
|
||||||
import jp.juggler.subwaytooter.api.*
|
import jp.juggler.subwaytooter.api.ApiTask
|
||||||
import jp.juggler.subwaytooter.api.entity.*
|
import jp.juggler.subwaytooter.api.TootParser
|
||||||
|
import jp.juggler.subwaytooter.api.entity.Host
|
||||||
|
import jp.juggler.subwaytooter.api.entity.InstanceCapability
|
||||||
|
import jp.juggler.subwaytooter.api.entity.TootReaction
|
||||||
|
import jp.juggler.subwaytooter.api.entity.TootStatus
|
||||||
|
import jp.juggler.subwaytooter.api.runApiTask
|
||||||
|
import jp.juggler.subwaytooter.api.syncStatus
|
||||||
import jp.juggler.subwaytooter.column.Column
|
import jp.juggler.subwaytooter.column.Column
|
||||||
import jp.juggler.subwaytooter.column.fireShowContent
|
import jp.juggler.subwaytooter.column.fireShowContent
|
||||||
import jp.juggler.subwaytooter.column.updateEmojiReactionByApiResponse
|
import jp.juggler.subwaytooter.column.updateEmojiReactionByApiResponse
|
||||||
import jp.juggler.subwaytooter.dialog.DlgConfirm
|
import jp.juggler.subwaytooter.dialog.DlgConfirm.confirm
|
||||||
import jp.juggler.subwaytooter.dialog.EmojiPicker
|
import jp.juggler.subwaytooter.dialog.launchEmojiPicker
|
||||||
import jp.juggler.subwaytooter.dialog.pickAccount
|
import jp.juggler.subwaytooter.dialog.pickAccount
|
||||||
import jp.juggler.subwaytooter.emoji.CustomEmoji
|
import jp.juggler.subwaytooter.emoji.CustomEmoji
|
||||||
import jp.juggler.subwaytooter.emoji.UnicodeEmoji
|
import jp.juggler.subwaytooter.emoji.UnicodeEmoji
|
||||||
@ -47,9 +53,13 @@ fun ActMain.reactionAdd(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (codeArg == null) {
|
if (codeArg == null) {
|
||||||
EmojiPicker(activity, accessInfo, closeOnSelected = true) { result ->
|
launchEmojiPicker(
|
||||||
|
activity,
|
||||||
|
accessInfo,
|
||||||
|
closeOnSelected = true
|
||||||
|
) { emoji, _ ->
|
||||||
var newUrl: String? = null
|
var newUrl: String? = null
|
||||||
val newCode: String = when (val emoji = result.emoji) {
|
val newCode: String = when (emoji) {
|
||||||
is UnicodeEmoji -> emoji.unifiedCode
|
is UnicodeEmoji -> emoji.unifiedCode
|
||||||
is CustomEmoji -> {
|
is CustomEmoji -> {
|
||||||
newUrl = emoji.staticUrl
|
newUrl = emoji.staticUrl
|
||||||
@ -61,7 +71,7 @@ fun ActMain.reactionAdd(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
reactionAdd(column, status, newCode, newUrl)
|
reactionAdd(column, status, newCode, newUrl)
|
||||||
}.show()
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var code = codeArg
|
var code = codeArg
|
||||||
@ -96,40 +106,26 @@ fun ActMain.reactionAdd(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isConfirmed) {
|
activity.launchAndShowError {
|
||||||
val options = DecodeOptions(
|
|
||||||
activity,
|
|
||||||
accessInfo,
|
|
||||||
decodeEmoji = true,
|
|
||||||
enlargeEmoji = 1.5f,
|
|
||||||
enlargeCustomEmoji = 1.5f
|
|
||||||
)
|
|
||||||
val emojiSpan = TootReaction.toSpannableStringBuilder(options, code, urlArg)
|
|
||||||
DlgConfirm.open(
|
|
||||||
activity,
|
|
||||||
getString(R.string.confirm_reaction, emojiSpan, AcctColor.getNickname(accessInfo)),
|
|
||||||
object : DlgConfirm.Callback {
|
|
||||||
override var isConfirmEnabled: Boolean
|
|
||||||
get() = accessInfo.confirm_reaction
|
|
||||||
set(bv) {
|
|
||||||
accessInfo.confirm_reaction = bv
|
|
||||||
accessInfo.saveSetting()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onOK() {
|
if (!isConfirmed) {
|
||||||
reactionAdd(
|
val options = DecodeOptions(
|
||||||
column,
|
activity,
|
||||||
status,
|
accessInfo,
|
||||||
codeArg = code,
|
decodeEmoji = true,
|
||||||
urlArg = urlArg,
|
enlargeEmoji = 1.5f,
|
||||||
isConfirmed = true
|
enlargeCustomEmoji = 1.5f
|
||||||
)
|
)
|
||||||
}
|
val emojiSpan = TootReaction.toSpannableStringBuilder(options, code, urlArg)
|
||||||
})
|
confirm(
|
||||||
return
|
getString(R.string.confirm_reaction, emojiSpan, AcctColor.getNickname(accessInfo)),
|
||||||
}
|
accessInfo.confirm_reaction,
|
||||||
|
) { newConfirmEnabled ->
|
||||||
|
accessInfo.confirm_reaction = newConfirmEnabled
|
||||||
|
accessInfo.saveSetting()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
launchMain {
|
|
||||||
var resultStatus: TootStatus? = null
|
var resultStatus: TootStatus? = null
|
||||||
runApiTask(accessInfo) { client ->
|
runApiTask(accessInfo) { client ->
|
||||||
when {
|
when {
|
||||||
@ -158,12 +154,7 @@ fun ActMain.reactionAdd(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}?.let { result ->
|
}?.let { result ->
|
||||||
|
result.error?.let { error(it) }
|
||||||
val error = result.error
|
|
||||||
if (error != null) {
|
|
||||||
activity.showToast(false, error)
|
|
||||||
return@launchMain
|
|
||||||
}
|
|
||||||
|
|
||||||
when (val resCode = result.response?.code) {
|
when (val resCode = result.response?.code) {
|
||||||
in 200 until 300 -> when (val newStatus = resultStatus) {
|
in 200 until 300 -> when (val newStatus = resultStatus) {
|
||||||
@ -209,26 +200,19 @@ fun ActMain.reactionRemove(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!confirmed) {
|
launchAndShowError {
|
||||||
val options = DecodeOptions(
|
|
||||||
activity,
|
|
||||||
accessInfo,
|
|
||||||
decodeEmoji = true,
|
|
||||||
enlargeEmoji = 1.5f,
|
|
||||||
enlargeCustomEmoji = 1.5f
|
|
||||||
)
|
|
||||||
val emojiSpan = reaction.toSpannableStringBuilder(options, status)
|
|
||||||
AlertDialog.Builder(activity)
|
|
||||||
.setMessage(getString(R.string.reaction_remove_confirm, emojiSpan))
|
|
||||||
.setNegativeButton(R.string.cancel, null)
|
|
||||||
.setPositiveButton(R.string.ok) { _, _ ->
|
|
||||||
reactionRemove(column, status, reaction, confirmed = true)
|
|
||||||
}
|
|
||||||
.show()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
launchMain {
|
if (!confirmed) {
|
||||||
|
val options = DecodeOptions(
|
||||||
|
activity,
|
||||||
|
accessInfo,
|
||||||
|
decodeEmoji = true,
|
||||||
|
enlargeEmoji = 1.5f,
|
||||||
|
enlargeCustomEmoji = 1.5f
|
||||||
|
)
|
||||||
|
val emojiSpan = reaction.toSpannableStringBuilder(options, status)
|
||||||
|
confirm(R.string.reaction_remove_confirm, emojiSpan)
|
||||||
|
}
|
||||||
var resultStatus: TootStatus? = null
|
var resultStatus: TootStatus? = null
|
||||||
runApiTask(accessInfo) { client ->
|
runApiTask(accessInfo) { client ->
|
||||||
when {
|
when {
|
||||||
@ -265,9 +249,13 @@ fun ActMain.reactionRemove(
|
|||||||
else -> {
|
else -> {
|
||||||
when (val newStatus = resultStatus) {
|
when (val newStatus = resultStatus) {
|
||||||
null ->
|
null ->
|
||||||
if (status.decreaseReactionMisskey(reaction.name, true, "removeReaction")) {
|
if (status.decreaseReactionMisskey(reaction.name,
|
||||||
|
true,
|
||||||
|
"removeReaction")
|
||||||
|
) {
|
||||||
// 1個だけ描画更新するのではなく、TLにある複数の要素をまとめて更新する
|
// 1個だけ描画更新するのではなく、TLにある複数の要素をまとめて更新する
|
||||||
column.fireShowContent(reason = "removeReaction complete", reset = true)
|
column.fireShowContent(reason = "removeReaction complete",
|
||||||
|
reset = true)
|
||||||
}
|
}
|
||||||
|
|
||||||
else ->
|
else ->
|
||||||
@ -290,15 +278,14 @@ private fun ActMain.reactionWithoutUi(
|
|||||||
resolvedStatus: TootStatus,
|
resolvedStatus: TootStatus,
|
||||||
reactionCode: String? = null,
|
reactionCode: String? = null,
|
||||||
reactionImage: String? = null,
|
reactionImage: String? = null,
|
||||||
isConfirmed: Boolean = false,
|
|
||||||
callback: () -> Unit,
|
callback: () -> Unit,
|
||||||
) {
|
) {
|
||||||
val activity = this
|
val activity = this
|
||||||
|
|
||||||
if (reactionCode == null) {
|
if (reactionCode == null) {
|
||||||
EmojiPicker(activity, accessInfo, closeOnSelected = true) { result ->
|
launchEmojiPicker(activity, accessInfo, closeOnSelected = true) { emoji, _ ->
|
||||||
var newUrl: String? = null
|
var newUrl: String? = null
|
||||||
val newCode = when (val emoji = result.emoji) {
|
val newCode = when (emoji) {
|
||||||
is UnicodeEmoji -> emoji.unifiedCode
|
is UnicodeEmoji -> emoji.unifiedCode
|
||||||
is CustomEmoji -> {
|
is CustomEmoji -> {
|
||||||
newUrl = emoji.staticUrl
|
newUrl = emoji.staticUrl
|
||||||
@ -314,78 +301,46 @@ private fun ActMain.reactionWithoutUi(
|
|||||||
resolvedStatus = resolvedStatus,
|
resolvedStatus = resolvedStatus,
|
||||||
reactionCode = newCode,
|
reactionCode = newCode,
|
||||||
reactionImage = newUrl,
|
reactionImage = newUrl,
|
||||||
isConfirmed = isConfirmed,
|
|
||||||
callback = callback
|
callback = callback
|
||||||
)
|
)
|
||||||
}.show()
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val canMultipleReaction = InstanceCapability.canMultipleReaction(accessInfo)
|
val canMultipleReaction = InstanceCapability.canMultipleReaction(accessInfo)
|
||||||
|
|
||||||
if (!isConfirmed) {
|
val options = DecodeOptions(
|
||||||
val options = DecodeOptions(
|
activity,
|
||||||
activity,
|
accessInfo,
|
||||||
accessInfo,
|
decodeEmoji = true,
|
||||||
decodeEmoji = true,
|
enlargeEmoji = 1.5f,
|
||||||
enlargeEmoji = 1.5f,
|
enlargeCustomEmoji = 1.5f
|
||||||
enlargeCustomEmoji = 1.5f
|
)
|
||||||
)
|
val emojiSpan = TootReaction.toSpannableStringBuilder(options, reactionCode, reactionImage)
|
||||||
val emojiSpan = TootReaction.toSpannableStringBuilder(options, reactionCode, reactionImage)
|
val isCustomEmoji = TootReaction.isCustomEmoji(reactionCode)
|
||||||
|
val url = resolvedStatus.url
|
||||||
|
|
||||||
val isCustomEmoji = TootReaction.isCustomEmoji(reactionCode)
|
launchAndShowError {
|
||||||
val url = resolvedStatus.url
|
|
||||||
when {
|
when {
|
||||||
isCustomEmoji && canMultipleReaction -> {
|
isCustomEmoji && canMultipleReaction ->
|
||||||
showToast(false, "can't reaction with custom emoji from this account")
|
error("can't reaction with custom emoji from this account")
|
||||||
return
|
|
||||||
}
|
|
||||||
isCustomEmoji && url?.likePleromaStatusUrl() == true -> DlgConfirm.openSimple(
|
|
||||||
activity,
|
|
||||||
getString(
|
|
||||||
R.string.confirm_reaction_to_pleroma,
|
|
||||||
emojiSpan,
|
|
||||||
AcctColor.getNickname(accessInfo),
|
|
||||||
resolvedStatus.account.acct.host?.pretty ?: "(null)"
|
|
||||||
),
|
|
||||||
) {
|
|
||||||
reactionWithoutUi(
|
|
||||||
accessInfo = accessInfo,
|
|
||||||
resolvedStatus = resolvedStatus,
|
|
||||||
reactionCode = reactionCode,
|
|
||||||
reactionImage = reactionImage,
|
|
||||||
isConfirmed = true,
|
|
||||||
callback = callback
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> DlgConfirm.open(
|
isCustomEmoji && url?.likePleromaStatusUrl() == true -> confirm(
|
||||||
activity,
|
R.string.confirm_reaction_to_pleroma,
|
||||||
|
emojiSpan,
|
||||||
|
AcctColor.getNickname(accessInfo),
|
||||||
|
resolvedStatus.account.acct.host?.pretty ?: "(null)"
|
||||||
|
)
|
||||||
|
|
||||||
|
else -> confirm(
|
||||||
getString(R.string.confirm_reaction, emojiSpan, AcctColor.getNickname(accessInfo)),
|
getString(R.string.confirm_reaction, emojiSpan, AcctColor.getNickname(accessInfo)),
|
||||||
object : DlgConfirm.Callback {
|
accessInfo.confirm_reaction,
|
||||||
override var isConfirmEnabled: Boolean
|
) { newConfirmEnabled ->
|
||||||
get() = accessInfo.confirm_reaction
|
accessInfo.confirm_reaction = newConfirmEnabled
|
||||||
set(bv) {
|
accessInfo.saveSetting()
|
||||||
accessInfo.confirm_reaction = bv
|
}
|
||||||
accessInfo.saveSetting()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onOK() {
|
|
||||||
reactionWithoutUi(
|
|
||||||
accessInfo = accessInfo,
|
|
||||||
resolvedStatus = resolvedStatus,
|
|
||||||
reactionCode = reactionCode,
|
|
||||||
reactionImage = reactionImage,
|
|
||||||
isConfirmed = true,
|
|
||||||
callback = callback
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
launchMain {
|
|
||||||
// var resultStatus: TootStatus? = null
|
// var resultStatus: TootStatus? = null
|
||||||
runApiTask(accessInfo) { client ->
|
runApiTask(accessInfo) { client ->
|
||||||
when {
|
when {
|
||||||
@ -398,7 +353,9 @@ private fun ActMain.reactionWithoutUi(
|
|||||||
) // 成功すると204 no content
|
) // 成功すると204 no content
|
||||||
|
|
||||||
canMultipleReaction -> client.request(
|
canMultipleReaction -> client.request(
|
||||||
"/api/v1/pleroma/statuses/${resolvedStatus.id}/reactions/${reactionCode.encodePercent("@")}",
|
"/api/v1/pleroma/statuses/${resolvedStatus.id}/reactions/${
|
||||||
|
reactionCode.encodePercent("@")
|
||||||
|
}",
|
||||||
"".toFormRequestBody().toPut()
|
"".toFormRequestBody().toPut()
|
||||||
) // 成功すると更新された投稿
|
) // 成功すると更新された投稿
|
||||||
|
|
||||||
@ -475,27 +432,30 @@ fun ActMain.reactionFromAnotherAccount(
|
|||||||
statusArg ?: return
|
statusArg ?: return
|
||||||
val activity = this
|
val activity = this
|
||||||
|
|
||||||
fun afterResolveStatus(actionAccount: SavedAccount, resolvedStatus: TootStatus) {
|
|
||||||
val code = if (reaction == null) {
|
|
||||||
null // あとで選択する
|
|
||||||
} else {
|
|
||||||
reactionFixCode(
|
|
||||||
timelineAccount = timelineAccount,
|
|
||||||
actionAccount = actionAccount,
|
|
||||||
reaction = reaction,
|
|
||||||
) ?: return // エラー終了の場合がある
|
|
||||||
}
|
|
||||||
|
|
||||||
reactionWithoutUi(
|
|
||||||
accessInfo = actionAccount,
|
|
||||||
resolvedStatus = resolvedStatus,
|
|
||||||
reactionCode = code,
|
|
||||||
callback = reactionCompleteCallback,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
launchMain {
|
launchMain {
|
||||||
|
|
||||||
|
fun afterResolveStatus(
|
||||||
|
actionAccount: SavedAccount,
|
||||||
|
resolvedStatus: TootStatus,
|
||||||
|
) {
|
||||||
|
val code = if (reaction == null) {
|
||||||
|
null // あとで選択する
|
||||||
|
} else {
|
||||||
|
reactionFixCode(
|
||||||
|
timelineAccount = timelineAccount,
|
||||||
|
actionAccount = actionAccount,
|
||||||
|
reaction = reaction,
|
||||||
|
) ?: return // エラー終了の場合がある
|
||||||
|
}
|
||||||
|
|
||||||
|
reactionWithoutUi(
|
||||||
|
accessInfo = actionAccount,
|
||||||
|
resolvedStatus = resolvedStatus,
|
||||||
|
reactionCode = code,
|
||||||
|
callback = reactionCompleteCallback,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
val list = accountListCanReaction() ?: return@launchMain
|
val list = accountListCanReaction() ?: return@launchMain
|
||||||
if (list.isEmpty()) {
|
if (list.isEmpty()) {
|
||||||
showToast(false, R.string.not_available_for_current_accounts)
|
showToast(false, R.string.not_available_for_current_accounts)
|
||||||
|
@ -2,22 +2,25 @@ package jp.juggler.subwaytooter.action
|
|||||||
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import jp.juggler.subwaytooter.*
|
import jp.juggler.subwaytooter.ActMain
|
||||||
|
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.*
|
||||||
import jp.juggler.subwaytooter.api.entity.*
|
import jp.juggler.subwaytooter.api.entity.EntityId
|
||||||
|
import jp.juggler.subwaytooter.api.entity.TootScheduled
|
||||||
|
import jp.juggler.subwaytooter.api.entity.TootStatus
|
||||||
import jp.juggler.subwaytooter.column.*
|
import jp.juggler.subwaytooter.column.*
|
||||||
import jp.juggler.subwaytooter.dialog.ActionsDialog
|
import jp.juggler.subwaytooter.dialog.ActionsDialog
|
||||||
import jp.juggler.subwaytooter.dialog.DlgConfirm
|
import jp.juggler.subwaytooter.dialog.DlgConfirm.confirm
|
||||||
import jp.juggler.subwaytooter.dialog.pickAccount
|
import jp.juggler.subwaytooter.dialog.pickAccount
|
||||||
import jp.juggler.subwaytooter.table.AcctColor
|
import jp.juggler.subwaytooter.table.AcctColor
|
||||||
import jp.juggler.subwaytooter.table.SavedAccount
|
import jp.juggler.subwaytooter.table.SavedAccount
|
||||||
import jp.juggler.subwaytooter.util.emptyCallback
|
import jp.juggler.subwaytooter.util.emptyCallback
|
||||||
import jp.juggler.util.*
|
import jp.juggler.util.*
|
||||||
|
import kotlinx.coroutines.CancellationException
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
fun ActMain.clickStatusDelete(
|
fun ActMain.clickStatusDelete(
|
||||||
accessInfo: SavedAccount,
|
accessInfo: SavedAccount,
|
||||||
@ -81,7 +84,8 @@ fun ActMain.clickScheduledToot(accessInfo: SavedAccount, item: TootScheduled, co
|
|||||||
scheduledPostEdit(accessInfo, item)
|
scheduledPostEdit(accessInfo, item)
|
||||||
}
|
}
|
||||||
.addAction(getString(R.string.delete)) {
|
.addAction(getString(R.string.delete)) {
|
||||||
scheduledPostDelete(accessInfo, item) {
|
launchAndShowError {
|
||||||
|
scheduledPostDelete(accessInfo, item)
|
||||||
column.onScheduleDeleted(item)
|
column.onScheduleDeleted(item)
|
||||||
showToast(false, R.string.scheduled_post_deleted)
|
showToast(false, R.string.scheduled_post_deleted)
|
||||||
}
|
}
|
||||||
@ -99,61 +103,44 @@ fun ActMain.favourite(
|
|||||||
crossAccountMode: CrossAccountMode,
|
crossAccountMode: CrossAccountMode,
|
||||||
callback: () -> Unit,
|
callback: () -> Unit,
|
||||||
bSet: Boolean = true,
|
bSet: Boolean = true,
|
||||||
bConfirmed: Boolean = false,
|
|
||||||
) {
|
) {
|
||||||
if (appState.isBusyFav(accessInfo, statusArg)) {
|
launchAndShowError {
|
||||||
showToast(false, R.string.wait_previous_operation)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 必要なら確認を出す
|
if (appState.isBusyFav(accessInfo, statusArg)) {
|
||||||
if (!bConfirmed && accessInfo.isMastodon) {
|
showToast(false, R.string.wait_previous_operation)
|
||||||
DlgConfirm.open(
|
return@launchAndShowError
|
||||||
this,
|
}
|
||||||
getString(
|
|
||||||
|
// 必要なら確認を出す
|
||||||
|
if (accessInfo.isMastodon) {
|
||||||
|
confirm(
|
||||||
|
getString(
|
||||||
|
when (bSet) {
|
||||||
|
true -> R.string.confirm_favourite_from
|
||||||
|
else -> R.string.confirm_unfavourite_from
|
||||||
|
},
|
||||||
|
AcctColor.getNickname(accessInfo)
|
||||||
|
),
|
||||||
when (bSet) {
|
when (bSet) {
|
||||||
true -> R.string.confirm_favourite_from
|
true -> accessInfo.confirm_favourite
|
||||||
else -> R.string.confirm_unfavourite_from
|
else -> accessInfo.confirm_unfavourite
|
||||||
},
|
|
||||||
AcctColor.getNickname(accessInfo)
|
|
||||||
),
|
|
||||||
object : DlgConfirm.Callback {
|
|
||||||
|
|
||||||
override fun onOK() {
|
|
||||||
favourite(
|
|
||||||
accessInfo,
|
|
||||||
statusArg,
|
|
||||||
crossAccountMode,
|
|
||||||
callback,
|
|
||||||
bSet = bSet,
|
|
||||||
bConfirmed = true
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
) { newConfirmEnabled ->
|
||||||
|
when (bSet) {
|
||||||
|
true -> accessInfo.confirm_favourite = newConfirmEnabled
|
||||||
|
else -> accessInfo.confirm_unfavourite = newConfirmEnabled
|
||||||
|
}
|
||||||
|
accessInfo.saveSetting()
|
||||||
|
reloadAccountSetting(accessInfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override var isConfirmEnabled: Boolean
|
//
|
||||||
get() = when (bSet) {
|
appState.setBusyFav(accessInfo, statusArg)
|
||||||
true -> accessInfo.confirm_favourite
|
|
||||||
else -> accessInfo.confirm_unfavourite
|
|
||||||
}
|
|
||||||
set(value) {
|
|
||||||
when (bSet) {
|
|
||||||
true -> accessInfo.confirm_favourite = value
|
|
||||||
else -> accessInfo.confirm_unfavourite = value
|
|
||||||
}
|
|
||||||
accessInfo.saveSetting()
|
|
||||||
reloadAccountSetting(accessInfo)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
// ファボ表示を更新中にする
|
||||||
appState.setBusyFav(accessInfo, statusArg)
|
showColumnMatchAccount(accessInfo)
|
||||||
|
|
||||||
// ファボ表示を更新中にする
|
|
||||||
showColumnMatchAccount(accessInfo)
|
|
||||||
|
|
||||||
launchMain {
|
|
||||||
var resultStatus: TootStatus? = null
|
var resultStatus: TootStatus? = null
|
||||||
val result = runApiTask(
|
val result = runApiTask(
|
||||||
accessInfo,
|
accessInfo,
|
||||||
@ -284,7 +271,6 @@ fun ActMain.bookmark(
|
|||||||
crossAccountMode: CrossAccountMode,
|
crossAccountMode: CrossAccountMode,
|
||||||
callback: () -> Unit,
|
callback: () -> Unit,
|
||||||
bSet: Boolean = true,
|
bSet: Boolean = true,
|
||||||
bConfirmed: Boolean = false,
|
|
||||||
) {
|
) {
|
||||||
if (appState.isBusyFav(accessInfo, statusArg)) {
|
if (appState.isBusyFav(accessInfo, statusArg)) {
|
||||||
showToast(false, R.string.wait_previous_operation)
|
showToast(false, R.string.wait_previous_operation)
|
||||||
@ -294,47 +280,30 @@ fun ActMain.bookmark(
|
|||||||
showToast(false, R.string.misskey_account_not_supported)
|
showToast(false, R.string.misskey_account_not_supported)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
launchAndShowError {
|
||||||
|
|
||||||
// 必要なら確認を出す
|
// 必要なら確認を出す
|
||||||
// ブックマークは解除する時だけ確認する
|
// ブックマークは解除する時だけ確認する
|
||||||
if (!bConfirmed && !bSet) {
|
if (!bSet) {
|
||||||
DlgConfirm.open(
|
confirm(
|
||||||
this,
|
getString(
|
||||||
getString(
|
R.string.confirm_unbookmark_from,
|
||||||
R.string.confirm_unbookmark_from,
|
AcctColor.getNickname(accessInfo)
|
||||||
AcctColor.getNickname(accessInfo)
|
),
|
||||||
),
|
accessInfo.confirm_unbookmark
|
||||||
object : DlgConfirm.Callback {
|
) { newConfirmEnabled ->
|
||||||
override var isConfirmEnabled: Boolean
|
accessInfo.confirm_unbookmark = newConfirmEnabled
|
||||||
get() = accessInfo.confirm_unbookmark
|
accessInfo.saveSetting()
|
||||||
set(value) {
|
reloadAccountSetting(accessInfo)
|
||||||
accessInfo.confirm_unbookmark = value
|
}
|
||||||
accessInfo.saveSetting()
|
}
|
||||||
reloadAccountSetting(accessInfo)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onOK() {
|
//
|
||||||
bookmark(
|
appState.setBusyBookmark(accessInfo, statusArg)
|
||||||
accessInfo = accessInfo,
|
|
||||||
statusArg = statusArg,
|
|
||||||
crossAccountMode = crossAccountMode,
|
|
||||||
callback = callback,
|
|
||||||
bSet = bSet,
|
|
||||||
bConfirmed = true,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
// ファボ表示を更新中にする
|
||||||
appState.setBusyBookmark(accessInfo, statusArg)
|
showColumnMatchAccount(accessInfo)
|
||||||
|
|
||||||
// ファボ表示を更新中にする
|
|
||||||
showColumnMatchAccount(accessInfo)
|
|
||||||
|
|
||||||
//
|
|
||||||
launchMain {
|
|
||||||
var resultStatus: TootStatus? = null
|
var resultStatus: TootStatus? = null
|
||||||
val result = runApiTask(accessInfo, progressStyle = ApiTask.PROGRESS_NONE) { client ->
|
val result = runApiTask(accessInfo, progressStyle = ApiTask.PROGRESS_NONE) { client ->
|
||||||
val targetStatus = if (crossAccountMode.isRemote) {
|
val targetStatus = if (crossAccountMode.isRemote) {
|
||||||
@ -580,40 +549,21 @@ fun ActMain.statusEdit(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun ActMain.scheduledPostDelete(
|
suspend fun ActMain.scheduledPostDelete(
|
||||||
accessInfo: SavedAccount,
|
accessInfo: SavedAccount,
|
||||||
item: TootScheduled,
|
item: TootScheduled,
|
||||||
bConfirmed: Boolean = false,
|
bConfirmed: Boolean = false,
|
||||||
callback: () -> Unit,
|
|
||||||
) {
|
) {
|
||||||
val act = this@scheduledPostDelete
|
|
||||||
if (!bConfirmed) {
|
if (!bConfirmed) {
|
||||||
DlgConfirm.openSimple(
|
confirm(R.string.scheduled_status_delete_confirm)
|
||||||
act,
|
|
||||||
getString(R.string.scheduled_status_delete_confirm)
|
|
||||||
) {
|
|
||||||
scheduledPostDelete(
|
|
||||||
accessInfo,
|
|
||||||
item,
|
|
||||||
bConfirmed = true,
|
|
||||||
callback = callback
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
launchMain {
|
|
||||||
runApiTask(accessInfo) { client ->
|
|
||||||
client.request(
|
|
||||||
"/api/v1/scheduled_statuses/${item.id}",
|
|
||||||
Request.Builder().delete()
|
|
||||||
)
|
|
||||||
}?.let { result ->
|
|
||||||
when (val error = result.error) {
|
|
||||||
null -> callback()
|
|
||||||
else -> showToast(true, error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
val result = runApiTask(accessInfo) { client ->
|
||||||
|
client.request(
|
||||||
|
"/api/v1/scheduled_statuses/${item.id}",
|
||||||
|
Request.Builder().delete()
|
||||||
|
)
|
||||||
|
} ?: throw CancellationException("scheduledPostDelete cancelled.")
|
||||||
|
result.error?.notEmpty()?.let { error(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun ActMain.scheduledPostEdit(
|
fun ActMain.scheduledPostEdit(
|
||||||
|
@ -5,16 +5,19 @@ import android.view.View
|
|||||||
import android.view.inputmethod.EditorInfo
|
import android.view.inputmethod.EditorInfo
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.core.view.GravityCompat
|
import androidx.core.view.GravityCompat
|
||||||
import jp.juggler.subwaytooter.*
|
import jp.juggler.subwaytooter.ActMain
|
||||||
|
import jp.juggler.subwaytooter.App1
|
||||||
|
import jp.juggler.subwaytooter.R
|
||||||
|
import jp.juggler.subwaytooter.Styler
|
||||||
import jp.juggler.subwaytooter.actpost.CompletionHelper
|
import jp.juggler.subwaytooter.actpost.CompletionHelper
|
||||||
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.dialog.pickAccount
|
import jp.juggler.subwaytooter.dialog.pickAccount
|
||||||
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.PostCompleteCallback
|
|
||||||
import jp.juggler.subwaytooter.util.PostImpl
|
import jp.juggler.subwaytooter.util.PostImpl
|
||||||
|
import jp.juggler.subwaytooter.util.PostResult
|
||||||
import jp.juggler.util.hideKeyboard
|
import jp.juggler.util.hideKeyboard
|
||||||
|
import jp.juggler.util.launchAndShowError
|
||||||
import jp.juggler.util.launchMain
|
import jp.juggler.util.launchMain
|
||||||
import org.jetbrains.anko.imageResource
|
import org.jetbrains.anko.imageResource
|
||||||
|
|
||||||
@ -104,39 +107,39 @@ fun ActMain.performQuickPost(account: SavedAccount?) {
|
|||||||
|
|
||||||
etQuickPost.hideKeyboard()
|
etQuickPost.hideKeyboard()
|
||||||
|
|
||||||
PostImpl(
|
launchAndShowError {
|
||||||
activity = this,
|
val postResult = PostImpl(
|
||||||
account = account,
|
activity = this@performQuickPost,
|
||||||
content = etQuickPost.text.toString().trim { it <= ' ' },
|
account = account,
|
||||||
spoilerText = null,
|
content = etQuickPost.text.toString().trim { it <= ' ' },
|
||||||
visibilityArg = when (quickPostVisibility) {
|
spoilerText = null,
|
||||||
TootVisibility.AccountSetting -> account.visibility
|
visibilityArg = when (quickPostVisibility) {
|
||||||
else -> quickPostVisibility
|
TootVisibility.AccountSetting -> account.visibility
|
||||||
},
|
else -> quickPostVisibility
|
||||||
bNSFW = false,
|
},
|
||||||
inReplyToId = null,
|
bNSFW = false,
|
||||||
attachmentListArg = null,
|
inReplyToId = null,
|
||||||
enqueteItemsArg = null,
|
attachmentListArg = null,
|
||||||
pollType = null,
|
enqueteItemsArg = null,
|
||||||
pollExpireSeconds = 0,
|
pollType = null,
|
||||||
pollHideTotals = false,
|
pollExpireSeconds = 0,
|
||||||
pollMultipleChoice = false,
|
pollHideTotals = false,
|
||||||
scheduledAt = 0L,
|
pollMultipleChoice = false,
|
||||||
scheduledId = null,
|
scheduledAt = 0L,
|
||||||
redraftStatusId = null,
|
scheduledId = null,
|
||||||
editStatusId = null,
|
redraftStatusId = null,
|
||||||
emojiMapCustom = App1.custom_emoji_lister.getMap(account),
|
editStatusId = null,
|
||||||
useQuoteToot = false,
|
emojiMapCustom = App1.custom_emoji_lister.getMapNonBlocking(account),
|
||||||
callback = object : PostCompleteCallback {
|
useQuoteToot = false,
|
||||||
override fun onScheduledPostComplete(targetAccount: SavedAccount) {}
|
).runSuspend()
|
||||||
override fun onPostComplete(targetAccount: SavedAccount, status: TootStatus) {
|
|
||||||
etQuickPost.setText("")
|
if (postResult is PostResult.Normal) {
|
||||||
postedAcct = targetAccount.acct
|
etQuickPost.setText("")
|
||||||
postedStatusId = status.id
|
postedAcct = postResult.targetAccount.acct
|
||||||
postedReplyId = status.in_reply_to_id
|
postedStatusId = postResult.status.id
|
||||||
postedRedraftId = null
|
postedReplyId = postResult.status.in_reply_to_id
|
||||||
refreshAfterPost()
|
postedRedraftId = null
|
||||||
}
|
refreshAfterPost()
|
||||||
}
|
}
|
||||||
).run()
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,10 +22,10 @@ fun ActPost.selectAccount(a: SavedAccount?) {
|
|||||||
views.btnAccount.setTextColor(attrColor(android.R.attr.textColorPrimary))
|
views.btnAccount.setTextColor(attrColor(android.R.attr.textColorPrimary))
|
||||||
views.btnAccount.setBackgroundResource(R.drawable.btn_bg_transparent_round6dp)
|
views.btnAccount.setBackgroundResource(R.drawable.btn_bg_transparent_round6dp)
|
||||||
} else {
|
} else {
|
||||||
|
launchMain {
|
||||||
// 先読みしてキャッシュに保持しておく
|
// 先読みしてキャッシュに保持しておく
|
||||||
App1.custom_emoji_lister.getList(a) {
|
|
||||||
// 何もしない
|
// 何もしない
|
||||||
|
App1.custom_emoji_lister.getList(a)
|
||||||
}
|
}
|
||||||
|
|
||||||
val ac = AcctColor.load(a)
|
val ac = AcctColor.load(a)
|
||||||
|
@ -288,64 +288,67 @@ fun ActPost.performMore() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun ActPost.performPost() {
|
fun ActPost.performPost() {
|
||||||
// アップロード中は投稿できない
|
val activity = this
|
||||||
if (attachmentList.any { it.status == PostAttachment.Status.Progress }) {
|
launchAndShowError {
|
||||||
showToast(false, R.string.media_attachment_still_uploading)
|
// アップロード中は投稿できない
|
||||||
return
|
if (attachmentList.any { it.status == PostAttachment.Status.Progress }) {
|
||||||
}
|
showToast(false, R.string.media_attachment_still_uploading)
|
||||||
|
return@launchAndShowError
|
||||||
val account = this.account ?: return
|
|
||||||
var pollType: TootPollsType? = null
|
|
||||||
var pollItems: ArrayList<String>? = null
|
|
||||||
var pollExpireSeconds = 0
|
|
||||||
var pollHideTotals = false
|
|
||||||
var pollMultipleChoice = false
|
|
||||||
when (views.spPollType.selectedItemPosition) {
|
|
||||||
1 -> {
|
|
||||||
pollType = TootPollsType.Mastodon
|
|
||||||
pollItems = pollChoiceList()
|
|
||||||
pollExpireSeconds = pollExpireSeconds()
|
|
||||||
pollHideTotals = views.cbHideTotals.isChecked
|
|
||||||
pollMultipleChoice = views.cbMultipleChoice.isChecked
|
|
||||||
}
|
}
|
||||||
2 -> {
|
|
||||||
pollType = TootPollsType.FriendsNico
|
|
||||||
pollItems = pollChoiceList()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PostImpl(
|
val account = activity.account ?: return@launchAndShowError
|
||||||
activity = this,
|
var pollType: TootPollsType? = null
|
||||||
account = account,
|
var pollItems: ArrayList<String>? = null
|
||||||
content = views.etContent.text.toString().trim { it <= ' ' },
|
var pollExpireSeconds = 0
|
||||||
spoilerText = when {
|
var pollHideTotals = false
|
||||||
!views.cbContentWarning.isChecked -> null
|
var pollMultipleChoice = false
|
||||||
else -> views.etContentWarning.text.toString().trim { it <= ' ' }
|
when (views.spPollType.selectedItemPosition) {
|
||||||
},
|
1 -> {
|
||||||
visibilityArg = states.visibility ?: TootVisibility.Public,
|
pollType = TootPollsType.Mastodon
|
||||||
bNSFW = views.cbNSFW.isChecked,
|
pollItems = pollChoiceList()
|
||||||
inReplyToId = states.inReplyToId,
|
pollExpireSeconds = pollExpireSeconds()
|
||||||
attachmentListArg = this.attachmentList,
|
pollHideTotals = views.cbHideTotals.isChecked
|
||||||
enqueteItemsArg = pollItems,
|
pollMultipleChoice = views.cbMultipleChoice.isChecked
|
||||||
pollType = pollType,
|
}
|
||||||
pollExpireSeconds = pollExpireSeconds,
|
2 -> {
|
||||||
pollHideTotals = pollHideTotals,
|
pollType = TootPollsType.FriendsNico
|
||||||
pollMultipleChoice = pollMultipleChoice,
|
pollItems = pollChoiceList()
|
||||||
scheduledAt = states.timeSchedule,
|
}
|
||||||
scheduledId = scheduledStatus?.id,
|
}
|
||||||
redraftStatusId = states.redraftStatusId,
|
|
||||||
editStatusId = states.editStatusId,
|
val postResult = PostImpl(
|
||||||
emojiMapCustom = App1.custom_emoji_lister.getMap(account),
|
activity = activity,
|
||||||
useQuoteToot = views.cbQuote.isChecked,
|
account = account,
|
||||||
callback = object : PostCompleteCallback {
|
content = views.etContent.text.toString().trim { it <= ' ' },
|
||||||
override fun onPostComplete(targetAccount: SavedAccount, status: TootStatus) {
|
spoilerText = when {
|
||||||
|
!views.cbContentWarning.isChecked -> null
|
||||||
|
else -> views.etContentWarning.text.toString().trim { it <= ' ' }
|
||||||
|
},
|
||||||
|
visibilityArg = states.visibility ?: TootVisibility.Public,
|
||||||
|
bNSFW = views.cbNSFW.isChecked,
|
||||||
|
inReplyToId = states.inReplyToId,
|
||||||
|
attachmentListArg = activity.attachmentList,
|
||||||
|
enqueteItemsArg = pollItems,
|
||||||
|
pollType = pollType,
|
||||||
|
pollExpireSeconds = pollExpireSeconds,
|
||||||
|
pollHideTotals = pollHideTotals,
|
||||||
|
pollMultipleChoice = pollMultipleChoice,
|
||||||
|
scheduledAt = states.timeSchedule,
|
||||||
|
scheduledId = scheduledStatus?.id,
|
||||||
|
redraftStatusId = states.redraftStatusId,
|
||||||
|
editStatusId = states.editStatusId,
|
||||||
|
emojiMapCustom = App1.custom_emoji_lister.getMapNonBlocking(account),
|
||||||
|
useQuoteToot = views.cbQuote.isChecked,
|
||||||
|
).runSuspend()
|
||||||
|
when(postResult){
|
||||||
|
is PostResult.Normal ->{
|
||||||
val data = Intent()
|
val data = Intent()
|
||||||
data.putExtra(ActPost.EXTRA_POSTED_ACCT, targetAccount.acct.ascii)
|
data.putExtra(ActPost.EXTRA_POSTED_ACCT, postResult.targetAccount.acct.ascii)
|
||||||
status.id.putTo(data, ActPost.EXTRA_POSTED_STATUS_ID)
|
postResult.status.id.putTo(data, ActPost.EXTRA_POSTED_STATUS_ID)
|
||||||
states.redraftStatusId?.putTo(data, ActPost.EXTRA_POSTED_REDRAFT_ID)
|
states.redraftStatusId?.putTo(data, ActPost.EXTRA_POSTED_REDRAFT_ID)
|
||||||
status.in_reply_to_id?.putTo(data, ActPost.EXTRA_POSTED_REPLY_ID)
|
postResult.status.in_reply_to_id?.putTo(data, ActPost.EXTRA_POSTED_REPLY_ID)
|
||||||
if (states.editStatusId != null) {
|
if (states.editStatusId != null) {
|
||||||
data.putExtra(ActPost.KEY_EDIT_STATUS, status.json.toString())
|
data.putExtra(ActPost.KEY_EDIT_STATUS, postResult.status.json.toString())
|
||||||
}
|
}
|
||||||
ActMain.refActMain?.get()?.onCompleteActPost(data)
|
ActMain.refActMain?.get()?.onCompleteActPost(data)
|
||||||
|
|
||||||
@ -360,11 +363,10 @@ fun ActPost.performPost() {
|
|||||||
this@performPost.finish()
|
this@performPost.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
is PostResult.Scheduled ->{
|
||||||
override fun onScheduledPostComplete(targetAccount: SavedAccount) {
|
|
||||||
showToast(false, getString(R.string.scheduled_status_sent))
|
showToast(false, getString(R.string.scheduled_status_sent))
|
||||||
val data = Intent()
|
val data = Intent()
|
||||||
data.putExtra(ActPost.EXTRA_POSTED_ACCT, targetAccount.acct.ascii)
|
data.putExtra(ActPost.EXTRA_POSTED_ACCT,postResult. targetAccount.acct.ascii)
|
||||||
|
|
||||||
if (isMultiWindowPost) {
|
if (isMultiWindowPost) {
|
||||||
resetText()
|
resetText()
|
||||||
@ -378,7 +380,7 @@ fun ActPost.performPost() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
).run()
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun ActPost.showContentWarningEnabled() {
|
fun ActPost.showContentWarningEnabled() {
|
||||||
|
@ -8,11 +8,9 @@ import android.view.View
|
|||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
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.*
|
import jp.juggler.subwaytooter.api.entity.TootTag
|
||||||
import jp.juggler.subwaytooter.api.entity.*
|
|
||||||
import jp.juggler.subwaytooter.dialog.ActionsDialog
|
import jp.juggler.subwaytooter.dialog.ActionsDialog
|
||||||
import jp.juggler.subwaytooter.dialog.EmojiPicker
|
import jp.juggler.subwaytooter.dialog.launchEmojiPicker
|
||||||
import jp.juggler.subwaytooter.dialog.EmojiPickerResult
|
|
||||||
import jp.juggler.subwaytooter.emoji.CustomEmoji
|
import jp.juggler.subwaytooter.emoji.CustomEmoji
|
||||||
import jp.juggler.subwaytooter.emoji.EmojiBase
|
import jp.juggler.subwaytooter.emoji.EmojiBase
|
||||||
import jp.juggler.subwaytooter.emoji.UnicodeEmoji
|
import jp.juggler.subwaytooter.emoji.UnicodeEmoji
|
||||||
@ -26,7 +24,6 @@ import jp.juggler.subwaytooter.util.EmojiDecoder
|
|||||||
import jp.juggler.subwaytooter.util.PopupAutoCompleteAcct
|
import jp.juggler.subwaytooter.util.PopupAutoCompleteAcct
|
||||||
import jp.juggler.subwaytooter.view.MyEditText
|
import jp.juggler.subwaytooter.view.MyEditText
|
||||||
import jp.juggler.util.*
|
import jp.juggler.util.*
|
||||||
import java.util.*
|
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
|
|
||||||
// 入力補完機能
|
// 入力補完機能
|
||||||
@ -108,7 +105,7 @@ class CompletionHelper(
|
|||||||
|
|
||||||
private var accessInfo: SavedAccount? = null
|
private var accessInfo: SavedAccount? = null
|
||||||
|
|
||||||
private val onEmojiListLoad: (list: ArrayList<CustomEmoji>) -> Unit = {
|
private val onEmojiListLoad: (list: List<CustomEmoji>) -> Unit = {
|
||||||
if (popup?.isShowing == true) procTextChanged.run()
|
if (popup?.isShowing == true) procTextChanged.run()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -261,9 +258,12 @@ class CompletionHelper(
|
|||||||
) = buildList<CharSequence> {
|
) = buildList<CharSequence> {
|
||||||
accessInfo ?: return@buildList
|
accessInfo ?: return@buildList
|
||||||
|
|
||||||
val customList =
|
val customList = App1.custom_emoji_lister.getListNonBlocking(
|
||||||
App1.custom_emoji_lister.getListWithAliases(accessInfo, onEmojiListLoad)
|
accessInfo,
|
||||||
?: return@buildList
|
withAliases = true,
|
||||||
|
callback = onEmojiListLoad
|
||||||
|
)
|
||||||
|
?: return@buildList
|
||||||
|
|
||||||
for (item in customList) {
|
for (item in customList) {
|
||||||
if (size >= limit) break
|
if (size >= limit) break
|
||||||
@ -310,7 +310,12 @@ class CompletionHelper(
|
|||||||
|
|
||||||
fun setInstance(accessInfo: SavedAccount?) {
|
fun setInstance(accessInfo: SavedAccount?) {
|
||||||
this.accessInfo = accessInfo
|
this.accessInfo = accessInfo
|
||||||
accessInfo?.let { App1.custom_emoji_lister.getList(it, onEmojiListLoad) }
|
accessInfo?.let {
|
||||||
|
App1.custom_emoji_lister.getListNonBlocking(
|
||||||
|
it,
|
||||||
|
callback = onEmojiListLoad
|
||||||
|
)
|
||||||
|
}
|
||||||
if (popup?.isShowing == true) procTextChanged.run()
|
if (popup?.isShowing == true) procTextChanged.run()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -400,8 +405,10 @@ class CompletionHelper(
|
|||||||
// et.setCustomSelectionActionModeCallback( action_mode_callback );
|
// et.setCustomSelectionActionModeCallback( action_mode_callback );
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun SpannableStringBuilder.appendEmoji(result: EmojiPickerResult) =
|
private fun SpannableStringBuilder.appendEmoji(
|
||||||
appendEmoji(result.bInstanceHasCustomEmoji, result.emoji)
|
emoji: EmojiBase,
|
||||||
|
bInstanceHasCustomEmoji: Boolean,
|
||||||
|
) = appendEmoji(bInstanceHasCustomEmoji, emoji)
|
||||||
|
|
||||||
private fun SpannableStringBuilder.appendEmoji(
|
private fun SpannableStringBuilder.appendEmoji(
|
||||||
bInstanceHasCustomEmoji: Boolean,
|
bInstanceHasCustomEmoji: Boolean,
|
||||||
@ -434,21 +441,22 @@ class CompletionHelper(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private val openPickerEmoji: Runnable = Runnable {
|
private val openPickerEmoji: Runnable = Runnable {
|
||||||
EmojiPicker(
|
launchEmojiPicker(
|
||||||
activity, accessInfo,
|
activity,
|
||||||
|
accessInfo,
|
||||||
closeOnSelected = PrefB.bpEmojiPickerCloseOnSelected(pref)
|
closeOnSelected = PrefB.bpEmojiPickerCloseOnSelected(pref)
|
||||||
) { result ->
|
) { emoji, bInstanceHasCustomEmoji ->
|
||||||
val et = this.et ?: return@EmojiPicker
|
val et = this@CompletionHelper.et ?: return@launchEmojiPicker
|
||||||
|
|
||||||
val src = et.text ?: ""
|
val src = et.text ?: ""
|
||||||
val srcLength = src.length
|
val srcLength = src.length
|
||||||
val end = min(srcLength, et.selectionEnd)
|
val end = min(srcLength, et.selectionEnd)
|
||||||
val start = src.lastIndexOf(':', end - 1)
|
val start = src.lastIndexOf(':', end - 1)
|
||||||
if (start == -1 || end - start < 1) return@EmojiPicker
|
if (start == -1 || end - start < 1) return@launchEmojiPicker
|
||||||
|
|
||||||
val sb = SpannableStringBuilder()
|
val sb = SpannableStringBuilder()
|
||||||
.append(src.subSequence(0, start))
|
.append(src.subSequence(0, start))
|
||||||
.appendEmoji(result)
|
.appendEmoji(emoji, bInstanceHasCustomEmoji)
|
||||||
|
|
||||||
val newSelection = sb.length
|
val newSelection = sb.length
|
||||||
if (end < srcLength) sb.append(src.subSequence(end, srcLength))
|
if (end < srcLength) sb.append(src.subSequence(end, srcLength))
|
||||||
@ -463,15 +471,16 @@ class CompletionHelper(
|
|||||||
activity,
|
activity,
|
||||||
"PostHelper/EmojiPicker/cb"
|
"PostHelper/EmojiPicker/cb"
|
||||||
).handler.post { et.showKeyboard() }
|
).handler.post { et.showKeyboard() }
|
||||||
}.show()
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun openEmojiPickerFromMore() {
|
fun openEmojiPickerFromMore() {
|
||||||
EmojiPicker(
|
launchEmojiPicker(
|
||||||
activity, accessInfo,
|
activity,
|
||||||
|
accessInfo,
|
||||||
closeOnSelected = PrefB.bpEmojiPickerCloseOnSelected(pref)
|
closeOnSelected = PrefB.bpEmojiPickerCloseOnSelected(pref)
|
||||||
) { result ->
|
) { emoji, bInstanceHasCustomEmoji ->
|
||||||
val et = this.et ?: return@EmojiPicker
|
val et = this@CompletionHelper.et ?: return@launchEmojiPicker
|
||||||
|
|
||||||
val src = et.text ?: ""
|
val src = et.text ?: ""
|
||||||
val srcLength = src.length
|
val srcLength = src.length
|
||||||
@ -480,7 +489,7 @@ class CompletionHelper(
|
|||||||
|
|
||||||
val sb = SpannableStringBuilder()
|
val sb = SpannableStringBuilder()
|
||||||
.append(src.subSequence(0, start))
|
.append(src.subSequence(0, start))
|
||||||
.appendEmoji(result)
|
.appendEmoji(emoji, bInstanceHasCustomEmoji)
|
||||||
|
|
||||||
val newSelection = sb.length
|
val newSelection = sb.length
|
||||||
if (end < srcLength) sb.append(src.subSequence(end, srcLength))
|
if (end < srcLength) sb.append(src.subSequence(end, srcLength))
|
||||||
@ -489,7 +498,7 @@ class CompletionHelper(
|
|||||||
et.setSelection(newSelection)
|
et.setSelection(newSelection)
|
||||||
|
|
||||||
procTextChanged.run()
|
procTextChanged.run()
|
||||||
}.show()
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun SpannableStringBuilder.appendHashTag(tagWithoutSharp: String): SpannableStringBuilder {
|
private fun SpannableStringBuilder.appendHashTag(tagWithoutSharp: String): SpannableStringBuilder {
|
||||||
|
@ -215,7 +215,7 @@ class TootReaction(
|
|||||||
// そのドメインの絵文字一覧を取得済みなら
|
// そのドメインの絵文字一覧を取得済みなら
|
||||||
// それを使う
|
// それを使う
|
||||||
App1.custom_emoji_lister
|
App1.custom_emoji_lister
|
||||||
.getMap(accessInfo)
|
.getMapNonBlocking(accessInfo)
|
||||||
?.get(key)
|
?.get(key)
|
||||||
?.chooseUrl()
|
?.chooseUrl()
|
||||||
?.notEmpty()
|
?.notEmpty()
|
||||||
|
@ -21,7 +21,7 @@ import jp.juggler.subwaytooter.api.entity.TootStatus
|
|||||||
import jp.juggler.subwaytooter.api.runApiTask
|
import jp.juggler.subwaytooter.api.runApiTask
|
||||||
import jp.juggler.subwaytooter.column.Column
|
import jp.juggler.subwaytooter.column.Column
|
||||||
import jp.juggler.subwaytooter.column.getContentColor
|
import jp.juggler.subwaytooter.column.getContentColor
|
||||||
import jp.juggler.subwaytooter.dialog.EmojiPicker
|
import jp.juggler.subwaytooter.dialog.launchEmojiPicker
|
||||||
import jp.juggler.subwaytooter.emoji.CustomEmoji
|
import jp.juggler.subwaytooter.emoji.CustomEmoji
|
||||||
import jp.juggler.subwaytooter.emoji.UnicodeEmoji
|
import jp.juggler.subwaytooter.emoji.UnicodeEmoji
|
||||||
import jp.juggler.subwaytooter.pref.PrefB
|
import jp.juggler.subwaytooter.pref.PrefB
|
||||||
@ -129,7 +129,7 @@ private fun ColumnViewHolder.showAnnouncementsEmpty() {
|
|||||||
private fun ColumnViewHolder.showAnnouncementColors(
|
private fun ColumnViewHolder.showAnnouncementColors(
|
||||||
expand: Boolean,
|
expand: Boolean,
|
||||||
enablePaging: Boolean,
|
enablePaging: Boolean,
|
||||||
contentColor: Int
|
contentColor: Int,
|
||||||
) {
|
) {
|
||||||
val alphaPrevNext = if (enablePaging) 1f else 0.3f
|
val alphaPrevNext = if (enablePaging) 1f else 0.3f
|
||||||
|
|
||||||
@ -413,14 +413,14 @@ private fun ColumnViewHolder.showReactions(
|
|||||||
|
|
||||||
fun ColumnViewHolder.reactionAdd(item: TootAnnouncement, sample: TootReaction?) {
|
fun ColumnViewHolder.reactionAdd(item: TootAnnouncement, sample: TootReaction?) {
|
||||||
val column = column ?: return
|
val column = column ?: return
|
||||||
|
|
||||||
if (sample == null) {
|
if (sample == null) {
|
||||||
EmojiPicker(activity, column.accessInfo, closeOnSelected = true) { result ->
|
launchEmojiPicker(activity, column.accessInfo, closeOnSelected = true) { emoji, _ ->
|
||||||
val emoji = result.emoji
|
|
||||||
val code = when (emoji) {
|
val code = when (emoji) {
|
||||||
is UnicodeEmoji -> emoji.unifiedCode
|
is UnicodeEmoji -> emoji.unifiedCode
|
||||||
is CustomEmoji -> emoji.shortcode
|
is CustomEmoji -> emoji.shortcode
|
||||||
}
|
}
|
||||||
ColumnViewHolder.log.d("addReaction: $code ${result.emoji.javaClass.simpleName}")
|
ColumnViewHolder.log.d("addReaction: $code ${emoji.javaClass.simpleName}")
|
||||||
reactionAdd(item, TootReaction.parseFedibird(jsonObject {
|
reactionAdd(item, TootReaction.parseFedibird(jsonObject {
|
||||||
put("name", code)
|
put("name", code)
|
||||||
put("count", 1)
|
put("count", 1)
|
||||||
@ -431,11 +431,10 @@ fun ColumnViewHolder.reactionAdd(item: TootAnnouncement, sample: TootReaction?)
|
|||||||
putNotNull("static_url", emoji.staticUrl)
|
putNotNull("static_url", emoji.staticUrl)
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
}.show()
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
activity.launchAndShowError {
|
||||||
launchMain {
|
|
||||||
activity.runApiTask(column.accessInfo) { client ->
|
activity.runApiTask(column.accessInfo) { client ->
|
||||||
client.request(
|
client.request(
|
||||||
"/api/v1/announcements/${item.id}/reactions/${sample.name.encodePercent()}",
|
"/api/v1/announcements/${item.id}/reactions/${sample.name.encodePercent()}",
|
||||||
|
@ -7,20 +7,21 @@ import jp.juggler.subwaytooter.ActMain
|
|||||||
import jp.juggler.subwaytooter.R
|
import jp.juggler.subwaytooter.R
|
||||||
import jp.juggler.subwaytooter.api.entity.TootReaction
|
import jp.juggler.subwaytooter.api.entity.TootReaction
|
||||||
import jp.juggler.subwaytooter.column.getContentColor
|
import jp.juggler.subwaytooter.column.getContentColor
|
||||||
import jp.juggler.subwaytooter.dialog.EmojiPicker
|
import jp.juggler.subwaytooter.dialog.launchEmojiPicker
|
||||||
import jp.juggler.subwaytooter.emoji.CustomEmoji
|
import jp.juggler.subwaytooter.emoji.CustomEmoji
|
||||||
import jp.juggler.subwaytooter.emoji.UnicodeEmoji
|
import jp.juggler.subwaytooter.emoji.UnicodeEmoji
|
||||||
import jp.juggler.subwaytooter.util.DecodeOptions
|
import jp.juggler.subwaytooter.util.DecodeOptions
|
||||||
import jp.juggler.subwaytooter.util.NetworkEmojiInvalidator
|
import jp.juggler.subwaytooter.util.NetworkEmojiInvalidator
|
||||||
import jp.juggler.subwaytooter.util.minWidthCompat
|
import jp.juggler.subwaytooter.util.minWidthCompat
|
||||||
import jp.juggler.subwaytooter.util.startMargin
|
import jp.juggler.subwaytooter.util.startMargin
|
||||||
|
import jp.juggler.util.launchAndShowError
|
||||||
import org.jetbrains.anko.allCaps
|
import org.jetbrains.anko.allCaps
|
||||||
|
|
||||||
fun ColumnViewHolder.addEmojiQuery(reaction: TootReaction? = null) {
|
fun ColumnViewHolder.addEmojiQuery(reaction: TootReaction? = null) {
|
||||||
val column = this.column ?: return
|
val column = this.column ?: return
|
||||||
if (reaction == null) {
|
if (reaction == null) {
|
||||||
EmojiPicker(activity, column.accessInfo, closeOnSelected = true) { result ->
|
launchEmojiPicker(activity, column.accessInfo, closeOnSelected = true) { emoji, _ ->
|
||||||
val newReaction = when (val emoji = result.emoji) {
|
val newReaction = when (emoji) {
|
||||||
is UnicodeEmoji -> TootReaction(name = emoji.unifiedCode)
|
is UnicodeEmoji -> TootReaction(name = emoji.unifiedCode)
|
||||||
is CustomEmoji -> TootReaction(
|
is CustomEmoji -> TootReaction(
|
||||||
name = emoji.shortcode,
|
name = emoji.shortcode,
|
||||||
@ -29,7 +30,7 @@ fun ColumnViewHolder.addEmojiQuery(reaction: TootReaction? = null) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
addEmojiQuery(newReaction)
|
addEmojiQuery(newReaction)
|
||||||
}.show()
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
val list = TootReaction.decodeEmojiQuery(column.searchQuery).toMutableList()
|
val list = TootReaction.decodeEmojiQuery(column.searchQuery).toMutableList()
|
||||||
@ -49,63 +50,64 @@ private fun ColumnViewHolder.removeEmojiQuery(target: TootReaction?) {
|
|||||||
|
|
||||||
fun ColumnViewHolder.updateReactionQueryView() {
|
fun ColumnViewHolder.updateReactionQueryView() {
|
||||||
val column = this.column ?: return
|
val column = this.column ?: return
|
||||||
|
|
||||||
flEmoji.removeAllViews()
|
|
||||||
|
|
||||||
for (invalidator in emojiQueryInvalidatorList) {
|
|
||||||
invalidator.register(null)
|
|
||||||
}
|
|
||||||
emojiQueryInvalidatorList.clear()
|
|
||||||
|
|
||||||
val options = DecodeOptions(
|
|
||||||
activity,
|
|
||||||
column.accessInfo,
|
|
||||||
decodeEmoji = true,
|
|
||||||
enlargeEmoji = 1.5f,
|
|
||||||
enlargeCustomEmoji = 1.5f
|
|
||||||
)
|
|
||||||
|
|
||||||
val act = this.activity // not Button(View).getActivity()
|
val act = this.activity // not Button(View).getActivity()
|
||||||
|
act.launchAndShowError {
|
||||||
|
|
||||||
val buttonHeight = ActMain.boostButtonSize
|
flEmoji.removeAllViews()
|
||||||
val marginBetween = (buttonHeight.toFloat() * 0.05f + 0.5f).toInt()
|
|
||||||
|
|
||||||
val paddingH = (buttonHeight.toFloat() * 0.1f + 0.5f).toInt()
|
for (invalidator in emojiQueryInvalidatorList) {
|
||||||
val paddingV = (buttonHeight.toFloat() * 0.1f + 0.5f).toInt()
|
invalidator.register(null)
|
||||||
|
}
|
||||||
val contentColor = column.getContentColor()
|
emojiQueryInvalidatorList.clear()
|
||||||
|
|
||||||
TootReaction.decodeEmojiQuery(column.searchQuery).forEachIndexed { index, reaction ->
|
val options = DecodeOptions(
|
||||||
val ssb = reaction.toSpannableStringBuilder(options, status = null)
|
act,
|
||||||
|
column.accessInfo,
|
||||||
val b = AppCompatButton(activity).apply {
|
decodeEmoji = true,
|
||||||
layoutParams = FlexboxLayout.LayoutParams(
|
enlargeEmoji = 1.5f,
|
||||||
FlexboxLayout.LayoutParams.WRAP_CONTENT,
|
enlargeCustomEmoji = 1.5f
|
||||||
buttonHeight
|
)
|
||||||
).apply {
|
|
||||||
if (index > 0) startMargin = marginBetween
|
val buttonHeight = ActMain.boostButtonSize
|
||||||
}
|
val marginBetween = (buttonHeight.toFloat() * 0.05f + 0.5f).toInt()
|
||||||
minWidthCompat = buttonHeight
|
|
||||||
|
val paddingH = (buttonHeight.toFloat() * 0.1f + 0.5f).toInt()
|
||||||
background = ContextCompat.getDrawable(act, R.drawable.btn_bg_transparent_round6dp)
|
val paddingV = (buttonHeight.toFloat() * 0.1f + 0.5f).toInt()
|
||||||
|
|
||||||
setTextColor(contentColor)
|
val contentColor = column.getContentColor()
|
||||||
setPadding(paddingH, paddingV, paddingH, paddingV)
|
|
||||||
|
TootReaction.decodeEmojiQuery(column.searchQuery).forEachIndexed { index, reaction ->
|
||||||
text = ssb
|
val ssb = reaction.toSpannableStringBuilder(options, status = null)
|
||||||
|
|
||||||
allCaps = false
|
val b = AppCompatButton(activity).apply {
|
||||||
tag = reaction
|
layoutParams = FlexboxLayout.LayoutParams(
|
||||||
|
FlexboxLayout.LayoutParams.WRAP_CONTENT,
|
||||||
setOnLongClickListener {
|
buttonHeight
|
||||||
removeEmojiQuery(it.tag as? TootReaction)
|
).apply {
|
||||||
true
|
if (index > 0) startMargin = marginBetween
|
||||||
}
|
}
|
||||||
// カスタム絵文字の場合、アニメーション等のコールバックを処理する必要がある
|
minWidthCompat = buttonHeight
|
||||||
val invalidator = NetworkEmojiInvalidator(act.handler, this)
|
|
||||||
invalidator.register(ssb)
|
background = ContextCompat.getDrawable(act, R.drawable.btn_bg_transparent_round6dp)
|
||||||
emojiQueryInvalidatorList.add(invalidator)
|
|
||||||
|
setTextColor(contentColor)
|
||||||
|
setPadding(paddingH, paddingV, paddingH, paddingV)
|
||||||
|
|
||||||
|
text = ssb
|
||||||
|
|
||||||
|
allCaps = false
|
||||||
|
tag = reaction
|
||||||
|
|
||||||
|
setOnLongClickListener {
|
||||||
|
removeEmojiQuery(it.tag as? TootReaction)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
// カスタム絵文字の場合、アニメーション等のコールバックを処理する必要がある
|
||||||
|
val invalidator = NetworkEmojiInvalidator(act.handler, this)
|
||||||
|
invalidator.register(ssb)
|
||||||
|
emojiQueryInvalidatorList.add(invalidator)
|
||||||
|
}
|
||||||
|
flEmoji.addView(b)
|
||||||
}
|
}
|
||||||
flEmoji.addView(b)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,60 +1,125 @@
|
|||||||
package jp.juggler.subwaytooter.dialog
|
package jp.juggler.subwaytooter.dialog
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.Activity
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.CheckBox
|
import androidx.annotation.StringRes
|
||||||
import android.widget.TextView
|
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import jp.juggler.subwaytooter.R
|
import jp.juggler.subwaytooter.R
|
||||||
|
import jp.juggler.subwaytooter.databinding.DlgConfirmBinding
|
||||||
|
import jp.juggler.util.dismissSafe
|
||||||
|
import kotlinx.coroutines.CancellationException
|
||||||
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
|
import kotlin.coroutines.resume
|
||||||
|
import kotlin.coroutines.resumeWithException
|
||||||
|
|
||||||
object DlgConfirm {
|
object DlgConfirm {
|
||||||
|
|
||||||
interface Callback {
|
// interface Callback {
|
||||||
var isConfirmEnabled: Boolean
|
// var isConfirmEnabled: Boolean
|
||||||
|
//
|
||||||
|
// fun onOK()
|
||||||
|
// }
|
||||||
|
|
||||||
fun onOK()
|
// @SuppressLint("InflateParams")
|
||||||
}
|
// fun open(activity: Activity, message: String, callback: Callback): Dialog {
|
||||||
|
//
|
||||||
|
// if (!callback.isConfirmEnabled) {
|
||||||
|
// callback.onOK()
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// val view = activity.layoutInflater.inflate(R.layout.dlg_confirm, null, false)
|
||||||
|
// val tvMessage = view.findViewById<TextView>(R.id.tvMessage)
|
||||||
|
// val cbSkipNext = view.findViewById<CheckBox>(R.id.cbSkipNext)
|
||||||
|
// tvMessage.text = message
|
||||||
|
//
|
||||||
|
// AlertDialog.Builder(activity)
|
||||||
|
// .setView(view)
|
||||||
|
// .setCancelable(true)
|
||||||
|
// .setNegativeButton(R.string.cancel, null)
|
||||||
|
// .setPositiveButton(R.string.ok) { _, _ ->
|
||||||
|
// if (cbSkipNext.isChecked) {
|
||||||
|
// callback.isConfirmEnabled = false
|
||||||
|
// }
|
||||||
|
// callback.onOK()
|
||||||
|
// }
|
||||||
|
// .show()
|
||||||
|
// }
|
||||||
|
|
||||||
|
// @SuppressLint("InflateParams")
|
||||||
|
// fun openSimple(activity: Activity, message: String, callback: () -> Unit) {
|
||||||
|
// val view = activity.layoutInflater.inflate(R.layout.dlg_confirm, null, false)
|
||||||
|
// val tvMessage = view.findViewById<TextView>(R.id.tvMessage)
|
||||||
|
// val cbSkipNext = view.findViewById<CheckBox>(R.id.cbSkipNext)
|
||||||
|
// tvMessage.text = message
|
||||||
|
// cbSkipNext.visibility = View.GONE
|
||||||
|
//
|
||||||
|
// AlertDialog.Builder(activity)
|
||||||
|
// .setView(view)
|
||||||
|
// .setCancelable(true)
|
||||||
|
// .setNegativeButton(R.string.cancel, null)
|
||||||
|
// .setPositiveButton(R.string.ok) { _, _ -> callback() }
|
||||||
|
// .show()
|
||||||
|
// }
|
||||||
|
|
||||||
@SuppressLint("InflateParams")
|
@SuppressLint("InflateParams")
|
||||||
fun open(activity: Activity, message: String, callback: Callback) {
|
suspend fun AppCompatActivity.confirm(
|
||||||
|
message: String,
|
||||||
if (!callback.isConfirmEnabled) {
|
getConfirmEnabled: Boolean,
|
||||||
callback.onOK()
|
setConfirmEnabled: (newConfirmEnabled: Boolean) -> Unit,
|
||||||
return
|
) {
|
||||||
}
|
if (!getConfirmEnabled) return
|
||||||
|
suspendCancellableCoroutine<Unit> { cont ->
|
||||||
val view = activity.layoutInflater.inflate(R.layout.dlg_confirm, null, false)
|
try {
|
||||||
val tvMessage = view.findViewById<TextView>(R.id.tvMessage)
|
val views = DlgConfirmBinding.inflate(layoutInflater)
|
||||||
val cbSkipNext = view.findViewById<CheckBox>(R.id.cbSkipNext)
|
views.tvMessage.text = message
|
||||||
tvMessage.text = message
|
val dialog = AlertDialog.Builder(this)
|
||||||
|
.setView(views.root)
|
||||||
AlertDialog.Builder(activity)
|
.setCancelable(true)
|
||||||
.setView(view)
|
.setNegativeButton(R.string.cancel, null)
|
||||||
.setCancelable(true)
|
.setPositiveButton(R.string.ok) { _, _ ->
|
||||||
.setNegativeButton(R.string.cancel, null)
|
if (views.cbSkipNext.isChecked) {
|
||||||
.setPositiveButton(R.string.ok) { _, _ ->
|
setConfirmEnabled(false)
|
||||||
if (cbSkipNext.isChecked) {
|
}
|
||||||
callback.isConfirmEnabled = false
|
if (cont.isActive) cont.resume(Unit)
|
||||||
|
}
|
||||||
|
dialog.setOnDismissListener {
|
||||||
|
if (cont.isActive) cont.resumeWithException(CancellationException("dialog cancelled."))
|
||||||
}
|
}
|
||||||
callback.onOK()
|
dialog.show()
|
||||||
|
} catch (ex: Throwable) {
|
||||||
|
cont.resumeWithException(ex)
|
||||||
}
|
}
|
||||||
.show()
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("InflateParams")
|
suspend fun AppCompatActivity.confirm(@StringRes messageId: Int, vararg args: Any?) =
|
||||||
fun openSimple(activity: Activity, message: String, callback: () -> Unit) {
|
confirm(getString(messageId, args))
|
||||||
val view = activity.layoutInflater.inflate(R.layout.dlg_confirm, null, false)
|
|
||||||
val tvMessage = view.findViewById<TextView>(R.id.tvMessage)
|
|
||||||
val cbSkipNext = view.findViewById<CheckBox>(R.id.cbSkipNext)
|
|
||||||
tvMessage.text = message
|
|
||||||
cbSkipNext.visibility = View.GONE
|
|
||||||
|
|
||||||
AlertDialog.Builder(activity)
|
suspend fun AppCompatActivity.confirm(message: String) {
|
||||||
.setView(view)
|
suspendCancellableCoroutine<Unit> { cont ->
|
||||||
.setCancelable(true)
|
try {
|
||||||
.setNegativeButton(R.string.cancel, null)
|
val views = DlgConfirmBinding.inflate(layoutInflater)
|
||||||
.setPositiveButton(R.string.ok) { _, _ -> callback() }
|
views.tvMessage.text = message
|
||||||
.show()
|
views.cbSkipNext.visibility = View.GONE
|
||||||
|
|
||||||
|
val dialog = AlertDialog.Builder(this)
|
||||||
|
.setView(views.root)
|
||||||
|
.setCancelable(true)
|
||||||
|
.setNegativeButton(R.string.cancel, null)
|
||||||
|
.setPositiveButton(R.string.ok) { _, _ ->
|
||||||
|
if (cont.isActive) cont.resume(Unit)
|
||||||
|
}
|
||||||
|
.create()
|
||||||
|
dialog.setOnDismissListener {
|
||||||
|
if (cont.isActive) cont.resumeWithException(CancellationException("dialog closed."))
|
||||||
|
}
|
||||||
|
dialog.show()
|
||||||
|
cont.invokeOnCancellation { dialog.dismissSafe() }
|
||||||
|
} catch (ex: Throwable) {
|
||||||
|
cont.resumeWithException(ex)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -18,6 +18,8 @@ class UnicodeEmoji(
|
|||||||
@DrawableRes val drawableId: Int = 0,
|
@DrawableRes val drawableId: Int = 0,
|
||||||
) : EmojiBase, Comparable<UnicodeEmoji> {
|
) : EmojiBase, Comparable<UnicodeEmoji> {
|
||||||
|
|
||||||
|
val namesLower = ArrayList<String>()
|
||||||
|
|
||||||
// unified code used in picker.
|
// unified code used in picker.
|
||||||
var unifiedCode = ""
|
var unifiedCode = ""
|
||||||
|
|
||||||
|
@ -1,20 +1,22 @@
|
|||||||
package jp.juggler.subwaytooter.emoji
|
package jp.juggler.subwaytooter.emoji
|
||||||
|
|
||||||
import java.util.ArrayList
|
import androidx.annotation.StringRes
|
||||||
|
import jp.juggler.subwaytooter.R
|
||||||
|
|
||||||
|
enum class EmojiCategory(@StringRes val titleId: Int) {
|
||||||
|
Recent(R.string.emoji_category_recent),
|
||||||
|
Custom(R.string.emoji_category_custom),
|
||||||
|
People(R.string.emoji_category_people),
|
||||||
|
ComplexTones(R.string.emoji_category_composite_tones),
|
||||||
|
Nature(R.string.emoji_category_nature),
|
||||||
|
Foods(R.string.emoji_category_foods),
|
||||||
|
Activities(R.string.emoji_category_activity),
|
||||||
|
Places(R.string.emoji_category_places),
|
||||||
|
Objects(R.string.emoji_category_objects),
|
||||||
|
Symbols(R.string.emoji_category_symbols),
|
||||||
|
Flags(R.string.emoji_category_flags),
|
||||||
|
Others(R.string.emoji_category_others),
|
||||||
|
|
||||||
enum class EmojiCategory {
|
|
||||||
Recent,
|
|
||||||
Custom,
|
|
||||||
People,
|
|
||||||
ComplexTones,
|
|
||||||
Nature,
|
|
||||||
Foods,
|
|
||||||
Activities,
|
|
||||||
Places,
|
|
||||||
Objects,
|
|
||||||
Symbols,
|
|
||||||
Flags,
|
|
||||||
Others,
|
|
||||||
;
|
;
|
||||||
|
|
||||||
val emojiList = ArrayList<UnicodeEmoji>()
|
val emojiList = ArrayList<UnicodeEmoji>()
|
||||||
|
@ -45,6 +45,7 @@ class EmojiMapLoader(
|
|||||||
private fun addName(emoji: UnicodeEmoji, name: String) {
|
private fun addName(emoji: UnicodeEmoji, name: String) {
|
||||||
dst.shortNameMap[name] = emoji
|
dst.shortNameMap[name] = emoji
|
||||||
dst.shortNameList.add(name)
|
dst.shortNameList.add(name)
|
||||||
|
emoji.namesLower.add(name.lowercase())
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun readEmojiDataLine(lno: Int, rawLine: String) {
|
private fun readEmojiDataLine(lno: Int, rawLine: String) {
|
||||||
|
@ -14,13 +14,17 @@ import jp.juggler.subwaytooter.R
|
|||||||
import jp.juggler.subwaytooter.action.reactionAdd
|
import jp.juggler.subwaytooter.action.reactionAdd
|
||||||
import jp.juggler.subwaytooter.action.reactionFromAnotherAccount
|
import jp.juggler.subwaytooter.action.reactionFromAnotherAccount
|
||||||
import jp.juggler.subwaytooter.action.reactionRemove
|
import jp.juggler.subwaytooter.action.reactionRemove
|
||||||
import jp.juggler.subwaytooter.api.*
|
|
||||||
import jp.juggler.subwaytooter.api.entity.TootReaction
|
import jp.juggler.subwaytooter.api.entity.TootReaction
|
||||||
import jp.juggler.subwaytooter.api.entity.TootStatus
|
import jp.juggler.subwaytooter.api.entity.TootStatus
|
||||||
import jp.juggler.subwaytooter.pref.PrefB
|
import jp.juggler.subwaytooter.pref.PrefB
|
||||||
import jp.juggler.subwaytooter.pref.PrefI
|
import jp.juggler.subwaytooter.pref.PrefI
|
||||||
import jp.juggler.subwaytooter.util.*
|
import jp.juggler.subwaytooter.util.DecodeOptions
|
||||||
import jp.juggler.util.*
|
import jp.juggler.subwaytooter.util.NetworkEmojiInvalidator
|
||||||
|
import jp.juggler.subwaytooter.util.minWidthCompat
|
||||||
|
import jp.juggler.subwaytooter.util.startMargin
|
||||||
|
import jp.juggler.util.attrColor
|
||||||
|
import jp.juggler.util.getAdaptiveRippleDrawableRound
|
||||||
|
import jp.juggler.util.notZero
|
||||||
import org.jetbrains.anko.allCaps
|
import org.jetbrains.anko.allCaps
|
||||||
import org.jetbrains.anko.dip
|
import org.jetbrains.anko.dip
|
||||||
|
|
||||||
|
@ -8,15 +8,17 @@ import jp.juggler.subwaytooter.api.entity.parseList
|
|||||||
import jp.juggler.subwaytooter.emoji.CustomEmoji
|
import jp.juggler.subwaytooter.emoji.CustomEmoji
|
||||||
import jp.juggler.subwaytooter.table.SavedAccount
|
import jp.juggler.subwaytooter.table.SavedAccount
|
||||||
import jp.juggler.util.*
|
import jp.juggler.util.*
|
||||||
import java.util.*
|
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue
|
import java.util.concurrent.ConcurrentLinkedQueue
|
||||||
|
import kotlin.coroutines.Continuation
|
||||||
|
import kotlin.coroutines.resume
|
||||||
|
import kotlin.coroutines.resumeWithException
|
||||||
|
import kotlin.coroutines.suspendCoroutine
|
||||||
|
|
||||||
class CustomEmojiLister(
|
class CustomEmojiLister(
|
||||||
val context: Context,
|
val context: Context,
|
||||||
private val handler: Handler,
|
private val handler: Handler,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
private val log = LogCategory("CustomEmojiLister")
|
private val log = LogCategory("CustomEmojiLister")
|
||||||
@ -31,8 +33,8 @@ class CustomEmojiLister(
|
|||||||
|
|
||||||
internal class CacheItem(
|
internal class CacheItem(
|
||||||
val key: String,
|
val key: String,
|
||||||
var list: ArrayList<CustomEmoji>? = null,
|
var list: List<CustomEmoji>,
|
||||||
var listWithAliases: ArrayList<CustomEmoji>? = null,
|
var listWithAliases: List<CustomEmoji>,
|
||||||
// ロードした時刻
|
// ロードした時刻
|
||||||
var timeUpdate: Long = elapsedTime,
|
var timeUpdate: Long = elapsedTime,
|
||||||
// 参照された時刻
|
// 参照された時刻
|
||||||
@ -40,10 +42,19 @@ class CustomEmojiLister(
|
|||||||
)
|
)
|
||||||
|
|
||||||
internal class Request(
|
internal class Request(
|
||||||
|
val cont: Continuation<List<CustomEmoji>>,
|
||||||
val accessInfo: SavedAccount,
|
val accessInfo: SavedAccount,
|
||||||
val reportWithAliases: Boolean = false,
|
val reportWithAliases: Boolean = false,
|
||||||
val onListLoaded: (list: ArrayList<CustomEmoji>) -> Unit?,
|
) {
|
||||||
)
|
fun resume(item: CacheItem) {
|
||||||
|
cont.resume(
|
||||||
|
when (reportWithAliases) {
|
||||||
|
true -> item.listWithAliases
|
||||||
|
else -> item.list
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 成功キャッシュ
|
// 成功キャッシュ
|
||||||
internal val cache = ConcurrentHashMap<String, CacheItem>()
|
internal val cache = ConcurrentHashMap<String, CacheItem>()
|
||||||
@ -51,16 +62,12 @@ class CustomEmojiLister(
|
|||||||
// エラーキャッシュ
|
// エラーキャッシュ
|
||||||
internal val cacheError = ConcurrentHashMap<String, Long>()
|
internal val cacheError = ConcurrentHashMap<String, Long>()
|
||||||
|
|
||||||
private val cacheErrorItem = CacheItem("error")
|
private val cacheErrorItem = CacheItem("error", emptyList(), emptyList())
|
||||||
|
|
||||||
// ロード要求
|
// ロード要求
|
||||||
internal val queue = ConcurrentLinkedQueue<Request>()
|
internal val queue = ConcurrentLinkedQueue<Request>()
|
||||||
|
|
||||||
private val worker: Worker
|
private val worker = Worker()
|
||||||
|
|
||||||
init {
|
|
||||||
this.worker = Worker()
|
|
||||||
}
|
|
||||||
|
|
||||||
// ネットワーク接続が変化したらエラーキャッシュをクリア
|
// ネットワーク接続が変化したらエラーキャッシュをクリア
|
||||||
fun onNetworkChanged() {
|
fun onNetworkChanged() {
|
||||||
@ -86,59 +93,50 @@ class CustomEmojiLister(
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getList(
|
// インスタンス用のカスタム絵文字のリストを取得する
|
||||||
|
// または例外を投げる
|
||||||
|
suspend fun getList(
|
||||||
accessInfo: SavedAccount,
|
accessInfo: SavedAccount,
|
||||||
onListLoaded: (list: ArrayList<CustomEmoji>) -> Unit,
|
withAliases: Boolean = false,
|
||||||
): ArrayList<CustomEmoji>? {
|
): List<CustomEmoji> {
|
||||||
try {
|
synchronized(cache) {
|
||||||
synchronized(cache) {
|
getCached(elapsedTime, accessInfo)
|
||||||
val item = getCached(elapsedTime, accessInfo)
|
}?.let { return it.list }
|
||||||
if (item != null) return item.list
|
return suspendCoroutine { cont ->
|
||||||
|
try {
|
||||||
|
queue.add(Request(cont, accessInfo, reportWithAliases = withAliases))
|
||||||
|
worker.notifyEx()
|
||||||
|
} catch (ex: Throwable) {
|
||||||
|
cont.resumeWithException(ex)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
queue.add(Request(accessInfo, onListLoaded = onListLoaded))
|
fun getListNonBlocking(
|
||||||
worker.notifyEx()
|
accessInfo: SavedAccount,
|
||||||
} catch (ex: Throwable) {
|
withAliases: Boolean = false,
|
||||||
log.trace(ex)
|
callback: ((List<CustomEmoji>) -> Unit)? = null,
|
||||||
|
): List<CustomEmoji>? {
|
||||||
|
synchronized(cache) {
|
||||||
|
getCached(elapsedTime, accessInfo)
|
||||||
|
}?.let { return it.list }
|
||||||
|
launchMain {
|
||||||
|
getList(accessInfo, withAliases).let { callback?.invoke(it) }
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getListWithAliases(
|
// suspend fun getMap(accessInfo: SavedAccount) =
|
||||||
accessInfo: SavedAccount,
|
// HashMap<String, CustomEmoji>().apply {
|
||||||
onListLoaded: (list: ArrayList<CustomEmoji>) -> Unit,
|
// getList(accessInfo).forEach { put(it.shortcode, it) }
|
||||||
): ArrayList<CustomEmoji>? {
|
// }
|
||||||
try {
|
|
||||||
synchronized(cache) {
|
fun getMapNonBlocking(accessInfo: SavedAccount) =
|
||||||
val item = getCached(elapsedTime, accessInfo)
|
getListNonBlocking(accessInfo)?.let {
|
||||||
if (item != null) return item.listWithAliases
|
HashMap<String, CustomEmoji>().apply {
|
||||||
|
it.forEach { put(it.shortcode, it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
queue.add(
|
|
||||||
Request(
|
|
||||||
accessInfo,
|
|
||||||
reportWithAliases = true,
|
|
||||||
onListLoaded = onListLoaded
|
|
||||||
)
|
|
||||||
)
|
|
||||||
worker.notifyEx()
|
|
||||||
} catch (ex: Throwable) {
|
|
||||||
log.trace(ex)
|
|
||||||
}
|
}
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getMap(accessInfo: SavedAccount): HashMap<String, CustomEmoji>? {
|
|
||||||
val list = getList(accessInfo) {
|
|
||||||
// 遅延ロード非対応
|
|
||||||
} ?: return null
|
|
||||||
//
|
|
||||||
val dst = HashMap<String, CustomEmoji>()
|
|
||||||
for (e in list) {
|
|
||||||
dst[e.shortcode] = e
|
|
||||||
}
|
|
||||||
return dst
|
|
||||||
}
|
|
||||||
|
|
||||||
private inner class Worker : WorkerBase() {
|
private inner class Worker : WorkerBase() {
|
||||||
|
|
||||||
@ -156,69 +154,10 @@ class CustomEmojiLister(
|
|||||||
waitEx(86400000L)
|
waitEx(86400000L)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
val cached = synchronized(cache) {
|
|
||||||
|
|
||||||
val item = getCached(elapsedTime, request.accessInfo)
|
|
||||||
return@synchronized if (item != null) {
|
|
||||||
val list = item.list
|
|
||||||
val listWithAliases = item.listWithAliases
|
|
||||||
if (list != null && listWithAliases != null) {
|
|
||||||
fireCallback(request, list, listWithAliases)
|
|
||||||
}
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
// キャッシュにはなかった
|
|
||||||
sweepCache()
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (cached) continue
|
|
||||||
|
|
||||||
val accessInfo = request.accessInfo
|
|
||||||
val cacheKey = accessInfo.apiHost.ascii
|
|
||||||
var list: ArrayList<CustomEmoji>? = null
|
|
||||||
var listWithAlias: ArrayList<CustomEmoji>? = null
|
|
||||||
try {
|
try {
|
||||||
val data = if (accessInfo.isMisskey) {
|
request.resume(handleRequest(request))
|
||||||
App1.getHttpCachedString(
|
|
||||||
"https://$cacheKey/api/meta",
|
|
||||||
accessInfo = accessInfo
|
|
||||||
) { builder ->
|
|
||||||
builder.post(JsonObject().toRequestBody())
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
App1.getHttpCachedString(
|
|
||||||
"https://$cacheKey/api/v1/custom_emojis",
|
|
||||||
accessInfo = accessInfo
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data != null) {
|
|
||||||
val a = decodeEmojiList(data, accessInfo)
|
|
||||||
list = a
|
|
||||||
listWithAlias = makeListWithAlias(a)
|
|
||||||
}
|
|
||||||
} catch (ex: Throwable) {
|
} catch (ex: Throwable) {
|
||||||
log.trace(ex)
|
request.cont.resumeWithException(ex)
|
||||||
}
|
|
||||||
|
|
||||||
synchronized(cache) {
|
|
||||||
val now = elapsedTime
|
|
||||||
if (list == null || listWithAlias == null) {
|
|
||||||
cacheError.put(cacheKey, now)
|
|
||||||
} else {
|
|
||||||
var item: CacheItem? = cache[cacheKey]
|
|
||||||
if (item == null) {
|
|
||||||
item = CacheItem(cacheKey, list, listWithAlias)
|
|
||||||
cache[cacheKey] = item
|
|
||||||
} else {
|
|
||||||
item.list = list
|
|
||||||
item.listWithAliases = listWithAlias
|
|
||||||
item.timeUpdate = now
|
|
||||||
}
|
|
||||||
fireCallback(request, list, listWithAlias)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (ex: Throwable) {
|
} catch (ex: Throwable) {
|
||||||
log.trace(ex)
|
log.trace(ex)
|
||||||
@ -227,20 +166,58 @@ class CustomEmojiLister(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun fireCallback(
|
private suspend fun handleRequest(request: Request): CacheItem {
|
||||||
request: Request,
|
synchronized(cache) {
|
||||||
list: ArrayList<CustomEmoji>,
|
(getCached(elapsedTime, request.accessInfo)
|
||||||
listWithAliases: ArrayList<CustomEmoji>,
|
?.takeIf { it != cacheErrorItem })
|
||||||
) {
|
.also {
|
||||||
handler.post {
|
if (it == null) {
|
||||||
request.onListLoaded(
|
// エラーキャッシュは一定時間で除去される
|
||||||
if (request.reportWithAliases) {
|
sweepCache()
|
||||||
listWithAliases
|
}
|
||||||
} else {
|
|
||||||
list
|
|
||||||
}
|
}
|
||||||
|
}?.let { return it }
|
||||||
|
|
||||||
|
val accessInfo = request.accessInfo
|
||||||
|
val cacheKey = accessInfo.apiHost.ascii
|
||||||
|
val data = if (accessInfo.isMisskey) {
|
||||||
|
App1.getHttpCachedString(
|
||||||
|
"https://$cacheKey/api/meta",
|
||||||
|
accessInfo = accessInfo
|
||||||
|
) { builder ->
|
||||||
|
builder.post(JsonObject().toRequestBody())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
App1.getHttpCachedString(
|
||||||
|
"https://$cacheKey/api/v1/custom_emojis",
|
||||||
|
accessInfo = accessInfo
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
var list: List<CustomEmoji>? = null
|
||||||
|
var listWithAlias: List<CustomEmoji>? = null
|
||||||
|
if (data != null) {
|
||||||
|
val a = decodeEmojiList(data, accessInfo)
|
||||||
|
list = a
|
||||||
|
listWithAlias = makeListWithAlias(a)
|
||||||
|
}
|
||||||
|
return synchronized(cache) {
|
||||||
|
val now = elapsedTime
|
||||||
|
if (list == null || listWithAlias == null) {
|
||||||
|
cacheError[cacheKey] = now
|
||||||
|
error("can't load custom emoji for ${accessInfo.apiHost}")
|
||||||
|
} else {
|
||||||
|
var item = cache[cacheKey]
|
||||||
|
if (item == null) {
|
||||||
|
item = CacheItem(cacheKey, list, listWithAlias)
|
||||||
|
cache[cacheKey] = item
|
||||||
|
} else {
|
||||||
|
item.list = list
|
||||||
|
item.listWithAliases = listWithAlias
|
||||||
|
item.timeUpdate = now
|
||||||
|
}
|
||||||
|
item
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// キャッシュの掃除
|
// キャッシュの掃除
|
||||||
@ -270,43 +247,35 @@ class CustomEmojiLister(
|
|||||||
private fun decodeEmojiList(
|
private fun decodeEmojiList(
|
||||||
data: String,
|
data: String,
|
||||||
accessInfo: SavedAccount,
|
accessInfo: SavedAccount,
|
||||||
): ArrayList<CustomEmoji>? {
|
): List<CustomEmoji> =
|
||||||
return try {
|
if (accessInfo.isMisskey) {
|
||||||
val list = if (accessInfo.isMisskey) {
|
parseList(
|
||||||
parseList(
|
CustomEmoji.decodeMisskey,
|
||||||
CustomEmoji.decodeMisskey,
|
accessInfo.apDomain,
|
||||||
accessInfo.apDomain,
|
data.decodeJsonObject().jsonArray("emojis")
|
||||||
data.decodeJsonObject().jsonArray("emojis")
|
)
|
||||||
)
|
} else {
|
||||||
} else {
|
parseList(
|
||||||
parseList(
|
CustomEmoji.decode,
|
||||||
CustomEmoji.decode,
|
accessInfo.apDomain,
|
||||||
accessInfo.apDomain,
|
data.decodeJsonArray()
|
||||||
data.decodeJsonArray()
|
)
|
||||||
)
|
}.apply {
|
||||||
}
|
sortWith(compareBy(String.CASE_INSENSITIVE_ORDER) { it.shortcode })
|
||||||
list.sortWith(compareBy(String.CASE_INSENSITIVE_ORDER) { it.shortcode })
|
|
||||||
list
|
|
||||||
} catch (ex: Throwable) {
|
|
||||||
log.e(ex, "decodeEmojiList failed. instance=${accessInfo.apiHost.ascii}")
|
|
||||||
null
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private fun makeListWithAlias(list: ArrayList<CustomEmoji>?): ArrayList<CustomEmoji> {
|
private fun makeListWithAlias(
|
||||||
val dst = ArrayList<CustomEmoji>()
|
list: List<CustomEmoji>,
|
||||||
if (list != null) {
|
) = ArrayList<CustomEmoji>().apply {
|
||||||
dst.addAll(list)
|
addAll(list)
|
||||||
for (item in list) {
|
for (item in list) {
|
||||||
val aliases = item.aliases ?: continue
|
val aliases = item.aliases ?: continue
|
||||||
for (alias in aliases) {
|
for (alias in aliases) {
|
||||||
if (alias.equals(item.shortcode, ignoreCase = true)) continue
|
if (alias.equals(item.shortcode, ignoreCase = true)) continue
|
||||||
dst.add(item.makeAlias(alias))
|
add(item.makeAlias(alias))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
dst.sortWith(compareBy(String.CASE_INSENSITIVE_ORDER) { it.alias ?: it.shortcode })
|
|
||||||
}
|
}
|
||||||
return dst
|
sortWith(compareBy(String.CASE_INSENSITIVE_ORDER) { it.alias ?: it.shortcode })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
package jp.juggler.subwaytooter.util
|
package jp.juggler.subwaytooter.util
|
||||||
|
|
||||||
import android.os.SystemClock
|
import android.os.SystemClock
|
||||||
import androidx.appcompat.app.AlertDialog
|
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import jp.juggler.subwaytooter.R
|
import jp.juggler.subwaytooter.R
|
||||||
import jp.juggler.subwaytooter.Styler
|
import jp.juggler.subwaytooter.Styler
|
||||||
import jp.juggler.subwaytooter.api.*
|
import jp.juggler.subwaytooter.api.*
|
||||||
import jp.juggler.subwaytooter.api.entity.*
|
import jp.juggler.subwaytooter.api.entity.*
|
||||||
import jp.juggler.subwaytooter.dialog.DlgConfirm
|
import jp.juggler.subwaytooter.dialog.DlgConfirm.confirm
|
||||||
import jp.juggler.subwaytooter.emoji.CustomEmoji
|
import jp.juggler.subwaytooter.emoji.CustomEmoji
|
||||||
import jp.juggler.subwaytooter.pref.PrefB
|
import jp.juggler.subwaytooter.pref.PrefB
|
||||||
import jp.juggler.subwaytooter.span.MyClickableSpan
|
import jp.juggler.subwaytooter.span.MyClickableSpan
|
||||||
@ -15,16 +14,21 @@ import jp.juggler.subwaytooter.table.AcctColor
|
|||||||
import jp.juggler.subwaytooter.table.SavedAccount
|
import jp.juggler.subwaytooter.table.SavedAccount
|
||||||
import jp.juggler.subwaytooter.table.TagSet
|
import jp.juggler.subwaytooter.table.TagSet
|
||||||
import jp.juggler.util.*
|
import jp.juggler.util.*
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.delay
|
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import okhttp3.RequestBody.Companion.toRequestBody
|
import okhttp3.RequestBody.Companion.toRequestBody
|
||||||
import java.lang.ref.WeakReference
|
import java.lang.ref.WeakReference
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
interface PostCompleteCallback {
|
sealed class PostResult {
|
||||||
fun onPostComplete(targetAccount: SavedAccount, status: TootStatus)
|
class Normal(
|
||||||
fun onScheduledPostComplete(targetAccount: SavedAccount)
|
val targetAccount: SavedAccount,
|
||||||
|
val status: TootStatus,
|
||||||
|
) : PostResult()
|
||||||
|
|
||||||
|
class Scheduled(
|
||||||
|
val targetAccount: SavedAccount,
|
||||||
|
) : PostResult()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("LongParameterList")
|
@Suppress("LongParameterList")
|
||||||
@ -51,8 +55,6 @@ class PostImpl(
|
|||||||
val editStatusId: EntityId?,
|
val editStatusId: EntityId?,
|
||||||
val emojiMapCustom: HashMap<String, CustomEmoji>?,
|
val emojiMapCustom: HashMap<String, CustomEmoji>?,
|
||||||
var useQuoteToot: Boolean,
|
var useQuoteToot: Boolean,
|
||||||
|
|
||||||
val callback: PostCompleteCallback,
|
|
||||||
) {
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
private val log = LogCategory("PostImpl")
|
private val log = LogCategory("PostImpl")
|
||||||
@ -70,11 +72,6 @@ class PostImpl(
|
|||||||
|
|
||||||
private var visibilityChecked: TootVisibility? = null
|
private var visibilityChecked: TootVisibility? = null
|
||||||
|
|
||||||
private var bConfirmTag: Boolean = false
|
|
||||||
var bConfirmAccount: Boolean = false
|
|
||||||
private var bConfirmRedraft: Boolean = false
|
|
||||||
private var bConfirmTagCharacter: Boolean = false
|
|
||||||
|
|
||||||
private val choiceMaxChars = when {
|
private val choiceMaxChars = when {
|
||||||
account.isMisskey -> 15
|
account.isMisskey -> 15
|
||||||
pollType == TootPollsType.FriendsNico -> 15
|
pollType == TootPollsType.FriendsNico -> 15
|
||||||
@ -130,106 +127,6 @@ class PostImpl(
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun confirm(): Boolean {
|
|
||||||
if (!bConfirmAccount) {
|
|
||||||
DlgConfirm.open(
|
|
||||||
activity,
|
|
||||||
activity.getString(R.string.confirm_post_from, AcctColor.getNickname(account)),
|
|
||||||
object : DlgConfirm.Callback {
|
|
||||||
override var isConfirmEnabled: Boolean
|
|
||||||
get() = account.confirm_post
|
|
||||||
set(bv) {
|
|
||||||
account.confirm_post = bv
|
|
||||||
account.saveSetting()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onOK() {
|
|
||||||
bConfirmAccount = true
|
|
||||||
run()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!bConfirmTagCharacter && PrefB.bpWarnHashtagAsciiAndNonAscii()) {
|
|
||||||
val tags = TootTag.findHashtags(content, account.isMisskey)
|
|
||||||
val badTags = tags
|
|
||||||
?.filter {
|
|
||||||
val hasAscii = reAscii.matcher(it).find()
|
|
||||||
val hasNotAscii = reNotAscii.matcher(it).find()
|
|
||||||
hasAscii && hasNotAscii
|
|
||||||
}
|
|
||||||
?.map { "#$it" }
|
|
||||||
|
|
||||||
if (badTags?.isNotEmpty() == true) {
|
|
||||||
AlertDialog.Builder(activity)
|
|
||||||
.setCancelable(true)
|
|
||||||
.setMessage(
|
|
||||||
activity.getString(
|
|
||||||
R.string.hashtag_contains_ascii_and_not_ascii,
|
|
||||||
badTags.joinToString(", ")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.setNegativeButton(R.string.cancel, null)
|
|
||||||
.setPositiveButton(R.string.ok) { _, _ ->
|
|
||||||
bConfirmTagCharacter = true
|
|
||||||
run()
|
|
||||||
}
|
|
||||||
.show()
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!bConfirmTag) {
|
|
||||||
val isMisskey = account.isMisskey
|
|
||||||
if (!visibilityArg.isTagAllowed(isMisskey)) {
|
|
||||||
val tags = TootTag.findHashtags(content, isMisskey)
|
|
||||||
if (tags != null) {
|
|
||||||
log.d("findHashtags ${tags.joinToString(",")}")
|
|
||||||
|
|
||||||
AlertDialog.Builder(activity)
|
|
||||||
.setCancelable(true)
|
|
||||||
.setMessage(R.string.hashtag_and_visibility_not_match)
|
|
||||||
.setNegativeButton(R.string.cancel, null)
|
|
||||||
.setPositiveButton(R.string.ok) { _, _ ->
|
|
||||||
bConfirmTag = true
|
|
||||||
run()
|
|
||||||
}
|
|
||||||
.show()
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!bConfirmRedraft && redraftStatusId != null) {
|
|
||||||
AlertDialog.Builder(activity)
|
|
||||||
.setCancelable(true)
|
|
||||||
.setMessage(R.string.delete_base_status_before_toot)
|
|
||||||
.setNegativeButton(R.string.cancel, null)
|
|
||||||
.setPositiveButton(R.string.ok) { _, _ ->
|
|
||||||
bConfirmRedraft = true
|
|
||||||
run()
|
|
||||||
}
|
|
||||||
.show()
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!bConfirmRedraft && scheduledId != null) {
|
|
||||||
AlertDialog.Builder(activity)
|
|
||||||
.setCancelable(true)
|
|
||||||
.setMessage(R.string.delete_scheduled_status_before_update)
|
|
||||||
.setNegativeButton(R.string.cancel, null)
|
|
||||||
.setPositiveButton(R.string.ok) { _, _ ->
|
|
||||||
bConfirmRedraft = true
|
|
||||||
run()
|
|
||||||
}
|
|
||||||
.show()
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
private var resultStatus: TootStatus? = null
|
private var resultStatus: TootStatus? = null
|
||||||
private var resultCredentialTmp: TootAccount? = null
|
private var resultCredentialTmp: TootAccount? = null
|
||||||
private var resultScheduledStatusSucceeded = false
|
private var resultScheduledStatusSucceeded = false
|
||||||
@ -503,14 +400,57 @@ class PostImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun run() {
|
suspend fun runSuspend(): PostResult {
|
||||||
if (!preCheck()) return
|
if (!preCheck()) throw CancellationException("preCheck failed.")
|
||||||
if (!confirm()) return
|
|
||||||
|
if (PrefB.bpWarnHashtagAsciiAndNonAscii()) {
|
||||||
|
TootTag.findHashtags(content, account.isMisskey)
|
||||||
|
?.filter {
|
||||||
|
val hasAscii = reAscii.matcher(it).find()
|
||||||
|
val hasNotAscii = reNotAscii.matcher(it).find()
|
||||||
|
hasAscii && hasNotAscii
|
||||||
|
}?.map { "#$it" }
|
||||||
|
?.notEmpty()
|
||||||
|
?.let { badTags ->
|
||||||
|
activity.confirm(
|
||||||
|
R.string.hashtag_contains_ascii_and_not_ascii,
|
||||||
|
badTags.joinToString(", ")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val isMisskey = account.isMisskey
|
||||||
|
if (!visibilityArg.isTagAllowed(isMisskey)) {
|
||||||
|
TootTag.findHashtags(content, isMisskey)
|
||||||
|
?.notEmpty()
|
||||||
|
?.let { tags ->
|
||||||
|
log.d("findHashtags ${tags.joinToString(",")}")
|
||||||
|
activity.confirm(
|
||||||
|
R.string.hashtag_and_visibility_not_match
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (redraftStatusId != null) {
|
||||||
|
activity.confirm(R.string.delete_base_status_before_toot)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scheduledId != null) {
|
||||||
|
activity.confirm(R.string.delete_scheduled_status_before_update)
|
||||||
|
}
|
||||||
|
|
||||||
|
activity.confirm(
|
||||||
|
activity.getString(R.string.confirm_post_from, AcctColor.getNickname(account)),
|
||||||
|
account.confirm_post
|
||||||
|
) { newConfirmEnabled ->
|
||||||
|
account.confirm_post = newConfirmEnabled
|
||||||
|
account.saveSetting()
|
||||||
|
}
|
||||||
|
|
||||||
// 投稿中に再度投稿ボタンが押された
|
// 投稿中に再度投稿ボタンが押された
|
||||||
if (postJob?.get()?.isActive == true) {
|
if (postJob?.get()?.isActive == true) {
|
||||||
activity.showToast(false, R.string.post_button_tapped_repeatly)
|
activity.showToast(false, R.string.post_button_tapped_repeatly)
|
||||||
return
|
throw CancellationException("preCheck failed.")
|
||||||
}
|
}
|
||||||
|
|
||||||
// ボタン連打判定
|
// ボタン連打判定
|
||||||
@ -519,10 +459,12 @@ class PostImpl(
|
|||||||
lastPostTapped = now
|
lastPostTapped = now
|
||||||
if (delta < 1000L) {
|
if (delta < 1000L) {
|
||||||
activity.showToast(false, R.string.post_button_tapped_repeatly)
|
activity.showToast(false, R.string.post_button_tapped_repeatly)
|
||||||
return
|
throw CancellationException("post_button_tapped_repeatly")
|
||||||
}
|
}
|
||||||
|
|
||||||
postJob = launchMain {
|
val job = Job().also { postJob = it.wrapWeakReference }
|
||||||
|
return withContext(Dispatchers.Main + job) {
|
||||||
|
|
||||||
activity.runApiTask(
|
activity.runApiTask(
|
||||||
account,
|
account,
|
||||||
progressSetup = { it.setCanceledOnTouchOutside(false) },
|
progressSetup = { it.setCanceledOnTouchOutside(false) },
|
||||||
@ -622,19 +564,21 @@ class PostImpl(
|
|||||||
saveStatusTag(status)
|
saveStatusTag(status)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}?.let { result ->
|
}.let { result ->
|
||||||
|
if (result == null) throw CancellationException()
|
||||||
|
|
||||||
val status = resultStatus
|
val status = resultStatus
|
||||||
val scheduledStatusSucceeded = resultScheduledStatusSucceeded
|
|
||||||
when {
|
when {
|
||||||
scheduledStatusSucceeded ->
|
resultScheduledStatusSucceeded ->
|
||||||
callback.onScheduledPostComplete(account)
|
PostResult.Scheduled(account)
|
||||||
|
|
||||||
// 連投してIdempotency が同じだった場合もエラーにはならず、ここを通る
|
// 連投してIdempotency が同じだった場合もエラーにはならず、ここを通る
|
||||||
status != null -> callback.onPostComplete(account, status)
|
status != null ->
|
||||||
|
PostResult.Normal(account, status)
|
||||||
|
|
||||||
else -> activity.showToast(true, result.error)
|
else -> error( result.error ?: "(result.error is null)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.wrapWeakReference
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package jp.juggler.util
|
package jp.juggler.util
|
||||||
|
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
import jp.juggler.subwaytooter.dialog.ProgressDialogEx
|
import jp.juggler.subwaytooter.dialog.ProgressDialogEx
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import java.lang.ref.WeakReference
|
import java.lang.ref.WeakReference
|
||||||
@ -30,6 +31,17 @@ fun launchMain(block: suspend CoroutineScope.() -> Unit): Job =
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
fun AppCompatActivity.launchAndShowError(block: suspend CoroutineScope.() -> Unit): Job =
|
||||||
|
lifecycleScope.launch() {
|
||||||
|
try {
|
||||||
|
block()
|
||||||
|
} catch (ex: Throwable) {
|
||||||
|
showError(ex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Default Dispatcherで動作するコルーチンを起動して、終了を待たずにリターンする。
|
// Default Dispatcherで動作するコルーチンを起動して、終了を待たずにリターンする。
|
||||||
// 起動されたアクティビティのライフサイクルに関わらず中断しない。
|
// 起動されたアクティビティのライフサイクルに関わらず中断しない。
|
||||||
fun launchDefault(block: suspend CoroutineScope.() -> Unit): Job =
|
fun launchDefault(block: suspend CoroutineScope.() -> Unit): Job =
|
||||||
|
@ -2,57 +2,87 @@ package jp.juggler.util
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import jp.juggler.subwaytooter.R
|
||||||
|
import kotlinx.coroutines.CancellationException
|
||||||
import me.drakeet.support.toast.ToastCompat
|
import me.drakeet.support.toast.ToastCompat
|
||||||
import java.lang.ref.WeakReference
|
import java.lang.ref.WeakReference
|
||||||
|
|
||||||
object ToastUtils {
|
private val log = LogCategory("ToastUtils")
|
||||||
|
private var refToast: WeakReference<Toast>? = null
|
||||||
|
|
||||||
private val log = LogCategory("ToastUtils")
|
internal fun showToastImpl(context: Context, bLong: Boolean, message: String): Boolean {
|
||||||
private var refToast: WeakReference<Toast>? = null
|
runOnMainLooper {
|
||||||
|
|
||||||
internal fun showToastImpl(context: Context, bLong: Boolean, message: String): Boolean {
|
// 前回のトーストの表示を終了する
|
||||||
runOnMainLooper {
|
try {
|
||||||
|
refToast?.get()?.cancel()
|
||||||
// 前回のトーストの表示を終了する
|
} catch (ex: Throwable) {
|
||||||
try {
|
log.trace(ex)
|
||||||
refToast?.get()?.cancel()
|
} finally {
|
||||||
} catch (ex: Throwable) {
|
refToast = null
|
||||||
log.trace(ex)
|
|
||||||
} finally {
|
|
||||||
refToast = null
|
|
||||||
}
|
|
||||||
|
|
||||||
// 新しいトーストを作る
|
|
||||||
try {
|
|
||||||
val duration = if (bLong) Toast.LENGTH_LONG else Toast.LENGTH_SHORT
|
|
||||||
val t = ToastCompat.makeText(context, message, duration)
|
|
||||||
t.setBadTokenListener { }
|
|
||||||
t.show()
|
|
||||||
refToast = WeakReference(t)
|
|
||||||
} catch (ex: Throwable) {
|
|
||||||
log.trace(ex)
|
|
||||||
}
|
|
||||||
|
|
||||||
// コールスタックの外側でエラーになる…
|
|
||||||
// android.view.WindowManager$BadTokenException:
|
|
||||||
// at android.view.ViewRootImpl.setView (ViewRootImpl.java:679)
|
|
||||||
// at android.view.WindowManagerGlobal.addView (WindowManagerGlobal.java:342)
|
|
||||||
// at android.view.WindowManagerImpl.addView (WindowManagerImpl.java:94)
|
|
||||||
// at android.widget.Toast$TN.handleShow (Toast.java:435)
|
|
||||||
// at android.widget.Toast$TN$2.handleMessage (Toast.java:345)
|
|
||||||
}
|
}
|
||||||
return false
|
|
||||||
|
// 新しいトーストを作る
|
||||||
|
try {
|
||||||
|
val duration = if (bLong) Toast.LENGTH_LONG else Toast.LENGTH_SHORT
|
||||||
|
val t = ToastCompat.makeText(context, message, duration)
|
||||||
|
t.setBadTokenListener { }
|
||||||
|
t.show()
|
||||||
|
refToast = WeakReference(t)
|
||||||
|
} catch (ex: Throwable) {
|
||||||
|
log.trace(ex)
|
||||||
|
}
|
||||||
|
|
||||||
|
// コールスタックの外側でエラーになる…
|
||||||
|
// android.view.WindowManager$BadTokenException:
|
||||||
|
// at android.view.ViewRootImpl.setView (ViewRootImpl.java:679)
|
||||||
|
// at android.view.WindowManagerGlobal.addView (WindowManagerGlobal.java:342)
|
||||||
|
// at android.view.WindowManagerImpl.addView (WindowManagerImpl.java:94)
|
||||||
|
// at android.widget.Toast$TN.handleShow (Toast.java:435)
|
||||||
|
// at android.widget.Toast$TN$2.handleMessage (Toast.java:345)
|
||||||
}
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Context.showToast(bLong: Boolean, caption: String?): Boolean =
|
fun Context.showToast(bLong: Boolean, caption: String?): Boolean =
|
||||||
ToastUtils.showToastImpl(this, bLong, caption ?: "(null)")
|
showToastImpl(this, bLong, caption ?: "(null)")
|
||||||
|
|
||||||
fun Context.showToast(ex: Throwable, caption: String = "error."): Boolean =
|
fun Context.showToast(ex: Throwable, caption: String = "error."): Boolean =
|
||||||
ToastUtils.showToastImpl(this, true, ex.withCaption(caption))
|
showToastImpl(this, true, ex.withCaption(caption))
|
||||||
|
|
||||||
fun Context.showToast(bLong: Boolean, stringId: Int, vararg args: Any): Boolean =
|
fun Context.showToast(bLong: Boolean, stringId: Int, vararg args: Any): Boolean =
|
||||||
ToastUtils.showToastImpl(this, bLong, getString(stringId, *args))
|
showToastImpl(this, bLong, getString(stringId, *args))
|
||||||
|
|
||||||
fun Context.showToast(ex: Throwable, stringId: Int, vararg args: Any): Boolean =
|
fun Context.showToast(ex: Throwable, stringId: Int, vararg args: Any): Boolean =
|
||||||
ToastUtils.showToastImpl(this, true, ex.withCaption(resources, stringId, *args))
|
showToastImpl(this, true, ex.withCaption(resources, stringId, *args))
|
||||||
|
|
||||||
|
fun AppCompatActivity.showError(ex: Throwable, caption: String = "error.") {
|
||||||
|
log.e(ex)
|
||||||
|
// キャンセル例外はUIに表示しない
|
||||||
|
if (ex is CancellationException) return
|
||||||
|
|
||||||
|
try {
|
||||||
|
AlertDialog.Builder(this)
|
||||||
|
.setTitle(R.string.error)
|
||||||
|
.setMessage(
|
||||||
|
listOf(
|
||||||
|
caption,
|
||||||
|
when (ex) {
|
||||||
|
is IllegalStateException ->
|
||||||
|
null
|
||||||
|
else ->
|
||||||
|
ex.javaClass.simpleName
|
||||||
|
},
|
||||||
|
ex.message,
|
||||||
|
)
|
||||||
|
.filter { !it.isNullOrBlank() }
|
||||||
|
.joinToString("\n")
|
||||||
|
)
|
||||||
|
.setPositiveButton(R.string.ok, null)
|
||||||
|
} catch (ex: Throwable) {
|
||||||
|
log.e(ex)
|
||||||
|
showToast(ex, caption)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,73 +1,90 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<LinearLayout
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
android:layout_width="320dp"
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical">
|
||||||
>
|
|
||||||
|
|
||||||
<LinearLayout
|
<com.google.android.flexbox.FlexboxLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="horizontal"
|
android:layout_margin="6dp"
|
||||||
android:paddingTop="6dp"
|
android:layout_marginBottom="0dp"
|
||||||
android:paddingStart="6dp"
|
android:orientation="horizontal">
|
||||||
android:paddingEnd="6dp"
|
|
||||||
>
|
<ImageButton
|
||||||
|
android:id="@+id/btnSkinTone0"
|
||||||
|
style="@style/emoji_picker_skin_tone_button"
|
||||||
|
android:background="#fde12c"
|
||||||
|
android:contentDescription="@string/skin_tone_unspecified"
|
||||||
|
android:src="@drawable/check_mark" />
|
||||||
|
|
||||||
<ImageButton
|
<ImageButton
|
||||||
android:id="@+id/btnSkinTone1"
|
android:id="@+id/btnSkinTone1"
|
||||||
android:layout_width="48dp"
|
style="@style/emoji_picker_skin_tone_button"
|
||||||
android:layout_height="48dp"
|
|
||||||
android:background="#f7dece"
|
android:background="#f7dece"
|
||||||
android:contentDescription="@string/skin_tone_light"
|
android:contentDescription="@string/skin_tone_light" />
|
||||||
android:src="@drawable/check_mark"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<ImageButton
|
<ImageButton
|
||||||
android:id="@+id/btnSkinTone2"
|
android:id="@+id/btnSkinTone2"
|
||||||
android:layout_width="48dp"
|
style="@style/emoji_picker_skin_tone_button"
|
||||||
android:layout_height="48dp"
|
|
||||||
android:background="#f3d2a2"
|
android:background="#f3d2a2"
|
||||||
android:contentDescription="@string/skin_tone_medium_light"
|
android:contentDescription="@string/skin_tone_medium_light" />
|
||||||
/>
|
|
||||||
|
|
||||||
<ImageButton
|
<ImageButton
|
||||||
android:id="@+id/btnSkinTone3"
|
android:id="@+id/btnSkinTone3"
|
||||||
android:layout_width="48dp"
|
style="@style/emoji_picker_skin_tone_button"
|
||||||
android:layout_height="48dp"
|
|
||||||
android:background="#d5ab88"
|
android:background="#d5ab88"
|
||||||
android:contentDescription="@string/skin_tone_medium"
|
android:contentDescription="@string/skin_tone_medium" />
|
||||||
/>
|
|
||||||
|
|
||||||
<ImageButton
|
<ImageButton
|
||||||
android:id="@+id/btnSkinTone4"
|
android:id="@+id/btnSkinTone4"
|
||||||
android:layout_width="48dp"
|
style="@style/emoji_picker_skin_tone_button"
|
||||||
android:layout_height="48dp"
|
|
||||||
android:background="#af7e57"
|
android:background="#af7e57"
|
||||||
android:contentDescription="@string/skin_tone_medium_dark"
|
android:contentDescription="@string/skin_tone_medium_dark" />
|
||||||
/>
|
|
||||||
|
|
||||||
<ImageButton
|
<ImageButton
|
||||||
android:id="@+id/btnSkinTone5"
|
android:id="@+id/btnSkinTone5"
|
||||||
android:layout_width="48dp"
|
style="@style/emoji_picker_skin_tone_button"
|
||||||
android:layout_height="48dp"
|
|
||||||
android:background="#7c533e"
|
android:background="#7c533e"
|
||||||
android:contentDescription="@string/skin_tone_dark"
|
android:contentDescription="@string/skin_tone_dark" />
|
||||||
/>
|
</com.google.android.flexbox.FlexboxLayout>
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<com.astuetz.PagerSlidingTabStrip
|
<EditText
|
||||||
android:id="@+id/pager_strip"
|
android:id="@+id/etFilter"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="48dip"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="top"
|
android:layout_margin="6dp"
|
||||||
/>
|
android:layout_marginBottom="0dp"
|
||||||
|
android:hint="@string/search_emojis"
|
||||||
|
android:importantForAccessibility="no"
|
||||||
|
android:importantForAutofill="no"
|
||||||
|
android:inputType="text" />
|
||||||
|
|
||||||
<jp.juggler.subwaytooter.view.MyViewPager
|
<HorizontalScrollView
|
||||||
android:id="@+id/pager"
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:fadeScrollbars="false"
|
||||||
|
android:padding="6dp"
|
||||||
|
android:paddingBottom="0dp"
|
||||||
|
android:scrollbarStyle="outsideOverlay"
|
||||||
|
android:scrollbars="horizontal">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/llCategories"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal" />
|
||||||
|
</HorizontalScrollView>
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/rvGrid"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
/>
|
android:clipToPadding="false"
|
||||||
|
android:fadeScrollbars="false"
|
||||||
|
android:padding="6dp"
|
||||||
|
android:scrollbarStyle="outsideOverlay"
|
||||||
|
android:scrollbars="vertical" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<jp.juggler.subwaytooter.view.HeaderGridView
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:id="@+id/gridView"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:columnWidth="48dp"
|
|
||||||
android:horizontalSpacing="4dp"
|
|
||||||
android:numColumns="auto_fit"
|
|
||||||
android:paddingBottom="6dp"
|
|
||||||
android:paddingEnd="6dp"
|
|
||||||
android:paddingStart="6dp"
|
|
||||||
android:stretchMode="columnWidth"
|
|
||||||
android:verticalSpacing="4dp"
|
|
||||||
/>
|
|
@ -1139,4 +1139,11 @@
|
|||||||
<string name="edit_history">編集履歴</string>
|
<string name="edit_history">編集履歴</string>
|
||||||
<string name="post_language_code">投稿の言語コード(空欄にすると端末の言語設定を使います)</string>
|
<string name="post_language_code">投稿の言語コード(空欄にすると端末の言語設定を使います)</string>
|
||||||
<string name="device_language">(端末の言語)</string>
|
<string name="device_language">(端末の言語)</string>
|
||||||
|
<string name="skin_tones">肌色</string>
|
||||||
|
<string name="skin_tone_unspecified">指定なし</string>
|
||||||
|
<string name="search_emojis">絵文字を検索…</string>
|
||||||
|
<string name="categories">カテゴリ</string>
|
||||||
|
<string name="error">エラー</string>
|
||||||
|
<string name="emoji_picker_custom_of">カスタム: %1$s</string>
|
||||||
|
<string name="others" >その他</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -1148,4 +1148,11 @@
|
|||||||
<string name="edit_history">Edit history</string>
|
<string name="edit_history">Edit history</string>
|
||||||
<string name="post_language_code">Language code of Post (leave empty to use device\'s language setting)</string>
|
<string name="post_language_code">Language code of Post (leave empty to use device\'s language setting)</string>
|
||||||
<string name="device_language">(device\'s language)</string>
|
<string name="device_language">(device\'s language)</string>
|
||||||
|
<string name="skin_tones">Skin tones</string>
|
||||||
|
<string name="skin_tone_unspecified">Unspecified</string>
|
||||||
|
<string name="search_emojis">Search emojis…</string>
|
||||||
|
<string name="categories">Categories</string>
|
||||||
|
<string name="error">Error</string>
|
||||||
|
<string name="emoji_picker_custom_of">Custom: %1$s</string>
|
||||||
|
<string name="others" >Others</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -258,4 +258,10 @@
|
|||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style name="emoji_picker_skin_tone_button">
|
||||||
|
<item name="android:layout_width">48dp</item>
|
||||||
|
<item name="android:layout_height">48dp</item>
|
||||||
|
<item name="android:layout_marginStart">2dp</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user