kopia lustrzana https://github.com/meshtastic/Meshtastic-Android
feat: add condition check to scroll to bottom of messages list (#882)
rodzic
0ba462db83
commit
2e009ca49c
|
@ -21,7 +21,6 @@ import com.geeksville.mesh.model.Channel
|
||||||
import com.geeksville.mesh.model.UIViewModel
|
import com.geeksville.mesh.model.UIViewModel
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import java.text.DateFormat
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
|
@ -48,19 +47,6 @@ class ContactsFragment : ScreenFragment("Messages"), Logging {
|
||||||
|
|
||||||
private val contactsAdapter = object : RecyclerView.Adapter<ViewHolder>() {
|
private val contactsAdapter = object : RecyclerView.Adapter<ViewHolder>() {
|
||||||
|
|
||||||
private val dateTimeFormat: DateFormat =
|
|
||||||
DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT)
|
|
||||||
private val timeFormat: DateFormat =
|
|
||||||
DateFormat.getTimeInstance(DateFormat.SHORT)
|
|
||||||
|
|
||||||
private fun getShortDateTime(time: Date): String {
|
|
||||||
// return time if within 24 hours, otherwise date/time
|
|
||||||
val oneDayMsec = 60 * 60 * 24 * 1000L
|
|
||||||
return if (System.currentTimeMillis() - time.time > oneDayMsec) {
|
|
||||||
dateTimeFormat.format(time)
|
|
||||||
} else timeFormat.format(time)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||||
val inflater = LayoutInflater.from(requireContext())
|
val inflater = LayoutInflater.from(requireContext())
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,17 @@ import dagger.hilt.android.AndroidEntryPoint
|
||||||
import java.text.DateFormat
|
import java.text.DateFormat
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
|
// return time if within 24 hours, otherwise date/time
|
||||||
|
internal fun getShortDateTime(date: Date): String {
|
||||||
|
val isWithin24Hours = System.currentTimeMillis() - date.time <= 24 * 60 * 60 * 1000L
|
||||||
|
|
||||||
|
return if (isWithin24Hours) {
|
||||||
|
DateFormat.getTimeInstance(DateFormat.SHORT).format(date)
|
||||||
|
} else {
|
||||||
|
DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT).format(date)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class MessagesFragment : Fragment(), Logging {
|
class MessagesFragment : Fragment(), Logging {
|
||||||
|
|
||||||
|
@ -58,19 +69,6 @@ class MessagesFragment : Fragment(), Logging {
|
||||||
|
|
||||||
private val messagesAdapter = object : RecyclerView.Adapter<ViewHolder>() {
|
private val messagesAdapter = object : RecyclerView.Adapter<ViewHolder>() {
|
||||||
|
|
||||||
private val dateTimeFormat: DateFormat =
|
|
||||||
DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT)
|
|
||||||
private val timeFormat: DateFormat =
|
|
||||||
DateFormat.getTimeInstance(DateFormat.SHORT)
|
|
||||||
|
|
||||||
private fun getShortDateTime(time: Date): String {
|
|
||||||
// return time if within 24 hours, otherwise date/time
|
|
||||||
val oneDayMsec = 60 * 60 * 24 * 1000L
|
|
||||||
return if (System.currentTimeMillis() - time.time > oneDayMsec) {
|
|
||||||
dateTimeFormat.format(time)
|
|
||||||
} else timeFormat.format(time)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||||
val inflater = LayoutInflater.from(requireContext())
|
val inflater = LayoutInflater.from(requireContext())
|
||||||
|
|
||||||
|
@ -83,6 +81,11 @@ class MessagesFragment : Fragment(), Logging {
|
||||||
|
|
||||||
var messages = listOf<Packet>()
|
var messages = listOf<Packet>()
|
||||||
var selectedList = ArrayList<Packet>()
|
var selectedList = ArrayList<Packet>()
|
||||||
|
val layoutManager get() = binding.messageListView.layoutManager as LinearLayoutManager
|
||||||
|
|
||||||
|
fun scrollToBottom() {
|
||||||
|
if (itemCount > 0) layoutManager.scrollToPosition(itemCount - 1)
|
||||||
|
}
|
||||||
|
|
||||||
override fun getItemCount(): Int = messages.size
|
override fun getItemCount(): Int = messages.size
|
||||||
|
|
||||||
|
@ -168,7 +171,7 @@ class MessagesFragment : Fragment(), Logging {
|
||||||
}
|
}
|
||||||
|
|
||||||
holder.itemView.setOnLongClickListener {
|
holder.itemView.setOnLongClickListener {
|
||||||
clickItem(holder)
|
clickItem(position)
|
||||||
if (actionMode == null) {
|
if (actionMode == null) {
|
||||||
actionMode =
|
actionMode =
|
||||||
(activity as AppCompatActivity).startSupportActionMode(actionModeCallback)
|
(activity as AppCompatActivity).startSupportActionMode(actionModeCallback)
|
||||||
|
@ -176,7 +179,7 @@ class MessagesFragment : Fragment(), Logging {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
holder.itemView.setOnClickListener {
|
holder.itemView.setOnClickListener {
|
||||||
if (actionMode != null) clickItem(holder)
|
if (actionMode != null) clickItem(position)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selectedList.contains(packet)) {
|
if (selectedList.contains(packet)) {
|
||||||
|
@ -194,12 +197,10 @@ class MessagesFragment : Fragment(), Logging {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun clickItem(holder: ViewHolder) {
|
private fun clickItem(position: Int) {
|
||||||
val position = holder.bindingAdapterPosition
|
val message = messages[position]
|
||||||
if (!selectedList.contains(messages[position])) {
|
selectedList.apply {
|
||||||
selectedList.add(messages[position])
|
if (contains(message)) remove(message) else add(message)
|
||||||
} else {
|
|
||||||
selectedList.remove(messages[position])
|
|
||||||
}
|
}
|
||||||
if (selectedList.isEmpty()) {
|
if (selectedList.isEmpty()) {
|
||||||
// finish action mode when no items selected
|
// finish action mode when no items selected
|
||||||
|
@ -213,12 +214,14 @@ class MessagesFragment : Fragment(), Logging {
|
||||||
|
|
||||||
/// Called when our node DB changes
|
/// Called when our node DB changes
|
||||||
fun onMessagesChanged(messages: List<Packet>) {
|
fun onMessagesChanged(messages: List<Packet>) {
|
||||||
|
val lastVisibleItemPosition = layoutManager.findLastVisibleItemPosition()
|
||||||
|
val shouldScrollToBottom =
|
||||||
|
lastVisibleItemPosition <= 0 || lastVisibleItemPosition == itemCount - 1
|
||||||
|
|
||||||
this.messages = messages
|
this.messages = messages
|
||||||
notifyDataSetChanged() // FIXME, this is super expensive and redraws all messages
|
notifyDataSetChanged() // FIXME, this is super expensive and redraws all messages
|
||||||
|
|
||||||
// scroll to the last line
|
if (shouldScrollToBottom) scrollToBottom()
|
||||||
if (itemCount != 0)
|
|
||||||
binding.messageListView.scrollToPosition(itemCount - 1)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -242,25 +245,24 @@ class MessagesFragment : Fragment(), Logging {
|
||||||
parentFragmentManager.popBackStack()
|
parentFragmentManager.popBackStack()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun sendMessageInputText() {
|
||||||
|
val str = binding.messageInputText.text.toString().trim()
|
||||||
|
if (str.isNotEmpty()) {
|
||||||
|
model.sendMessage(str)
|
||||||
|
messagesAdapter.scrollToBottom()
|
||||||
|
}
|
||||||
|
binding.messageInputText.setText("") // blow away the string the user just entered
|
||||||
|
// requireActivity().hideKeyboard()
|
||||||
|
}
|
||||||
|
|
||||||
binding.sendButton.setOnClickListener {
|
binding.sendButton.setOnClickListener {
|
||||||
debug("User clicked sendButton")
|
debug("User clicked sendButton")
|
||||||
|
sendMessageInputText()
|
||||||
val str = binding.messageInputText.text.toString().trim()
|
|
||||||
if (str.isNotEmpty())
|
|
||||||
model.sendMessage(str)
|
|
||||||
binding.messageInputText.setText("") // blow away the string the user just entered
|
|
||||||
|
|
||||||
// requireActivity().hideKeyboard()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.messageInputText.onEditorAction(EditorInfo.IME_ACTION_SEND) {
|
binding.messageInputText.onEditorAction(EditorInfo.IME_ACTION_SEND) {
|
||||||
debug("received IME_ACTION_SEND")
|
debug("received IME_ACTION_SEND")
|
||||||
|
sendMessageInputText()
|
||||||
val str = binding.messageInputText.text.toString().trim()
|
|
||||||
if (str.isNotEmpty()) model.sendMessage(str)
|
|
||||||
binding.messageInputText.setText("") // blow away the string the user just entered
|
|
||||||
|
|
||||||
// requireActivity().hideKeyboard()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.messageListView.adapter = messagesAdapter
|
binding.messageListView.adapter = messagesAdapter
|
||||||
|
@ -269,6 +271,7 @@ class MessagesFragment : Fragment(), Logging {
|
||||||
binding.messageListView.layoutManager = layoutManager
|
binding.messageListView.layoutManager = layoutManager
|
||||||
|
|
||||||
model.messages.observe(viewLifecycleOwner) {
|
model.messages.observe(viewLifecycleOwner) {
|
||||||
|
if (it.isNotEmpty() && it.first().contact_key != model.contactKey.value) return@observe
|
||||||
debug("New messages received: ${it.size}")
|
debug("New messages received: ${it.size}")
|
||||||
messagesAdapter.onMessagesChanged(it)
|
messagesAdapter.onMessagesChanged(it)
|
||||||
}
|
}
|
||||||
|
@ -314,6 +317,7 @@ class MessagesFragment : Fragment(), Logging {
|
||||||
binding.messageInputText.setSelection(newText.length)
|
binding.messageInputText.setSelection(newText.length)
|
||||||
} else {
|
} else {
|
||||||
model.sendMessage(action.message)
|
model.sendMessage(action.message)
|
||||||
|
messagesAdapter.scrollToBottom()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
binding.quickChatLayout.addView(button)
|
binding.quickChatLayout.addView(button)
|
||||||
|
|
Ładowanie…
Reference in New Issue