kopia lustrzana https://github.com/meshtastic/Meshtastic-Android
Store QuickChatActions in the database
rodzic
8c2d3a4041
commit
7da224a1ce
|
@ -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()
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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<QuickChatActionDao>) {
|
||||
private val quickChatActionDao by lazy {
|
||||
quickChatDaoLazy.get()
|
||||
}
|
||||
|
||||
suspend fun getAllActions(): Flow<List<QuickChatAction>> = 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)
|
||||
}
|
||||
}
|
|
@ -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<List<QuickChatAction>>
|
||||
|
||||
@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)
|
||||
|
||||
}
|
|
@ -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,
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
}
|
||||
}
|
|
@ -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<LocalOnlyProtos.LocalConfig?>()
|
||||
val localConfig: LiveData<LocalOnlyProtos.LocalConfig?> get() = _localConfig
|
||||
|
||||
private val _quickChatActions =
|
||||
MutableStateFlow<List<com.geeksville.mesh.database.entity.QuickChatAction>>(
|
||||
emptyList()
|
||||
)
|
||||
val quickChatActions: StateFlow<List<QuickChatAction>> = _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<QuickChatAction>()
|
||||
val quickChatActions: List<QuickChatAction> 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
Ładowanie…
Reference in New Issue