Merge pull request #462 from Douile/dev-quick-chat

Add quick chat actions
pull/460/head^2
Andre Kirchhoff 2022-08-16 18:43:13 -03:00 zatwierdzone przez GitHub
commit 79b3b1c024
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
20 zmienionych plików z 718 dodań i 10 usunięć

Wyświetl plik

@ -1070,6 +1070,15 @@ class MainActivity : BaseActivity(), Logging,
chooseMapStyle()
return true
}
R.id.preferences_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)
}
}

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,38 @@
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(action: QuickChatAction) = withContext(Dispatchers.IO) {
quickChatActionDao.delete(action)
}
suspend fun update(action: QuickChatAction) = withContext(Dispatchers.IO) {
quickChatActionDao.update(action)
}
suspend fun setItemPosition(uuid: Long, newPos: Int) = withContext(Dispatchers.IO) {
quickChatActionDao.updateActionPosition(uuid, newPos)
}
}

Wyświetl plik

@ -0,0 +1,37 @@
package com.geeksville.mesh.database.dao
import androidx.room.*
import com.geeksville.mesh.database.entity.QuickChatAction
import kotlinx.coroutines.flow.Flow
@Dao
interface QuickChatActionDao {
@Query("Select * from quick_chat order by position asc")
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)
@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>=:position")
fun decrementPositionsAfter(position: Int)
}

Wyświetl plik

@ -0,0 +1,19 @@
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")
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="position") val position: Int) {
enum class Mode {
Append,
Instant,
}
}

Wyświetl plik

@ -14,11 +14,14 @@ 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
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@ -61,6 +64,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 +74,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 +91,11 @@ class UIViewModel @Inject constructor(
_localConfig.value = config
}
}
viewModelScope.launch {
quickChatActionRepository.getAllActions().collect { actions ->
_quickChatActions.value = actions
}
}
debug("ViewModel created")
}
@ -445,5 +460,43 @@ class UIViewModel @Inject constructor(
}
}
fun addQuickChatAction(name: String, value: String, mode: QuickChatAction.Mode) {
viewModelScope.launch(Dispatchers.Main) {
val action = QuickChatAction(0, name, value, mode, _quickChatActions.value.size)
quickChatActionRepository.insert(action)
}
}
fun deleteQuickChatAction(action: QuickChatAction) {
viewModelScope.launch(Dispatchers.Main) {
quickChatActionRepository.delete(action)
}
}
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,
action.position
)
quickChatActionRepository.update(newAction)
}
}
fun updateActionPositions(actions: List<QuickChatAction>) {
viewModelScope.launch(Dispatchers.Main) {
for (position in actions.indices) {
quickChatActionRepository.setItemPosition(actions[position].uuid, position)
}
}
}
}

Wyświetl plik

@ -0,0 +1,33 @@
package com.geeksville.mesh.ui
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView
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(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
adapter.swapItems(viewHolder.absoluteAdapterPosition, target.absoluteAdapterPosition)
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) {
TODO("Not yet implemented")
}
}

Wyświetl plik

@ -3,25 +3,25 @@ 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.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
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.UIViewModel
@ -57,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, _ ->
@ -293,9 +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
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 {
// 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.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)
}
}
}
}

Wyświetl plik

@ -0,0 +1,68 @@
package com.geeksville.mesh.ui
import android.content.Context
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(
private val context: Context,
private val onEdit: (action: QuickChatAction) -> Unit,
private val repositionAction: (fromPos: Int, toPos: Int) -> Unit,
private val commitAction: () -> Unit,
) : RecyclerView.Adapter<QuickChatActionAdapter.ActionViewHolder>(), DragManageAdapter.SwapAdapter {
private val inflater: LayoutInflater = LayoutInflater.from(context)
private var actions = emptyList<QuickChatAction>()
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 {
val itemView = inflater.inflate(R.layout.adapter_quick_chat_action_layout, parent, false)
return ActionViewHolder(itemView)
}
override fun onBindViewHolder(holder: ActionViewHolder, position: Int) {
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<QuickChatAction>) {
this.actions = actions
notifyDataSetChanged()
}
override fun getItemCount() = actions.size
override fun swapItems(fromPosition: Int, toPosition: Int) {
repositionAction(fromPosition, toPosition)
notifyItemMoved(fromPosition, toPosition)
}
override fun commitSwaps() {
commitAction()
}
}

Wyświetl plik

