添付メディアの説明文の入力時にサムネイル表示を行う。複数行入力に対応する。
This commit is contained in:
parent
2ebf63151a
commit
b6f30f0097
|
@ -1,6 +1,5 @@
|
||||||
package jp.juggler.subwaytooter.action
|
package jp.juggler.subwaytooter.action
|
||||||
|
|
||||||
import android.app.Dialog
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import jp.juggler.subwaytooter.*
|
import jp.juggler.subwaytooter.*
|
||||||
import jp.juggler.subwaytooter.actmain.addColumn
|
import jp.juggler.subwaytooter.actmain.addColumn
|
||||||
|
@ -139,26 +138,19 @@ private suspend fun ActMain.createUser(
|
||||||
* @param onComplete 非nullならアカウント認証が終わったタイミングで呼ばれる
|
* @param onComplete 非nullならアカウント認証が終わったタイミングで呼ばれる
|
||||||
*/
|
*/
|
||||||
// アクセストークンの手動入力(更新)
|
// アクセストークンの手動入力(更新)
|
||||||
fun ActMain.accessTokenPrompt(
|
suspend fun ActMain.accessTokenPrompt(
|
||||||
apiHost: Host,
|
apiHost: Host,
|
||||||
onComplete: (() -> Unit)? = null,
|
onComplete: (() -> Unit)? = null,
|
||||||
) {
|
) {
|
||||||
DlgTextInput.show(
|
showTextInputDialog(
|
||||||
this,
|
title = getString(R.string.access_token_or_api_token),
|
||||||
getString(R.string.access_token_or_api_token),
|
initialText = null,
|
||||||
null,
|
onEmptyText = { showToast(true, R.string.token_not_specified) },
|
||||||
callback = object : DlgTextInput.Callback {
|
) { text ->
|
||||||
override fun onEmptyError() {
|
|
||||||
showToast(true, R.string.token_not_specified)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onOK(dialog: Dialog, text: String) {
|
|
||||||
launchMain {
|
|
||||||
try {
|
try {
|
||||||
val accessToken = text.trim()
|
val accessToken = text.trim()
|
||||||
val auth2Result = runApiTask2(apiHost) { client ->
|
val auth2Result = runApiTask2(apiHost) { client ->
|
||||||
val ti =
|
val ti = TootInstance.getExOrThrow(client, forceAccessToken = accessToken)
|
||||||
TootInstance.getExOrThrow(client, forceAccessToken = accessToken)
|
|
||||||
|
|
||||||
val tokenJson = JsonObject()
|
val tokenJson = JsonObject()
|
||||||
|
|
||||||
|
@ -167,7 +159,6 @@ fun ActMain.accessTokenPrompt(
|
||||||
outTokenInfo = tokenJson, // 更新される
|
outTokenInfo = tokenJson, // 更新される
|
||||||
misskeyVersion = ti.misskeyVersionMajor
|
misskeyVersion = ti.misskeyVersionMajor
|
||||||
)
|
)
|
||||||
|
|
||||||
val parser = TootParser(this, linkHelper = LinkHelper.create(ti))
|
val parser = TootParser(this, linkHelper = LinkHelper.create(ti))
|
||||||
|
|
||||||
Auth2Result(
|
Auth2Result(
|
||||||
|
@ -178,18 +169,19 @@ fun ActMain.accessTokenPrompt(
|
||||||
?: error("can't parse user information."),
|
?: error("can't parse user information."),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (afterAccountVerify(auth2Result)) {
|
when (afterAccountVerify(auth2Result)) {
|
||||||
dialog.dismissSafe()
|
false -> false
|
||||||
|
else -> {
|
||||||
onComplete?.invoke()
|
onComplete?.invoke()
|
||||||
|
true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (ex: Throwable) {
|
} catch (ex: Throwable) {
|
||||||
showApiError(ex)
|
showApiError(ex)
|
||||||
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// アカウント設定
|
// アカウント設定
|
||||||
fun ActMain.accountOpenSetting() {
|
fun ActMain.accountOpenSetting() {
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package jp.juggler.subwaytooter.action
|
package jp.juggler.subwaytooter.action
|
||||||
|
|
||||||
import android.app.Dialog
|
|
||||||
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.addColumn
|
import jp.juggler.subwaytooter.actmain.addColumn
|
||||||
|
@ -14,8 +13,8 @@ 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.DlgConfirm.confirm
|
import jp.juggler.subwaytooter.dialog.DlgConfirm.confirm
|
||||||
import jp.juggler.subwaytooter.dialog.DlgTextInput
|
|
||||||
import jp.juggler.subwaytooter.dialog.actionsDialog
|
import jp.juggler.subwaytooter.dialog.actionsDialog
|
||||||
|
import jp.juggler.subwaytooter.dialog.showTextInputDialog
|
||||||
import jp.juggler.subwaytooter.table.SavedAccount
|
import jp.juggler.subwaytooter.table.SavedAccount
|
||||||
import jp.juggler.util.coroutine.launchAndShowError
|
import jp.juggler.util.coroutine.launchAndShowError
|
||||||
import jp.juggler.util.coroutine.launchMain
|
import jp.juggler.util.coroutine.launchMain
|
||||||
|
@ -23,7 +22,6 @@ import jp.juggler.util.data.buildJsonObject
|
||||||
import jp.juggler.util.log.showToast
|
import jp.juggler.util.log.showToast
|
||||||
import jp.juggler.util.network.toPostRequestBuilder
|
import jp.juggler.util.network.toPostRequestBuilder
|
||||||
import jp.juggler.util.network.toPutRequestBuilder
|
import jp.juggler.util.network.toPutRequestBuilder
|
||||||
import jp.juggler.util.ui.dismissSafe
|
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
|
|
||||||
fun ActMain.clickListTl(pos: Int, accessInfo: SavedAccount, item: TimelineItem?) {
|
fun ActMain.clickListTl(pos: Int, accessInfo: SavedAccount, item: TimelineItem?) {
|
||||||
|
@ -164,24 +162,17 @@ fun ActMain.listDelete(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun ActMain.listRename(
|
suspend fun ActMain.listRename(
|
||||||
accessInfo: SavedAccount,
|
accessInfo: SavedAccount,
|
||||||
item: TootList,
|
item: TootList,
|
||||||
) {
|
) {
|
||||||
|
showTextInputDialog(
|
||||||
DlgTextInput.show(
|
title = getString(R.string.rename),
|
||||||
this,
|
initialText = item.title,
|
||||||
getString(R.string.rename),
|
onEmptyText = { showToast(false, R.string.list_name_empty) },
|
||||||
item.title,
|
) { text ->
|
||||||
callback = object : DlgTextInput.Callback {
|
|
||||||
override fun onEmptyError() {
|
|
||||||
showToast(false, R.string.list_name_empty)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onOK(dialog: Dialog, text: String) {
|
|
||||||
launchMain {
|
|
||||||
var resultList: TootList? = null
|
var resultList: TootList? = null
|
||||||
runApiTask(accessInfo) { client ->
|
val result = runApiTask(accessInfo) { client ->
|
||||||
if (accessInfo.isMisskey) {
|
if (accessInfo.isMisskey) {
|
||||||
client.request(
|
client.request(
|
||||||
"/api/users/lists/update",
|
"/api/users/lists/update",
|
||||||
|
@ -206,19 +197,19 @@ fun ActMain.listRename(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}?.let { result ->
|
}
|
||||||
|
result ?: return@showTextInputDialog true
|
||||||
when (val list = resultList) {
|
when (val list = resultList) {
|
||||||
null -> showToast(false, result.error)
|
null -> {
|
||||||
|
showToast(false, result.error)
|
||||||
|
false
|
||||||
|
}
|
||||||
else -> {
|
else -> {
|
||||||
for (column in appState.columnList) {
|
for (column in appState.columnList) {
|
||||||
column.onListNameUpdated(accessInfo, list)
|
column.onListNameUpdated(accessInfo, list)
|
||||||
}
|
}
|
||||||
dialog.dismissSafe()
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
package jp.juggler.subwaytooter.actpost
|
package jp.juggler.subwaytooter.actpost
|
||||||
|
|
||||||
import android.app.Dialog
|
import android.app.Dialog
|
||||||
|
import android.graphics.Bitmap
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import android.text.InputType
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import android.view.WindowManager
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import jp.juggler.subwaytooter.ActPost
|
import jp.juggler.subwaytooter.ActPost
|
||||||
import jp.juggler.subwaytooter.R
|
import jp.juggler.subwaytooter.R
|
||||||
|
@ -13,10 +16,12 @@ import jp.juggler.subwaytooter.api.entity.TootAttachment.Companion.tootAttachmen
|
||||||
import jp.juggler.subwaytooter.api.entity.TootAttachment.Companion.tootAttachmentJson
|
import jp.juggler.subwaytooter.api.entity.TootAttachment.Companion.tootAttachmentJson
|
||||||
import jp.juggler.subwaytooter.api.runApiTask
|
import jp.juggler.subwaytooter.api.runApiTask
|
||||||
import jp.juggler.subwaytooter.calcIconRound
|
import jp.juggler.subwaytooter.calcIconRound
|
||||||
|
import jp.juggler.subwaytooter.databinding.DlgFocusPointBinding
|
||||||
import jp.juggler.subwaytooter.defaultColorIcon
|
import jp.juggler.subwaytooter.defaultColorIcon
|
||||||
import jp.juggler.subwaytooter.dialog.DlgFocusPoint
|
|
||||||
import jp.juggler.subwaytooter.dialog.DlgTextInput
|
|
||||||
import jp.juggler.subwaytooter.dialog.actionsDialog
|
import jp.juggler.subwaytooter.dialog.actionsDialog
|
||||||
|
import jp.juggler.subwaytooter.dialog.decodeAttachmentBitmap
|
||||||
|
import jp.juggler.subwaytooter.dialog.focusPointDialog
|
||||||
|
import jp.juggler.subwaytooter.dialog.showTextInputDialog
|
||||||
import jp.juggler.subwaytooter.pref.PrefB
|
import jp.juggler.subwaytooter.pref.PrefB
|
||||||
import jp.juggler.subwaytooter.util.AttachmentRequest
|
import jp.juggler.subwaytooter.util.AttachmentRequest
|
||||||
import jp.juggler.subwaytooter.util.PostAttachment
|
import jp.juggler.subwaytooter.util.PostAttachment
|
||||||
|
@ -29,7 +34,10 @@ import jp.juggler.util.log.showToast
|
||||||
import jp.juggler.util.log.withCaption
|
import jp.juggler.util.log.withCaption
|
||||||
import jp.juggler.util.network.toPutRequestBuilder
|
import jp.juggler.util.network.toPutRequestBuilder
|
||||||
import jp.juggler.util.ui.dismissSafe
|
import jp.juggler.util.ui.dismissSafe
|
||||||
|
import jp.juggler.util.ui.isLiveActivity
|
||||||
import jp.juggler.util.ui.vg
|
import jp.juggler.util.ui.vg
|
||||||
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
|
import kotlin.coroutines.resumeWithException
|
||||||
|
|
||||||
private val log = LogCategory("ActPostAttachment")
|
private val log = LogCategory("ActPostAttachment")
|
||||||
|
|
||||||
|
@ -212,13 +220,10 @@ private fun ActPost.appendArrachmentUrl(a: TootAttachment) {
|
||||||
|
|
||||||
// 添付した画像をタップ
|
// 添付した画像をタップ
|
||||||
fun ActPost.performAttachmentClick(idx: Int) {
|
fun ActPost.performAttachmentClick(idx: Int) {
|
||||||
val pa = try {
|
|
||||||
attachmentList[idx]
|
|
||||||
} catch (ex: Throwable) {
|
|
||||||
showToast(false, ex.withCaption("can't get attachment item[$idx]."))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
launchAndShowError {
|
launchAndShowError {
|
||||||
|
val pa = attachmentList.elementAtOrNull(idx)
|
||||||
|
?: error("can't get attachment item[$idx].")
|
||||||
|
|
||||||
actionsDialog(getString(R.string.media_attachment)) {
|
actionsDialog(getString(R.string.media_attachment)) {
|
||||||
action(getString(R.string.set_description)) {
|
action(getString(R.string.set_description)) {
|
||||||
editAttachmentDescription(pa)
|
editAttachmentDescription(pa)
|
||||||
|
@ -265,11 +270,12 @@ fun ActPost.deleteAttachment(pa: PostAttachment) {
|
||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun ActPost.openFocusPoint(pa: PostAttachment) {
|
suspend fun ActPost.openFocusPoint(pa: PostAttachment) {
|
||||||
val attachment = pa.attachment ?: return
|
val attachment = pa.attachment ?: return
|
||||||
DlgFocusPoint(this, attachment)
|
focusPointDialog(
|
||||||
.setCallback { x, y -> sendFocusPoint(pa, attachment, x, y) }
|
attachment = attachment,
|
||||||
.show()
|
callback = { x, y -> sendFocusPoint(pa, attachment, x, y) }
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun ActPost.sendFocusPoint(pa: PostAttachment, attachment: TootAttachment, x: Float, y: Float) {
|
fun ActPost.sendFocusPoint(pa: PostAttachment, attachment: TootAttachment, x: Float, y: Float) {
|
||||||
|
@ -300,42 +306,65 @@ fun ActPost.sendFocusPoint(pa: PostAttachment, attachment: TootAttachment, x: Fl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun ActPost.editAttachmentDescription(pa: PostAttachment) {
|
suspend fun ActPost.editAttachmentDescription(pa: PostAttachment) {
|
||||||
val a = pa.attachment
|
val a = pa.attachment
|
||||||
if (a == null) {
|
if (a == null) {
|
||||||
showToast(true, R.string.attachment_description_cant_edit_while_uploading)
|
showToast(true, R.string.attachment_description_cant_edit_while_uploading)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
DlgTextInput.show(
|
|
||||||
this,
|
|
||||||
getString(R.string.attachment_description),
|
|
||||||
a.description,
|
|
||||||
callback = object : DlgTextInput.Callback {
|
|
||||||
override fun onEmptyError() {
|
|
||||||
showToast(true, R.string.description_empty)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onOK(dialog: Dialog, text: String) {
|
|
||||||
val attachmentId = pa.attachment?.id ?: return
|
val attachmentId = pa.attachment?.id ?: return
|
||||||
val account = this@editAttachmentDescription.account ?: return
|
val account = this.account ?: return
|
||||||
launchMain {
|
var bitmap: Bitmap? = null
|
||||||
|
try {
|
||||||
|
val url = a.preview_url
|
||||||
|
if (url != null) {
|
||||||
|
val result = runApiTask { client ->
|
||||||
|
try {
|
||||||
|
val (result, data) = client.getHttpBytes(url)
|
||||||
|
data?.let {
|
||||||
|
bitmap = decodeAttachmentBitmap(it, 1024)
|
||||||
|
?: return@runApiTask TootApiResult("image decode failed.")
|
||||||
|
}
|
||||||
|
result
|
||||||
|
} catch (ex: Throwable) {
|
||||||
|
TootApiResult(ex.withCaption("preview loading failed."))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result ?: return
|
||||||
|
if (!isLiveActivity) return
|
||||||
|
result.error?.let{
|
||||||
|
showToast(true, result.error ?: "error")
|
||||||
|
// not exit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
showTextInputDialog(
|
||||||
|
title = getString(R.string.attachment_description),
|
||||||
|
bitmap = bitmap,
|
||||||
|
inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_FLAG_MULTI_LINE,
|
||||||
|
initialText = a.description,
|
||||||
|
onEmptyText = { showToast(true, R.string.description_empty) },
|
||||||
|
) { text ->
|
||||||
val (result, newAttachment) = attachmentUploader.setAttachmentDescription(
|
val (result, newAttachment) = attachmentUploader.setAttachmentDescription(
|
||||||
account,
|
account,
|
||||||
attachmentId,
|
attachmentId,
|
||||||
text
|
text
|
||||||
)
|
)
|
||||||
|
result ?: return@showTextInputDialog true
|
||||||
when (newAttachment) {
|
when (newAttachment) {
|
||||||
null -> result?.error?.let { showToast(true, it) }
|
null -> {
|
||||||
|
result.error?.let { showToast(true, it) }
|
||||||
|
false
|
||||||
|
}
|
||||||
else -> {
|
else -> {
|
||||||
pa.attachment = newAttachment
|
pa.attachment = newAttachment
|
||||||
showMediaAttachment()
|
showMediaAttachment()
|
||||||
dialog.dismissSafe()
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
bitmap?.recycle()
|
||||||
}
|
}
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun ActPost.onPickCustomThumbnailImpl(pa: PostAttachment, src: GetContentResultEntry) {
|
fun ActPost.onPickCustomThumbnailImpl(pa: PostAttachment, src: GetContentResultEntry) {
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package jp.juggler.subwaytooter.columnviewholder
|
package jp.juggler.subwaytooter.columnviewholder
|
||||||
|
|
||||||
import android.app.Dialog
|
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.text.Spannable
|
import android.text.Spannable
|
||||||
import android.text.SpannableStringBuilder
|
import android.text.SpannableStringBuilder
|
||||||
|
@ -22,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.*
|
import jp.juggler.subwaytooter.column.*
|
||||||
import jp.juggler.subwaytooter.databinding.LvHeaderProfileBinding
|
import jp.juggler.subwaytooter.databinding.LvHeaderProfileBinding
|
||||||
import jp.juggler.subwaytooter.dialog.DlgTextInput
|
import jp.juggler.subwaytooter.dialog.showTextInputDialog
|
||||||
import jp.juggler.subwaytooter.emoji.EmojiMap
|
import jp.juggler.subwaytooter.emoji.EmojiMap
|
||||||
import jp.juggler.subwaytooter.itemviewholder.DlgContextMenu
|
import jp.juggler.subwaytooter.itemviewholder.DlgContextMenu
|
||||||
import jp.juggler.subwaytooter.pref.PrefB
|
import jp.juggler.subwaytooter.pref.PrefB
|
||||||
|
@ -41,7 +40,7 @@ import jp.juggler.subwaytooter.util.openCustomTab
|
||||||
import jp.juggler.subwaytooter.util.startMargin
|
import jp.juggler.subwaytooter.util.startMargin
|
||||||
import jp.juggler.subwaytooter.view.MyLinkMovementMethod
|
import jp.juggler.subwaytooter.view.MyLinkMovementMethod
|
||||||
import jp.juggler.subwaytooter.view.MyTextView
|
import jp.juggler.subwaytooter.view.MyTextView
|
||||||
import jp.juggler.util.coroutine.launchMain
|
import jp.juggler.util.coroutine.launchAndShowError
|
||||||
import jp.juggler.util.data.buildJsonObject
|
import jp.juggler.util.data.buildJsonObject
|
||||||
import jp.juggler.util.data.intoStringResource
|
import jp.juggler.util.data.intoStringResource
|
||||||
import jp.juggler.util.data.notEmpty
|
import jp.juggler.util.data.notEmpty
|
||||||
|
@ -49,7 +48,6 @@ import jp.juggler.util.data.notZero
|
||||||
import jp.juggler.util.log.showToast
|
import jp.juggler.util.log.showToast
|
||||||
import jp.juggler.util.network.toPostRequestBuilder
|
import jp.juggler.util.network.toPostRequestBuilder
|
||||||
import jp.juggler.util.ui.attrColor
|
import jp.juggler.util.ui.attrColor
|
||||||
import jp.juggler.util.ui.dismissSafe
|
|
||||||
import jp.juggler.util.ui.setIconDrawableId
|
import jp.juggler.util.ui.setIconDrawableId
|
||||||
import jp.juggler.util.ui.vg
|
import jp.juggler.util.ui.vg
|
||||||
import org.jetbrains.anko.textColor
|
import org.jetbrains.anko.textColor
|
||||||
|
@ -444,22 +442,18 @@ internal class ViewHolderHeaderProfile(
|
||||||
val who = whoRef.get()
|
val who = whoRef.get()
|
||||||
val relation = this.relation
|
val relation = this.relation
|
||||||
val lastColumn = column
|
val lastColumn = column
|
||||||
DlgTextInput.show(
|
activity.launchAndShowError {
|
||||||
activity,
|
activity.showTextInputDialog(
|
||||||
daoAcctColor.getStringWithNickname(
|
title = daoAcctColor.getStringWithNickname(
|
||||||
activity,
|
activity,
|
||||||
R.string.personal_notes_of,
|
R.string.personal_notes_of,
|
||||||
who.acct
|
who.acct
|
||||||
),
|
),
|
||||||
relation?.note ?: "",
|
initialText = relation?.note ?: "",
|
||||||
allowEmpty = true,
|
allowEmpty = true,
|
||||||
callback = object : DlgTextInput.Callback {
|
onEmptyText = {},
|
||||||
override fun onEmptyError() {
|
) { text ->
|
||||||
}
|
val result = activity.runApiTask(column.accessInfo) { client ->
|
||||||
|
|
||||||
override fun onOK(dialog: Dialog, text: String) {
|
|
||||||
launchMain {
|
|
||||||
activity.runApiTask(column.accessInfo) { client ->
|
|
||||||
when {
|
when {
|
||||||
accessInfo.isPseudo ->
|
accessInfo.isPseudo ->
|
||||||
TootApiResult("Personal notes is not supported on pseudo account.")
|
TootApiResult("Personal notes is not supported on pseudo account.")
|
||||||
|
@ -473,21 +467,22 @@ internal class ViewHolderHeaderProfile(
|
||||||
}.toPostRequestBuilder()
|
}.toPostRequestBuilder()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}?.let { result ->
|
}
|
||||||
|
result ?: return@showTextInputDialog true
|
||||||
when (val error = result.error) {
|
when (val error = result.error) {
|
||||||
null -> {
|
null -> {
|
||||||
relation?.note = text
|
relation?.note = text
|
||||||
dialog.dismissSafe()
|
|
||||||
if (lastColumn == column) bindData(column)
|
if (lastColumn == column) bindData(column)
|
||||||
|
true
|
||||||
}
|
}
|
||||||
else -> activity.showToast(true, error)
|
else -> {
|
||||||
|
activity.showToast(true, error)
|
||||||
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -496,7 +491,6 @@ internal class ViewHolderHeaderProfile(
|
||||||
|
|
||||||
R.id.btnFollow -> {
|
R.id.btnFollow -> {
|
||||||
activity.followFromAnotherAccount(
|
activity.followFromAnotherAccount(
|
||||||
|
|
||||||
activity.nextPosition(column),
|
activity.nextPosition(column),
|
||||||
accessInfo,
|
accessInfo,
|
||||||
whoRef?.get()
|
whoRef?.get()
|
||||||
|
@ -506,7 +500,6 @@ internal class ViewHolderHeaderProfile(
|
||||||
|
|
||||||
R.id.btnMoved -> {
|
R.id.btnMoved -> {
|
||||||
activity.followFromAnotherAccount(
|
activity.followFromAnotherAccount(
|
||||||
|
|
||||||
activity.nextPosition(column),
|
activity.nextPosition(column),
|
||||||
accessInfo,
|
accessInfo,
|
||||||
movedRef?.get()
|
movedRef?.get()
|
||||||
|
|
|
@ -1,73 +1,28 @@
|
||||||
package jp.juggler.subwaytooter.dialog
|
package jp.juggler.subwaytooter.dialog
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.app.Dialog
|
import android.app.Dialog
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.graphics.BitmapFactory
|
import android.graphics.BitmapFactory
|
||||||
import android.view.View
|
|
||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import jp.juggler.subwaytooter.R
|
|
||||||
import jp.juggler.subwaytooter.api.*
|
import jp.juggler.subwaytooter.api.*
|
||||||
import jp.juggler.subwaytooter.api.entity.TootAttachment
|
import jp.juggler.subwaytooter.api.entity.TootAttachment
|
||||||
import jp.juggler.subwaytooter.view.FocusPointView
|
import jp.juggler.subwaytooter.databinding.DlgFocusPointBinding
|
||||||
import jp.juggler.util.*
|
import jp.juggler.util.*
|
||||||
import jp.juggler.util.coroutine.launchMain
|
|
||||||
import jp.juggler.util.data.*
|
import jp.juggler.util.data.*
|
||||||
import jp.juggler.util.log.*
|
import jp.juggler.util.log.*
|
||||||
import jp.juggler.util.ui.*
|
import jp.juggler.util.ui.*
|
||||||
|
import kotlinx.coroutines.CancellationException
|
||||||
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
|
import kotlin.coroutines.resumeWithException
|
||||||
|
|
||||||
@SuppressLint("InflateParams")
|
private val log = LogCategory("DlgFocusPoint")
|
||||||
class DlgFocusPoint(
|
|
||||||
val activity: AppCompatActivity,
|
|
||||||
val attachment: TootAttachment,
|
|
||||||
) : View.OnClickListener {
|
|
||||||
|
|
||||||
companion object {
|
fun decodeAttachmentBitmap(
|
||||||
|
|
||||||
val log = LogCategory("DlgFocusPoint")
|
|
||||||
}
|
|
||||||
|
|
||||||
val dialog: Dialog
|
|
||||||
private val focusPointView: FocusPointView
|
|
||||||
var bitmap: Bitmap? = null
|
|
||||||
|
|
||||||
init {
|
|
||||||
val viewRoot = activity.layoutInflater.inflate(R.layout.dlg_focus_point, null, false)
|
|
||||||
focusPointView = viewRoot.findViewById(R.id.ivFocus)
|
|
||||||
viewRoot.findViewById<View>(R.id.btnClose).setOnClickListener(this)
|
|
||||||
|
|
||||||
this.dialog = Dialog(activity)
|
|
||||||
dialog.setContentView(viewRoot)
|
|
||||||
dialog.setOnDismissListener {
|
|
||||||
bitmap?.recycle()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onClick(v: View) {
|
|
||||||
when (v.id) {
|
|
||||||
R.id.btnClose -> dialog.dismissSafe()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setCallback(callback: (x: Float, y: Float) -> Unit): DlgFocusPoint {
|
|
||||||
focusPointView.callback = callback
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
fun show() {
|
|
||||||
val url = attachment.preview_url
|
|
||||||
if (url == null) {
|
|
||||||
activity.showToast(false, "missing image url")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
val options = BitmapFactory.Options()
|
|
||||||
|
|
||||||
fun decodeBitmap(
|
|
||||||
data: ByteArray,
|
data: ByteArray,
|
||||||
@Suppress("SameParameterValue") pixelMax: Int,
|
@Suppress("SameParameterValue") pixelMax: Int,
|
||||||
): Bitmap? {
|
): Bitmap? {
|
||||||
|
val options = BitmapFactory.Options()
|
||||||
options.inJustDecodeBounds = true
|
options.inJustDecodeBounds = true
|
||||||
options.inScaled = false
|
options.inScaled = false
|
||||||
options.outWidth = 0
|
options.outWidth = 0
|
||||||
|
@ -90,13 +45,22 @@ class DlgFocusPoint(
|
||||||
return BitmapFactory.decodeByteArray(data, 0, data.size, options)
|
return BitmapFactory.decodeByteArray(data, 0, data.size, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
launchMain {
|
suspend fun AppCompatActivity.focusPointDialog(
|
||||||
var resultBitmap: Bitmap? = null
|
attachment: TootAttachment,
|
||||||
val result = activity.runApiTask { client ->
|
callback: (x: Float, y: Float) -> Unit,
|
||||||
|
) {
|
||||||
|
var bitmap: Bitmap? = null
|
||||||
|
try {
|
||||||
|
val url = attachment.preview_url
|
||||||
|
if (url == null) {
|
||||||
|
showToast(false, "missing image url")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val result = runApiTask { client ->
|
||||||
try {
|
try {
|
||||||
val (result, data) = client.getHttpBytes(url)
|
val (result, data) = client.getHttpBytes(url)
|
||||||
data?.let {
|
data?.let {
|
||||||
resultBitmap = decodeBitmap(it, 1024)
|
bitmap = decodeAttachmentBitmap(it, 1024)
|
||||||
?: return@runApiTask TootApiResult("image decode failed.")
|
?: return@runApiTask TootApiResult("image decode failed.")
|
||||||
}
|
}
|
||||||
result
|
result
|
||||||
|
@ -104,26 +68,34 @@ class DlgFocusPoint(
|
||||||
TootApiResult(ex.withCaption("preview loading failed."))
|
TootApiResult(ex.withCaption("preview loading failed."))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val bitmap = resultBitmap
|
result ?: return
|
||||||
when {
|
if (bitmap == null) {
|
||||||
bitmap == null -> {
|
showToast(true, result.error ?: "error")
|
||||||
activity.showToast(true, result?.error ?: "?")
|
return
|
||||||
dialog.dismissSafe()
|
} else if (!isLiveActivity) {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
activity.isFinishing -> {
|
val dialog = Dialog(this)
|
||||||
bitmap.recycle()
|
val views = DlgFocusPointBinding.inflate(layoutInflater)
|
||||||
dialog.dismissSafe()
|
dialog.setContentView(views.root)
|
||||||
}
|
views.ivFocus.setAttachment(attachment, bitmap!!)
|
||||||
else -> {
|
views.ivFocus.callback = callback
|
||||||
this@DlgFocusPoint.bitmap = bitmap
|
|
||||||
focusPointView.setAttachment(attachment, bitmap)
|
|
||||||
dialog.window?.setLayout(
|
dialog.window?.setLayout(
|
||||||
WindowManager.LayoutParams.MATCH_PARENT,
|
WindowManager.LayoutParams.MATCH_PARENT,
|
||||||
WindowManager.LayoutParams.MATCH_PARENT
|
WindowManager.LayoutParams.MATCH_PARENT
|
||||||
)
|
)
|
||||||
|
suspendCancellableCoroutine { cont ->
|
||||||
|
views.btnClose.setOnClickListener {
|
||||||
|
if (cont.isActive) cont.resume(Unit) {}
|
||||||
|
dialog.dismissSafe()
|
||||||
|
}
|
||||||
|
dialog.setOnDismissListener {
|
||||||
|
if (cont.isActive) cont.resumeWithException(CancellationException())
|
||||||
|
}
|
||||||
|
cont.invokeOnCancellation { dialog.dismissSafe() }
|
||||||
dialog.show()
|
dialog.show()
|
||||||
}
|
}
|
||||||
}
|
} finally {
|
||||||
}
|
bitmap?.recycle()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ import jp.juggler.subwaytooter.util.NetworkEmojiInvalidator
|
||||||
import jp.juggler.subwaytooter.view.MyListView
|
import jp.juggler.subwaytooter.view.MyListView
|
||||||
import jp.juggler.subwaytooter.view.MyNetworkImageView
|
import jp.juggler.subwaytooter.view.MyNetworkImageView
|
||||||
import jp.juggler.util.*
|
import jp.juggler.util.*
|
||||||
|
import jp.juggler.util.coroutine.launchAndShowError
|
||||||
import jp.juggler.util.coroutine.launchMain
|
import jp.juggler.util.coroutine.launchMain
|
||||||
import jp.juggler.util.data.*
|
import jp.juggler.util.data.*
|
||||||
import jp.juggler.util.log.*
|
import jp.juggler.util.log.*
|
||||||
|
@ -247,30 +248,28 @@ class DlgListMember(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun openListCreator() {
|
private fun openListCreator() {
|
||||||
DlgTextInput.show(
|
activity.launchAndShowError {
|
||||||
activity,
|
activity.showTextInputDialog(
|
||||||
activity.getString(R.string.list_create),
|
title = activity.getString(R.string.list_create),
|
||||||
null,
|
initialText = null,
|
||||||
callback = object : DlgTextInput.Callback {
|
onEmptyText = {
|
||||||
|
|
||||||
override fun onEmptyError() {
|
|
||||||
activity.showToast(false, R.string.list_name_empty)
|
activity.showToast(false, R.string.list_name_empty)
|
||||||
}
|
},
|
||||||
|
) { text ->
|
||||||
override fun onOK(dialog: Dialog, text: String) {
|
when (val listOwner1 = this@DlgListMember.listOwner) {
|
||||||
val list_owner = this@DlgListMember.listOwner
|
null -> {
|
||||||
|
|
||||||
if (list_owner == null) {
|
|
||||||
activity.showToast(false, "list owner is not selected.")
|
activity.showToast(false, "list owner is not selected.")
|
||||||
return
|
false
|
||||||
}
|
}
|
||||||
|
else -> {
|
||||||
activity.listCreate(list_owner, text) {
|
activity.listCreate(listOwner1, text) {
|
||||||
dialog.dismissSafe()
|
|
||||||
loadLists()
|
loadLists()
|
||||||
}
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class ErrorItem(val message: String)
|
internal class ErrorItem(val message: String)
|
||||||
|
|
|
@ -1,67 +1,69 @@
|
||||||
package jp.juggler.subwaytooter.dialog
|
package jp.juggler.subwaytooter.dialog
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.app.Activity
|
|
||||||
import android.app.Dialog
|
import android.app.Dialog
|
||||||
import android.view.View
|
import android.graphics.Bitmap
|
||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
import android.view.inputmethod.EditorInfo
|
import android.view.inputmethod.EditorInfo
|
||||||
import android.widget.EditText
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import android.widget.TextView
|
import jp.juggler.subwaytooter.databinding.DlgTextInputBinding
|
||||||
|
import jp.juggler.util.coroutine.launchAndShowError
|
||||||
|
import jp.juggler.util.data.notEmpty
|
||||||
|
import jp.juggler.util.ui.dismissSafe
|
||||||
|
import jp.juggler.util.ui.visible
|
||||||
|
import kotlinx.coroutines.CancellationException
|
||||||
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
|
import kotlin.coroutines.resumeWithException
|
||||||
|
|
||||||
import jp.juggler.subwaytooter.R
|
suspend fun AppCompatActivity.showTextInputDialog(
|
||||||
|
title: CharSequence,
|
||||||
object DlgTextInput {
|
|
||||||
|
|
||||||
interface Callback {
|
|
||||||
fun onOK(dialog: Dialog, text: String)
|
|
||||||
|
|
||||||
fun onEmptyError()
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("InflateParams")
|
|
||||||
fun show(
|
|
||||||
activity: Activity,
|
|
||||||
caption: CharSequence,
|
|
||||||
initialText: CharSequence?,
|
initialText: CharSequence?,
|
||||||
allowEmpty: Boolean = false,
|
allowEmpty: Boolean = false,
|
||||||
callback: Callback,
|
inputType: Int? = null,
|
||||||
|
bitmap: Bitmap? = null,
|
||||||
|
onEmptyText: suspend () -> Unit,
|
||||||
|
// returns true if we can close dialog
|
||||||
|
onOk: suspend (String) -> Boolean,
|
||||||
) {
|
) {
|
||||||
val view = activity.layoutInflater.inflate(R.layout.dlg_text_input, null, false)
|
val views = DlgTextInputBinding.inflate(layoutInflater)
|
||||||
val etInput = view.findViewById<EditText>(R.id.etInput)
|
views.tvCaption.text = title
|
||||||
val btnOk = view.findViewById<View>(R.id.btnOk)
|
initialText?.notEmpty()?.let {
|
||||||
val tvCaption = view.findViewById<TextView>(R.id.tvCaption)
|
views.etInput.setText(it)
|
||||||
|
views.etInput.setSelection(it.length)
|
||||||
tvCaption.text = caption
|
|
||||||
if (initialText != null && initialText.isNotEmpty()) {
|
|
||||||
etInput.setText(initialText)
|
|
||||||
etInput.setSelection(initialText.length)
|
|
||||||
}
|
}
|
||||||
|
// views.llInput.maxHeight = (100f * resources.displayMetrics.density + 0.5f).toInt()
|
||||||
etInput.setOnEditorActionListener { _, actionId, _ ->
|
inputType?.let { views.etInput.inputType = it }
|
||||||
|
bitmap?.let { views.ivBitmap.visible().setImageBitmap(it) }
|
||||||
|
views.etInput.setOnEditorActionListener { _, actionId, _ ->
|
||||||
if (actionId == EditorInfo.IME_ACTION_DONE) {
|
if (actionId == EditorInfo.IME_ACTION_DONE) {
|
||||||
btnOk.performClick()
|
views.btnOk.performClick()
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
val dialog = Dialog(this)
|
||||||
val dialog = Dialog(activity)
|
dialog.setContentView(views.root)
|
||||||
dialog.setContentView(view)
|
dialog.window?.setLayout(
|
||||||
btnOk.setOnClickListener {
|
WindowManager.LayoutParams.MATCH_PARENT,
|
||||||
val token = etInput.text.toString().trim { it <= ' ' }
|
WindowManager.LayoutParams.WRAP_CONTENT
|
||||||
|
)
|
||||||
if (token.isEmpty() && !allowEmpty) {
|
suspendCancellableCoroutine { cont ->
|
||||||
callback.onEmptyError()
|
views.btnOk.setOnClickListener {
|
||||||
} else {
|
launchAndShowError {
|
||||||
callback.onOK(dialog, token)
|
val text = views.etInput.text.toString().trim { it <= ' ' }
|
||||||
|
if (text.isEmpty() && !allowEmpty) {
|
||||||
|
onEmptyText()
|
||||||
|
} else if (onOk(text)) {
|
||||||
|
if (cont.isActive) cont.resume(Unit) {}
|
||||||
|
dialog.dismissSafe()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
view.findViewById<View>(R.id.btnCancel).setOnClickListener { dialog.cancel() }
|
views.btnCancel.setOnClickListener { dialog.cancel() }
|
||||||
|
dialog.setOnDismissListener {
|
||||||
dialog.window?.setLayout(WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.WRAP_CONTENT)
|
if (cont.isActive) cont.resumeWithException(CancellationException())
|
||||||
|
}
|
||||||
|
cont.invokeOnCancellation { dialog.dismissSafe() }
|
||||||
dialog.show()
|
dialog.show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,29 +1,56 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<jp.juggler.subwaytooter.view.MaxHeightScrollView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:fadeScrollbars="false"
|
||||||
|
android:scrollbarStyle="outsideOverlay"
|
||||||
|
app:maxHeight="240dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingHorizontal="12dp"
|
||||||
|
android:paddingVertical="6dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/ivBitmap"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="100dp"
|
||||||
|
android:layout_marginBottom="4dp"
|
||||||
|
android:importantForAccessibility="no"
|
||||||
|
android:scaleType="fitCenter"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:src="@drawable/ic_face"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/tvCaption"
|
android:id="@+id/tvCaption"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="12dp"
|
|
||||||
android:layout_marginTop="12dp"
|
|
||||||
android:layout_marginEnd="12dp"
|
|
||||||
android:labelFor="@+id/etInput"
|
android:labelFor="@+id/etInput"
|
||||||
tools:ignore="LabelFor" />
|
tools:ignore="LabelFor"
|
||||||
|
tools:text="title title title title title title title title title title title title title title title title title title title title title title title title title title title title title title title title title title title " />
|
||||||
|
|
||||||
<EditText
|
<EditText
|
||||||
android:id="@+id/etInput"
|
android:id="@+id/etInput"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="12dp"
|
|
||||||
android:layout_marginEnd="12dp"
|
|
||||||
android:imeOptions="actionDone"
|
android:imeOptions="actionDone"
|
||||||
android:importantForAutofill="no"
|
android:importantForAutofill="no"
|
||||||
android:inputType="text" />
|
android:inputType="text"
|
||||||
|
tools:inputType="textMultiLine"
|
||||||
|
tools:text="text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text " />
|
||||||
|
</LinearLayout>
|
||||||
|
</jp.juggler.subwaytooter.view.MaxHeightScrollView>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
style="?android:attr/buttonBarStyle"
|
style="?android:attr/buttonBarStyle"
|
||||||
|
|
Loading…
Reference in New Issue