Skip to content

Commit

Permalink
Merge pull request #25 from capcom6/feature/message-details
Browse files Browse the repository at this point in the history
Added: message details info
  • Loading branch information
capcom6 authored Dec 20, 2023
2 parents ffecd10 + c55525b commit f8031cf
Show file tree
Hide file tree
Showing 15 changed files with 321 additions and 23 deletions.
8 changes: 5 additions & 3 deletions app/src/main/java/me/capcom/smsgateway/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import com.google.android.material.tabs.TabLayoutMediator
import me.capcom.smsgateway.databinding.ActivityMainBinding
import me.capcom.smsgateway.ui.MessagesListFragment
import me.capcom.smsgateway.ui.HolderFragment
import me.capcom.smsgateway.ui.SettingsFragment

class MainActivity : AppCompatActivity() {
Expand All @@ -18,7 +18,8 @@ class MainActivity : AppCompatActivity() {
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)

binding.viewPager.adapter = FragmentsAdapter(this)
val adapter = FragmentsAdapter(this)
binding.viewPager.adapter = adapter

TabLayoutMediator(binding.tabLayout, binding.viewPager) { tab, position ->
when (position) {
Expand All @@ -37,12 +38,13 @@ class MainActivity : AppCompatActivity() {

class FragmentsAdapter(activity: AppCompatActivity) :
androidx.viewpager2.adapter.FragmentStateAdapter(activity) {

override fun getItemCount(): Int = 2

override fun createFragment(position: Int): Fragment {
return when (position) {
0 -> SettingsFragment.newInstance()
else -> MessagesListFragment.newInstance()
else -> HolderFragment.newInstance()
}
}

Expand Down
4 changes: 2 additions & 2 deletions app/src/main/java/me/capcom/smsgateway/data/dao/MessageDao.kt
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
package me.capcom.smsgateway.data.dao

import androidx.lifecycle.LiveData
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Transaction
import kotlinx.coroutines.flow.Flow
import me.capcom.smsgateway.data.entities.Message
import me.capcom.smsgateway.data.entities.MessageRecipient
import me.capcom.smsgateway.data.entities.MessageWithRecipients

@Dao
interface MessageDao {
@Query("SELECT * FROM message ORDER BY createdAt DESC LIMIT 50")
fun selectLast(): Flow<List<Message>>
fun selectLast(): LiveData<List<Message>>

@Transaction
@Query("SELECT * FROM message WHERE id = :id")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package me.capcom.smsgateway.modules.messages

import me.capcom.smsgateway.modules.messages.repositories.MessagesRepository
import me.capcom.smsgateway.modules.messages.vm.MessageDetailsViewModel
import me.capcom.smsgateway.modules.messages.vm.MessagesListViewModel
import org.koin.androidx.viewmodel.dsl.viewModel
import org.koin.dsl.module
Expand All @@ -9,4 +10,5 @@ val messagesModule = module {
single { MessagesRepository(get()) }
single { MessagesService(get(), get()) }
viewModel { MessagesListViewModel(get()) }
viewModel { MessageDetailsViewModel(get()) }
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package me.capcom.smsgateway.modules.messages.repositories

import androidx.lifecycle.distinctUntilChanged
import me.capcom.smsgateway.data.dao.MessageDao

class MessagesRepository(private val dao: MessageDao) {
val lastMessages = dao.selectLast()
val lastMessages = dao.selectLast().distinctUntilChanged()

fun get(id: String) = dao.get(id)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package me.capcom.smsgateway.modules.messages.vm

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import me.capcom.smsgateway.data.entities.MessageWithRecipients
import me.capcom.smsgateway.modules.messages.repositories.MessagesRepository

class MessageDetailsViewModel(
private val messagesRepo: MessagesRepository
) : ViewModel() {
private val _message = MutableLiveData<MessageWithRecipients>()
val message: LiveData<MessageWithRecipients> = _message

fun get(id: String) {
viewModelScope.launch(Dispatchers.IO) {
_message.postValue(messagesRepo.get(id))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,12 @@ package me.capcom.smsgateway.modules.messages.vm

import androidx.lifecycle.LiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.asLiveData
import kotlinx.coroutines.flow.distinctUntilChanged
import me.capcom.smsgateway.data.entities.Message
import me.capcom.smsgateway.modules.messages.repositories.MessagesRepository

class MessagesListViewModel(
messagesRepo: MessagesRepository
) : ViewModel() {
val messages: LiveData<List<Message>> =
messagesRepo.lastMessages.distinctUntilChanged().asLiveData()
messagesRepo.lastMessages
}
42 changes: 42 additions & 0 deletions app/src/main/java/me/capcom/smsgateway/ui/HolderFragment.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package me.capcom.smsgateway.ui

import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.fragment.app.commit
import me.capcom.smsgateway.R

class HolderFragment : Fragment() {
override fun onAttach(context: Context) {
super.onAttach(context)
parentFragmentManager.commit {
setPrimaryNavigationFragment(this@HolderFragment)
}
}

override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_holder, container, false)
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

childFragmentManager.commit {
add(R.id.rootLayout, MessagesListFragment.newInstance())
}
}

companion object {
fun newInstance() =
HolderFragment().apply {

}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package me.capcom.smsgateway.ui

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.DividerItemDecoration
import me.capcom.smsgateway.databinding.FragmentMessageDetailsBinding
import me.capcom.smsgateway.modules.messages.vm.MessageDetailsViewModel
import me.capcom.smsgateway.ui.adapters.MessageRecipientsAdapter
import org.koin.androidx.viewmodel.ext.android.viewModel

class MessageDetailsFragment : Fragment() {
private val viewModel: MessageDetailsViewModel by viewModel()
private var _binding: FragmentMessageDetailsBinding? = null
private val binding get() = _binding!!

private val id: String
get() = requireNotNull(requireArguments().getString(ARG_ID)) { "id is null" }

private val recipientsAdapter by lazy { MessageRecipientsAdapter() }

override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
_binding = FragmentMessageDetailsBinding.inflate(inflater, container, false)
return binding.root
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

binding.recyclerViewRecipients.adapter = recipientsAdapter
binding.recyclerViewRecipients.addItemDecoration(
DividerItemDecoration(requireContext(), DividerItemDecoration.VERTICAL)
)

viewModel.message.observe(viewLifecycleOwner) {
binding.textMessageId.text = it.message.id
binding.textMessage.text = it.message.text
binding.textMessageState.text = it.state.name
recipientsAdapter.submitList(it.recipients)
}
viewModel.get(id)
}

override fun onDestroyView() {
super.onDestroyView()
_binding = null
}

companion object {
private const val ARG_ID = "id"
fun newInstance(id: String) =
MessageDetailsFragment().apply {
arguments = Bundle().apply {
putString(ARG_ID, id)
}
}
}
}
14 changes: 12 additions & 2 deletions app/src/main/java/me/capcom/smsgateway/ui/MessagesListFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,19 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.fragment.app.commit
import androidx.recyclerview.widget.DividerItemDecoration
import me.capcom.smsgateway.R
import me.capcom.smsgateway.data.entities.Message
import me.capcom.smsgateway.databinding.FragmentMessagesListBinding
import me.capcom.smsgateway.modules.messages.vm.MessagesListViewModel
import me.capcom.smsgateway.ui.adapters.MessagesAdapter
import org.koin.androidx.viewmodel.ext.android.viewModel

class MessagesListFragment : Fragment() {
class MessagesListFragment : Fragment(), MessagesAdapter.OnItemClickListener<Message> {

private val viewModel: MessagesListViewModel by viewModel()
private val messagesAdapter = MessagesAdapter()
private val messagesAdapter = MessagesAdapter(this)
private var _binding: FragmentMessagesListBinding? = null
private val binding get() = _binding!!

Expand Down Expand Up @@ -55,4 +58,11 @@ class MessagesListFragment : Fragment() {
fun newInstance() =
MessagesListFragment()
}

override fun onItemClick(item: Message) {
parentFragmentManager.commit {
replace(R.id.rootLayout, MessageDetailsFragment.newInstance(item.id))
addToBackStack(null)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package me.capcom.smsgateway.ui.adapters

import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import me.capcom.smsgateway.data.entities.MessageRecipient
import me.capcom.smsgateway.databinding.ItemMessageBinding
import me.capcom.smsgateway.ui.styles.color

class MessageRecipientsAdapter :
ListAdapter<MessageRecipient, MessageRecipientsAdapter.RecipientViewHolder>(
RecipientsDiffCallback()
) {

override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): RecipientViewHolder {
return RecipientViewHolder.create(parent)
}

override fun onBindViewHolder(holder: RecipientViewHolder, position: Int) {
val message = getItem(position)

holder.bind(message)
}

class RecipientViewHolder(private val binding: ItemMessageBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(recipient: MessageRecipient) {
binding.textViewId.text = recipient.phoneNumber
binding.textViewState.text = recipient.state.name
binding.textViewDate.text = recipient.error
binding.imageViewState.setColorFilter(recipient.state.color)
}

companion object {
fun create(parent: ViewGroup): RecipientViewHolder {
return RecipientViewHolder(
ItemMessageBinding.inflate(
LayoutInflater.from(parent.context), parent, false
)
)
}
}
}

class RecipientsDiffCallback :
androidx.recyclerview.widget.DiffUtil.ItemCallback<MessageRecipient>() {
override fun areItemsTheSame(
oldItem: MessageRecipient,
newItem: MessageRecipient
): Boolean {
return oldItem.phoneNumber == newItem.phoneNumber
}

override fun areContentsTheSame(
oldItem: MessageRecipient,
newItem: MessageRecipient
): Boolean {
return oldItem == newItem
}
}
}
Original file line number Diff line number Diff line change
@@ -1,20 +1,27 @@
package me.capcom.smsgateway.ui.adapters

import android.graphics.Color
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import me.capcom.smsgateway.data.entities.Message
import me.capcom.smsgateway.databinding.ItemMessageBinding
import me.capcom.smsgateway.ui.styles.color
import java.text.DateFormat
import java.util.Date

class MessagesAdapter :
class MessagesAdapter(
private val onItemClickListener: OnItemClickListener<Message>
) :
ListAdapter<Message, MessagesAdapter.MessageViewHolder>(MessageDiffCallback()) {

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MessageViewHolder {
return MessageViewHolder.create(parent)
return MessageViewHolder.create(parent).also { holder ->
holder.itemView.setOnClickListener {
val message = getItem(holder.adapterPosition)
onItemClickListener.onItemClick(message)
}
}
}

override fun onBindViewHolder(holder: MessageViewHolder, position: Int) {
Expand All @@ -30,15 +37,7 @@ class MessagesAdapter :
binding.textViewDate.text =
DateFormat.getDateTimeInstance().format(Date(message.createdAt))
binding.textViewState.text = message.state.name
val tintColor = when (message.state) {
Message.State.Pending -> Color.parseColor("#FFBB86FC")
Message.State.Processed -> Color.parseColor("#FF6200EE")
Message.State.Sent -> Color.parseColor("#FF3700B3")
Message.State.Delivered -> Color.parseColor("#FF03DAC5")
Message.State.Failed -> Color.parseColor("#FF018786")
}

binding.imageViewState.setColorFilter(tintColor)
binding.imageViewState.setColorFilter(message.state.color)
}

companion object {
Expand All @@ -61,4 +60,8 @@ class MessagesAdapter :
return oldItem == newItem
}
}

interface OnItemClickListener<T> {
fun onItemClick(item: T)
}
}
Loading

0 comments on commit f8031cf

Please sign in to comment.