Store QuickChatActions in the database

pull/462/head
Douile 2022-08-11 16:43:26 +01:00
rodzic 8c2d3a4041
commit 7da224a1ce
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: DAB413485BA6CFFD
10 zmienionych plików z 201 dodań i 59 usunięć

Wyświetl plik

@ -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()
}
}

Wyświetl plik

@ -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 {

Wyświetl plik

@ -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)
}
}

Wyświetl plik

@ -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)
}

Wyświetl plik

@ -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,
}
}

Wyświetl plik

@ -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,
}
}

Wyświetl plik

@ -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)
}
}
}

Wyświetl plik

@ -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)
}
}

Wyświetl plik

@ -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,

Wyświetl plik

@ -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)