From 1bdb6bf340074b721ebf3a294c7ce72c965833f6 Mon Sep 17 00:00:00 2001 From: Douile <25043847+Douile@users.noreply.github.com> Date: Fri, 12 Aug 2022 15:35:27 +0100 Subject: [PATCH] Add drag to re-arrange support for quick chat actions --- .../database/QuickChatActionRepository.kt | 10 +++-- .../mesh/database/dao/QuickChatActionDao.kt | 39 ++++++++++++++++--- .../mesh/database/entity/QuickChatAction.kt | 6 ++- .../java/com/geeksville/mesh/model/UIState.kt | 13 +++++-- .../geeksville/mesh/ui/DragManageAdapter.kt | 24 ++++++++++++ .../geeksville/mesh/ui/MessagesFragment.kt | 1 + .../mesh/ui/QuickChatActionAdapter.kt | 7 +++- .../mesh/ui/QuickChatSettingsFragment.kt | 6 +++ 8 files changed, 92 insertions(+), 14 deletions(-) create mode 100644 app/src/main/java/com/geeksville/mesh/ui/DragManageAdapter.kt diff --git a/app/src/main/java/com/geeksville/mesh/database/QuickChatActionRepository.kt b/app/src/main/java/com/geeksville/mesh/database/QuickChatActionRepository.kt index b95098175..0dcb7106c 100644 --- a/app/src/main/java/com/geeksville/mesh/database/QuickChatActionRepository.kt +++ b/app/src/main/java/com/geeksville/mesh/database/QuickChatActionRepository.kt @@ -24,11 +24,15 @@ class QuickChatActionRepository @Inject constructor(private val quickChatDaoLazy quickChatActionDao.deleteAll() } - suspend fun delete(uuid: Long) = withContext(Dispatchers.IO) { - quickChatActionDao.delete(uuid) + suspend fun delete(action: QuickChatAction) = withContext(Dispatchers.IO) { + quickChatActionDao.delete(action) } - suspend fun update(action:QuickChatAction) = withContext(Dispatchers.IO) { + suspend fun update(action: QuickChatAction) = withContext(Dispatchers.IO) { quickChatActionDao.update(action) } + + suspend fun moveAction(action: QuickChatAction, newPos: Int) = withContext(Dispatchers.IO) { + quickChatActionDao.moveAction(action, newPos) + } } \ No newline at end of file diff --git a/app/src/main/java/com/geeksville/mesh/database/dao/QuickChatActionDao.kt b/app/src/main/java/com/geeksville/mesh/database/dao/QuickChatActionDao.kt index 138b1131b..ef3f97107 100644 --- a/app/src/main/java/com/geeksville/mesh/database/dao/QuickChatActionDao.kt +++ b/app/src/main/java/com/geeksville/mesh/database/dao/QuickChatActionDao.kt @@ -1,9 +1,6 @@ package com.geeksville.mesh.database.dao -import androidx.room.Dao -import androidx.room.Insert -import androidx.room.Query -import androidx.room.Update +import androidx.room.* import com.geeksville.mesh.database.entity.QuickChatAction import kotlinx.coroutines.flow.Flow @@ -20,9 +17,41 @@ interface QuickChatActionDao { fun deleteAll() @Query("Delete from quick_chat where uuid=:uuid") - fun delete(uuid: Long) + fun _delete(uuid: Long) + + @Transaction + fun delete(action: QuickChatAction) { + _delete(action.uuid) + decrementPositionsAfter(action.position) + } @Update fun update(action: QuickChatAction) + @Query("Update quick_chat set position=:position WHERE uuid=:uuid") + fun updateActionPosition(uuid: Long, position: Int) + + @Query("Update quick_chat set position=position+1 where position>=:startInclusive and position<:endExclusive") + fun incrementPositionsBetween(startInclusive: Int, endExclusive: Int) + + @Query("Update quick_chat SET position=position-1 where position>:startExclusive and position<=:endInclusive") + fun decrementPositionsBetween(startExclusive: Int, endInclusive: Int) + + @Query("Update quick_chat set position=position-1 where position>=:position") + fun decrementPositionsAfter(position: Int) + + @Transaction + fun moveAction(action: QuickChatAction, newPos: Int) { + // FIXME: Check newPos is valid + if (newPos < action.position) { + incrementPositionsBetween(newPos, action.position) + updateActionPosition(action.uuid, newPos) + } else if (newPos > action.position) { + decrementPositionsBetween(action.position, newPos) + updateActionPosition(action.uuid, newPos) + } else { + // Do nothing: moving to same position + } + } + } \ No newline at end of file diff --git a/app/src/main/java/com/geeksville/mesh/database/entity/QuickChatAction.kt b/app/src/main/java/com/geeksville/mesh/database/entity/QuickChatAction.kt index 26da5ecb9..deeb2d45c 100644 --- a/app/src/main/java/com/geeksville/mesh/database/entity/QuickChatAction.kt +++ b/app/src/main/java/com/geeksville/mesh/database/entity/QuickChatAction.kt @@ -2,14 +2,16 @@ package com.geeksville.mesh.database.entity import androidx.room.ColumnInfo import androidx.room.Entity +import androidx.room.Index import androidx.room.PrimaryKey -@Entity(tableName = "quick_chat") +@Entity(tableName = "quick_chat", indices = [Index(value=["position"], unique=true)]) data class QuickChatAction( @PrimaryKey(autoGenerate = true) val uuid: Long, @ColumnInfo(name="name") val name: String, @ColumnInfo(name="message") val message: String, - @ColumnInfo(name="mode") val mode: Mode) { + @ColumnInfo(name="mode") val mode: Mode, + @ColumnInfo(name="position") val position: Int) { enum class Mode { Append, Instant, diff --git a/app/src/main/java/com/geeksville/mesh/model/UIState.kt b/app/src/main/java/com/geeksville/mesh/model/UIState.kt index 10ae8200d..df9476d5b 100644 --- a/app/src/main/java/com/geeksville/mesh/model/UIState.kt +++ b/app/src/main/java/com/geeksville/mesh/model/UIState.kt @@ -461,14 +461,14 @@ class UIViewModel @Inject constructor( fun addQuickChatAction(name: String, value: String, mode: QuickChatAction.Mode) { viewModelScope.launch(Dispatchers.Main) { - val action = QuickChatAction(0, name, value, mode) + val action = QuickChatAction(0, name, value, mode, _quickChatActions.value.size) quickChatActionRepository.insert(action) } } fun deleteQuickChatAction(action: QuickChatAction) { viewModelScope.launch(Dispatchers.Main) { - quickChatActionRepository.delete(action.uuid) + quickChatActionRepository.delete(action) } } @@ -483,10 +483,17 @@ class UIViewModel @Inject constructor( action.uuid, name ?: action.name, message ?: action.message, - mode ?: action.mode + mode ?: action.mode, + action.position ) quickChatActionRepository.update(newAction) } } + + fun moveQuickChatAction(action: QuickChatAction, newPos: Int) { + viewModelScope.launch(Dispatchers.Main) { + quickChatActionRepository.moveAction(action, newPos) + } + } } diff --git a/app/src/main/java/com/geeksville/mesh/ui/DragManageAdapter.kt b/app/src/main/java/com/geeksville/mesh/ui/DragManageAdapter.kt new file mode 100644 index 000000000..1d24a9e9e --- /dev/null +++ b/app/src/main/java/com/geeksville/mesh/ui/DragManageAdapter.kt @@ -0,0 +1,24 @@ +package com.geeksville.mesh.ui + +import android.content.Context +import androidx.recyclerview.widget.ItemTouchHelper +import androidx.recyclerview.widget.RecyclerView + +class DragManageAdapter(var adapter: SwapAdapter, context: Context, dragDirs: Int, swipeDirs: Int) : ItemTouchHelper.SimpleCallback(dragDirs, swipeDirs) { + interface SwapAdapter { + fun swapItems(fromPosition: Int, toPosition: Int) + } + + override fun onMove( + recyclerView: RecyclerView, + viewHolder: RecyclerView.ViewHolder, + target: RecyclerView.ViewHolder + ): Boolean { + adapter.swapItems(viewHolder.absoluteAdapterPosition, target.absoluteAdapterPosition) + TODO("Not yet implemented") + } + + override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) { + TODO("Not yet implemented") + } +} \ No newline at end of file diff --git a/app/src/main/java/com/geeksville/mesh/ui/MessagesFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/MessagesFragment.kt index 084d33a71..fb44c3a85 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/MessagesFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/MessagesFragment.kt @@ -307,6 +307,7 @@ class MessagesFragment : Fragment(), Logging { model.quickChatActions.asLiveData().observe(viewLifecycleOwner) { actions -> actions?.let { + binding.quickChatLayout.removeAllViews() for (action in actions) { val button = Button(context) button.setText(action.name) diff --git a/app/src/main/java/com/geeksville/mesh/ui/QuickChatActionAdapter.kt b/app/src/main/java/com/geeksville/mesh/ui/QuickChatActionAdapter.kt index e4061f812..070a723e1 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/QuickChatActionAdapter.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/QuickChatActionAdapter.kt @@ -13,7 +13,7 @@ import com.geeksville.mesh.database.entity.QuickChatAction class QuickChatActionAdapter internal constructor( context: Context, private val onEdit: (action: QuickChatAction) -> Unit -) : RecyclerView.Adapter() { +) : RecyclerView.Adapter(), DragManageAdapter.SwapAdapter { private val inflater: LayoutInflater = LayoutInflater.from(context) private var actions = emptyList() @@ -50,4 +50,9 @@ class QuickChatActionAdapter internal constructor( override fun getItemCount() = actions.size + override fun swapItems(fromPosition: Int, toPosition: Int) { + // TODO: Update data + notifyItemMoved(fromPosition, toPosition) + } + } \ No newline at end of file diff --git a/app/src/main/java/com/geeksville/mesh/ui/QuickChatSettingsFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/QuickChatSettingsFragment.kt index bcb7a3a19..f0f952f2f 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/QuickChatSettingsFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/QuickChatSettingsFragment.kt @@ -9,6 +9,7 @@ import android.widget.EditText import androidx.core.widget.addTextChangedListener import androidx.fragment.app.activityViewModels import androidx.lifecycle.asLiveData +import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.LinearLayoutManager import com.geeksville.android.Logging import com.geeksville.mesh.R @@ -84,11 +85,16 @@ class QuickChatSettingsFragment : ScreenFragment("Quick Chat settings"), Logging dialog.show() } + val dragCallback = DragManageAdapter(quickChatActionAdapter, requireContext(), ItemTouchHelper.UP or ItemTouchHelper.DOWN, 0) + val helper = ItemTouchHelper(dragCallback) + binding.quickChatSettingsView.apply { this.layoutManager = LinearLayoutManager(requireContext()) this.adapter = quickChatActionAdapter + helper.attachToRecyclerView(this) } + model.quickChatActions.asLiveData().observe(viewLifecycleOwner) { actions -> actions?.let { quickChatActionAdapter.setActions(actions) } }