From ed88170c42d6bf0bc764ac179e8fc8e60090eeeb Mon Sep 17 00:00:00 2001 From: Douile <25043847+Douile@users.noreply.github.com> Date: Tue, 9 Aug 2022 15:26:52 +0100 Subject: [PATCH 01/10] Quick chat --- .../java/com/geeksville/mesh/MainActivity.kt | 9 +++++ .../geeksville/mesh/ui/MessagesFragment.kt | 14 ++++++-- .../mesh/ui/QuickChatSettingsFragment.kt | 35 +++++++++++++++++++ app/src/main/res/layout/messages_fragment.xml | 23 ++++++++++-- .../layout/quick_chat_settings_fragment.xml | 6 ++++ app/src/main/res/menu/menu_main.xml | 4 +++ 6 files changed, 86 insertions(+), 5 deletions(-) create mode 100644 app/src/main/java/com/geeksville/mesh/ui/QuickChatSettingsFragment.kt create mode 100644 app/src/main/res/layout/quick_chat_settings_fragment.xml diff --git a/app/src/main/java/com/geeksville/mesh/MainActivity.kt b/app/src/main/java/com/geeksville/mesh/MainActivity.kt index 14420a6bb..3f3f462a1 100644 --- a/app/src/main/java/com/geeksville/mesh/MainActivity.kt +++ b/app/src/main/java/com/geeksville/mesh/MainActivity.kt @@ -1070,6 +1070,15 @@ class MainActivity : BaseActivity(), Logging, chooseMapStyle() return true } + R.id.prefernces_quick_chat -> { + val fragmentManager: FragmentManager = supportFragmentManager + val fragmentTransaction: FragmentTransaction = fragmentManager.beginTransaction() + val nameFragment = QuickChatSettingsFragment() + fragmentTransaction.add(R.id.mainActivityLayout, nameFragment) + fragmentTransaction.addToBackStack(null) + fragmentTransaction.commit() + return true + } else -> super.onOptionsItemSelected(item) } } 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 e925032ff..f6a54908b 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/MessagesFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/MessagesFragment.kt @@ -6,9 +6,7 @@ import android.os.Bundle import android.text.InputType import android.view.* import android.view.inputmethod.EditorInfo -import android.widget.EditText -import android.widget.ImageView -import android.widget.TextView +import android.widget.* import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.view.ActionMode import androidx.cardview.widget.CardView @@ -297,6 +295,16 @@ class MessagesFragment : Fragment(), Logging { binding.textInputLayout.isEnabled = connected binding.sendButton.isEnabled = connected } + + val test = Button(context) + test.setLayoutParams(LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT)) + test.setText("RGR") + test.setOnClickListener { + //model.messagesState.sendMessage("Roger", contactId) + binding.messageInputText.setText(binding.messageInputText.text?.append(" Roger")) + binding.messageInputText.setSelection(binding.messageInputText.text?.length ?: 0) + } + binding.quickChatLayout.addView(test) } private inner class ActionModeCallback : ActionMode.Callback { diff --git a/app/src/main/java/com/geeksville/mesh/ui/QuickChatSettingsFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/QuickChatSettingsFragment.kt new file mode 100644 index 000000000..b3db127d3 --- /dev/null +++ b/app/src/main/java/com/geeksville/mesh/ui/QuickChatSettingsFragment.kt @@ -0,0 +1,35 @@ +package com.geeksville.mesh.ui + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.activityViewModels +import com.geeksville.android.Logging +import com.geeksville.mesh.databinding.AdvancedSettingsBinding +import com.geeksville.mesh.databinding.QuickChatSettingsFragmentBinding +import com.geeksville.mesh.model.UIViewModel +import dagger.hilt.android.AndroidEntryPoint + +@AndroidEntryPoint +class QuickChatSettingsFragment : ScreenFragment("Quick Chat settings"), Logging { + private var _binding: QuickChatSettingsFragmentBinding? = null + + private val binding get() = _binding!! + + private val model: UIViewModel by activityViewModels() + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + _binding = QuickChatSettingsFragmentBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + // TODO + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/messages_fragment.xml b/app/src/main/res/layout/messages_fragment.xml index 5cbbd5b92..f4e615d17 100644 --- a/app/src/main/res/layout/messages_fragment.xml +++ b/app/src/main/res/layout/messages_fragment.xml @@ -1,6 +1,7 @@ @@ -37,10 +38,28 @@ android:layout_height="0dp" android:layout_margin="8dp" android:contentDescription="@string/text_messages" - app:layout_constraintBottom_toTopOf="@+id/textInputLayout" + app:layout_constraintBottom_toTopOf="@+id/quickChatView" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@id/toolbar" /> + app:layout_constraintTop_toBottomOf="@id/toolbar" > + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/menu_main.xml b/app/src/main/res/menu/menu_main.xml index 748e1b9bf..eb598d76c 100644 --- a/app/src/main/res/menu/menu_main.xml +++ b/app/src/main/res/menu/menu_main.xml @@ -36,6 +36,10 @@ android:id="@+id/preferences_map_style" android:title="@string/preferences_map_style" app:showAsAction="withText" /> + Date: Wed, 10 Aug 2022 17:26:15 +0100 Subject: [PATCH 02/10] Fix typo in quick chat dropdown id --- app/src/main/java/com/geeksville/mesh/MainActivity.kt | 2 +- app/src/main/res/menu/menu_main.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/geeksville/mesh/MainActivity.kt b/app/src/main/java/com/geeksville/mesh/MainActivity.kt index 3f3f462a1..b239af13c 100644 --- a/app/src/main/java/com/geeksville/mesh/MainActivity.kt +++ b/app/src/main/java/com/geeksville/mesh/MainActivity.kt @@ -1070,7 +1070,7 @@ class MainActivity : BaseActivity(), Logging, chooseMapStyle() return true } - R.id.prefernces_quick_chat -> { + R.id.preferences_quick_chat -> { val fragmentManager: FragmentManager = supportFragmentManager val fragmentTransaction: FragmentTransaction = fragmentManager.beginTransaction() val nameFragment = QuickChatSettingsFragment() diff --git a/app/src/main/res/menu/menu_main.xml b/app/src/main/res/menu/menu_main.xml index eb598d76c..042f6b7b5 100644 --- a/app/src/main/res/menu/menu_main.xml +++ b/app/src/main/res/menu/menu_main.xml @@ -37,7 +37,7 @@ android:title="@string/preferences_map_style" app:showAsAction="withText" /> Date: Wed, 10 Aug 2022 17:29:17 +0100 Subject: [PATCH 03/10] Add QuickChatAction model --- .../java/com/geeksville/mesh/model/QuickChatAction.kt | 11 +++++++++++ .../main/java/com/geeksville/mesh/model/UIState.kt | 7 +++++++ 2 files changed, 18 insertions(+) create mode 100644 app/src/main/java/com/geeksville/mesh/model/QuickChatAction.kt diff --git a/app/src/main/java/com/geeksville/mesh/model/QuickChatAction.kt b/app/src/main/java/com/geeksville/mesh/model/QuickChatAction.kt new file mode 100644 index 000000000..96e251512 --- /dev/null +++ b/app/src/main/java/com/geeksville/mesh/model/QuickChatAction.kt @@ -0,0 +1,11 @@ +package com.geeksville.mesh.model + +data class QuickChatAction( + val name: String, + val message: String, + val mode: Mode) { + 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 a0c620751..d513efd43 100644 --- a/app/src/main/java/com/geeksville/mesh/model/UIState.kt +++ b/app/src/main/java/com/geeksville/mesh/model/UIState.kt @@ -445,5 +445,12 @@ class UIViewModel @Inject constructor( } } + private val _quickChatActions = mutableListOf() + val quickChatActions: List get() = _quickChatActions + + fun addQuickChatAction(name: String, value: String, mode: QuickChatAction.Mode) { + val action = QuickChatAction(name, value, mode) + _quickChatActions.add(action) + } } From 483eca94e948dd3c72a1120ae6db2d06ff5b1839 Mon Sep 17 00:00:00 2001 From: Douile <25043847+Douile@users.noreply.github.com> Date: Wed, 10 Aug 2022 17:31:16 +0100 Subject: [PATCH 04/10] Use QuickChatAction model to add buttons in message fragment Also fix replace the vertical scroll view with horizontal --- .../geeksville/mesh/ui/MessagesFragment.kt | 32 ++++++++++++++----- app/src/main/res/layout/messages_fragment.xml | 8 +++-- 2 files changed, 29 insertions(+), 11 deletions(-) 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 f6a54908b..c51d48c31 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/MessagesFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/MessagesFragment.kt @@ -22,6 +22,7 @@ import com.geeksville.mesh.MessageStatus import com.geeksville.mesh.R import com.geeksville.mesh.databinding.AdapterMessageLayoutBinding import com.geeksville.mesh.databinding.MessagesFragmentBinding +import com.geeksville.mesh.model.QuickChatAction import com.geeksville.mesh.model.UIViewModel import com.geeksville.mesh.service.MeshService import com.google.android.material.chip.Chip @@ -296,15 +297,30 @@ class MessagesFragment : Fragment(), Logging { binding.sendButton.isEnabled = connected } - val test = Button(context) - test.setLayoutParams(LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT)) - test.setText("RGR") - test.setOnClickListener { - //model.messagesState.sendMessage("Roger", contactId) - binding.messageInputText.setText(binding.messageInputText.text?.append(" Roger")) - binding.messageInputText.setSelection(binding.messageInputText.text?.length ?: 0) + for (action in model.quickChatActions) { + val button = Button(context) + button.setText(action.name) + if (action.mode == QuickChatAction.Mode.Instant) { + button.backgroundTintList = ContextCompat.getColorStateList(requireActivity(), R.color.colorMyMsg) + + } + button.setOnClickListener { + if (action.mode == QuickChatAction.Mode.Append) { + val originalText = binding.messageInputText.text ?: "" + val needsSpace = !originalText.endsWith(' ') && originalText.isNotEmpty() + val newText = buildString { + append(originalText) + if (needsSpace) append(' ') + append(action.message) + } + binding.messageInputText.setText(newText) + binding.messageInputText.setSelection(newText.length) + } else { + model.messagesState.sendMessage(action.message, contactId) + } + } + binding.quickChatLayout.addView(button) } - binding.quickChatLayout.addView(test) } private inner class ActionModeCallback : ActionMode.Callback { diff --git a/app/src/main/res/layout/messages_fragment.xml b/app/src/main/res/layout/messages_fragment.xml index f4e615d17..db6c22f67 100644 --- a/app/src/main/res/layout/messages_fragment.xml +++ b/app/src/main/res/layout/messages_fragment.xml @@ -45,11 +45,13 @@ - @@ -59,7 +61,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" /> - + Date: Wed, 10 Aug 2022 17:34:18 +0100 Subject: [PATCH 05/10] Add basic quick chat action settings --- .../mesh/ui/QuickChatActionAdapter.kt | 53 +++++++++ .../mesh/ui/QuickChatSettingsFragment.kt | 103 +++++++++++++++++- .../drawable/ic_baseline_drag_handle_24.xml | 5 + .../main/res/drawable/ic_baseline_edit_24.xml | 5 + .../adapter_quick_chat_action_layout.xml | 62 +++++++++++ .../main/res/layout/dialog_add_quick_chat.xml | 35 ++++++ .../layout/quick_chat_settings_fragment.xml | 46 +++++++- app/src/main/res/values/strings.xml | 3 + 8 files changed, 309 insertions(+), 3 deletions(-) create mode 100644 app/src/main/java/com/geeksville/mesh/ui/QuickChatActionAdapter.kt create mode 100644 app/src/main/res/drawable/ic_baseline_drag_handle_24.xml create mode 100644 app/src/main/res/drawable/ic_baseline_edit_24.xml create mode 100644 app/src/main/res/layout/adapter_quick_chat_action_layout.xml create mode 100644 app/src/main/res/layout/dialog_add_quick_chat.xml diff --git a/app/src/main/java/com/geeksville/mesh/ui/QuickChatActionAdapter.kt b/app/src/main/java/com/geeksville/mesh/ui/QuickChatActionAdapter.kt new file mode 100644 index 000000000..d774e31f1 --- /dev/null +++ b/app/src/main/java/com/geeksville/mesh/ui/QuickChatActionAdapter.kt @@ -0,0 +1,53 @@ +package com.geeksville.mesh.ui + +import android.content.Context +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import com.geeksville.mesh.R +import com.geeksville.mesh.model.QuickChatAction + +class QuickChatActionAdapter internal constructor( + context: Context, + private val onEdit: (action: QuickChatAction) -> Unit +) : RecyclerView.Adapter() { + + private val inflater: LayoutInflater = LayoutInflater.from(context) + private var actions = emptyList() + private val TAG = "QuickChatAdapter" + + inner class ActionViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + val actionName: TextView = itemView.findViewById(R.id.quickChatActionName) + val actionValue: TextView = itemView.findViewById(R.id.quickChatActionValue) + val actionEdit: View = itemView.findViewById(R.id.quickChatActionEdit) + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ActionViewHolder { + val itemView = inflater.inflate(R.layout.adapter_quick_chat_action_layout, parent, false) + Log.d(TAG, "Created view holder") + return ActionViewHolder(itemView) + } + + override fun onBindViewHolder(holder: ActionViewHolder, position: Int) { + val current = actions[position] + holder.actionName.text = current.name + holder.actionValue.text = current.message + holder.actionEdit.setOnClickListener{ + onEdit(current) + } + Log.d(TAG, "Bound actions") + } + + + internal fun setActions(actions: List) { + this.actions = actions + notifyDataSetChanged() + Log.d(TAG, String.format("setActions(size=%d, count=%d)", actions.size, itemCount)) + } + + override fun getItemCount() = actions.size + +} \ 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 b3db127d3..c84ae6beb 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/QuickChatSettingsFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/QuickChatSettingsFragment.kt @@ -30,6 +30,107 @@ class QuickChatSettingsFragment : ScreenFragment("Quick Chat settings"), Logging override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - // TODO + Log.d(TAG, "viewCreated") + + binding.quickChatSettingsCreateButton.setOnClickListener { + Log.d(TAG, "Create quick chat") + + val builder = createEditDialog(requireContext(), "New quick chat") + + builder.builder.setPositiveButton("Add") { view, x -> + + val name = builder.nameInput.text.toString().trim() + val message = builder.messageInput.text.toString() + if (name.isNotEmpty() and message.isNotEmpty()) + model.addQuickChatAction( + name, message, + if (builder.modeSwitch.isChecked) QuickChatAction.Mode.Instant else QuickChatAction.Mode.Append + ) + // TODO + } + builder.builder.setNegativeButton("Cancel") { _, _ -> + // TODO + } + + val dialog = builder.builder.create() + dialog.getButton(0).isEnabled = false + dialog.show() + } + + model.addQuickChatAction("TST", "Test", QuickChatAction.Mode.Append) + val quickChatActionAdapter = + QuickChatActionAdapter(requireContext()) { action: QuickChatAction -> + val builder = createEditDialog(requireContext(), "Edit quick chat") + builder.nameInput.setText(action.name) + builder.messageInput.setText(action.message) + builder.modeSwitch.isChecked = action.mode == QuickChatAction.Mode.Instant + + builder.builder.setNegativeButton(R.string.cancel) { _, _ -> } + builder.builder.setPositiveButton(R.string.save_btn) { _, _ -> + // TODO + } + val dialog = builder.builder.create() + dialog.show() + } + + binding.quickChatSettingsView.apply { + this.layoutManager = LinearLayoutManager(requireContext()) + this.adapter = quickChatActionAdapter + } + + quickChatActionAdapter.setActions(model.quickChatActions) + Log.d(TAG, "viewCreation done") + } + + data class DialogBuilder( + val builder: MaterialAlertDialogBuilder, + val nameInput: EditText, + val messageInput: EditText, + val modeSwitch: SwitchMaterial + ) + + private fun getMessageName(message: String): String { + return if (message.length <= 3) { + message.uppercase() + } else { + buildString { + append(message.first().uppercase()) + append(message[message.length / 2].uppercase()) + append(message.last().uppercase()) + } + } + } + + private fun createEditDialog(context: Context, title: String): DialogBuilder { + val builder = MaterialAlertDialogBuilder(context) + builder.setTitle(title) + + val layout = + LayoutInflater.from(requireContext()).inflate(R.layout.dialog_add_quick_chat, null) + + val nameInput: EditText = layout.findViewById(R.id.addQuickChatName) + val messageInput: EditText = layout.findViewById(R.id.addQuickChatMessage) + val modeSwitch: SwitchMaterial = layout.findViewById(R.id.addQuickChatMode) + + var nameHasChanged = false + + modeSwitch.setOnCheckedChangeListener { _, _ -> + modeSwitch.setText(if (modeSwitch.isChecked) R.string.mode_instant else R.string.mode_append) + } + + messageInput.addTextChangedListener { text -> + if (!nameHasChanged) { + nameInput.setText(getMessageName(text.toString())) + } + } + + nameInput.addTextChangedListener { + if (nameInput.isFocused) nameHasChanged = true + } + + // TODO: Don't enable positive button until there is name and message + builder.setView(layout) + + return DialogBuilder(builder, nameInput, messageInput, modeSwitch) } } \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_baseline_drag_handle_24.xml b/app/src/main/res/drawable/ic_baseline_drag_handle_24.xml new file mode 100644 index 000000000..e13f29fd3 --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_drag_handle_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_baseline_edit_24.xml b/app/src/main/res/drawable/ic_baseline_edit_24.xml new file mode 100644 index 000000000..2cda9c112 --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_edit_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/layout/adapter_quick_chat_action_layout.xml b/app/src/main/res/layout/adapter_quick_chat_action_layout.xml new file mode 100644 index 000000000..e3b265dfe --- /dev/null +++ b/app/src/main/res/layout/adapter_quick_chat_action_layout.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_add_quick_chat.xml b/app/src/main/res/layout/dialog_add_quick_chat.xml new file mode 100644 index 000000000..e4ebd7722 --- /dev/null +++ b/app/src/main/res/layout/dialog_add_quick_chat.xml @@ -0,0 +1,35 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/quick_chat_settings_fragment.xml b/app/src/main/res/layout/quick_chat_settings_fragment.xml index 77d9ef65f..6cffcd915 100644 --- a/app/src/main/res/layout/quick_chat_settings_fragment.xml +++ b/app/src/main/res/layout/quick_chat_settings_fragment.xml @@ -1,6 +1,48 @@ - + android:layout_height="match_parent" + android:background="@color/colorAdvancedBackground"> + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5788505eb..9f76d8b02 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -144,4 +144,7 @@ Resend Shutdown Reboot + Message + Append to message + Instantly send From 7da224a1ceef069d9adfd0440bd448274209ed7d Mon Sep 17 00:00:00 2001 From: Douile <25043847+Douile@users.noreply.github.com> Date: Thu, 11 Aug 2022 16:43:26 +0100 Subject: [PATCH 06/10] Store QuickChatActions in the database --- .../mesh/database/DatabaseModule.kt | 6 ++ .../mesh/database/MeshtasticDatabase.kt | 5 +- .../database/QuickChatActionRepository.kt | 34 +++++++++ .../mesh/database/dao/QuickChatActionDao.kt | 28 ++++++++ .../mesh/database/entity/QuickChatAction.kt | 17 +++++ .../geeksville/mesh/model/QuickChatAction.kt | 11 --- .../java/com/geeksville/mesh/model/UIState.kt | 46 ++++++++++-- .../geeksville/mesh/ui/MessagesFragment.kt | 70 +++++++++++-------- .../mesh/ui/QuickChatActionAdapter.kt | 2 +- .../mesh/ui/QuickChatSettingsFragment.kt | 41 +++++++---- 10 files changed, 201 insertions(+), 59 deletions(-) create mode 100644 app/src/main/java/com/geeksville/mesh/database/QuickChatActionRepository.kt create mode 100644 app/src/main/java/com/geeksville/mesh/database/dao/QuickChatActionDao.kt create mode 100644 app/src/main/java/com/geeksville/mesh/database/entity/QuickChatAction.kt delete mode 100644 app/src/main/java/com/geeksville/mesh/model/QuickChatAction.kt diff --git a/app/src/main/java/com/geeksville/mesh/database/DatabaseModule.kt b/app/src/main/java/com/geeksville/mesh/database/DatabaseModule.kt index 1f2732ddf..f849637b7 100644 --- a/app/src/main/java/com/geeksville/mesh/database/DatabaseModule.kt +++ b/app/src/main/java/com/geeksville/mesh/database/DatabaseModule.kt @@ -2,6 +2,7 @@ package com.geeksville.mesh.database import android.app.Application import com.geeksville.mesh.database.dao.PacketDao +import com.geeksville.mesh.database.dao.QuickChatActionDao import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -20,4 +21,9 @@ class DatabaseModule { fun providePacketDao(database: MeshtasticDatabase): PacketDao { return database.packetDao() } + + @Provides + fun provideQuickChatActionDao(database: MeshtasticDatabase): QuickChatActionDao { + return database.quickChatActionDao() + } } \ No newline at end of file diff --git a/app/src/main/java/com/geeksville/mesh/database/MeshtasticDatabase.kt b/app/src/main/java/com/geeksville/mesh/database/MeshtasticDatabase.kt index 6d11eb1c3..b49e628f8 100644 --- a/app/src/main/java/com/geeksville/mesh/database/MeshtasticDatabase.kt +++ b/app/src/main/java/com/geeksville/mesh/database/MeshtasticDatabase.kt @@ -5,11 +5,14 @@ import androidx.room.Database import androidx.room.Room import androidx.room.RoomDatabase import com.geeksville.mesh.database.dao.PacketDao +import com.geeksville.mesh.database.dao.QuickChatActionDao import com.geeksville.mesh.database.entity.Packet +import com.geeksville.mesh.database.entity.QuickChatAction -@Database(entities = [Packet::class], version = 1, exportSchema = false) +@Database(entities = [Packet::class, QuickChatAction::class], version = 2, exportSchema = false) abstract class MeshtasticDatabase : RoomDatabase() { abstract fun packetDao(): PacketDao + abstract fun quickChatActionDao(): QuickChatActionDao companion object { fun getDatabase(context: Context): MeshtasticDatabase { diff --git a/app/src/main/java/com/geeksville/mesh/database/QuickChatActionRepository.kt b/app/src/main/java/com/geeksville/mesh/database/QuickChatActionRepository.kt new file mode 100644 index 000000000..b95098175 --- /dev/null +++ b/app/src/main/java/com/geeksville/mesh/database/QuickChatActionRepository.kt @@ -0,0 +1,34 @@ +package com.geeksville.mesh.database + +import com.geeksville.mesh.database.dao.QuickChatActionDao +import com.geeksville.mesh.database.entity.QuickChatAction +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.withContext +import javax.inject.Inject + +class QuickChatActionRepository @Inject constructor(private val quickChatDaoLazy: dagger.Lazy) { + private val quickChatActionDao by lazy { + quickChatDaoLazy.get() + } + + suspend fun getAllActions(): Flow> = withContext(Dispatchers.IO) { + quickChatActionDao.getAll() + } + + suspend fun insert(action: QuickChatAction) = withContext(Dispatchers.IO) { + quickChatActionDao.insert(action) + } + + suspend fun deleteAll() = withContext(Dispatchers.IO) { + quickChatActionDao.deleteAll() + } + + suspend fun delete(uuid: Long) = withContext(Dispatchers.IO) { + quickChatActionDao.delete(uuid) + } + + suspend fun update(action:QuickChatAction) = withContext(Dispatchers.IO) { + quickChatActionDao.update(action) + } +} \ 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 new file mode 100644 index 000000000..138b1131b --- /dev/null +++ b/app/src/main/java/com/geeksville/mesh/database/dao/QuickChatActionDao.kt @@ -0,0 +1,28 @@ +package com.geeksville.mesh.database.dao + +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.Query +import androidx.room.Update +import com.geeksville.mesh.database.entity.QuickChatAction +import kotlinx.coroutines.flow.Flow + +@Dao +interface QuickChatActionDao { + + @Query("Select * from quick_chat") + fun getAll(): Flow> + + @Insert + fun insert(action: QuickChatAction) + + @Query("Delete from quick_chat") + fun deleteAll() + + @Query("Delete from quick_chat where uuid=:uuid") + fun delete(uuid: Long) + + @Update + fun update(action: QuickChatAction) + +} \ 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 new file mode 100644 index 000000000..26da5ecb9 --- /dev/null +++ b/app/src/main/java/com/geeksville/mesh/database/entity/QuickChatAction.kt @@ -0,0 +1,17 @@ +package com.geeksville.mesh.database.entity + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey + +@Entity(tableName = "quick_chat") +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) { + enum class Mode { + Append, + Instant, + } +} \ No newline at end of file diff --git a/app/src/main/java/com/geeksville/mesh/model/QuickChatAction.kt b/app/src/main/java/com/geeksville/mesh/model/QuickChatAction.kt deleted file mode 100644 index 96e251512..000000000 --- a/app/src/main/java/com/geeksville/mesh/model/QuickChatAction.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.geeksville.mesh.model - -data class QuickChatAction( - val name: String, - val message: String, - val mode: Mode) { - 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 d513efd43..10ae8200d 100644 --- a/app/src/main/java/com/geeksville/mesh/model/UIState.kt +++ b/app/src/main/java/com/geeksville/mesh/model/UIState.kt @@ -14,7 +14,9 @@ import androidx.lifecycle.viewModelScope import com.geeksville.android.Logging import com.geeksville.mesh.* import com.geeksville.mesh.database.PacketRepository +import com.geeksville.mesh.database.QuickChatActionRepository import com.geeksville.mesh.database.entity.Packet +import com.geeksville.mesh.database.entity.QuickChatAction import com.geeksville.mesh.repository.datastore.LocalConfigRepository import com.geeksville.mesh.service.MeshService import com.geeksville.mesh.util.positionToMeter @@ -61,6 +63,7 @@ class UIViewModel @Inject constructor( private val app: Application, private val packetRepository: PacketRepository, private val localConfigRepository: LocalConfigRepository, + private val quickChatActionRepository: QuickChatActionRepository, private val preferences: SharedPreferences ) : ViewModel(), Logging { @@ -70,6 +73,12 @@ class UIViewModel @Inject constructor( private val _localConfig = MutableLiveData() val localConfig: LiveData get() = _localConfig + private val _quickChatActions = + MutableStateFlow>( + emptyList() + ) + val quickChatActions: StateFlow> = _quickChatActions + init { viewModelScope.launch { packetRepository.getAllPackets().collect { packets -> @@ -81,6 +90,11 @@ class UIViewModel @Inject constructor( _localConfig.value = config } } + viewModelScope.launch { + quickChatActionRepository.getAllActions().collect { actions -> + _quickChatActions.value = actions + } + } debug("ViewModel created") } @@ -445,12 +459,34 @@ class UIViewModel @Inject constructor( } } - private val _quickChatActions = mutableListOf() - val quickChatActions: List get() = _quickChatActions - fun addQuickChatAction(name: String, value: String, mode: QuickChatAction.Mode) { - val action = QuickChatAction(name, value, mode) - _quickChatActions.add(action) + viewModelScope.launch(Dispatchers.Main) { + val action = QuickChatAction(0, name, value, mode) + quickChatActionRepository.insert(action) + } + } + + fun deleteQuickChatAction(action: QuickChatAction) { + viewModelScope.launch(Dispatchers.Main) { + quickChatActionRepository.delete(action.uuid) + } + } + + fun updateQuickChatAction( + action: QuickChatAction, + name: String?, + message: String?, + mode: QuickChatAction.Mode? + ) { + viewModelScope.launch(Dispatchers.Main) { + val newAction = QuickChatAction( + action.uuid, + name ?: action.name, + message ?: action.message, + mode ?: action.mode + ) + quickChatActionRepository.update(newAction) + } } } 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 c51d48c31..084d33a71 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/MessagesFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/MessagesFragment.kt @@ -3,7 +3,6 @@ package com.geeksville.mesh.ui import android.graphics.Color import android.graphics.drawable.GradientDrawable import android.os.Bundle -import android.text.InputType import android.view.* import android.view.inputmethod.EditorInfo import android.widget.* @@ -11,18 +10,20 @@ import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.view.ActionMode import androidx.cardview.widget.CardView import androidx.core.content.ContextCompat +import androidx.core.view.allViews import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.fragment.app.setFragmentResultListener +import androidx.lifecycle.asLiveData import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.geeksville.android.Logging import com.geeksville.mesh.DataPacket import com.geeksville.mesh.MessageStatus import com.geeksville.mesh.R +import com.geeksville.mesh.database.entity.QuickChatAction import com.geeksville.mesh.databinding.AdapterMessageLayoutBinding import com.geeksville.mesh.databinding.MessagesFragmentBinding -import com.geeksville.mesh.model.QuickChatAction import com.geeksville.mesh.model.UIViewModel import com.geeksville.mesh.service.MeshService import com.google.android.material.chip.Chip @@ -56,6 +57,8 @@ class MessagesFragment : Fragment(), Logging { private val model: UIViewModel by activityViewModels() + private var isConnected = false + // Allows textMultiline with IME_ACTION_SEND private fun EditText.onActionSend(func: () -> Unit) { setOnEditorActionListener { _, actionId, _ -> @@ -292,34 +295,45 @@ class MessagesFragment : Fragment(), Logging { // If connection state _OR_ myID changes we have to fix our ability to edit outgoing messages model.connectionState.observe(viewLifecycleOwner) { connectionState -> // If we don't know our node ID and we are offline don't let user try to send - val connected = connectionState == MeshService.ConnectionState.CONNECTED - binding.textInputLayout.isEnabled = connected - binding.sendButton.isEnabled = connected - } - - for (action in model.quickChatActions) { - val button = Button(context) - button.setText(action.name) - if (action.mode == QuickChatAction.Mode.Instant) { - button.backgroundTintList = ContextCompat.getColorStateList(requireActivity(), R.color.colorMyMsg) - - } - button.setOnClickListener { - if (action.mode == QuickChatAction.Mode.Append) { - val originalText = binding.messageInputText.text ?: "" - val needsSpace = !originalText.endsWith(' ') && originalText.isNotEmpty() - val newText = buildString { - append(originalText) - if (needsSpace) append(' ') - append(action.message) - } - binding.messageInputText.setText(newText) - binding.messageInputText.setSelection(newText.length) - } else { - model.messagesState.sendMessage(action.message, contactId) + isConnected = connectionState == MeshService.ConnectionState.CONNECTED + binding.textInputLayout.isEnabled = isConnected + binding.sendButton.isEnabled = isConnected + for (subView: View in binding.quickChatLayout.allViews) { + if (subView is Button) { + subView.isEnabled = isConnected + } + } + } + + model.quickChatActions.asLiveData().observe(viewLifecycleOwner) { actions -> + actions?.let { + for (action in actions) { + val button = Button(context) + button.setText(action.name) + button.isEnabled = isConnected + if (action.mode == QuickChatAction.Mode.Instant) { + //button.setBackgroundColor(Color.rgb(200, 200, 200)) + button.backgroundTintList = ContextCompat.getColorStateList(requireActivity(), R.color.colorMyMsg) + + } + button.setOnClickListener { + if (action.mode == QuickChatAction.Mode.Append) { + val originalText = binding.messageInputText.text ?: "" + val needsSpace = !originalText.endsWith(' ') && originalText.isNotEmpty() + val newText = buildString { + append(originalText) + if (needsSpace) append(' ') + append(action.message) + } + binding.messageInputText.setText(newText) + binding.messageInputText.setSelection(newText.length) + } else { + model.messagesState.sendMessage(action.message, contactId) + } + } + binding.quickChatLayout.addView(button) } } - binding.quickChatLayout.addView(button) } } 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 d774e31f1..e4061f812 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/QuickChatActionAdapter.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/QuickChatActionAdapter.kt @@ -8,7 +8,7 @@ import android.view.ViewGroup import android.widget.TextView import androidx.recyclerview.widget.RecyclerView import com.geeksville.mesh.R -import com.geeksville.mesh.model.QuickChatAction +import com.geeksville.mesh.database.entity.QuickChatAction class QuickChatActionAdapter internal constructor( context: Context, 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 c84ae6beb..bcb7a3a19 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/QuickChatSettingsFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/QuickChatSettingsFragment.kt @@ -1,14 +1,22 @@ package com.geeksville.mesh.ui +import android.content.Context import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.EditText +import androidx.core.widget.addTextChangedListener import androidx.fragment.app.activityViewModels +import androidx.lifecycle.asLiveData +import androidx.recyclerview.widget.LinearLayoutManager import com.geeksville.android.Logging -import com.geeksville.mesh.databinding.AdvancedSettingsBinding +import com.geeksville.mesh.R import com.geeksville.mesh.databinding.QuickChatSettingsFragmentBinding +import com.geeksville.mesh.database.entity.QuickChatAction import com.geeksville.mesh.model.UIViewModel +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import com.google.android.material.switchmaterial.SwitchMaterial import dagger.hilt.android.AndroidEntryPoint @AndroidEntryPoint @@ -41,23 +49,17 @@ class QuickChatSettingsFragment : ScreenFragment("Quick Chat settings"), Logging val name = builder.nameInput.text.toString().trim() val message = builder.messageInput.text.toString() - if (name.isNotEmpty() and message.isNotEmpty()) + if (builder.isNotEmpty()) model.addQuickChatAction( name, message, if (builder.modeSwitch.isChecked) QuickChatAction.Mode.Instant else QuickChatAction.Mode.Append ) - // TODO - } - builder.builder.setNegativeButton("Cancel") { _, _ -> - // TODO } val dialog = builder.builder.create() - dialog.getButton(0).isEnabled = false dialog.show() } - model.addQuickChatAction("TST", "Test", QuickChatAction.Mode.Append) val quickChatActionAdapter = QuickChatActionAdapter(requireContext()) { action: QuickChatAction -> val builder = createEditDialog(requireContext(), "Edit quick chat") @@ -65,9 +67,18 @@ class QuickChatSettingsFragment : ScreenFragment("Quick Chat settings"), Logging builder.messageInput.setText(action.message) builder.modeSwitch.isChecked = action.mode == QuickChatAction.Mode.Instant - builder.builder.setNegativeButton(R.string.cancel) { _, _ -> } + builder.builder.setNegativeButton(R.string.delete) { _, _ -> + model.deleteQuickChatAction(action) + } builder.builder.setPositiveButton(R.string.save_btn) { _, _ -> - // TODO + if (builder.isNotEmpty()) { + model.updateQuickChatAction( + action, + builder.nameInput.text.toString(), + builder.messageInput.text.toString(), + if (builder.modeSwitch.isChecked) QuickChatAction.Mode.Instant else QuickChatAction.Mode.Append + ) + } } val dialog = builder.builder.create() dialog.show() @@ -78,7 +89,10 @@ class QuickChatSettingsFragment : ScreenFragment("Quick Chat settings"), Logging this.adapter = quickChatActionAdapter } - quickChatActionAdapter.setActions(model.quickChatActions) + model.quickChatActions.asLiveData().observe(viewLifecycleOwner) { actions -> + actions?.let { quickChatActionAdapter.setActions(actions) } + } + Log.d(TAG, "viewCreation done") } @@ -87,7 +101,9 @@ class QuickChatSettingsFragment : ScreenFragment("Quick Chat settings"), Logging val nameInput: EditText, val messageInput: EditText, val modeSwitch: SwitchMaterial - ) + ) { + fun isNotEmpty(): Boolean = nameInput.text.isNotEmpty() and messageInput.text.isNotEmpty() + } private fun getMessageName(message: String): String { return if (message.length <= 3) { @@ -128,7 +144,6 @@ class QuickChatSettingsFragment : ScreenFragment("Quick Chat settings"), Logging if (nameInput.isFocused) nameHasChanged = true } - // TODO: Don't enable positive button until there is name and message builder.setView(layout) return DialogBuilder(builder, nameInput, messageInput, modeSwitch) 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 07/10] 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) } } From 01e24ff6a425909555b322f8524958c9bf61fb69 Mon Sep 17 00:00:00 2001 From: Douile <25043847+Douile@users.noreply.github.com> Date: Tue, 16 Aug 2022 11:46:57 +0100 Subject: [PATCH 08/10] Add position via dragging the recyclerview --- .../database/QuickChatActionRepository.kt | 4 +-- .../mesh/database/dao/QuickChatActionDao.kt | 22 +-------------- .../mesh/database/entity/QuickChatAction.kt | 2 +- .../java/com/geeksville/mesh/model/UIState.kt | 7 +++-- .../geeksville/mesh/ui/DragManageAdapter.kt | 15 ++++++++-- .../mesh/ui/QuickChatActionAdapter.kt | 17 +++++------ .../mesh/ui/QuickChatSettingsFragment.kt | 28 +++++++++++-------- 7 files changed, 46 insertions(+), 49 deletions(-) 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 0dcb7106c..0bef1c756 100644 --- a/app/src/main/java/com/geeksville/mesh/database/QuickChatActionRepository.kt +++ b/app/src/main/java/com/geeksville/mesh/database/QuickChatActionRepository.kt @@ -32,7 +32,7 @@ class QuickChatActionRepository @Inject constructor(private val quickChatDaoLazy quickChatActionDao.update(action) } - suspend fun moveAction(action: QuickChatAction, newPos: Int) = withContext(Dispatchers.IO) { - quickChatActionDao.moveAction(action, newPos) + suspend fun setItemPosition(uuid: Long, newPos: Int) = withContext(Dispatchers.IO) { + quickChatActionDao.updateActionPosition(uuid, 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 ef3f97107..8af931323 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 @@ -7,7 +7,7 @@ import kotlinx.coroutines.flow.Flow @Dao interface QuickChatActionDao { - @Query("Select * from quick_chat") + @Query("Select * from quick_chat order by position asc") fun getAll(): Flow> @Insert @@ -31,27 +31,7 @@ interface QuickChatActionDao { @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 deeb2d45c..f9cc237f6 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 @@ -5,7 +5,7 @@ import androidx.room.Entity import androidx.room.Index import androidx.room.PrimaryKey -@Entity(tableName = "quick_chat", indices = [Index(value=["position"], unique=true)]) +@Entity(tableName = "quick_chat") data class QuickChatAction( @PrimaryKey(autoGenerate = true) val uuid: Long, @ColumnInfo(name="name") val name: String, 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 df9476d5b..e60170e11 100644 --- a/app/src/main/java/com/geeksville/mesh/model/UIState.kt +++ b/app/src/main/java/com/geeksville/mesh/model/UIState.kt @@ -21,6 +21,7 @@ import com.geeksville.mesh.repository.datastore.LocalConfigRepository import com.geeksville.mesh.service.MeshService import com.geeksville.mesh.util.positionToMeter import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.CoroutineStart import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow @@ -490,9 +491,11 @@ class UIViewModel @Inject constructor( } } - fun moveQuickChatAction(action: QuickChatAction, newPos: Int) { + fun updateActionPositions(actions: List) { viewModelScope.launch(Dispatchers.Main) { - quickChatActionRepository.moveAction(action, newPos) + for (position in 0..actions.size) { + quickChatActionRepository.setItemPosition(actions[position].uuid, position) + } } } } diff --git a/app/src/main/java/com/geeksville/mesh/ui/DragManageAdapter.kt b/app/src/main/java/com/geeksville/mesh/ui/DragManageAdapter.kt index 1d24a9e9e..913fa8a51 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/DragManageAdapter.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/DragManageAdapter.kt @@ -1,12 +1,13 @@ 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) { +class DragManageAdapter(var adapter: SwapAdapter, dragDirs: Int, swipeDirs: Int) : + ItemTouchHelper.SimpleCallback(dragDirs, swipeDirs) { interface SwapAdapter { fun swapItems(fromPosition: Int, toPosition: Int) + fun commitSwaps() } override fun onMove( @@ -15,7 +16,15 @@ class DragManageAdapter(var adapter: SwapAdapter, context: Context, dragDirs: In target: RecyclerView.ViewHolder ): Boolean { adapter.swapItems(viewHolder.absoluteAdapterPosition, target.absoluteAdapterPosition) - TODO("Not yet implemented") + return true + } + + override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) { + super.onSelectedChanged(viewHolder, actionState) + + if (actionState == ItemTouchHelper.ACTION_STATE_IDLE) { + adapter.commitSwaps() + } } override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) { 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 070a723e1..d4563aceb 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/QuickChatActionAdapter.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/QuickChatActionAdapter.kt @@ -1,7 +1,6 @@ package com.geeksville.mesh.ui import android.content.Context -import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -12,12 +11,13 @@ import com.geeksville.mesh.database.entity.QuickChatAction class QuickChatActionAdapter internal constructor( context: Context, - private val onEdit: (action: QuickChatAction) -> Unit + private val onEdit: (action: QuickChatAction) -> Unit, + private val repositionAction: (fromPos: Int, toPos: Int) -> Unit, + private val commitAction: () -> Unit, ) : RecyclerView.Adapter(), DragManageAdapter.SwapAdapter { private val inflater: LayoutInflater = LayoutInflater.from(context) private var actions = emptyList() - private val TAG = "QuickChatAdapter" inner class ActionViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { val actionName: TextView = itemView.findViewById(R.id.quickChatActionName) @@ -27,7 +27,6 @@ class QuickChatActionAdapter internal constructor( override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ActionViewHolder { val itemView = inflater.inflate(R.layout.adapter_quick_chat_action_layout, parent, false) - Log.d(TAG, "Created view holder") return ActionViewHolder(itemView) } @@ -35,24 +34,26 @@ class QuickChatActionAdapter internal constructor( val current = actions[position] holder.actionName.text = current.name holder.actionValue.text = current.message - holder.actionEdit.setOnClickListener{ + holder.actionEdit.setOnClickListener { onEdit(current) } - Log.d(TAG, "Bound actions") } internal fun setActions(actions: List) { this.actions = actions notifyDataSetChanged() - Log.d(TAG, String.format("setActions(size=%d, count=%d)", actions.size, itemCount)) } override fun getItemCount() = actions.size override fun swapItems(fromPosition: Int, toPosition: Int) { - // TODO: Update data + repositionAction(fromPosition, toPosition) notifyItemMoved(fromPosition, toPosition) } + override fun commitSwaps() { + commitAction() + } + } \ 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 f0f952f2f..03ddda23e 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/QuickChatSettingsFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/QuickChatSettingsFragment.kt @@ -13,12 +13,13 @@ import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.LinearLayoutManager import com.geeksville.android.Logging import com.geeksville.mesh.R -import com.geeksville.mesh.databinding.QuickChatSettingsFragmentBinding import com.geeksville.mesh.database.entity.QuickChatAction +import com.geeksville.mesh.databinding.QuickChatSettingsFragmentBinding import com.geeksville.mesh.model.UIViewModel import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.switchmaterial.SwitchMaterial import dagger.hilt.android.AndroidEntryPoint +import java.util.* @AndroidEntryPoint class QuickChatSettingsFragment : ScreenFragment("Quick Chat settings"), Logging { @@ -28,6 +29,8 @@ class QuickChatSettingsFragment : ScreenFragment("Quick Chat settings"), Logging private val model: UIViewModel by activityViewModels() + private lateinit var actions: List + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? @@ -39,11 +42,7 @@ class QuickChatSettingsFragment : ScreenFragment("Quick Chat settings"), Logging override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - Log.d(TAG, "viewCreated") - binding.quickChatSettingsCreateButton.setOnClickListener { - Log.d(TAG, "Create quick chat") - val builder = createEditDialog(requireContext(), "New quick chat") builder.builder.setPositiveButton("Add") { view, x -> @@ -62,7 +61,7 @@ class QuickChatSettingsFragment : ScreenFragment("Quick Chat settings"), Logging } val quickChatActionAdapter = - QuickChatActionAdapter(requireContext()) { action: QuickChatAction -> + QuickChatActionAdapter(requireContext(), { action: QuickChatAction -> val builder = createEditDialog(requireContext(), "Edit quick chat") builder.nameInput.setText(action.name) builder.messageInput.setText(action.message) @@ -83,9 +82,14 @@ class QuickChatSettingsFragment : ScreenFragment("Quick Chat settings"), Logging } val dialog = builder.builder.create() dialog.show() - } + }, { fromPos, toPos -> + Collections.swap(actions, fromPos, toPos) + }, { + model.updateActionPositions(actions) + }) - val dragCallback = DragManageAdapter(quickChatActionAdapter, requireContext(), ItemTouchHelper.UP or ItemTouchHelper.DOWN, 0) + val dragCallback = + DragManageAdapter(quickChatActionAdapter, ItemTouchHelper.UP or ItemTouchHelper.DOWN, 0) val helper = ItemTouchHelper(dragCallback) binding.quickChatSettingsView.apply { @@ -94,12 +98,12 @@ class QuickChatSettingsFragment : ScreenFragment("Quick Chat settings"), Logging helper.attachToRecyclerView(this) } - model.quickChatActions.asLiveData().observe(viewLifecycleOwner) { actions -> - actions?.let { quickChatActionAdapter.setActions(actions) } + actions?.let { + quickChatActionAdapter.setActions(actions) + this.actions = actions + } } - - Log.d(TAG, "viewCreation done") } data class DialogBuilder( From b1b554d97d326769c0a4cedebcc0d374a894a0c9 Mon Sep 17 00:00:00 2001 From: Douile <25043847+Douile@users.noreply.github.com> Date: Tue, 16 Aug 2022 12:25:10 +0100 Subject: [PATCH 09/10] Add icon and colours to quick chat settings --- .../mesh/ui/QuickChatActionAdapter.kt | 13 ++++++- .../mesh/ui/QuickChatSettingsFragment.kt | 22 +++++++++-- .../drawable/ic_baseline_fast_forward_24.xml | 5 +++ .../adapter_quick_chat_action_layout.xml | 15 +++++++- .../main/res/layout/dialog_add_quick_chat.xml | 38 +++++++++++++++++-- 5 files changed, 81 insertions(+), 12 deletions(-) create mode 100644 app/src/main/res/drawable/ic_baseline_fast_forward_24.xml 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 d4563aceb..dd60d5b0f 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/QuickChatActionAdapter.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/QuickChatActionAdapter.kt @@ -5,12 +5,13 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.TextView +import androidx.core.content.ContextCompat import androidx.recyclerview.widget.RecyclerView import com.geeksville.mesh.R import com.geeksville.mesh.database.entity.QuickChatAction class QuickChatActionAdapter internal constructor( - context: Context, + private val context: Context, private val onEdit: (action: QuickChatAction) -> Unit, private val repositionAction: (fromPos: Int, toPos: Int) -> Unit, private val commitAction: () -> Unit, @@ -20,9 +21,11 @@ class QuickChatActionAdapter internal constructor( private var actions = emptyList() inner class ActionViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + val container: View = itemView.findViewById(R.id.quickChatActionContainer) val actionName: TextView = itemView.findViewById(R.id.quickChatActionName) val actionValue: TextView = itemView.findViewById(R.id.quickChatActionValue) val actionEdit: View = itemView.findViewById(R.id.quickChatActionEdit) + val actionInstant: View = itemView.findViewById(R.id.quickChatActionInstant) } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ActionViewHolder { @@ -34,12 +37,18 @@ class QuickChatActionAdapter internal constructor( val current = actions[position] holder.actionName.text = current.name holder.actionValue.text = current.message + val isInstant = current.mode == QuickChatAction.Mode.Instant + holder.actionInstant.visibility = if (isInstant) View.VISIBLE else View.INVISIBLE + if (isInstant) { + holder.container.backgroundTintList = ContextCompat.getColorStateList(context, R.color.colorMyMsg) + } else { + holder.container.backgroundTintList = null + } holder.actionEdit.setOnClickListener { onEdit(current) } } - internal fun setActions(actions: List) { this.actions = actions notifyDataSetChanged() 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 03ddda23e..56c53ed33 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/QuickChatSettingsFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/QuickChatSettingsFragment.kt @@ -6,6 +6,9 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.EditText +import android.widget.ImageView +import androidx.core.content.ContextCompat +import androidx.core.view.isVisible import androidx.core.widget.addTextChangedListener import androidx.fragment.app.activityViewModels import androidx.lifecycle.asLiveData @@ -65,7 +68,9 @@ class QuickChatSettingsFragment : ScreenFragment("Quick Chat settings"), Logging val builder = createEditDialog(requireContext(), "Edit quick chat") builder.nameInput.setText(action.name) builder.messageInput.setText(action.message) - builder.modeSwitch.isChecked = action.mode == QuickChatAction.Mode.Instant + val isInstant = action.mode == QuickChatAction.Mode.Instant + builder.modeSwitch.isChecked = isInstant + builder.instantImage.visibility = if (isInstant) View.VISIBLE else View.INVISIBLE builder.builder.setNegativeButton(R.string.delete) { _, _ -> model.deleteQuickChatAction(action) @@ -110,7 +115,8 @@ class QuickChatSettingsFragment : ScreenFragment("Quick Chat settings"), Logging val builder: MaterialAlertDialogBuilder, val nameInput: EditText, val messageInput: EditText, - val modeSwitch: SwitchMaterial + val modeSwitch: SwitchMaterial, + val instantImage: ImageView ) { fun isNotEmpty(): Boolean = nameInput.text.isNotEmpty() and messageInput.text.isNotEmpty() } @@ -137,11 +143,19 @@ class QuickChatSettingsFragment : ScreenFragment("Quick Chat settings"), Logging val nameInput: EditText = layout.findViewById(R.id.addQuickChatName) val messageInput: EditText = layout.findViewById(R.id.addQuickChatMessage) val modeSwitch: SwitchMaterial = layout.findViewById(R.id.addQuickChatMode) + val instantImage: ImageView = layout.findViewById(R.id.addQuickChatInsant) + instantImage.visibility = if (modeSwitch.isChecked) View.VISIBLE else View.INVISIBLE var nameHasChanged = false modeSwitch.setOnCheckedChangeListener { _, _ -> - modeSwitch.setText(if (modeSwitch.isChecked) R.string.mode_instant else R.string.mode_append) + if (modeSwitch.isChecked) { + modeSwitch.setText(R.string.mode_instant) + instantImage.visibility = View.VISIBLE + } else { + modeSwitch.setText(R.string.mode_append) + instantImage.visibility = View.INVISIBLE + } } messageInput.addTextChangedListener { text -> @@ -156,6 +170,6 @@ class QuickChatSettingsFragment : ScreenFragment("Quick Chat settings"), Logging builder.setView(layout) - return DialogBuilder(builder, nameInput, messageInput, modeSwitch) + return DialogBuilder(builder, nameInput, messageInput, modeSwitch, instantImage) } } \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_baseline_fast_forward_24.xml b/app/src/main/res/drawable/ic_baseline_fast_forward_24.xml new file mode 100644 index 000000000..ac50e268d --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_fast_forward_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/layout/adapter_quick_chat_action_layout.xml b/app/src/main/res/layout/adapter_quick_chat_action_layout.xml index e3b265dfe..d37bae544 100644 --- a/app/src/main/res/layout/adapter_quick_chat_action_layout.xml +++ b/app/src/main/res/layout/adapter_quick_chat_action_layout.xml @@ -8,6 +8,7 @@ @@ -16,6 +17,16 @@ android:layout_width="match_parent" android:layout_height="match_parent"> + + - + android:orientation="horizontal"> + + + + + + + + + \ No newline at end of file From 36b5a9eefbffe21234f46e810c02262530b1bb15 Mon Sep 17 00:00:00 2001 From: Douile <25043847+Douile@users.noreply.github.com> Date: Tue, 16 Aug 2022 12:25:40 +0100 Subject: [PATCH 10/10] Fix iterating items in repositionItems --- app/src/main/java/com/geeksville/mesh/model/UIState.kt | 2 +- app/src/main/java/com/geeksville/mesh/ui/MessagesFragment.kt | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) 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 e60170e11..daf27fa2d 100644 --- a/app/src/main/java/com/geeksville/mesh/model/UIState.kt +++ b/app/src/main/java/com/geeksville/mesh/model/UIState.kt @@ -493,7 +493,7 @@ class UIViewModel @Inject constructor( fun updateActionPositions(actions: List) { viewModelScope.launch(Dispatchers.Main) { - for (position in 0..actions.size) { + for (position in actions.indices) { quickChatActionRepository.setItemPosition(actions[position].uuid, position) } } 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 fb44c3a85..a6566ca72 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/MessagesFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/MessagesFragment.kt @@ -307,15 +307,14 @@ class MessagesFragment : Fragment(), Logging { model.quickChatActions.asLiveData().observe(viewLifecycleOwner) { actions -> actions?.let { + // This seems kinda hacky it might be better to replace with a recycler view binding.quickChatLayout.removeAllViews() for (action in actions) { val button = Button(context) button.setText(action.name) button.isEnabled = isConnected if (action.mode == QuickChatAction.Mode.Instant) { - //button.setBackgroundColor(Color.rgb(200, 200, 200)) button.backgroundTintList = ContextCompat.getColorStateList(requireActivity(), R.color.colorMyMsg) - } button.setOnClickListener { if (action.mode == QuickChatAction.Mode.Append) {