From cc8ee248a225f8e3c7aae900d657ba5fd3e30c28 Mon Sep 17 00:00:00 2001 From: oussamah Date: Tue, 6 Jul 2021 09:21:19 +0100 Subject: [PATCH 01/15] handle navigation from conversationList screen to conversation screen --- app/src/main/AndroidManifest.xml | 10 ++++- .../com/wire/android/core/di/Modules.kt | 2 +- .../android/core/ui/navigation/Navigator.kt | 4 +- .../navigation/ConversationNavigator.kt | 9 +++++ .../content/ui/ConversationActivity.kt | 37 +++++++++++++++++++ .../content/ui/ConversationFragment.kt | 7 ++++ .../conversation/di/ConversationsModule.kt | 13 ++++++- .../list/ui/ConversationListAdapter.kt | 5 ++- .../list/ui/ConversationListFragment.kt | 12 +++++- .../list/ui/ConversationViewHolder.kt | 9 ++++- .../main/res/layout/activity_conversation.xml | 28 ++++++++++++++ .../res/layout/conversation_list_item.xml | 1 + .../main/res/layout/fragment_conversation.xml | 7 ++++ .../navigation/ConversationNavigatorTest.kt | 34 +++++++++++++++++ .../list/ui/ConversationListAdapterTest.kt | 7 +++- 15 files changed, 173 insertions(+), 12 deletions(-) create mode 100644 app/src/main/kotlin/com/wire/android/feature/conversation/content/navigation/ConversationNavigator.kt create mode 100644 app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationActivity.kt create mode 100644 app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationFragment.kt create mode 100644 app/src/main/res/layout/activity_conversation.xml create mode 100644 app/src/main/res/layout/fragment_conversation.xml create mode 100644 app/src/test/kotlin/com/wire/android/feature/conversation/content/navigation/ConversationNavigatorTest.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 513fef6c10..6721962be8 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -39,8 +39,9 @@ android:name=".feature.auth.login.LoginActivity" android:theme="@style/AppTheme.Authentication" /> - + + + + \ No newline at end of file diff --git a/app/src/main/kotlin/com/wire/android/core/di/Modules.kt b/app/src/main/kotlin/com/wire/android/core/di/Modules.kt index 17f7a9357e..c4fdd54b1c 100644 --- a/app/src/main/kotlin/com/wire/android/core/di/Modules.kt +++ b/app/src/main/kotlin/com/wire/android/core/di/Modules.kt @@ -65,7 +65,7 @@ val uiModule = module { factory { MaterialDialogBuilderProvider() } factory { DialogBuilder(get()) } - single { Navigator(get(), get(), get(), get(), get()) } + single { Navigator(get(), get(), get(), get(), get(), get()) } single { FragmentStackHandler() } single { UriNavigationHandler() } diff --git a/app/src/main/kotlin/com/wire/android/core/ui/navigation/Navigator.kt b/app/src/main/kotlin/com/wire/android/core/ui/navigation/Navigator.kt index 31a32058ba..2efc4cab9b 100644 --- a/app/src/main/kotlin/com/wire/android/core/ui/navigation/Navigator.kt +++ b/app/src/main/kotlin/com/wire/android/core/ui/navigation/Navigator.kt @@ -2,6 +2,7 @@ package com.wire.android.core.ui.navigation import com.wire.android.feature.auth.login.ui.navigation.LoginNavigator import com.wire.android.feature.auth.registration.ui.navigation.CreateAccountNavigator +import com.wire.android.feature.conversation.content.navigation.ConversationNavigator import com.wire.android.feature.conversation.list.ui.navigation.MainNavigator import com.wire.android.feature.profile.ui.ProfileNavigator import com.wire.android.feature.welcome.ui.navigation.WelcomeNavigator @@ -11,5 +12,6 @@ class Navigator( val createAccount: CreateAccountNavigator, val login: LoginNavigator, val main: MainNavigator, - val profile: ProfileNavigator + val profile: ProfileNavigator, + val conversation: ConversationNavigator ) diff --git a/app/src/main/kotlin/com/wire/android/feature/conversation/content/navigation/ConversationNavigator.kt b/app/src/main/kotlin/com/wire/android/feature/conversation/content/navigation/ConversationNavigator.kt new file mode 100644 index 0000000000..2b15ea20ad --- /dev/null +++ b/app/src/main/kotlin/com/wire/android/feature/conversation/content/navigation/ConversationNavigator.kt @@ -0,0 +1,9 @@ +package com.wire.android.feature.conversation.content.navigation + +import android.content.Context +import com.wire.android.feature.conversation.content.ui.ConversationActivity + +class ConversationNavigator { + fun openConversationScreen(context: Context, conversationId: String, conversationTitle: String) = + context.startActivity(ConversationActivity.newIntent(context, conversationId, conversationTitle)) +} diff --git a/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationActivity.kt b/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationActivity.kt new file mode 100644 index 0000000000..6e1cc11aef --- /dev/null +++ b/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationActivity.kt @@ -0,0 +1,37 @@ +package com.wire.android.feature.conversation.content.ui + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import com.wire.android.R +import kotlinx.android.synthetic.main.activity_conversation.* + +class ConversationActivity : AppCompatActivity(R.layout.activity_conversation) { + + private val conversationTitle get() = intent.getStringExtra(ARG_CONVERSATION_TITLE) + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setUpBackNavigation() + setUpConversationTitle() + } + + private fun setUpBackNavigation() { + conversationToolbar.setNavigationOnClickListener { onBackPressed() } + } + + private fun setUpConversationTitle() { + conversationToolbar.title = conversationTitle + } + + companion object { + fun newIntent(context: Context, conversationId: String, conversationTitle: String) = + Intent(context, ConversationActivity::class.java).apply { + putExtra(ARG_CONVERSATION_ID, conversationId) + putExtra(ARG_CONVERSATION_TITLE, conversationTitle) + } + private const val ARG_CONVERSATION_ID = "conversation-id-arg" + private const val ARG_CONVERSATION_TITLE = "conversation-title-arg" + } +} diff --git a/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationFragment.kt b/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationFragment.kt new file mode 100644 index 0000000000..80661058e4 --- /dev/null +++ b/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationFragment.kt @@ -0,0 +1,7 @@ +package com.wire.android.feature.conversation.content.ui + +import androidx.fragment.app.Fragment +import com.wire.android.R + +//TODO to implement in next PR +class ConversationFragment : Fragment(R.layout.fragment_conversation) diff --git a/app/src/main/kotlin/com/wire/android/feature/conversation/di/ConversationsModule.kt b/app/src/main/kotlin/com/wire/android/feature/conversation/di/ConversationsModule.kt index f0694587fc..008fa4954e 100644 --- a/app/src/main/kotlin/com/wire/android/feature/conversation/di/ConversationsModule.kt +++ b/app/src/main/kotlin/com/wire/android/feature/conversation/di/ConversationsModule.kt @@ -9,8 +9,8 @@ import com.wire.android.feature.conversation.content.datasources.MessageDataSour import com.wire.android.feature.conversation.content.datasources.local.MessageLocalDataSource import com.wire.android.feature.conversation.content.mapper.MessageMapper import com.wire.android.feature.conversation.content.mapper.MessageStateMapper -import com.wire.android.core.date.DateStringMapper import com.wire.android.feature.conversation.content.mapper.MessageTypeMapper +import com.wire.android.feature.conversation.content.navigation.ConversationNavigator import com.wire.android.feature.conversation.data.ConversationDataSource import com.wire.android.feature.conversation.data.ConversationMapper import com.wire.android.feature.conversation.data.ConversationRepository @@ -25,6 +25,7 @@ import com.wire.android.feature.conversation.list.datasources.ConversationListMa import com.wire.android.feature.conversation.list.datasources.local.ConversationListLocalDataSource import com.wire.android.feature.conversation.list.ui.ConversationListAdapter import com.wire.android.feature.conversation.list.ui.ConversationListDiffCallback +import com.wire.android.feature.conversation.list.ui.ConversationListItem import com.wire.android.feature.conversation.list.ui.ConversationListViewModel import com.wire.android.feature.conversation.list.ui.icon.ConversationIconProvider import com.wire.android.feature.conversation.list.ui.navigation.MainNavigator @@ -59,7 +60,14 @@ val conversationsModule = module { } val conversationListModule = module { - factory { ConversationListAdapter(get(), get(), get()) } + factory { (param: (conversationListItem: ConversationListItem?) -> Unit) -> + ConversationListAdapter( + get(), + get(), + get(), + clickListener = param + ) + } factory { ConversationListDiffCallback() } viewModel { ConversationListViewModel(get(), get(), get(), get(), get()) } @@ -84,4 +92,5 @@ val conversationContentModule = module { factory { MessageStateMapper() } factory { MessageMapper(get(), get(), get()) } factory { MessageDataSource(get(), get()) } + single { ConversationNavigator() } } diff --git a/app/src/main/kotlin/com/wire/android/feature/conversation/list/ui/ConversationListAdapter.kt b/app/src/main/kotlin/com/wire/android/feature/conversation/list/ui/ConversationListAdapter.kt index 5f6043ae0a..172066b538 100644 --- a/app/src/main/kotlin/com/wire/android/feature/conversation/list/ui/ConversationListAdapter.kt +++ b/app/src/main/kotlin/com/wire/android/feature/conversation/list/ui/ConversationListAdapter.kt @@ -9,14 +9,15 @@ import com.wire.android.feature.conversation.list.ui.icon.ConversationIconProvid class ConversationListAdapter( private val viewHolderInflater: ViewHolderInflater, diffCallback: ConversationListDiffCallback, - private val iconProvider: ConversationIconProvider + private val iconProvider: ConversationIconProvider, + private val clickListener: (conversationListItem: ConversationListItem?) -> Unit ) : PagingDataAdapter(diffCallback) { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ConversationViewHolder = ConversationViewHolder(parent, viewHolderInflater, iconProvider) override fun onBindViewHolder(holder: ConversationViewHolder, position: Int) { - getItem(position)?.let { holder.bind(it) } //TODO what does null mean? + getItem(position)?.let { holder.bind(it, clickListener) } //TODO what does null mean? } } diff --git a/app/src/main/kotlin/com/wire/android/feature/conversation/list/ui/ConversationListFragment.kt b/app/src/main/kotlin/com/wire/android/feature/conversation/list/ui/ConversationListFragment.kt index b7fb51fb89..5c947e1496 100644 --- a/app/src/main/kotlin/com/wire/android/feature/conversation/list/ui/ConversationListFragment.kt +++ b/app/src/main/kotlin/com/wire/android/feature/conversation/list/ui/ConversationListFragment.kt @@ -20,12 +20,15 @@ import kotlinx.android.synthetic.main.fragment_conversation_list.* import kotlinx.coroutines.launch import org.koin.android.ext.android.inject import org.koin.android.viewmodel.ext.android.viewModel +import org.koin.core.parameter.parametersOf class ConversationListFragment : Fragment(R.layout.fragment_conversation_list) { private val viewModel by viewModel() - private val conversationListAdapter by inject() + private val conversationListAdapter by inject{ + parametersOf({ conversationListItem : ConversationListItem -> conversationItemClickListener(conversationListItem) }) + } private val navigator by inject() @@ -112,6 +115,13 @@ class ConversationListFragment : Fragment(R.layout.fragment_conversation_list) { private fun subscribeToEvents() = viewModel.subscribeToEvents() + private fun conversationItemClickListener(conversationListItem: ConversationListItem?) { + activity?.let { + if(conversationListItem != null && conversationListItem.conversation.name != null) + navigator.conversation.openConversationScreen(it, conversationListItem.conversation.id, conversationListItem.conversation.name) + } + } + companion object { fun newInstance() = ConversationListFragment() } diff --git a/app/src/main/kotlin/com/wire/android/feature/conversation/list/ui/ConversationViewHolder.kt b/app/src/main/kotlin/com/wire/android/feature/conversation/list/ui/ConversationViewHolder.kt index 1d8a372d8b..bb7bf773a1 100644 --- a/app/src/main/kotlin/com/wire/android/feature/conversation/list/ui/ConversationViewHolder.kt +++ b/app/src/main/kotlin/com/wire/android/feature/conversation/list/ui/ConversationViewHolder.kt @@ -3,6 +3,7 @@ package com.wire.android.feature.conversation.list.ui import android.view.ViewGroup import android.widget.FrameLayout import android.widget.TextView +import androidx.constraintlayout.widget.ConstraintLayout import androidx.recyclerview.widget.RecyclerView import com.google.android.material.imageview.ShapeableImageView import com.wire.android.R @@ -16,17 +17,23 @@ class ConversationViewHolder( private val iconProvider: ConversationIconProvider ) : RecyclerView.ViewHolder(inflater.inflate(R.layout.conversation_list_item, parent)) { + private val conversationItemLayout by lazyFind(R.id.conversationItemLayout) + private val nameTextView by lazyFind(R.id.conversationItemNameTextView) private val iconLayout by lazyFind(R.id.conversationItemIconLayout) private val iconImageView by lazyFind(R.id.conversationItemIconImageView) - fun bind(item: ConversationListItem) { + fun bind(item: ConversationListItem, clickListener: (conversationListItem: ConversationListItem?) -> Unit) { val name = item.conversation.name.orEmpty() //TODO: handle empty name case properly nameTextView.text = name displayConversationIcon(item) + + conversationItemLayout.setOnClickListener { + clickListener(item) + } } private fun displayConversationIcon(item: ConversationListItem) { diff --git a/app/src/main/res/layout/activity_conversation.xml b/app/src/main/res/layout/activity_conversation.xml new file mode 100644 index 0000000000..0234ebff18 --- /dev/null +++ b/app/src/main/res/layout/activity_conversation.xml @@ -0,0 +1,28 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/conversation_list_item.xml b/app/src/main/res/layout/conversation_list_item.xml index 3b12d38bcf..49cd50d93e 100644 --- a/app/src/main/res/layout/conversation_list_item.xml +++ b/app/src/main/res/layout/conversation_list_item.xml @@ -2,6 +2,7 @@ + + + \ No newline at end of file diff --git a/app/src/test/kotlin/com/wire/android/feature/conversation/content/navigation/ConversationNavigatorTest.kt b/app/src/test/kotlin/com/wire/android/feature/conversation/content/navigation/ConversationNavigatorTest.kt new file mode 100644 index 0000000000..c79c03ea90 --- /dev/null +++ b/app/src/test/kotlin/com/wire/android/feature/conversation/content/navigation/ConversationNavigatorTest.kt @@ -0,0 +1,34 @@ +package com.wire.android.feature.conversation.content.navigation + +import android.app.Activity +import android.content.Intent +import com.wire.android.AndroidTest +import com.wire.android.core.extension.EMPTY +import com.wire.android.feature.conversation.content.ui.ConversationActivity +import io.mockk.mockk +import io.mockk.slot +import io.mockk.verify +import org.amshove.kluent.shouldBeEqualTo +import org.junit.Before +import org.junit.Test + +class ConversationNavigatorTest : AndroidTest() { + + private lateinit var conversationNavigator: ConversationNavigator + + @Before + fun setUp() { + conversationNavigator = ConversationNavigator() + } + + @Test + fun `given openConversationScreen is called, then opens ConversationActivity`() { + val activity = mockk(relaxed = true) + + conversationNavigator.openConversationScreen(activity, String.EMPTY, String.EMPTY) + + val intentSlot = slot() + verify(exactly = 1) { activity.startActivity(capture(intentSlot)) } + intentSlot.captured.component?.className shouldBeEqualTo ConversationActivity::class.java.canonicalName + } +} diff --git a/app/src/test/kotlin/com/wire/android/feature/conversation/list/ui/ConversationListAdapterTest.kt b/app/src/test/kotlin/com/wire/android/feature/conversation/list/ui/ConversationListAdapterTest.kt index 0fee7ec5db..0ebf751650 100644 --- a/app/src/test/kotlin/com/wire/android/feature/conversation/list/ui/ConversationListAdapterTest.kt +++ b/app/src/test/kotlin/com/wire/android/feature/conversation/list/ui/ConversationListAdapterTest.kt @@ -26,11 +26,14 @@ class ConversationListAdapterTest : UnitTest() { @MockK private lateinit var diffCallback: ConversationListDiffCallback + @MockK + private lateinit var clickListener : (conversationListItem: ConversationListItem?) -> Unit + private lateinit var conversationListAdapter: ConversationListAdapter @Before fun setUp() { - conversationListAdapter = ConversationListAdapter(viewHolderInflater, diffCallback, mockk()) + conversationListAdapter = ConversationListAdapter(viewHolderInflater, diffCallback, mockk(), clickListener) // conversationListAdapter.updateData(conversationList) } @@ -55,7 +58,7 @@ class ConversationListAdapterTest : UnitTest() { conversationListAdapter.onBindViewHolder(holder, position) verify(exactly = 1) { conversationListItems[position] } - verify(exactly = 1) { holder.bind(item) } + verify(exactly = 1) { holder.bind(item, clickListener) } } @Test From a1368ed9e282e3fa8f04a38965062e8f57f14c9d Mon Sep 17 00:00:00 2001 From: AndroidBob Date: Mon, 5 Jul 2021 14:57:29 +0200 Subject: [PATCH 02/15] added missing line for change --- Jenkinsfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Jenkinsfile b/Jenkinsfile index 13c37382c5..10cf4c5b39 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -2,6 +2,7 @@ pipeline { agent { docker { args '-u 1000:133 --network build-machine -v /var/run/docker.sock:/var/run/docker.sock -e DOCKER_HOST=unix:///var/run/docker.sock' + label 'android-reloaded-builder' image 'android-reloaded-agent:latest' } From 1b9b52d211c8b3bcea4f558d71825b1559caff68 Mon Sep 17 00:00:00 2001 From: oussamah Date: Tue, 6 Jul 2021 11:01:29 +0100 Subject: [PATCH 03/15] fix Static Code Analysis error --- .../conversation/list/ui/ConversationListFragment.kt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/src/main/kotlin/com/wire/android/feature/conversation/list/ui/ConversationListFragment.kt b/app/src/main/kotlin/com/wire/android/feature/conversation/list/ui/ConversationListFragment.kt index 5c947e1496..1cf38cc3a3 100644 --- a/app/src/main/kotlin/com/wire/android/feature/conversation/list/ui/ConversationListFragment.kt +++ b/app/src/main/kotlin/com/wire/android/feature/conversation/list/ui/ConversationListFragment.kt @@ -117,8 +117,12 @@ class ConversationListFragment : Fragment(R.layout.fragment_conversation_list) { private fun conversationItemClickListener(conversationListItem: ConversationListItem?) { activity?.let { - if(conversationListItem != null && conversationListItem.conversation.name != null) - navigator.conversation.openConversationScreen(it, conversationListItem.conversation.id, conversationListItem.conversation.name) + if (conversationListItem != null && conversationListItem.conversation.name != null) + navigator.conversation.openConversationScreen( + it, + conversationListItem.conversation.id, + conversationListItem.conversation.name + ) } } From 4d876af1c7d31b988ce004ec2dc1b4c3a715ee49 Mon Sep 17 00:00:00 2001 From: oussamah Date: Tue, 6 Jul 2021 18:25:26 +0100 Subject: [PATCH 04/15] center toolbar title in conversation screen --- app/src/main/AndroidManifest.xml | 3 +-- .../conversation/content/ui/ConversationActivity.kt | 6 ++++-- app/src/main/res/layout/activity_conversation.xml | 10 +++++++++- app/src/main/res/values/styles_textview.xml | 6 ++++++ 4 files changed, 20 insertions(+), 5 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 6721962be8..e92d72122b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -52,8 +52,7 @@ android:theme="@style/AppTheme.Authentication" /> + android:name=".feature.conversation.content.ui.ConversationActivity" /> diff --git a/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationActivity.kt b/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationActivity.kt index 6e1cc11aef..51903fae10 100644 --- a/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationActivity.kt +++ b/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationActivity.kt @@ -13,8 +13,8 @@ class ConversationActivity : AppCompatActivity(R.layout.activity_conversation) { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setUpBackNavigation() setUpConversationTitle() + setUpBackNavigation() } private fun setUpBackNavigation() { @@ -22,7 +22,9 @@ class ConversationActivity : AppCompatActivity(R.layout.activity_conversation) { } private fun setUpConversationTitle() { - conversationToolbar.title = conversationTitle + setSupportActionBar(conversationToolbar) + conversationToolbarTitleTextView.text = conversationTitle + supportActionBar?.setDisplayShowTitleEnabled(false) } companion object { diff --git a/app/src/main/res/layout/activity_conversation.xml b/app/src/main/res/layout/activity_conversation.xml index 0234ebff18..3cc7a63658 100644 --- a/app/src/main/res/layout/activity_conversation.xml +++ b/app/src/main/res/layout/activity_conversation.xml @@ -13,7 +13,15 @@ app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" - app:navigationIcon="?homeAsUpIndicator" /> + app:navigationIcon="?homeAsUpIndicator"> + + + 32sp + + From f72e925d03a911c87669d839eb80f35dfc10af16 Mon Sep 17 00:00:00 2001 From: oussamah Date: Fri, 9 Jul 2021 00:32:54 +0100 Subject: [PATCH 05/15] show conversation history in chat screen --- .../datasources/local/MessageDaoTest.kt | 24 ++++++++- .../android/core/events/di/EventModule.kt | 2 +- .../datasources/mapper/ContactMapper.kt | 2 +- .../conversation/content/MessageRepository.kt | 3 +- .../content/datasources/MessageDataSource.kt | 14 ++++-- .../local/MessageAndContactEntity.kt | 15 ++++++ .../content/datasources/local/MessageDao.kt | 4 +- .../local/MessageLocalDataSource.kt | 2 +- .../content/ui/ConversationActivity.kt | 9 ++++ .../content/ui/ConversationAdapter.kt | 50 +++++++++++++++++++ .../content/ui/ConversationFragment.kt | 40 ++++++++++++++- .../ui/ConversationTextMessageViewHolder.kt | 28 +++++++++++ .../content/ui/ConversationViewModel.kt | 28 +++++++++++ .../content/ui/MessageAndContact.kt | 9 ++++ .../content/usecase/GetConversationUseCase.kt | 15 ++++++ .../conversation/di/ConversationsModule.kt | 8 ++- .../list/ui/ConversationListFragment.kt | 2 +- .../layout/conversation_chat_item_text.xml | 49 ++++++++++++++++++ .../main/res/layout/fragment_conversation.xml | 12 ++++- app/src/main/res/values-land/dimens.xml | 3 ++ app/src/main/res/values/dimens.xml | 3 ++ app/src/main/res/values/styles_textview.xml | 17 +++++++ .../datasources/mapper/ContactMapperTest.kt | 26 ++++++++++ .../datasources/MessageDataSourceTest.kt | 21 ++++++-- .../local/MessageLocalDataSourceTest.kt | 10 ++-- 25 files changed, 373 insertions(+), 23 deletions(-) create mode 100644 app/src/main/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageAndContactEntity.kt create mode 100644 app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationAdapter.kt create mode 100644 app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationTextMessageViewHolder.kt create mode 100644 app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationViewModel.kt create mode 100644 app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/MessageAndContact.kt create mode 100644 app/src/main/kotlin/com/wire/android/feature/conversation/content/usecase/GetConversationUseCase.kt create mode 100644 app/src/main/res/layout/conversation_chat_item_text.xml diff --git a/app/src/androidTest/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageDaoTest.kt b/app/src/androidTest/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageDaoTest.kt index 19c759da81..8b506fa22a 100644 --- a/app/src/androidTest/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageDaoTest.kt +++ b/app/src/androidTest/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageDaoTest.kt @@ -2,6 +2,8 @@ package com.wire.android.feature.conversation.content.datasources.local import com.wire.android.InstrumentationTest import com.wire.android.core.storage.db.user.UserDatabase +import com.wire.android.feature.contact.datasources.local.ContactDao +import com.wire.android.feature.contact.datasources.local.ContactEntity import com.wire.android.feature.conversation.data.local.ConversationDao import com.wire.android.feature.conversation.data.local.ConversationEntity import com.wire.android.framework.storage.db.DatabaseTestRule @@ -9,6 +11,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.first import kotlinx.coroutines.runBlocking import org.amshove.kluent.shouldBeEqualTo +import org.amshove.kluent.shouldBeInstanceOf import org.junit.Before import org.junit.Rule import org.junit.Test @@ -20,15 +23,18 @@ class MessageDaoTest : InstrumentationTest() { val databaseTestRule = DatabaseTestRule.create(appContext) private lateinit var messageDao: MessageDao + private lateinit var contactDao: ContactDao private lateinit var conversationDao: ConversationDao private lateinit var conversationEntity: ConversationEntity private lateinit var messageEntity: MessageEntity + private lateinit var contactEntity: ContactEntity @Before fun setUp() { val userDatabase = databaseTestRule.database messageDao = userDatabase.messageDao() + contactDao = userDatabase.contactDao() conversationDao = userDatabase.conversationDao() conversationEntity = ConversationEntity(TEST_CONVERSATION_ID, TEST_CONVERSATION_NAME, TEST_CONVERSATION_TYPE) @@ -42,8 +48,15 @@ class MessageDaoTest : InstrumentationTest() { time = TEST_MESSAGE_TIME ) + contactEntity = ContactEntity( + id = TEST_USER_ID, + name = TEST_CONTACT_NAME, + assetKey = TEST_CONTACT_ASSET_KEY + ) + runBlocking { conversationDao.insert(conversationEntity) + contactDao.insert(contactEntity) messageDao.insert(messageEntity) } } @@ -53,8 +66,12 @@ class MessageDaoTest : InstrumentationTest() { runBlocking { val result = messageDao.messagesByConversationId(TEST_CONVERSATION_ID) - result.first().size shouldBeEqualTo 1 - result.first().first() shouldBeEqualTo messageEntity + with(result.first()) { + size shouldBeEqualTo 1 + first() shouldBeInstanceOf MessageAndContactEntity::class + messageEntity shouldBeEqualTo messageEntity + contactEntity shouldBeEqualTo contactEntity + } } } @@ -88,5 +105,8 @@ class MessageDaoTest : InstrumentationTest() { private const val TEST_MESSAGE_CONTENT = "message-content" private const val TEST_MESSAGE_STATE = "message-state" private const val TEST_MESSAGE_TIME = "message-time" + + private const val TEST_CONTACT_NAME = "contact-name" + private const val TEST_CONTACT_ASSET_KEY = "contact-asset-key" } } diff --git a/app/src/main/kotlin/com/wire/android/core/events/di/EventModule.kt b/app/src/main/kotlin/com/wire/android/core/events/di/EventModule.kt index 1ca5948695..f4a780e1d6 100644 --- a/app/src/main/kotlin/com/wire/android/core/events/di/EventModule.kt +++ b/app/src/main/kotlin/com/wire/android/core/events/di/EventModule.kt @@ -29,7 +29,7 @@ val eventModule = module { return scarlet.create() } //TODO hardcoded client to be replaced with current clientId - single { WebSocketConfig("cac5f0abcafab91e") } + single { WebSocketConfig("5d5f22a8b7a38acf") } single { provideWebSocketService( get(), diff --git a/app/src/main/kotlin/com/wire/android/feature/contact/datasources/mapper/ContactMapper.kt b/app/src/main/kotlin/com/wire/android/feature/contact/datasources/mapper/ContactMapper.kt index 9678cac0f3..1657b8cc02 100644 --- a/app/src/main/kotlin/com/wire/android/feature/contact/datasources/mapper/ContactMapper.kt +++ b/app/src/main/kotlin/com/wire/android/feature/contact/datasources/mapper/ContactMapper.kt @@ -22,7 +22,7 @@ class ContactMapper(private val assetMapper: AssetMapper) { fun fromContactEntityList(entityList: List): List = entityList.map { fromContactEntity(it) } - private fun fromContactEntity(entity: ContactEntity): Contact = + fun fromContactEntity(entity: ContactEntity): Contact = Contact( id = entity.id, name = entity.name, diff --git a/app/src/main/kotlin/com/wire/android/feature/conversation/content/MessageRepository.kt b/app/src/main/kotlin/com/wire/android/feature/conversation/content/MessageRepository.kt index 8ffc90d08f..cdf5357d2a 100644 --- a/app/src/main/kotlin/com/wire/android/feature/conversation/content/MessageRepository.kt +++ b/app/src/main/kotlin/com/wire/android/feature/conversation/content/MessageRepository.kt @@ -1,8 +1,9 @@ package com.wire.android.feature.conversation.content +import com.wire.android.feature.conversation.content.ui.MessageAndContact import kotlinx.coroutines.flow.Flow interface MessageRepository { suspend fun decryptMessage(message: Message) - suspend fun conversationMessages(conversationId: String): Flow> + suspend fun conversationMessages(conversationId: String): Flow> } diff --git a/app/src/main/kotlin/com/wire/android/feature/conversation/content/datasources/MessageDataSource.kt b/app/src/main/kotlin/com/wire/android/feature/conversation/content/datasources/MessageDataSource.kt index b303bec5ed..7353255006 100644 --- a/app/src/main/kotlin/com/wire/android/feature/conversation/content/datasources/MessageDataSource.kt +++ b/app/src/main/kotlin/com/wire/android/feature/conversation/content/datasources/MessageDataSource.kt @@ -4,10 +4,12 @@ import android.util.Base64 import com.wire.android.core.crypto.CryptoBoxClient import com.wire.android.core.exception.Failure import com.wire.android.core.functional.Either +import com.wire.android.feature.contact.datasources.mapper.ContactMapper import com.wire.android.feature.conversation.content.Message import com.wire.android.feature.conversation.content.MessageRepository import com.wire.android.feature.conversation.content.datasources.local.MessageLocalDataSource import com.wire.android.feature.conversation.content.mapper.MessageMapper +import com.wire.android.feature.conversation.content.ui.MessageAndContact import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.flow.Flow @@ -17,6 +19,7 @@ import kotlinx.coroutines.launch class MessageDataSource( private val messageLocalDataSource: MessageLocalDataSource, private val messageMapper: MessageMapper, + private val contactMapper: ContactMapper, private val cryptoBoxClient: CryptoBoxClient ) : MessageRepository { @@ -42,8 +45,13 @@ class MessageDataSource( return messageLocalDataSource.save(messageEntity) } - override suspend fun conversationMessages(conversationId: String): Flow> = - messageLocalDataSource.messagesByConversationId(conversationId).map { messages -> - messages.map { messageMapper.fromEntityToMessage(it) } + override suspend fun conversationMessages(conversationId: String): Flow> = + messageLocalDataSource.messagesByConversationId(conversationId).map { messagesWithContact -> + messagesWithContact.map { + MessageAndContact( + messageMapper.fromEntityToMessage(it.messageEntity), + contactMapper.fromContactEntity(it.contactEntity) + ) + } } } diff --git a/app/src/main/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageAndContactEntity.kt b/app/src/main/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageAndContactEntity.kt new file mode 100644 index 0000000000..307241f54f --- /dev/null +++ b/app/src/main/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageAndContactEntity.kt @@ -0,0 +1,15 @@ +package com.wire.android.feature.conversation.content.datasources.local + +import androidx.room.Embedded +import androidx.room.Relation +import com.wire.android.feature.contact.datasources.local.ContactEntity + +class MessageAndContactEntity( + @Embedded + val messageEntity: MessageEntity, + @Relation( + parentColumn = "sender_user_id", + entityColumn = "id" + ) + val contactEntity: ContactEntity +) diff --git a/app/src/main/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageDao.kt b/app/src/main/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageDao.kt index 72ad9df166..7393342139 100644 --- a/app/src/main/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageDao.kt +++ b/app/src/main/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageDao.kt @@ -4,6 +4,8 @@ import androidx.room.Dao import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Query +import androidx.room.Transaction + import kotlinx.coroutines.flow.Flow @Dao @@ -13,5 +15,5 @@ interface MessageDao { suspend fun insert(message: MessageEntity) @Query("SELECT * from message where conversation_id = :conversationId") - fun messagesByConversationId(conversationId: String): Flow> + fun messagesByConversationId(conversationId: String): Flow> } diff --git a/app/src/main/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageLocalDataSource.kt b/app/src/main/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageLocalDataSource.kt index a0235080a9..7082f17bea 100644 --- a/app/src/main/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageLocalDataSource.kt +++ b/app/src/main/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageLocalDataSource.kt @@ -8,6 +8,6 @@ import kotlinx.coroutines.flow.Flow class MessageLocalDataSource(private val messageDao: MessageDao) : DatabaseService { suspend fun save(message: MessageEntity): Either = request { messageDao.insert(message) } - fun messagesByConversationId(conversationId: String): Flow> = + fun messagesByConversationId(conversationId: String): Flow> = messageDao.messagesByConversationId(conversationId) } diff --git a/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationActivity.kt b/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationActivity.kt index 51903fae10..ec46c5e2a0 100644 --- a/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationActivity.kt +++ b/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationActivity.kt @@ -6,15 +6,23 @@ import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import com.wire.android.R import kotlinx.android.synthetic.main.activity_conversation.* +import org.koin.android.viewmodel.ext.android.viewModel class ConversationActivity : AppCompatActivity(R.layout.activity_conversation) { + private val conversationId get() = intent.getStringExtra(ARG_CONVERSATION_ID) private val conversationTitle get() = intent.getStringExtra(ARG_CONVERSATION_TITLE) + private val viewModel by viewModel() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setUpConversationTitle() setUpBackNavigation() + cacheConversationId() + } + + private fun cacheConversationId() { + viewModel.conversationId.value = conversationId } private fun setUpBackNavigation() { @@ -33,6 +41,7 @@ class ConversationActivity : AppCompatActivity(R.layout.activity_conversation) { putExtra(ARG_CONVERSATION_ID, conversationId) putExtra(ARG_CONVERSATION_TITLE, conversationTitle) } + private const val ARG_CONVERSATION_ID = "conversation-id-arg" private const val ARG_CONVERSATION_TITLE = "conversation-title-arg" } diff --git a/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationAdapter.kt b/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationAdapter.kt new file mode 100644 index 0000000000..893a39bbf6 --- /dev/null +++ b/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationAdapter.kt @@ -0,0 +1,50 @@ +package com.wire.android.feature.conversation.content.ui + +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.wire.android.core.ui.recyclerview.ViewHolderInflater + +class ConversationAdapter(private val viewHolderInflater: ViewHolderInflater) : + RecyclerView.Adapter() { + + private var messages: List = ArrayList() + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + return ConversationTextMessageViewHolder(parent, viewHolderInflater) + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + if (getItemViewType(position) == VIEW_TYPE_TEXT_MESSAGE) { + val shouldShowAvatar = shouldShowAvatar(position) + (holder as ConversationTextMessageViewHolder).bind( + (messages[position] as MessageAndContact), + shouldShowAvatar + ) + } + } + + override fun getItemViewType(position: Int): Int { + return when (messages[position]) { + is MessageAndContact -> VIEW_TYPE_TEXT_MESSAGE + else -> VIEW_TYPE_UNKNOWN + } + } + + override fun getItemCount(): Int = messages.size + + fun setList(newItems: List) { + this.messages = newItems + notifyDataSetChanged() + } + + private fun shouldShowAvatar(position: Int): Boolean { + val currentMessage = (messages[position] as MessageAndContact).message + return (position == 0) || + (position > 0 && currentMessage.senderUserId != (messages[position - 1] as MessageAndContact).message.senderUserId) + } + + companion object { + private const val VIEW_TYPE_TEXT_MESSAGE = 10 + private const val VIEW_TYPE_UNKNOWN = -1 + } +} diff --git a/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationFragment.kt b/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationFragment.kt index 80661058e4..bb9a163669 100644 --- a/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationFragment.kt +++ b/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationFragment.kt @@ -1,7 +1,43 @@ package com.wire.android.feature.conversation.content.ui +import android.os.Bundle +import android.view.View import androidx.fragment.app.Fragment +import androidx.fragment.app.activityViewModels +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView import com.wire.android.R +import kotlinx.android.synthetic.main.fragment_conversation.* +import org.koin.android.ext.android.inject -//TODO to implement in next PR -class ConversationFragment : Fragment(R.layout.fragment_conversation) +class ConversationFragment : Fragment(R.layout.fragment_conversation) { + + private val viewModel by activityViewModels() + private val conversationListAdapter by inject() + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + setUpRecycler(view) + observeConversationId() + observeMessages() + } + + private fun observeConversationId() { + viewModel.conversationId.observe(viewLifecycleOwner) { + viewModel.fetchMessages(it) + } + } + + private fun setUpRecycler(view: View) { + val recyclerView: RecyclerView = view.findViewById(R.id.conversationRecyclerView) + recyclerView.layoutManager = LinearLayoutManager(context) + recyclerView.adapter = conversationListAdapter + } + + private fun observeMessages() { + viewModel.conversationMessagesLiveData.observe(viewLifecycleOwner) { + conversationRecyclerView.scrollToPosition(it.size - 1); + conversationListAdapter.setList(it) + } + } +} diff --git a/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationTextMessageViewHolder.kt b/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationTextMessageViewHolder.kt new file mode 100644 index 0000000000..0baaefe244 --- /dev/null +++ b/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationTextMessageViewHolder.kt @@ -0,0 +1,28 @@ +package com.wire.android.feature.conversation.content.ui + +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import com.google.android.material.imageview.ShapeableImageView +import com.wire.android.R +import com.wire.android.core.extension.lazyFind +import com.wire.android.core.ui.recyclerview.ViewHolderInflater + +class ConversationTextMessageViewHolder(parent: ViewGroup, inflater: ViewHolderInflater) : + RecyclerView.ViewHolder(inflater.inflate(R.layout.conversation_chat_item_text, parent)) { + + private val conversationChatItemUsernameTextView by lazyFind(R.id.conversationChatItemUsernameTextView) + private val conversationChatItemTextMessageTextView by lazyFind(R.id.conversationChatItemTextMessageTextView) + private val conversationChatItemUserAvatarImageView by lazyFind(R.id.conversationChatItemUserAvatarImageView) + + fun bind(message: MessageAndContact, shouldShowAvatar: Boolean) { + if (shouldShowAvatar) + conversationChatItemUserAvatarImageView.visibility = View.VISIBLE + else + conversationChatItemUserAvatarImageView.visibility = View.GONE + + conversationChatItemUsernameTextView.text = message.contact.name + conversationChatItemTextMessageTextView.text = message.message.content + } +} diff --git a/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationViewModel.kt b/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationViewModel.kt new file mode 100644 index 0000000000..a942ed8481 --- /dev/null +++ b/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationViewModel.kt @@ -0,0 +1,28 @@ +package com.wire.android.feature.conversation.content.ui + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.wire.android.core.async.DispatcherProvider +import com.wire.android.core.usecase.DefaultUseCaseExecutor +import com.wire.android.core.usecase.UseCaseExecutor +import com.wire.android.feature.conversation.content.usecase.GetConversationUseCase +import com.wire.android.feature.conversation.content.usecase.GetConversationUseCaseParams + +class ConversationViewModel( + override val dispatcherProvider: DispatcherProvider, + private val getConversationUseCase: GetConversationUseCase +) : ViewModel(), UseCaseExecutor by DefaultUseCaseExecutor(dispatcherProvider) { + + val conversationId: MutableLiveData = MutableLiveData() + private val _conversationMessagesLiveData = MutableLiveData>() + val conversationMessagesLiveData: LiveData> = _conversationMessagesLiveData + + fun fetchMessages(conversationId: String) { + val params = GetConversationUseCaseParams(conversationId = conversationId) + getConversationUseCase(viewModelScope, params) { + _conversationMessagesLiveData.value = it + } + } +} diff --git a/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/MessageAndContact.kt b/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/MessageAndContact.kt new file mode 100644 index 0000000000..8072e04c88 --- /dev/null +++ b/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/MessageAndContact.kt @@ -0,0 +1,9 @@ +package com.wire.android.feature.conversation.content.ui + +import com.wire.android.feature.contact.Contact +import com.wire.android.feature.conversation.content.Message + +data class MessageAndContact( + val message: Message, + val contact: Contact +) diff --git a/app/src/main/kotlin/com/wire/android/feature/conversation/content/usecase/GetConversationUseCase.kt b/app/src/main/kotlin/com/wire/android/feature/conversation/content/usecase/GetConversationUseCase.kt new file mode 100644 index 0000000000..cb30cebbe4 --- /dev/null +++ b/app/src/main/kotlin/com/wire/android/feature/conversation/content/usecase/GetConversationUseCase.kt @@ -0,0 +1,15 @@ +package com.wire.android.feature.conversation.content.usecase + +import com.wire.android.core.usecase.ObservableUseCase +import com.wire.android.feature.conversation.content.MessageRepository +import com.wire.android.feature.conversation.content.ui.MessageAndContact +import kotlinx.coroutines.flow.Flow + +class GetConversationUseCase(private val messageRepository: MessageRepository) : + ObservableUseCase, GetConversationUseCaseParams> { + + override suspend fun run(params: GetConversationUseCaseParams): Flow> = + messageRepository.conversationMessages(params.conversationId) +} + +data class GetConversationUseCaseParams(val conversationId: String) diff --git a/app/src/main/kotlin/com/wire/android/feature/conversation/di/ConversationsModule.kt b/app/src/main/kotlin/com/wire/android/feature/conversation/di/ConversationsModule.kt index 7455199072..29276158f2 100644 --- a/app/src/main/kotlin/com/wire/android/feature/conversation/di/ConversationsModule.kt +++ b/app/src/main/kotlin/com/wire/android/feature/conversation/di/ConversationsModule.kt @@ -11,6 +11,9 @@ import com.wire.android.feature.conversation.content.mapper.MessageMapper import com.wire.android.feature.conversation.content.mapper.MessageStateMapper import com.wire.android.feature.conversation.content.mapper.MessageTypeMapper import com.wire.android.feature.conversation.content.navigation.ConversationNavigator +import com.wire.android.feature.conversation.content.ui.ConversationAdapter +import com.wire.android.feature.conversation.content.ui.ConversationViewModel +import com.wire.android.feature.conversation.content.usecase.GetConversationUseCase import com.wire.android.feature.conversation.data.ConversationDataSource import com.wire.android.feature.conversation.data.ConversationMapper import com.wire.android.feature.conversation.data.ConversationRepository @@ -91,6 +94,9 @@ val conversationContentModule = module { factory { MessageTypeMapper() } factory { MessageStateMapper() } factory { MessageMapper(get(), get(), get()) } - factory { MessageDataSource(get(), get(), get()) } + factory { MessageDataSource(get(), get(), get(), get()) } single { ConversationNavigator() } + factory { GetConversationUseCase(get()) } + viewModel { ConversationViewModel(get(), get()) } + factory { ConversationAdapter(get()) } } diff --git a/app/src/main/kotlin/com/wire/android/feature/conversation/list/ui/ConversationListFragment.kt b/app/src/main/kotlin/com/wire/android/feature/conversation/list/ui/ConversationListFragment.kt index 6e269251c3..5760b06f64 100644 --- a/app/src/main/kotlin/com/wire/android/feature/conversation/list/ui/ConversationListFragment.kt +++ b/app/src/main/kotlin/com/wire/android/feature/conversation/list/ui/ConversationListFragment.kt @@ -26,7 +26,7 @@ class ConversationListFragment : Fragment(R.layout.fragment_conversation_list) { private val viewModel by viewModel() - private val conversationListAdapter by inject{ + private val conversationListAdapter by inject { parametersOf({ conversationListItem : ConversationListItem -> conversationItemClickListener(conversationListItem) }) } diff --git a/app/src/main/res/layout/conversation_chat_item_text.xml b/app/src/main/res/layout/conversation_chat_item_text.xml new file mode 100644 index 0000000000..b86f063546 --- /dev/null +++ b/app/src/main/res/layout/conversation_chat_item_text.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_conversation.xml b/app/src/main/res/layout/fragment_conversation.xml index f47bb86bec..c3a2c02377 100644 --- a/app/src/main/res/layout/fragment_conversation.xml +++ b/app/src/main/res/layout/fragment_conversation.xml @@ -1,7 +1,17 @@ + android:background="?android:colorBackground"> + \ No newline at end of file diff --git a/app/src/main/res/values-land/dimens.xml b/app/src/main/res/values-land/dimens.xml index 3db3cc37f7..633245137e 100644 --- a/app/src/main/res/values-land/dimens.xml +++ b/app/src/main/res/values-land/dimens.xml @@ -17,4 +17,7 @@ 8dp 70dp 70dp + + 0.1 + \ No newline at end of file diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index 769b69da6a..d13e2fd1e6 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -101,4 +101,7 @@ 24dp + 32dp + 0.16 + diff --git a/app/src/main/res/values/styles_textview.xml b/app/src/main/res/values/styles_textview.xml index 64ab426bc0..7dacf347cc 100644 --- a/app/src/main/res/values/styles_textview.xml +++ b/app/src/main/res/values/styles_textview.xml @@ -30,7 +30,24 @@ + + + + diff --git a/app/src/test/kotlin/com/wire/android/feature/contact/datasources/mapper/ContactMapperTest.kt b/app/src/test/kotlin/com/wire/android/feature/contact/datasources/mapper/ContactMapperTest.kt index 0c03bd7414..f5aa074f19 100644 --- a/app/src/test/kotlin/com/wire/android/feature/contact/datasources/mapper/ContactMapperTest.kt +++ b/app/src/test/kotlin/com/wire/android/feature/contact/datasources/mapper/ContactMapperTest.kt @@ -1,9 +1,16 @@ package com.wire.android.feature.contact.datasources.mapper import com.wire.android.UnitTest +import com.wire.android.feature.contact.Contact import com.wire.android.feature.contact.datasources.local.ContactEntity import com.wire.android.feature.contact.datasources.remote.ContactResponse +import com.wire.android.feature.conversation.content.Message +import com.wire.android.feature.conversation.content.Sent +import com.wire.android.feature.conversation.content.Text +import com.wire.android.feature.conversation.content.datasources.local.MessageEntity +import com.wire.android.feature.conversation.content.mapper.MessageMapperTest import com.wire.android.framework.collections.second +import com.wire.android.shared.asset.Asset import com.wire.android.shared.asset.PublicAsset import com.wire.android.shared.asset.datasources.remote.AssetResponse import com.wire.android.shared.asset.mapper.AssetMapper @@ -14,6 +21,7 @@ import org.amshove.kluent.shouldBeEqualTo import org.amshove.kluent.shouldBeInstanceOf import org.junit.Before import org.junit.Test +import java.time.OffsetDateTime class ContactMapperTest : UnitTest() { @@ -91,6 +99,24 @@ class ContactMapperTest : UnitTest() { } } + @Test + fun `given fromContactEntity is called, then maps the ContactEntity and returns a Contact`() { + val contactEntity = ContactEntity( + id = TEST_CONTACT_ID_1, + name = TEST_CONTACT_NAME_1, + assetKey = TEST_ASSET_KEY + ) + + val result = contactMapper.fromContactEntity(contactEntity) + + result.let { + it shouldBeInstanceOf Contact::class + it.id shouldBeEqualTo TEST_CONTACT_ID_1 + it.name shouldBeEqualTo TEST_CONTACT_NAME_1 + it.profilePicture shouldBeInstanceOf Asset::class + } + } + companion object { private const val ASSET_SIZE_COMPLETE = "complete" private const val TEST_ASSET_KEY = "asset_key_356" diff --git a/app/src/test/kotlin/com/wire/android/feature/conversation/content/datasources/MessageDataSourceTest.kt b/app/src/test/kotlin/com/wire/android/feature/conversation/content/datasources/MessageDataSourceTest.kt index f553da2e83..b865208a0d 100644 --- a/app/src/test/kotlin/com/wire/android/feature/conversation/content/datasources/MessageDataSourceTest.kt +++ b/app/src/test/kotlin/com/wire/android/feature/conversation/content/datasources/MessageDataSourceTest.kt @@ -12,6 +12,10 @@ import com.wire.android.feature.conversation.content.datasources.local.MessageEn import com.wire.android.feature.conversation.content.datasources.local.MessageLocalDataSource import com.wire.android.feature.conversation.content.mapper.MessageMapper import com.wire.android.core.exception.Failure +import com.wire.android.feature.contact.Contact +import com.wire.android.feature.contact.datasources.local.ContactEntity +import com.wire.android.feature.contact.datasources.mapper.ContactMapper +import com.wire.android.feature.conversation.content.datasources.local.MessageAndContactEntity import io.mockk.impl.annotations.MockK import io.mockk.verify import io.mockk.coVerify @@ -37,6 +41,9 @@ class MessageDataSourceTest : UnitTest() { @MockK private lateinit var messageMapper: MessageMapper + @MockK + private lateinit var contactMapper: ContactMapper + @MockK private lateinit var cryptoBoxClient: CryptoBoxClient @@ -48,7 +55,7 @@ class MessageDataSourceTest : UnitTest() { @Before fun setUp() { - messageDataSource = MessageDataSource(messageLocalDataSource, messageMapper, cryptoBoxClient) + messageDataSource = MessageDataSource(messageLocalDataSource, messageMapper, contactMapper, cryptoBoxClient) } @Test @@ -104,16 +111,24 @@ class MessageDataSourceTest : UnitTest() { @Test fun `given conversationMessages is called, when messageLocalDataSource emits messages, then propagates mapped items`(){ val conversationId = "conversation-id" + val contactEntity = mockk() val messageEntity = mockk() + val messageAndContactEntity = mockk().also { + every { it.messageEntity } returns messageEntity + every { it.contactEntity } returns contactEntity + } val message = mockk() + val contact = mockk() every { messageMapper.fromEntityToMessage(messageEntity) } returns message - coEvery { messageLocalDataSource.messagesByConversationId(conversationId) } returns flowOf(listOf(messageEntity)) + every { contactMapper.fromContactEntity(contactEntity) } returns contact + coEvery { messageLocalDataSource.messagesByConversationId(conversationId) } returns flowOf(listOf(messageAndContactEntity)) runBlocking { val result = messageDataSource.conversationMessages(conversationId) + with(result.first()){ size shouldBeEqualTo 1 - get(0) shouldBeEqualTo message + get(0).message shouldBeEqualTo message } } } diff --git a/app/src/test/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageLocalDataSourceTest.kt b/app/src/test/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageLocalDataSourceTest.kt index 42f3670a1e..6c9e825120 100644 --- a/app/src/test/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageLocalDataSourceTest.kt +++ b/app/src/test/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageLocalDataSourceTest.kt @@ -65,18 +65,18 @@ class MessageLocalDataSourceTest : UnitTest() { @Test fun `given messagesByConversationId is called, when dao emits some messages, then emits a list of messages`() { val conversationId = "conversationId" - val messageEntity1 = mockk() - val messageEntity2 = mockk() + val messageAndContactEntity1 = mockk() + val messageAndContactEntity2 = mockk() - coEvery { messageDao.messagesByConversationId(conversationId) } returns flowOf(listOf(messageEntity1, messageEntity2)) + coEvery { messageDao.messagesByConversationId(conversationId) } returns flowOf(listOf(messageAndContactEntity1, messageAndContactEntity2)) runBlocking { val result = messageLocalDataSource.messagesByConversationId(conversationId) with(result.first()){ size shouldBeEqualTo 2 - get(0) shouldBeEqualTo messageEntity1 - get(1) shouldBeEqualTo messageEntity2 + get(0) shouldBeEqualTo messageAndContactEntity1 + get(1) shouldBeEqualTo messageAndContactEntity2 } } } From b707de11eaa96b5f10ebdffe2bae42f6531f97af Mon Sep 17 00:00:00 2001 From: oussamah Date: Fri, 9 Jul 2021 08:38:51 +0100 Subject: [PATCH 06/15] fix detekt build error --- .../content/datasources/local/MessageLocalDataSourceTest.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/test/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageLocalDataSourceTest.kt b/app/src/test/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageLocalDataSourceTest.kt index 6c9e825120..2765cf4400 100644 --- a/app/src/test/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageLocalDataSourceTest.kt +++ b/app/src/test/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageLocalDataSourceTest.kt @@ -68,7 +68,8 @@ class MessageLocalDataSourceTest : UnitTest() { val messageAndContactEntity1 = mockk() val messageAndContactEntity2 = mockk() - coEvery { messageDao.messagesByConversationId(conversationId) } returns flowOf(listOf(messageAndContactEntity1, messageAndContactEntity2)) + coEvery { messageDao.messagesByConversationId(conversationId) } returns + flowOf(listOf(messageAndContactEntity1, messageAndContactEntity2)) runBlocking { val result = messageLocalDataSource.messagesByConversationId(conversationId) From d5415a042c1fd838114733eb346db73a797de188 Mon Sep 17 00:00:00 2001 From: oussamah Date: Fri, 9 Jul 2021 12:40:39 +0100 Subject: [PATCH 07/15] support dark mode in chat screen --- app/src/main/AndroidManifest.xml | 3 ++- .../kotlin/com/wire/android/core/events/di/EventModule.kt | 2 +- .../conversation/content/ui/ConversationActivity.kt | 2 +- .../conversation/content/ui/ConversationFragment.kt | 2 +- .../conversation/content/ui/ConversationViewModel.kt | 8 +++++++- app/src/main/res/layout/conversation_chat_item_text.xml | 2 +- app/src/main/res/values-night/themes_conversation.xml | 6 ++++++ app/src/main/res/values/themes_conversation.xml | 6 ++++++ .../datasources/local/MessageLocalDataSourceTest.kt | 2 +- 9 files changed, 26 insertions(+), 7 deletions(-) create mode 100644 app/src/main/res/values-night/themes_conversation.xml create mode 100644 app/src/main/res/values/themes_conversation.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e92d72122b..02224efd6f 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -52,7 +52,8 @@ android:theme="@style/AppTheme.Authentication" /> + android:name=".feature.conversation.content.ui.ConversationActivity" + android:theme="@style/AppTheme.Conversation"/> diff --git a/app/src/main/kotlin/com/wire/android/core/events/di/EventModule.kt b/app/src/main/kotlin/com/wire/android/core/events/di/EventModule.kt index f4a780e1d6..a8887a9b6c 100644 --- a/app/src/main/kotlin/com/wire/android/core/events/di/EventModule.kt +++ b/app/src/main/kotlin/com/wire/android/core/events/di/EventModule.kt @@ -29,7 +29,7 @@ val eventModule = module { return scarlet.create() } //TODO hardcoded client to be replaced with current clientId - single { WebSocketConfig("5d5f22a8b7a38acf") } + single { WebSocketConfig("e0259ddff3b4f77f") } single { provideWebSocketService( get(), diff --git a/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationActivity.kt b/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationActivity.kt index ec46c5e2a0..3b9dd0372f 100644 --- a/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationActivity.kt +++ b/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationActivity.kt @@ -22,7 +22,7 @@ class ConversationActivity : AppCompatActivity(R.layout.activity_conversation) { } private fun cacheConversationId() { - viewModel.conversationId.value = conversationId + conversationId?.let { viewModel.cacheConversationId(it) } } private fun setUpBackNavigation() { diff --git a/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationFragment.kt b/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationFragment.kt index bb9a163669..9977b302e8 100644 --- a/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationFragment.kt +++ b/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationFragment.kt @@ -23,7 +23,7 @@ class ConversationFragment : Fragment(R.layout.fragment_conversation) { } private fun observeConversationId() { - viewModel.conversationId.observe(viewLifecycleOwner) { + viewModel.conversationIdLiveData.observe(viewLifecycleOwner) { viewModel.fetchMessages(it) } } diff --git a/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationViewModel.kt b/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationViewModel.kt index a942ed8481..6fe0807b62 100644 --- a/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationViewModel.kt +++ b/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationViewModel.kt @@ -15,10 +15,16 @@ class ConversationViewModel( private val getConversationUseCase: GetConversationUseCase ) : ViewModel(), UseCaseExecutor by DefaultUseCaseExecutor(dispatcherProvider) { - val conversationId: MutableLiveData = MutableLiveData() + private val _conversationIdLiveData: MutableLiveData = MutableLiveData() + val conversationIdLiveData: LiveData = _conversationIdLiveData + private val _conversationMessagesLiveData = MutableLiveData>() val conversationMessagesLiveData: LiveData> = _conversationMessagesLiveData + fun cacheConversationId(conversationId: String) { + _conversationIdLiveData.value = conversationId + } + fun fetchMessages(conversationId: String) { val params = GetConversationUseCaseParams(conversationId = conversationId) getConversationUseCase(viewModelScope, params) { diff --git a/app/src/main/res/layout/conversation_chat_item_text.xml b/app/src/main/res/layout/conversation_chat_item_text.xml index b86f063546..0f23238e34 100644 --- a/app/src/main/res/layout/conversation_chat_item_text.xml +++ b/app/src/main/res/layout/conversation_chat_item_text.xml @@ -18,7 +18,7 @@ android:id="@+id/conversationChatItemUserAvatarImageView" android:layout_width="@dimen/conversation_chat_avatar_size" android:layout_height="@dimen/conversation_chat_avatar_size" - android:background="@color/black" + android:background="?colorSecondary" app:layout_constraintEnd_toStartOf="@id/conversationGuideline" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" diff --git a/app/src/main/res/values-night/themes_conversation.xml b/app/src/main/res/values-night/themes_conversation.xml new file mode 100644 index 0000000000..0b14b74b1d --- /dev/null +++ b/app/src/main/res/values-night/themes_conversation.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/values/themes_conversation.xml b/app/src/main/res/values/themes_conversation.xml new file mode 100644 index 0000000000..dd03bfb0ac --- /dev/null +++ b/app/src/main/res/values/themes_conversation.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/app/src/test/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageLocalDataSourceTest.kt b/app/src/test/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageLocalDataSourceTest.kt index 2765cf4400..9f6ab20afc 100644 --- a/app/src/test/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageLocalDataSourceTest.kt +++ b/app/src/test/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageLocalDataSourceTest.kt @@ -74,7 +74,7 @@ class MessageLocalDataSourceTest : UnitTest() { runBlocking { val result = messageLocalDataSource.messagesByConversationId(conversationId) - with(result.first()){ + with(result.first()) { size shouldBeEqualTo 2 get(0) shouldBeEqualTo messageAndContactEntity1 get(1) shouldBeEqualTo messageAndContactEntity2 From 8825622f6cc8725db35dbbb4939c2ea1ea1d7543 Mon Sep 17 00:00:00 2001 From: Marco Bierbach Date: Fri, 9 Jul 2021 12:21:54 +0200 Subject: [PATCH 08/15] refactored the pipeline and adjusted when and how the unit and acceptance tests reports are stored (#234) --- Jenkinsfile | 46 ++++++++++++---------------------------------- 1 file changed, 12 insertions(+), 34 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 10cf4c5b39..214a443376 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -102,6 +102,7 @@ docker run --privileged --network build-machine -d -e DEVICE="Nexus 5" --name ${ sh './gradlew runUnitTests' } + publishHTML(allowMissing: true, alwaysLinkToLastBuild: true, keepAll: true, reportDir: "app/build/reports/tests/test${flavor}DebugUnitTest/", reportFiles: 'index.html', reportName: 'Unit Test Report', reportTitles: 'Unit Test') } } @@ -141,50 +142,27 @@ docker run --privileged --network build-machine -d -e DEVICE="Nexus 5" --name ${ } stage('Acceptance Tests') { - parallel { - stage('Acceptance Tests') { - steps { - script { - last_started = env.STAGE_NAME - } - - withGradle() { - sh './gradlew runAcceptanceTests' - } - - } + steps { + script { + last_started = env.STAGE_NAME } - stage('Publish Unit Report') { - steps { - echo 'Publish JUnit report' - publishHTML(allowMissing: true, alwaysLinkToLastBuild: true, keepAll: true, reportDir: "app/build/reports/tests/test${flavor}DebugUnitTest/", reportFiles: 'index.html', reportName: 'Unit Test Report', reportTitles: 'Unit Test') - } + withGradle() { + sh './gradlew runAcceptanceTests' } + publishHTML(allowMissing: true, alwaysLinkToLastBuild: true, keepAll: true, reportDir: "app/build/reports/androidTests/connected/flavors/${flavor.toUpperCase()}/", reportFiles: 'index.html', reportName: 'Acceptance Test Report', reportTitles: 'Acceptance Test') } } stage('Assemble') { - parallel { - stage('Assemble') { - steps { - script { - last_started = env.STAGE_NAME - } - - withGradle() { - sh './gradlew assembleApp' - } - - } + steps { + script { + last_started = env.STAGE_NAME } - stage('Publish Acceptance Test') { - steps { - echo 'Publish Acceptance Test' - publishHTML(allowMissing: true, alwaysLinkToLastBuild: true, keepAll: true, reportDir: "app/build/reports/androidTests/connected/flavors/${flavor.toUpperCase()}/", reportFiles: 'index.html', reportName: 'Acceptance Test Report', reportTitles: 'Acceptance Test') - } + withGradle() { + sh './gradlew assembleApp' } } From 51b2dc6db7614d677250bcf38b46282ceab413a4 Mon Sep 17 00:00:00 2001 From: oussamah Date: Fri, 9 Jul 2021 19:23:22 +0100 Subject: [PATCH 09/15] unit test --- .../datasources/local/MessageDaoTest.kt | 11 ++ .../android/core/events/di/EventModule.kt | 2 +- .../contact/datasources/local/ContactDao.kt | 4 + .../content/datasources/local/MessageDao.kt | 2 - .../datasources/local/MessageEntity.kt | 7 + .../content/ui/ConversationAdapter.kt | 6 +- .../datasources/MessageDataSourceTest.kt | 1 + .../content/ui/ConversationAdapterTest.kt | 121 ++++++++++++++++++ .../content/ui/ConversationViewModelTest.kt | 63 +++++++++ .../usecase/GetConversationUseCaseTest.kt | 42 ++++++ 10 files changed, 253 insertions(+), 6 deletions(-) create mode 100644 app/src/test/kotlin/com/wire/android/feature/conversation/content/ui/ConversationAdapterTest.kt create mode 100644 app/src/test/kotlin/com/wire/android/feature/conversation/content/ui/ConversationViewModelTest.kt create mode 100644 app/src/test/kotlin/com/wire/android/feature/conversation/content/usecase/GetConversationUseCaseTest.kt diff --git a/app/src/androidTest/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageDaoTest.kt b/app/src/androidTest/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageDaoTest.kt index 8b506fa22a..520368459e 100644 --- a/app/src/androidTest/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageDaoTest.kt +++ b/app/src/androidTest/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageDaoTest.kt @@ -84,6 +84,17 @@ class MessageDaoTest : InstrumentationTest() { } } + @Test + fun givenAMessageInConversationExists_whenContactIsDeleted_thenMessagesAreDeleted() { + runBlocking { + contactDao.delete(contactEntity) + + val result = messageDao.messagesByConversationId(TEST_CONVERSATION_ID) + + result.first().size shouldBeEqualTo 0 + } + } + @Test fun givenAMessageInConversationExists_whenConversationIsDeleted_thenMessagesAreDeleted() { runBlocking { diff --git a/app/src/main/kotlin/com/wire/android/core/events/di/EventModule.kt b/app/src/main/kotlin/com/wire/android/core/events/di/EventModule.kt index a8887a9b6c..6b7c8da613 100644 --- a/app/src/main/kotlin/com/wire/android/core/events/di/EventModule.kt +++ b/app/src/main/kotlin/com/wire/android/core/events/di/EventModule.kt @@ -29,7 +29,7 @@ val eventModule = module { return scarlet.create() } //TODO hardcoded client to be replaced with current clientId - single { WebSocketConfig("e0259ddff3b4f77f") } + single { WebSocketConfig("6c66c7e14640db8f") } single { provideWebSocketService( get(), diff --git a/app/src/main/kotlin/com/wire/android/feature/contact/datasources/local/ContactDao.kt b/app/src/main/kotlin/com/wire/android/feature/contact/datasources/local/ContactDao.kt index 338cbe7f85..d28599eb30 100644 --- a/app/src/main/kotlin/com/wire/android/feature/contact/datasources/local/ContactDao.kt +++ b/app/src/main/kotlin/com/wire/android/feature/contact/datasources/local/ContactDao.kt @@ -4,6 +4,7 @@ import androidx.room.Dao import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Query +import androidx.room.Delete @Dao interface ContactDao { @@ -19,4 +20,7 @@ interface ContactDao { @Query("SELECT * FROM contact") suspend fun contacts(): List + + @Delete + fun delete(contactEntity: ContactEntity) } diff --git a/app/src/main/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageDao.kt b/app/src/main/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageDao.kt index 7393342139..9082e30483 100644 --- a/app/src/main/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageDao.kt +++ b/app/src/main/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageDao.kt @@ -4,8 +4,6 @@ import androidx.room.Dao import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Query -import androidx.room.Transaction - import kotlinx.coroutines.flow.Flow @Dao diff --git a/app/src/main/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageEntity.kt b/app/src/main/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageEntity.kt index d4d6f47dec..2f8aee3554 100644 --- a/app/src/main/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageEntity.kt +++ b/app/src/main/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageEntity.kt @@ -4,6 +4,7 @@ import androidx.room.ColumnInfo import androidx.room.Entity import androidx.room.ForeignKey import androidx.room.PrimaryKey +import com.wire.android.feature.contact.datasources.local.ContactEntity import com.wire.android.feature.conversation.data.local.ConversationEntity @Entity( @@ -14,6 +15,12 @@ import com.wire.android.feature.conversation.data.local.ConversationEntity parentColumns = arrayOf("id"), childColumns = arrayOf("conversation_id"), onDelete = ForeignKey.CASCADE + ), + ForeignKey( + entity = ContactEntity::class, + parentColumns = arrayOf("id"), + childColumns = arrayOf("sender_user_id"), + onDelete = ForeignKey.CASCADE ) ], ) diff --git a/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationAdapter.kt b/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationAdapter.kt index 893a39bbf6..1321141570 100644 --- a/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationAdapter.kt +++ b/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationAdapter.kt @@ -32,7 +32,7 @@ class ConversationAdapter(private val viewHolderInflater: ViewHolderInflater) : override fun getItemCount(): Int = messages.size - fun setList(newItems: List) { + fun setList(newItems: List) { this.messages = newItems notifyDataSetChanged() } @@ -44,7 +44,7 @@ class ConversationAdapter(private val viewHolderInflater: ViewHolderInflater) : } companion object { - private const val VIEW_TYPE_TEXT_MESSAGE = 10 - private const val VIEW_TYPE_UNKNOWN = -1 + const val VIEW_TYPE_TEXT_MESSAGE = 10 + const val VIEW_TYPE_UNKNOWN = -1 } } diff --git a/app/src/test/kotlin/com/wire/android/feature/conversation/content/datasources/MessageDataSourceTest.kt b/app/src/test/kotlin/com/wire/android/feature/conversation/content/datasources/MessageDataSourceTest.kt index b865208a0d..9d82af1016 100644 --- a/app/src/test/kotlin/com/wire/android/feature/conversation/content/datasources/MessageDataSourceTest.kt +++ b/app/src/test/kotlin/com/wire/android/feature/conversation/content/datasources/MessageDataSourceTest.kt @@ -129,6 +129,7 @@ class MessageDataSourceTest : UnitTest() { with(result.first()){ size shouldBeEqualTo 1 get(0).message shouldBeEqualTo message + get(0).contact shouldBeEqualTo contact } } } diff --git a/app/src/test/kotlin/com/wire/android/feature/conversation/content/ui/ConversationAdapterTest.kt b/app/src/test/kotlin/com/wire/android/feature/conversation/content/ui/ConversationAdapterTest.kt new file mode 100644 index 0000000000..bce1a71fc1 --- /dev/null +++ b/app/src/test/kotlin/com/wire/android/feature/conversation/content/ui/ConversationAdapterTest.kt @@ -0,0 +1,121 @@ +package com.wire.android.feature.conversation.content.ui + +import android.view.ViewGroup +import com.wire.android.UnitTest +import com.wire.android.core.ui.recyclerview.ViewHolderInflater +import com.wire.android.feature.contact.Contact +import com.wire.android.feature.conversation.content.Message +import io.mockk.every +import io.mockk.impl.annotations.MockK +import io.mockk.mockk +import io.mockk.spyk +import io.mockk.verify +import org.amshove.kluent.any +import org.amshove.kluent.shouldBeEqualTo +import org.amshove.kluent.shouldBeInstanceOf +import org.junit.Before +import org.junit.Test + +class ConversationAdapterTest : UnitTest() { + + @MockK + private lateinit var viewHolderInflater: ViewHolderInflater + + @MockK + private lateinit var messages: List + + private lateinit var conversationAdapter: ConversationAdapter + private lateinit var spyAdapter: ConversationAdapter + + @Before + fun setUp() { + conversationAdapter = ConversationAdapter(viewHolderInflater) + spyAdapter = spyk(conversationAdapter) + every { spyAdapter.notifyDataSetChanged() } returns Unit + } + + @Test + fun `given onCreateViewHolder is called, then creates an instance of ConversationTextMessageViewHolder`() { + val parent = mockk() + + val viewHolder = conversationAdapter.onCreateViewHolder(parent, 0) + + viewHolder shouldBeInstanceOf ConversationTextMessageViewHolder::class.java + } + + @Test + fun `given onBindViewHolder is called, when viewType is text message, then calls message holder to bind the item at the position`() { + val holder = mockk(relaxUnitFun = true) + val message = mockk(relaxed = true) + val contact = mockk(relaxed = true) + + val item = mockk().also { + every { it.message } returns message + every { it.contact } returns contact + } + every { messages[TEST_POSITION] } returns item + + val spyAdapterLocal = spyk(conversationAdapter, recordPrivateCalls = true) + every { spyAdapterLocal["shouldShowAvatar"](TEST_POSITION) } returns false + every { spyAdapterLocal.notifyDataSetChanged() } returns Unit + every { spyAdapterLocal.getItemViewType(TEST_POSITION) } returns ConversationAdapter.VIEW_TYPE_TEXT_MESSAGE + spyAdapterLocal.setList(messages) + + spyAdapterLocal.onBindViewHolder(holder, TEST_POSITION) + + verify(exactly = 1) { spyAdapterLocal.notifyDataSetChanged() } + verify(exactly = 1) { spyAdapterLocal.getItemViewType(TEST_POSITION) } + verify(exactly = 1) { messages[TEST_POSITION] } + verify(exactly = 1) { holder.bind(item, false) } + } + + @Test + fun `given onBindViewHolder is called, when viewType is unknown, then do not bind items`() { + val holder = mockk(relaxUnitFun = true) + every { spyAdapter.getItemViewType(any()) } returns ConversationAdapter.VIEW_TYPE_UNKNOWN + + spyAdapter.onBindViewHolder(holder, any()) + + verify(exactly = 0) { holder.bind(any(), false) } + } + + @Test + fun `given getItemViewType is called, when item is unknown, then return VIEW_TYPE_UNKNOWN`() { + spyAdapter.setList(messages) + every { messages[TEST_POSITION] } returns "" + + val result = spyAdapter.getItemViewType(TEST_POSITION) + + result shouldBeEqualTo ConversationAdapter.VIEW_TYPE_UNKNOWN + } + + @Test + fun `given getItemViewType is called, when item is MessageText, then return VIEW_TYPE_TEXT_MESSAGE`() { + val messageAndContact = mockk() + spyAdapter.setList(messages) + every { messages[TEST_POSITION] } returns messageAndContact + + val result = spyAdapter.getItemViewType(TEST_POSITION) + + result shouldBeEqualTo ConversationAdapter.VIEW_TYPE_TEXT_MESSAGE + } + + + @Test + fun `given getItemCount is called, then returns the size of message list`() { + val messages = mockk>().also { + every { it.size } returns TEST_LIST_SIZE + } + spyAdapter.setList(messages) + + val itemCount = spyAdapter.itemCount + + itemCount shouldBeEqualTo TEST_LIST_SIZE + verify { spyAdapter.notifyDataSetChanged() } + } + + private companion object { + private const val TEST_LIST_SIZE = 5 + private const val TEST_POSITION = 3 + } +} diff --git a/app/src/test/kotlin/com/wire/android/feature/conversation/content/ui/ConversationViewModelTest.kt b/app/src/test/kotlin/com/wire/android/feature/conversation/content/ui/ConversationViewModelTest.kt new file mode 100644 index 0000000000..141ab463a6 --- /dev/null +++ b/app/src/test/kotlin/com/wire/android/feature/conversation/content/ui/ConversationViewModelTest.kt @@ -0,0 +1,63 @@ +package com.wire.android.feature.conversation.content.ui + +import com.wire.android.UnitTest +import com.wire.android.feature.conversation.content.usecase.GetConversationUseCase +import com.wire.android.framework.coroutines.CoroutinesTestRule +import com.wire.android.framework.livedata.shouldBeUpdated +import io.mockk.coEvery +import io.mockk.coVerify +import io.mockk.impl.annotations.MockK +import io.mockk.mockk +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.flowOf +import org.amshove.kluent.shouldBeEqualTo +import org.junit.Before +import org.junit.Rule +import org.junit.Test + +@ExperimentalCoroutinesApi +class ConversationViewModelTest : UnitTest() { + + @get:Rule + val coroutinesTestRule = CoroutinesTestRule() + + @MockK + private lateinit var getConversationUseCase: GetConversationUseCase + + private lateinit var conversationViewModel: ConversationViewModel + + @Before + fun setup() { + conversationViewModel = ConversationViewModel( + coroutinesTestRule.dispatcherProvider, + getConversationUseCase + ) + } + + @Test + fun `given cacheConversationId is called, when conversationId is valid, then updates conversationIdLiveData`() { + + conversationViewModel.cacheConversationId(TEST_CONVERSATION_ID) + + conversationViewModel.conversationIdLiveData.shouldBeUpdated { + it shouldBeEqualTo TEST_CONVERSATION_ID + } + } + + @Test + fun `given fetchMessages is called, when getConversationUseCase emits items, then updates conversationMessagesLiveData`() { + val items = mockk>() + coEvery { getConversationUseCase.run(any()) } returns flowOf(items) + + conversationViewModel.fetchMessages(TEST_CONVERSATION_ID) + + conversationViewModel.conversationMessagesLiveData.shouldBeUpdated { + it shouldBeEqualTo items + } + coVerify(exactly = 1) { getConversationUseCase.run(any()) } + } + + companion object { + private const val TEST_CONVERSATION_ID = "conversation-id" + } +} diff --git a/app/src/test/kotlin/com/wire/android/feature/conversation/content/usecase/GetConversationUseCaseTest.kt b/app/src/test/kotlin/com/wire/android/feature/conversation/content/usecase/GetConversationUseCaseTest.kt new file mode 100644 index 0000000000..367afb1fce --- /dev/null +++ b/app/src/test/kotlin/com/wire/android/feature/conversation/content/usecase/GetConversationUseCaseTest.kt @@ -0,0 +1,42 @@ +package com.wire.android.feature.conversation.content.usecase + +import com.wire.android.UnitTest +import com.wire.android.feature.conversation.content.MessageRepository +import com.wire.android.feature.conversation.content.ui.MessageAndContact +import io.mockk.coEvery +import io.mockk.impl.annotations.MockK +import io.mockk.mockk +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.runBlocking +import org.amshove.kluent.shouldBeEqualTo +import org.junit.Before +import org.junit.Test + +class GetConversationUseCaseTest : UnitTest() { + + @MockK + private lateinit var messageRepository: MessageRepository + + private lateinit var getConversationUseCase: GetConversationUseCase + + @Before + fun setUp() { + getConversationUseCase = GetConversationUseCase(messageRepository) + } + + @Test + fun `given run is called, when messageRepository emits items, then propagates items`() { + val items = mockk>() + val params = GetConversationUseCaseParams(conversationId = TEST_CONVERSATION_ID) + coEvery { messageRepository.conversationMessages(any()) } returns flowOf(items) + + val result = runBlocking { getConversationUseCase.run(params) } + + runBlocking { result.first() shouldBeEqualTo items } + } + + companion object { + private const val TEST_CONVERSATION_ID = "conversation-id" + } +} From ffcbfdae4e5d3f4125dfc196be4b4b46ae374572 Mon Sep 17 00:00:00 2001 From: oussamah Date: Fri, 9 Jul 2021 21:26:59 +0100 Subject: [PATCH 10/15] cleanup --- .../content/datasources/local/MessageDaoTest.kt | 8 +------- .../com/wire/android/core/events/di/EventModule.kt | 2 +- .../conversation/content/ui/ConversationActivity.kt | 1 - .../conversation/content/ui/ConversationAdapter.kt | 9 ++++----- .../feature/conversation/content/ui/MessageAndContact.kt | 5 +---- .../conversation/list/ui/ConversationListFragment.kt | 2 +- .../conversation/content/ui/ConversationAdapterTest.kt | 3 --- .../conversation/content/ui/ConversationViewModelTest.kt | 9 ++------- 8 files changed, 10 insertions(+), 29 deletions(-) diff --git a/app/src/androidTest/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageDaoTest.kt b/app/src/androidTest/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageDaoTest.kt index 520368459e..e9b7aaf787 100644 --- a/app/src/androidTest/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageDaoTest.kt +++ b/app/src/androidTest/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageDaoTest.kt @@ -47,12 +47,7 @@ class MessageDaoTest : InstrumentationTest() { state = TEST_MESSAGE_STATE, time = TEST_MESSAGE_TIME ) - - contactEntity = ContactEntity( - id = TEST_USER_ID, - name = TEST_CONTACT_NAME, - assetKey = TEST_CONTACT_ASSET_KEY - ) + contactEntity = ContactEntity(TEST_USER_ID, TEST_CONTACT_NAME, TEST_CONTACT_ASSET_KEY) runBlocking { conversationDao.insert(conversationEntity) @@ -116,7 +111,6 @@ class MessageDaoTest : InstrumentationTest() { private const val TEST_MESSAGE_CONTENT = "message-content" private const val TEST_MESSAGE_STATE = "message-state" private const val TEST_MESSAGE_TIME = "message-time" - private const val TEST_CONTACT_NAME = "contact-name" private const val TEST_CONTACT_ASSET_KEY = "contact-asset-key" } diff --git a/app/src/main/kotlin/com/wire/android/core/events/di/EventModule.kt b/app/src/main/kotlin/com/wire/android/core/events/di/EventModule.kt index 6b7c8da613..1ca5948695 100644 --- a/app/src/main/kotlin/com/wire/android/core/events/di/EventModule.kt +++ b/app/src/main/kotlin/com/wire/android/core/events/di/EventModule.kt @@ -29,7 +29,7 @@ val eventModule = module { return scarlet.create() } //TODO hardcoded client to be replaced with current clientId - single { WebSocketConfig("6c66c7e14640db8f") } + single { WebSocketConfig("cac5f0abcafab91e") } single { provideWebSocketService( get(), diff --git a/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationActivity.kt b/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationActivity.kt index 3b9dd0372f..c8245d31d5 100644 --- a/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationActivity.kt +++ b/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationActivity.kt @@ -41,7 +41,6 @@ class ConversationActivity : AppCompatActivity(R.layout.activity_conversation) { putExtra(ARG_CONVERSATION_ID, conversationId) putExtra(ARG_CONVERSATION_TITLE, conversationTitle) } - private const val ARG_CONVERSATION_ID = "conversation-id-arg" private const val ARG_CONVERSATION_TITLE = "conversation-title-arg" } diff --git a/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationAdapter.kt b/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationAdapter.kt index 1321141570..a290a5a0ce 100644 --- a/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationAdapter.kt +++ b/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationAdapter.kt @@ -4,14 +4,13 @@ import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import com.wire.android.core.ui.recyclerview.ViewHolderInflater -class ConversationAdapter(private val viewHolderInflater: ViewHolderInflater) : - RecyclerView.Adapter() { +class ConversationAdapter(private val viewHolderInflater: ViewHolderInflater) : RecyclerView.Adapter() { private var messages: List = ArrayList() - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { - return ConversationTextMessageViewHolder(parent, viewHolderInflater) - } + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder = + ConversationTextMessageViewHolder(parent, viewHolderInflater) + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { if (getItemViewType(position) == VIEW_TYPE_TEXT_MESSAGE) { diff --git a/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/MessageAndContact.kt b/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/MessageAndContact.kt index 8072e04c88..c6830fa071 100644 --- a/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/MessageAndContact.kt +++ b/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/MessageAndContact.kt @@ -3,7 +3,4 @@ package com.wire.android.feature.conversation.content.ui import com.wire.android.feature.contact.Contact import com.wire.android.feature.conversation.content.Message -data class MessageAndContact( - val message: Message, - val contact: Contact -) +data class MessageAndContact(val message: Message, val contact: Contact) diff --git a/app/src/main/kotlin/com/wire/android/feature/conversation/list/ui/ConversationListFragment.kt b/app/src/main/kotlin/com/wire/android/feature/conversation/list/ui/ConversationListFragment.kt index 5760b06f64..6e269251c3 100644 --- a/app/src/main/kotlin/com/wire/android/feature/conversation/list/ui/ConversationListFragment.kt +++ b/app/src/main/kotlin/com/wire/android/feature/conversation/list/ui/ConversationListFragment.kt @@ -26,7 +26,7 @@ class ConversationListFragment : Fragment(R.layout.fragment_conversation_list) { private val viewModel by viewModel() - private val conversationListAdapter by inject { + private val conversationListAdapter by inject{ parametersOf({ conversationListItem : ConversationListItem -> conversationItemClickListener(conversationListItem) }) } diff --git a/app/src/test/kotlin/com/wire/android/feature/conversation/content/ui/ConversationAdapterTest.kt b/app/src/test/kotlin/com/wire/android/feature/conversation/content/ui/ConversationAdapterTest.kt index bce1a71fc1..6eeff2b84c 100644 --- a/app/src/test/kotlin/com/wire/android/feature/conversation/content/ui/ConversationAdapterTest.kt +++ b/app/src/test/kotlin/com/wire/android/feature/conversation/content/ui/ConversationAdapterTest.kt @@ -48,7 +48,6 @@ class ConversationAdapterTest : UnitTest() { val holder = mockk(relaxUnitFun = true) val message = mockk(relaxed = true) val contact = mockk(relaxed = true) - val item = mockk().also { every { it.message } returns message every { it.contact } returns contact @@ -63,7 +62,6 @@ class ConversationAdapterTest : UnitTest() { spyAdapterLocal.onBindViewHolder(holder, TEST_POSITION) - verify(exactly = 1) { spyAdapterLocal.notifyDataSetChanged() } verify(exactly = 1) { spyAdapterLocal.getItemViewType(TEST_POSITION) } verify(exactly = 1) { messages[TEST_POSITION] } verify(exactly = 1) { holder.bind(item, false) } @@ -111,7 +109,6 @@ class ConversationAdapterTest : UnitTest() { val itemCount = spyAdapter.itemCount itemCount shouldBeEqualTo TEST_LIST_SIZE - verify { spyAdapter.notifyDataSetChanged() } } private companion object { diff --git a/app/src/test/kotlin/com/wire/android/feature/conversation/content/ui/ConversationViewModelTest.kt b/app/src/test/kotlin/com/wire/android/feature/conversation/content/ui/ConversationViewModelTest.kt index 141ab463a6..8dc83d5c7d 100644 --- a/app/src/test/kotlin/com/wire/android/feature/conversation/content/ui/ConversationViewModelTest.kt +++ b/app/src/test/kotlin/com/wire/android/feature/conversation/content/ui/ConversationViewModelTest.kt @@ -28,10 +28,7 @@ class ConversationViewModelTest : UnitTest() { @Before fun setup() { - conversationViewModel = ConversationViewModel( - coroutinesTestRule.dispatcherProvider, - getConversationUseCase - ) + conversationViewModel = ConversationViewModel(coroutinesTestRule.dispatcherProvider, getConversationUseCase) } @Test @@ -51,9 +48,7 @@ class ConversationViewModelTest : UnitTest() { conversationViewModel.fetchMessages(TEST_CONVERSATION_ID) - conversationViewModel.conversationMessagesLiveData.shouldBeUpdated { - it shouldBeEqualTo items - } + conversationViewModel.conversationMessagesLiveData.shouldBeUpdated { it shouldBeEqualTo items } coVerify(exactly = 1) { getConversationUseCase.run(any()) } } From ce02ea172c7e0a8e66eb729f9eda966948e29bc2 Mon Sep 17 00:00:00 2001 From: oussamah Date: Fri, 9 Jul 2021 21:51:05 +0100 Subject: [PATCH 11/15] cleanup --- app/src/main/AndroidManifest.xml | 3 +-- .../content/datasources/local/MessageEntity.kt | 7 ------- .../feature/conversation/content/ui/ConversationAdapter.kt | 1 - app/src/main/res/layout/fragment_conversation.xml | 3 +-- app/src/main/res/values-night/themes_conversation.xml | 6 ------ app/src/main/res/values/themes_conversation.xml | 6 ------ .../datasources/local/MessageLocalDataSourceTest.kt | 2 +- 7 files changed, 3 insertions(+), 25 deletions(-) delete mode 100644 app/src/main/res/values-night/themes_conversation.xml delete mode 100644 app/src/main/res/values/themes_conversation.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 02224efd6f..e92d72122b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -52,8 +52,7 @@ android:theme="@style/AppTheme.Authentication" /> + android:name=".feature.conversation.content.ui.ConversationActivity" /> diff --git a/app/src/main/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageEntity.kt b/app/src/main/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageEntity.kt index 2f8aee3554..d4d6f47dec 100644 --- a/app/src/main/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageEntity.kt +++ b/app/src/main/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageEntity.kt @@ -4,7 +4,6 @@ import androidx.room.ColumnInfo import androidx.room.Entity import androidx.room.ForeignKey import androidx.room.PrimaryKey -import com.wire.android.feature.contact.datasources.local.ContactEntity import com.wire.android.feature.conversation.data.local.ConversationEntity @Entity( @@ -15,12 +14,6 @@ import com.wire.android.feature.conversation.data.local.ConversationEntity parentColumns = arrayOf("id"), childColumns = arrayOf("conversation_id"), onDelete = ForeignKey.CASCADE - ), - ForeignKey( - entity = ContactEntity::class, - parentColumns = arrayOf("id"), - childColumns = arrayOf("sender_user_id"), - onDelete = ForeignKey.CASCADE ) ], ) diff --git a/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationAdapter.kt b/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationAdapter.kt index a290a5a0ce..fa47f03913 100644 --- a/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationAdapter.kt +++ b/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationAdapter.kt @@ -11,7 +11,6 @@ class ConversationAdapter(private val viewHolderInflater: ViewHolderInflater) : override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder = ConversationTextMessageViewHolder(parent, viewHolderInflater) - override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { if (getItemViewType(position) == VIEW_TYPE_TEXT_MESSAGE) { val shouldShowAvatar = shouldShowAvatar(position) diff --git a/app/src/main/res/layout/fragment_conversation.xml b/app/src/main/res/layout/fragment_conversation.xml index c3a2c02377..bc928cce60 100644 --- a/app/src/main/res/layout/fragment_conversation.xml +++ b/app/src/main/res/layout/fragment_conversation.xml @@ -1,9 +1,8 @@ + xmlns:app="http://schemas.android.com/apk/res-auto"> - - - \ No newline at end of file diff --git a/app/src/main/res/values/themes_conversation.xml b/app/src/main/res/values/themes_conversation.xml deleted file mode 100644 index dd03bfb0ac..0000000000 --- a/app/src/main/res/values/themes_conversation.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - \ No newline at end of file diff --git a/app/src/test/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageLocalDataSourceTest.kt b/app/src/test/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageLocalDataSourceTest.kt index 9f6ab20afc..2765cf4400 100644 --- a/app/src/test/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageLocalDataSourceTest.kt +++ b/app/src/test/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageLocalDataSourceTest.kt @@ -74,7 +74,7 @@ class MessageLocalDataSourceTest : UnitTest() { runBlocking { val result = messageLocalDataSource.messagesByConversationId(conversationId) - with(result.first()) { + with(result.first()){ size shouldBeEqualTo 2 get(0) shouldBeEqualTo messageAndContactEntity1 get(1) shouldBeEqualTo messageAndContactEntity2 From 9fa403f848a15ebd7bb633775a33e5aae374873f Mon Sep 17 00:00:00 2001 From: oussamah Date: Fri, 9 Jul 2021 22:18:16 +0100 Subject: [PATCH 12/15] cleanup --- .../content/datasources/local/MessageDaoTest.kt | 11 ----------- .../feature/contact/datasources/local/ContactDao.kt | 3 --- 2 files changed, 14 deletions(-) diff --git a/app/src/androidTest/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageDaoTest.kt b/app/src/androidTest/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageDaoTest.kt index e9b7aaf787..8f049b4f0a 100644 --- a/app/src/androidTest/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageDaoTest.kt +++ b/app/src/androidTest/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageDaoTest.kt @@ -79,17 +79,6 @@ class MessageDaoTest : InstrumentationTest() { } } - @Test - fun givenAMessageInConversationExists_whenContactIsDeleted_thenMessagesAreDeleted() { - runBlocking { - contactDao.delete(contactEntity) - - val result = messageDao.messagesByConversationId(TEST_CONVERSATION_ID) - - result.first().size shouldBeEqualTo 0 - } - } - @Test fun givenAMessageInConversationExists_whenConversationIsDeleted_thenMessagesAreDeleted() { runBlocking { diff --git a/app/src/main/kotlin/com/wire/android/feature/contact/datasources/local/ContactDao.kt b/app/src/main/kotlin/com/wire/android/feature/contact/datasources/local/ContactDao.kt index d28599eb30..d7c9a40878 100644 --- a/app/src/main/kotlin/com/wire/android/feature/contact/datasources/local/ContactDao.kt +++ b/app/src/main/kotlin/com/wire/android/feature/contact/datasources/local/ContactDao.kt @@ -20,7 +20,4 @@ interface ContactDao { @Query("SELECT * FROM contact") suspend fun contacts(): List - - @Delete - fun delete(contactEntity: ContactEntity) } From c527bded45cc7d9e361c4e1c042cdb6c9e0b34cd Mon Sep 17 00:00:00 2001 From: oussamah Date: Mon, 12 Jul 2021 11:21:01 +0100 Subject: [PATCH 13/15] rename data class --- .../content/datasources/local/MessageDaoTest.kt | 2 +- .../kotlin/com/wire/android/core/events/di/EventModule.kt | 2 +- .../feature/conversation/content/MessageRepository.kt | 4 ++-- .../conversation/content/datasources/MessageDataSource.kt | 6 +++--- ...ndContactEntity.kt => CombinedMessageContactEntity.kt} | 2 +- .../conversation/content/datasources/local/MessageDao.kt | 2 +- .../content/datasources/local/MessageLocalDataSource.kt | 2 +- .../{MessageAndContact.kt => CombinedMessageContact.kt} | 2 +- .../conversation/content/ui/ConversationAdapter.kt | 8 ++++---- .../content/ui/ConversationTextMessageViewHolder.kt | 6 +++--- .../conversation/content/ui/ConversationViewModel.kt | 4 ++-- .../content/usecase/GetConversationUseCase.kt | 6 +++--- .../content/datasources/MessageDataSourceTest.kt | 4 ++-- .../datasources/local/MessageLocalDataSourceTest.kt | 4 ++-- .../conversation/content/ui/ConversationAdapterTest.kt | 4 ++-- .../conversation/content/ui/ConversationViewModelTest.kt | 2 +- .../content/usecase/GetConversationUseCaseTest.kt | 4 ++-- 17 files changed, 32 insertions(+), 32 deletions(-) rename app/src/main/kotlin/com/wire/android/feature/conversation/content/datasources/local/{MessageAndContactEntity.kt => CombinedMessageContactEntity.kt} (91%) rename app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/{MessageAndContact.kt => CombinedMessageContact.kt} (68%) diff --git a/app/src/androidTest/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageDaoTest.kt b/app/src/androidTest/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageDaoTest.kt index 8f049b4f0a..5d19209a32 100644 --- a/app/src/androidTest/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageDaoTest.kt +++ b/app/src/androidTest/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageDaoTest.kt @@ -63,7 +63,7 @@ class MessageDaoTest : InstrumentationTest() { with(result.first()) { size shouldBeEqualTo 1 - first() shouldBeInstanceOf MessageAndContactEntity::class + first() shouldBeInstanceOf CombinedMessageContactEntity::class messageEntity shouldBeEqualTo messageEntity contactEntity shouldBeEqualTo contactEntity } diff --git a/app/src/main/kotlin/com/wire/android/core/events/di/EventModule.kt b/app/src/main/kotlin/com/wire/android/core/events/di/EventModule.kt index 1ca5948695..1eb3281c2f 100644 --- a/app/src/main/kotlin/com/wire/android/core/events/di/EventModule.kt +++ b/app/src/main/kotlin/com/wire/android/core/events/di/EventModule.kt @@ -29,7 +29,7 @@ val eventModule = module { return scarlet.create() } //TODO hardcoded client to be replaced with current clientId - single { WebSocketConfig("cac5f0abcafab91e") } + single { WebSocketConfig("7d86fca5842c6d29") } single { provideWebSocketService( get(), diff --git a/app/src/main/kotlin/com/wire/android/feature/conversation/content/MessageRepository.kt b/app/src/main/kotlin/com/wire/android/feature/conversation/content/MessageRepository.kt index cdf5357d2a..f9933de181 100644 --- a/app/src/main/kotlin/com/wire/android/feature/conversation/content/MessageRepository.kt +++ b/app/src/main/kotlin/com/wire/android/feature/conversation/content/MessageRepository.kt @@ -1,9 +1,9 @@ package com.wire.android.feature.conversation.content -import com.wire.android.feature.conversation.content.ui.MessageAndContact +import com.wire.android.feature.conversation.content.ui.CombinedMessageContact import kotlinx.coroutines.flow.Flow interface MessageRepository { suspend fun decryptMessage(message: Message) - suspend fun conversationMessages(conversationId: String): Flow> + suspend fun conversationMessages(conversationId: String): Flow> } diff --git a/app/src/main/kotlin/com/wire/android/feature/conversation/content/datasources/MessageDataSource.kt b/app/src/main/kotlin/com/wire/android/feature/conversation/content/datasources/MessageDataSource.kt index 7353255006..27201e7665 100644 --- a/app/src/main/kotlin/com/wire/android/feature/conversation/content/datasources/MessageDataSource.kt +++ b/app/src/main/kotlin/com/wire/android/feature/conversation/content/datasources/MessageDataSource.kt @@ -9,7 +9,7 @@ import com.wire.android.feature.conversation.content.Message import com.wire.android.feature.conversation.content.MessageRepository import com.wire.android.feature.conversation.content.datasources.local.MessageLocalDataSource import com.wire.android.feature.conversation.content.mapper.MessageMapper -import com.wire.android.feature.conversation.content.ui.MessageAndContact +import com.wire.android.feature.conversation.content.ui.CombinedMessageContact import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.flow.Flow @@ -45,10 +45,10 @@ class MessageDataSource( return messageLocalDataSource.save(messageEntity) } - override suspend fun conversationMessages(conversationId: String): Flow> = + override suspend fun conversationMessages(conversationId: String): Flow> = messageLocalDataSource.messagesByConversationId(conversationId).map { messagesWithContact -> messagesWithContact.map { - MessageAndContact( + CombinedMessageContact( messageMapper.fromEntityToMessage(it.messageEntity), contactMapper.fromContactEntity(it.contactEntity) ) diff --git a/app/src/main/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageAndContactEntity.kt b/app/src/main/kotlin/com/wire/android/feature/conversation/content/datasources/local/CombinedMessageContactEntity.kt similarity index 91% rename from app/src/main/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageAndContactEntity.kt rename to app/src/main/kotlin/com/wire/android/feature/conversation/content/datasources/local/CombinedMessageContactEntity.kt index 307241f54f..f3bc915622 100644 --- a/app/src/main/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageAndContactEntity.kt +++ b/app/src/main/kotlin/com/wire/android/feature/conversation/content/datasources/local/CombinedMessageContactEntity.kt @@ -4,7 +4,7 @@ import androidx.room.Embedded import androidx.room.Relation import com.wire.android.feature.contact.datasources.local.ContactEntity -class MessageAndContactEntity( +class CombinedMessageContactEntity( @Embedded val messageEntity: MessageEntity, @Relation( diff --git a/app/src/main/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageDao.kt b/app/src/main/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageDao.kt index 9082e30483..5355c1a1c9 100644 --- a/app/src/main/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageDao.kt +++ b/app/src/main/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageDao.kt @@ -13,5 +13,5 @@ interface MessageDao { suspend fun insert(message: MessageEntity) @Query("SELECT * from message where conversation_id = :conversationId") - fun messagesByConversationId(conversationId: String): Flow> + fun messagesByConversationId(conversationId: String): Flow> } diff --git a/app/src/main/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageLocalDataSource.kt b/app/src/main/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageLocalDataSource.kt index 7082f17bea..b7211c3318 100644 --- a/app/src/main/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageLocalDataSource.kt +++ b/app/src/main/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageLocalDataSource.kt @@ -8,6 +8,6 @@ import kotlinx.coroutines.flow.Flow class MessageLocalDataSource(private val messageDao: MessageDao) : DatabaseService { suspend fun save(message: MessageEntity): Either = request { messageDao.insert(message) } - fun messagesByConversationId(conversationId: String): Flow> = + fun messagesByConversationId(conversationId: String): Flow> = messageDao.messagesByConversationId(conversationId) } diff --git a/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/MessageAndContact.kt b/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/CombinedMessageContact.kt similarity index 68% rename from app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/MessageAndContact.kt rename to app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/CombinedMessageContact.kt index c6830fa071..424a35d900 100644 --- a/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/MessageAndContact.kt +++ b/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/CombinedMessageContact.kt @@ -3,4 +3,4 @@ package com.wire.android.feature.conversation.content.ui import com.wire.android.feature.contact.Contact import com.wire.android.feature.conversation.content.Message -data class MessageAndContact(val message: Message, val contact: Contact) +data class CombinedMessageContact(val message: Message, val contact: Contact) diff --git a/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationAdapter.kt b/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationAdapter.kt index fa47f03913..e8eebaac43 100644 --- a/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationAdapter.kt +++ b/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationAdapter.kt @@ -15,7 +15,7 @@ class ConversationAdapter(private val viewHolderInflater: ViewHolderInflater) : if (getItemViewType(position) == VIEW_TYPE_TEXT_MESSAGE) { val shouldShowAvatar = shouldShowAvatar(position) (holder as ConversationTextMessageViewHolder).bind( - (messages[position] as MessageAndContact), + (messages[position] as CombinedMessageContact), shouldShowAvatar ) } @@ -23,7 +23,7 @@ class ConversationAdapter(private val viewHolderInflater: ViewHolderInflater) : override fun getItemViewType(position: Int): Int { return when (messages[position]) { - is MessageAndContact -> VIEW_TYPE_TEXT_MESSAGE + is CombinedMessageContact -> VIEW_TYPE_TEXT_MESSAGE else -> VIEW_TYPE_UNKNOWN } } @@ -36,9 +36,9 @@ class ConversationAdapter(private val viewHolderInflater: ViewHolderInflater) : } private fun shouldShowAvatar(position: Int): Boolean { - val currentMessage = (messages[position] as MessageAndContact).message + val currentMessage = (messages[position] as CombinedMessageContact).message return (position == 0) || - (position > 0 && currentMessage.senderUserId != (messages[position - 1] as MessageAndContact).message.senderUserId) + (position > 0 && currentMessage.senderUserId != (messages[position - 1] as CombinedMessageContact).message.senderUserId) } companion object { diff --git a/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationTextMessageViewHolder.kt b/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationTextMessageViewHolder.kt index 0baaefe244..f3e8504c85 100644 --- a/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationTextMessageViewHolder.kt +++ b/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationTextMessageViewHolder.kt @@ -16,13 +16,13 @@ class ConversationTextMessageViewHolder(parent: ViewGroup, inflater: ViewHolderI private val conversationChatItemTextMessageTextView by lazyFind(R.id.conversationChatItemTextMessageTextView) private val conversationChatItemUserAvatarImageView by lazyFind(R.id.conversationChatItemUserAvatarImageView) - fun bind(message: MessageAndContact, shouldShowAvatar: Boolean) { + fun bind(combinedMessage: CombinedMessageContact, shouldShowAvatar: Boolean) { if (shouldShowAvatar) conversationChatItemUserAvatarImageView.visibility = View.VISIBLE else conversationChatItemUserAvatarImageView.visibility = View.GONE - conversationChatItemUsernameTextView.text = message.contact.name - conversationChatItemTextMessageTextView.text = message.message.content + conversationChatItemUsernameTextView.text = combinedMessage.contact.name + conversationChatItemTextMessageTextView.text = combinedMessage.message.content } } diff --git a/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationViewModel.kt b/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationViewModel.kt index 6fe0807b62..4971512d4f 100644 --- a/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationViewModel.kt +++ b/app/src/main/kotlin/com/wire/android/feature/conversation/content/ui/ConversationViewModel.kt @@ -18,8 +18,8 @@ class ConversationViewModel( private val _conversationIdLiveData: MutableLiveData = MutableLiveData() val conversationIdLiveData: LiveData = _conversationIdLiveData - private val _conversationMessagesLiveData = MutableLiveData>() - val conversationMessagesLiveData: LiveData> = _conversationMessagesLiveData + private val _conversationMessagesLiveData = MutableLiveData>() + val conversationMessagesLiveData: LiveData> = _conversationMessagesLiveData fun cacheConversationId(conversationId: String) { _conversationIdLiveData.value = conversationId diff --git a/app/src/main/kotlin/com/wire/android/feature/conversation/content/usecase/GetConversationUseCase.kt b/app/src/main/kotlin/com/wire/android/feature/conversation/content/usecase/GetConversationUseCase.kt index cb30cebbe4..85c21a5ae9 100644 --- a/app/src/main/kotlin/com/wire/android/feature/conversation/content/usecase/GetConversationUseCase.kt +++ b/app/src/main/kotlin/com/wire/android/feature/conversation/content/usecase/GetConversationUseCase.kt @@ -2,13 +2,13 @@ package com.wire.android.feature.conversation.content.usecase import com.wire.android.core.usecase.ObservableUseCase import com.wire.android.feature.conversation.content.MessageRepository -import com.wire.android.feature.conversation.content.ui.MessageAndContact +import com.wire.android.feature.conversation.content.ui.CombinedMessageContact import kotlinx.coroutines.flow.Flow class GetConversationUseCase(private val messageRepository: MessageRepository) : - ObservableUseCase, GetConversationUseCaseParams> { + ObservableUseCase, GetConversationUseCaseParams> { - override suspend fun run(params: GetConversationUseCaseParams): Flow> = + override suspend fun run(params: GetConversationUseCaseParams): Flow> = messageRepository.conversationMessages(params.conversationId) } diff --git a/app/src/test/kotlin/com/wire/android/feature/conversation/content/datasources/MessageDataSourceTest.kt b/app/src/test/kotlin/com/wire/android/feature/conversation/content/datasources/MessageDataSourceTest.kt index 9d82af1016..b35f82f70f 100644 --- a/app/src/test/kotlin/com/wire/android/feature/conversation/content/datasources/MessageDataSourceTest.kt +++ b/app/src/test/kotlin/com/wire/android/feature/conversation/content/datasources/MessageDataSourceTest.kt @@ -15,7 +15,7 @@ import com.wire.android.core.exception.Failure import com.wire.android.feature.contact.Contact import com.wire.android.feature.contact.datasources.local.ContactEntity import com.wire.android.feature.contact.datasources.mapper.ContactMapper -import com.wire.android.feature.conversation.content.datasources.local.MessageAndContactEntity +import com.wire.android.feature.conversation.content.datasources.local.CombinedMessageContactEntity import io.mockk.impl.annotations.MockK import io.mockk.verify import io.mockk.coVerify @@ -113,7 +113,7 @@ class MessageDataSourceTest : UnitTest() { val conversationId = "conversation-id" val contactEntity = mockk() val messageEntity = mockk() - val messageAndContactEntity = mockk().also { + val messageAndContactEntity = mockk().also { every { it.messageEntity } returns messageEntity every { it.contactEntity } returns contactEntity } diff --git a/app/src/test/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageLocalDataSourceTest.kt b/app/src/test/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageLocalDataSourceTest.kt index 2765cf4400..a3f04a3d62 100644 --- a/app/src/test/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageLocalDataSourceTest.kt +++ b/app/src/test/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageLocalDataSourceTest.kt @@ -65,8 +65,8 @@ class MessageLocalDataSourceTest : UnitTest() { @Test fun `given messagesByConversationId is called, when dao emits some messages, then emits a list of messages`() { val conversationId = "conversationId" - val messageAndContactEntity1 = mockk() - val messageAndContactEntity2 = mockk() + val messageAndContactEntity1 = mockk() + val messageAndContactEntity2 = mockk() coEvery { messageDao.messagesByConversationId(conversationId) } returns flowOf(listOf(messageAndContactEntity1, messageAndContactEntity2)) diff --git a/app/src/test/kotlin/com/wire/android/feature/conversation/content/ui/ConversationAdapterTest.kt b/app/src/test/kotlin/com/wire/android/feature/conversation/content/ui/ConversationAdapterTest.kt index 6eeff2b84c..220d712b9b 100644 --- a/app/src/test/kotlin/com/wire/android/feature/conversation/content/ui/ConversationAdapterTest.kt +++ b/app/src/test/kotlin/com/wire/android/feature/conversation/content/ui/ConversationAdapterTest.kt @@ -48,7 +48,7 @@ class ConversationAdapterTest : UnitTest() { val holder = mockk(relaxUnitFun = true) val message = mockk(relaxed = true) val contact = mockk(relaxed = true) - val item = mockk().also { + val item = mockk().also { every { it.message } returns message every { it.contact } returns contact } @@ -89,7 +89,7 @@ class ConversationAdapterTest : UnitTest() { @Test fun `given getItemViewType is called, when item is MessageText, then return VIEW_TYPE_TEXT_MESSAGE`() { - val messageAndContact = mockk() + val messageAndContact = mockk() spyAdapter.setList(messages) every { messages[TEST_POSITION] } returns messageAndContact diff --git a/app/src/test/kotlin/com/wire/android/feature/conversation/content/ui/ConversationViewModelTest.kt b/app/src/test/kotlin/com/wire/android/feature/conversation/content/ui/ConversationViewModelTest.kt index 8dc83d5c7d..db27f2c04f 100644 --- a/app/src/test/kotlin/com/wire/android/feature/conversation/content/ui/ConversationViewModelTest.kt +++ b/app/src/test/kotlin/com/wire/android/feature/conversation/content/ui/ConversationViewModelTest.kt @@ -43,7 +43,7 @@ class ConversationViewModelTest : UnitTest() { @Test fun `given fetchMessages is called, when getConversationUseCase emits items, then updates conversationMessagesLiveData`() { - val items = mockk>() + val items = mockk>() coEvery { getConversationUseCase.run(any()) } returns flowOf(items) conversationViewModel.fetchMessages(TEST_CONVERSATION_ID) diff --git a/app/src/test/kotlin/com/wire/android/feature/conversation/content/usecase/GetConversationUseCaseTest.kt b/app/src/test/kotlin/com/wire/android/feature/conversation/content/usecase/GetConversationUseCaseTest.kt index 367afb1fce..1754db3bd2 100644 --- a/app/src/test/kotlin/com/wire/android/feature/conversation/content/usecase/GetConversationUseCaseTest.kt +++ b/app/src/test/kotlin/com/wire/android/feature/conversation/content/usecase/GetConversationUseCaseTest.kt @@ -2,7 +2,7 @@ package com.wire.android.feature.conversation.content.usecase import com.wire.android.UnitTest import com.wire.android.feature.conversation.content.MessageRepository -import com.wire.android.feature.conversation.content.ui.MessageAndContact +import com.wire.android.feature.conversation.content.ui.CombinedMessageContact import io.mockk.coEvery import io.mockk.impl.annotations.MockK import io.mockk.mockk @@ -27,7 +27,7 @@ class GetConversationUseCaseTest : UnitTest() { @Test fun `given run is called, when messageRepository emits items, then propagates items`() { - val items = mockk>() + val items = mockk>() val params = GetConversationUseCaseParams(conversationId = TEST_CONVERSATION_ID) coEvery { messageRepository.conversationMessages(any()) } returns flowOf(items) From 1d36940e735102af6389ec0643938851bc964432 Mon Sep 17 00:00:00 2001 From: oussamah Date: Mon, 12 Jul 2021 22:30:03 +0100 Subject: [PATCH 14/15] unit test fix --- .../conversation/content/datasources/local/MessageDaoTest.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/androidTest/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageDaoTest.kt b/app/src/androidTest/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageDaoTest.kt index 8f049b4f0a..93802c8124 100644 --- a/app/src/androidTest/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageDaoTest.kt +++ b/app/src/androidTest/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageDaoTest.kt @@ -64,8 +64,8 @@ class MessageDaoTest : InstrumentationTest() { with(result.first()) { size shouldBeEqualTo 1 first() shouldBeInstanceOf MessageAndContactEntity::class - messageEntity shouldBeEqualTo messageEntity - contactEntity shouldBeEqualTo contactEntity + first().messageEntity shouldBeEqualTo messageEntity + first().contactEntity shouldBeEqualTo contactEntity } } } From 99e68e9851e45f0fb4312c53952d8ce07bac0e4e Mon Sep 17 00:00:00 2001 From: oussamah Date: Mon, 12 Jul 2021 23:24:05 +0100 Subject: [PATCH 15/15] rename data class --- .../content/datasources/local/MessageDaoTest.kt | 2 +- .../content/datasources/MessageDataSourceTest.kt | 4 ++-- .../datasources/local/MessageLocalDataSourceTest.kt | 10 +++++----- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/src/androidTest/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageDaoTest.kt b/app/src/androidTest/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageDaoTest.kt index 93802c8124..8a6e11fb1c 100644 --- a/app/src/androidTest/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageDaoTest.kt +++ b/app/src/androidTest/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageDaoTest.kt @@ -63,7 +63,7 @@ class MessageDaoTest : InstrumentationTest() { with(result.first()) { size shouldBeEqualTo 1 - first() shouldBeInstanceOf MessageAndContactEntity::class + first() shouldBeInstanceOf CombinedMessageContactEntity::class first().messageEntity shouldBeEqualTo messageEntity first().contactEntity shouldBeEqualTo contactEntity } diff --git a/app/src/test/kotlin/com/wire/android/feature/conversation/content/datasources/MessageDataSourceTest.kt b/app/src/test/kotlin/com/wire/android/feature/conversation/content/datasources/MessageDataSourceTest.kt index b35f82f70f..0ec7e4aebc 100644 --- a/app/src/test/kotlin/com/wire/android/feature/conversation/content/datasources/MessageDataSourceTest.kt +++ b/app/src/test/kotlin/com/wire/android/feature/conversation/content/datasources/MessageDataSourceTest.kt @@ -113,7 +113,7 @@ class MessageDataSourceTest : UnitTest() { val conversationId = "conversation-id" val contactEntity = mockk() val messageEntity = mockk() - val messageAndContactEntity = mockk().also { + val combinedMessageContactEntity = mockk().also { every { it.messageEntity } returns messageEntity every { it.contactEntity } returns contactEntity } @@ -121,7 +121,7 @@ class MessageDataSourceTest : UnitTest() { val contact = mockk() every { messageMapper.fromEntityToMessage(messageEntity) } returns message every { contactMapper.fromContactEntity(contactEntity) } returns contact - coEvery { messageLocalDataSource.messagesByConversationId(conversationId) } returns flowOf(listOf(messageAndContactEntity)) + coEvery { messageLocalDataSource.messagesByConversationId(conversationId) } returns flowOf(listOf(combinedMessageContactEntity)) runBlocking { val result = messageDataSource.conversationMessages(conversationId) diff --git a/app/src/test/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageLocalDataSourceTest.kt b/app/src/test/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageLocalDataSourceTest.kt index a3f04a3d62..f3b534692b 100644 --- a/app/src/test/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageLocalDataSourceTest.kt +++ b/app/src/test/kotlin/com/wire/android/feature/conversation/content/datasources/local/MessageLocalDataSourceTest.kt @@ -65,19 +65,19 @@ class MessageLocalDataSourceTest : UnitTest() { @Test fun `given messagesByConversationId is called, when dao emits some messages, then emits a list of messages`() { val conversationId = "conversationId" - val messageAndContactEntity1 = mockk() - val messageAndContactEntity2 = mockk() + val combinedMessageContactEntity1 = mockk() + val combinedMessageContactEntity2 = mockk() coEvery { messageDao.messagesByConversationId(conversationId) } returns - flowOf(listOf(messageAndContactEntity1, messageAndContactEntity2)) + flowOf(listOf(combinedMessageContactEntity1, combinedMessageContactEntity2)) runBlocking { val result = messageLocalDataSource.messagesByConversationId(conversationId) with(result.first()){ size shouldBeEqualTo 2 - get(0) shouldBeEqualTo messageAndContactEntity1 - get(1) shouldBeEqualTo messageAndContactEntity2 + get(0) shouldBeEqualTo combinedMessageContactEntity1 + get(1) shouldBeEqualTo combinedMessageContactEntity2 } } }