@ -0,0 +1,175 @@
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 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
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import com.geeksville.android.Logging
import com.geeksville.mesh.R
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 {
private var _binding: QuickChatSettingsFragmentBinding? = null
private val binding get() = _binding!!
private val model: UIViewModel by activityViewModels()
private lateinit var actions: List<QuickChatAction>
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)
binding.quickChatSettingsCreateButton.setOnClickListener {
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 (builder.isNotEmpty())
model.addQuickChatAction(
name, message,
if (builder.modeSwitch.isChecked) QuickChatAction.Mode.Instant else QuickChatAction.Mode.Append
)
}
val dialog = builder.builder.create()
dialog.show()
}
val quickChatActionAdapter =
QuickChatActionAdapter(requireContext(), { action: QuickChatAction ->
val builder = createEditDialog(requireContext(), "Edit quick chat")
builder.nameInput.setText(action.name)
builder.messageInput.setText(action.message)
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)
}
builder.builder.setPositiveButton(R.string.save_btn) { _, _ ->
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()
}, { fromPos, toPos ->
Collections.swap(actions, fromPos, toPos)
}, {
model.updateActionPositions(actions)
})
val dragCallback =
DragManageAdapter(quickChatActionAdapter, 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)
this.actions = actions
}
}
}
data class DialogBuilder(
val builder: MaterialAlertDialogBuilder,
val nameInput: EditText,
val messageInput: EditText,
val modeSwitch: SwitchMaterial,
val instantImage: ImageView
) {
fun isNotEmpty(): Boolean = nameInput.text.isNotEmpty() and messageInput.text.isNotEmpty()
}
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)
val instantImage: ImageView = layout.findViewById(R.id.addQuickChatInsant)
instantImage.visibility = if (modeSwitch.isChecked) View.VISIBLE else View.INVISIBLE
var nameHasChanged = false
modeSwitch.setOnCheckedChangeListener { _, _ ->
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 ->
if (!nameHasChanged) {
nameInput.setText(getMessageName(text.toString()))
}
}
nameInput.addTextChangedListener {
if (nameInput.isFocused) nameHasChanged = true
}
builder.setView(layout)
return DialogBuilder(builder, nameInput, messageInput, modeSwitch, instantImage)
}
}

Wyświetl plik

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="?attr/colorControlNormal"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M20,9H4v2h16V9zM4,15h16v-2H4V15z"/>
</vector>

Wyświetl plik

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="?attr/colorControlNormal"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z"/>
</vector>

Wyświetl plik

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="?attr/colorControlNormal"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M4,18l8.5,-6L4,6v12zM13,6v12l8.5,-6L13,6z"/>
</vector>

Wyświetl plik

@ -0,0 +1,73 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<com.google.android.material.card.MaterialCardView
style="@style/Widget.App.CardView"
android:id="@+id/quickChatActionContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/quickChatActionInstant"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_baseline_fast_forward_24" />
<TextView
android:id="@+id/quickChatActionName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:textSize="20sp"
app:layout_constraintStart_toEndOf="@+id/quickChatActionInstant"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/quickChatActionValue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@+id/quickChatActionInstant"
app:layout_constraintTop_toBottomOf="@+id/quickChatActionName" />
<ImageButton
android:id="@+id/quickChatActionEdit"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginEnd="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/quickChatActionDrag"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_baseline_edit_24" />
<ImageView
android:id="@+id/quickChatActionDrag"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_baseline_drag_handle_24" />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView>
</LinearLayout>

Wyświetl plik

@ -0,0 +1,65 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="16dp">
<EditText
android:id="@+id/addQuickChatMessage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:ems="10"
android:hint="@string/message"
android:inputType="textShortMessage"
android:minHeight="48dp" />
<EditText
android:id="@+id/addQuickChatName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:ems="10"
android:hint="@string/name"
android:inputType="textShortMessage"
android:minHeight="48dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
</LinearLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/addQuickChatInsant"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_baseline_fast_forward_24" />
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/addQuickChatMode"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:text="@string/mode_append"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/addQuickChatInsant"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>

Wyświetl plik

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorAdvancedBackground">
@ -37,10 +38,30 @@
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" >
</androidx.recyclerview.widget.RecyclerView>
<HorizontalScrollView
android:id="@+id/quickChatView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toTopOf="@+id/textInputLayout"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
<LinearLayout
android:id="@+id/quickChatLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" />
</HorizontalScrollView>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/textInputLayout"

Wyświetl plik

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorAdvancedBackground">
<TextView
android:id="@+id/quickChatSettingsTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:text="Quick chat options"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/quickChatSettingsView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginStart="8dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/quickChatSettingsTitle"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/quickChatSettingsCreateButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:clickable="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:srcCompat="@drawable/ic_twotone_add_24" />
</androidx.constraintlayout.widget.ConstraintLayout>

Wyświetl plik

@ -36,6 +36,10 @@
android:id="@+id/preferences_map_style"
android:title="@string/preferences_map_style"
app:showAsAction="withText" />
<item
android:id="@+id/preferences_quick_chat"
android:title="Quick chat options"
app:showAsAction="withText" />
<item
android:id="@+id/about"
android:title="@string/about"

Wyświetl plik

@ -144,4 +144,7 @@
<string name="resend">Resend</string>
<string name="shutdown">Shutdown</string>
<string name="reboot">Reboot</string>
<string name="message">Message</string>
<string name="mode_append">Append to message</string>
<string name="mode_instant">Instantly send</string>
</resources>