239 lines
8.3 KiB
Kotlin
239 lines
8.3 KiB
Kotlin
package jp.juggler.subwaytooter.dialog
|
|
|
|
import android.annotation.SuppressLint
|
|
import android.app.Dialog
|
|
import android.view.LayoutInflater
|
|
import android.view.MotionEvent
|
|
import android.view.ViewGroup
|
|
import androidx.appcompat.app.AppCompatActivity
|
|
import androidx.recyclerview.widget.ItemTouchHelper
|
|
import androidx.recyclerview.widget.LinearLayoutManager
|
|
import androidx.recyclerview.widget.RecyclerView
|
|
import com.bumptech.glide.Glide
|
|
import jp.juggler.subwaytooter.R
|
|
import jp.juggler.subwaytooter.databinding.AttachmentRearrangeDialogBinding
|
|
import jp.juggler.subwaytooter.databinding.AttachmentsRearrangeItemBinding
|
|
import jp.juggler.subwaytooter.defaultColorIcon
|
|
import jp.juggler.subwaytooter.util.PostAttachment
|
|
import jp.juggler.util.data.ellipsizeDot3
|
|
import jp.juggler.util.log.LogCategory
|
|
import jp.juggler.util.ui.attrColor
|
|
import jp.juggler.util.ui.dismissSafe
|
|
import jp.juggler.util.ui.dp
|
|
import kotlinx.coroutines.CancellationException
|
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
|
import org.jetbrains.anko.backgroundColor
|
|
import kotlin.coroutines.resumeWithException
|
|
|
|
private val log = LogCategory("DlgAttachmentRearrange")
|
|
|
|
/**
|
|
* 投稿画面で添付メディアを並べ替えるダイアログを開き、OKボタンが押されるまで非同期待機する。
|
|
* OK以外の方法で閉じたらCancellationExceptionを投げる。
|
|
*/
|
|
suspend fun AppCompatActivity.dialogArrachmentRearrange(
|
|
initialList: List<PostAttachment>,
|
|
): List<PostAttachment> = suspendCancellableCoroutine { cont ->
|
|
val views = AttachmentRearrangeDialogBinding.inflate(layoutInflater)
|
|
val dialog = Dialog(this).apply {
|
|
setContentView(views.root)
|
|
setOnDismissListener {
|
|
if (cont.isActive) cont.resumeWithException(CancellationException())
|
|
}
|
|
}
|
|
|
|
cont.invokeOnCancellation { dialog.dismissSafe() }
|
|
|
|
val myAdapter = RearrangeAdapter(layoutInflater, initialList)
|
|
|
|
views.btnCancel.setOnClickListener {
|
|
dialog.dismissSafe()
|
|
}
|
|
|
|
views.btnOk.setOnClickListener {
|
|
if (cont.isActive) cont.resume(myAdapter.list) {}
|
|
dialog.dismissSafe()
|
|
}
|
|
|
|
views.listView.apply {
|
|
layoutManager = LinearLayoutManager(context)
|
|
adapter = myAdapter
|
|
myAdapter.itemTouchHelper.attachToRecyclerView(this)
|
|
}
|
|
|
|
dialog.window?.setLayout(dp(300), dp(440))
|
|
dialog.show()
|
|
}
|
|
|
|
/**
|
|
* 並べ替えダイアログ内部のRecyclerViewに使うAdapter
|
|
*/
|
|
private class RearrangeAdapter(
|
|
private val inflater: LayoutInflater,
|
|
initialList: List<PostAttachment>,
|
|
) : RecyclerView.Adapter<RearrangeAdapter.MyViewHolder>(), MyDragCallback.Changer {
|
|
|
|
val list = ArrayList(initialList)
|
|
|
|
private var lastStateViewHolder: MyViewHolder? = null
|
|
private var draggingItem: PostAttachment? = null
|
|
|
|
val itemTouchHelper = ItemTouchHelper(MyDragCallback(this))
|
|
|
|
override fun getItemCount() = list.size
|
|
|
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
|
|
MyViewHolder(parent)
|
|
|
|
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
|
|
holder.bind(list.elementAtOrNull(position))
|
|
}
|
|
|
|
// implements MyDragCallback.Changer
|
|
override fun onMove(posFrom: Int, posTo: Int): Boolean {
|
|
val item = list.removeAt(posFrom)
|
|
list.add(posTo, item)
|
|
notifyItemMoved(posFrom, posTo)
|
|
return true
|
|
}
|
|
|
|
// implements MyDragCallback.Changer
|
|
override fun onState(
|
|
caller: String,
|
|
viewHolder: RecyclerView.ViewHolder?,
|
|
actionState: Int,
|
|
) {
|
|
log.d("onState: caller=$caller, viewHolder=$viewHolder, actionState=$actionState")
|
|
|
|
val holder = (viewHolder as? MyViewHolder)
|
|
// 最後にドラッグ対象となったViewHolderを覚えておく
|
|
holder?.let { lastStateViewHolder = it }
|
|
// 現在ドラッグ対象のPostAttachmentを覚えておく
|
|
val pa = holder?.lastItem
|
|
draggingItem = when {
|
|
pa != null && actionState == ItemTouchHelper.ACTION_STATE_DRAG -> pa
|
|
else -> null
|
|
}
|
|
// 表示の更新
|
|
holder?.bind()
|
|
lastStateViewHolder?.takeIf { it != holder }?.bind()
|
|
}
|
|
|
|
private val iconPlaceHolder = defaultColorIcon(inflater.context, R.drawable.ic_hourglass)
|
|
private val iconError = defaultColorIcon(inflater.context, R.drawable.ic_error)
|
|
private val iconFallback = defaultColorIcon(inflater.context, R.drawable.ic_clip)
|
|
|
|
@SuppressLint("ClickableViewAccessibility")
|
|
inner class MyViewHolder(
|
|
parent: ViewGroup,
|
|
val views: AttachmentsRearrangeItemBinding =
|
|
AttachmentsRearrangeItemBinding.inflate(inflater, parent, false),
|
|
) : RecyclerView.ViewHolder(views.root) {
|
|
|
|
var lastItem: PostAttachment? = null
|
|
|
|
init {
|
|
// リスト項目のタッチですぐにドラッグを開始する
|
|
views.root.setOnTouchListener { _, event ->
|
|
if (event.actionMasked == MotionEvent.ACTION_DOWN) {
|
|
itemTouchHelper.startDrag(this)
|
|
}
|
|
false
|
|
}
|
|
}
|
|
|
|
fun bind(item: PostAttachment? = lastItem) {
|
|
item ?: return
|
|
lastItem = item
|
|
|
|
val context = views.root.context
|
|
|
|
// ドラッグ中は背景色を変える
|
|
views.root.apply {
|
|
when {
|
|
draggingItem === item -> backgroundColor =
|
|
context.attrColor(R.attr.colorSearchFormBackground)
|
|
|
|
else -> background = null
|
|
}
|
|
}
|
|
|
|
// サムネイルのロード開始
|
|
views.ivThumbnail.apply {
|
|
when (val imageUrl = item.attachment?.preview_url) {
|
|
null, "" -> {
|
|
val iconDrawable = when (item.status) {
|
|
PostAttachment.Status.Progress -> iconPlaceHolder
|
|
PostAttachment.Status.Error -> iconError
|
|
else -> iconFallback
|
|
}
|
|
Glide.with(context).clear(this)
|
|
setImageDrawable(iconDrawable)
|
|
}
|
|
|
|
else -> {
|
|
Glide.with(context)
|
|
.load(imageUrl)
|
|
.placeholder(iconPlaceHolder)
|
|
.error(iconError)
|
|
.fallback(iconFallback)
|
|
.into(this)
|
|
}
|
|
}
|
|
}
|
|
|
|
// テキストの表示
|
|
views.tvText.text = item.attachment?.run {
|
|
"${type.id} ${description?.ellipsizeDot3(40) ?: ""}"
|
|
} ?: ""
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* RectclerViewのDrag&Drop操作に関するコールバック
|
|
*/
|
|
private class MyDragCallback(
|
|
private val changer: Changer,
|
|
) : ItemTouchHelper.SimpleCallback(
|
|
ItemTouchHelper.UP or ItemTouchHelper.DOWN,
|
|
0 // no swipe
|
|
) {
|
|
// アダプタに行わせたい処理のinterface
|
|
interface Changer {
|
|
fun onMove(posFrom: Int, posTo: Int): Boolean
|
|
fun onState(caller: String, viewHolder: RecyclerView.ViewHolder?, actionState: Int)
|
|
}
|
|
|
|
override fun isLongPressDragEnabled() = false
|
|
|
|
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) = Unit
|
|
|
|
override fun onMove(
|
|
recyclerView: RecyclerView,
|
|
viewHolder: RecyclerView.ViewHolder,
|
|
target: RecyclerView.ViewHolder,
|
|
): Boolean = changer.onMove(
|
|
// position of drag from
|
|
viewHolder.bindingAdapterPosition,
|
|
// position of drag to
|
|
target.bindingAdapterPosition,
|
|
)
|
|
|
|
override fun onSelectedChanged(
|
|
viewHolder: RecyclerView.ViewHolder?,
|
|
actionState: Int,
|
|
) {
|
|
super.onSelectedChanged(viewHolder, actionState)
|
|
changer.onState("onSelectedChanged", viewHolder, actionState)
|
|
}
|
|
|
|
override fun clearView(
|
|
recyclerView: RecyclerView,
|
|
viewHolder: RecyclerView.ViewHolder,
|
|
) {
|
|
super.clearView(recyclerView, viewHolder)
|
|
changer.onState("clearView", viewHolder, ItemTouchHelper.ACTION_STATE_IDLE)
|
|
}
|
|
}
|