Эх сурвалжийг харах

add option to list open conversations

Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
Marcel Hibbe 2 жил өмнө
parent
commit
fdde5d2667
22 өөрчлөгдсөн 879 нэмэгдсэн , 228 устгасан
  1. 4 0
      app/src/main/AndroidManifest.xml
  2. 38 26
      app/src/main/java/com/nextcloud/talk/contacts/ContactsActivity.kt
  3. 8 0
      app/src/main/java/com/nextcloud/talk/dagger/modules/RepositoryModule.kt
  4. 6 0
      app/src/main/java/com/nextcloud/talk/dagger/modules/ViewModelModule.kt
  5. 135 0
      app/src/main/java/com/nextcloud/talk/openconversations/ListOpenConversationsActivity.kt
  6. 84 0
      app/src/main/java/com/nextcloud/talk/openconversations/adapters/OpenConversationsAdapter.kt
  7. 25 0
      app/src/main/java/com/nextcloud/talk/openconversations/data/JoinConversationModel.kt
  8. 27 0
      app/src/main/java/com/nextcloud/talk/openconversations/data/OpenConversation.kt
  9. 25 0
      app/src/main/java/com/nextcloud/talk/openconversations/data/OpenConversationsModel.kt
  10. 28 0
      app/src/main/java/com/nextcloud/talk/openconversations/data/OpenConversationsRepository.kt
  11. 58 0
      app/src/main/java/com/nextcloud/talk/openconversations/data/OpenConversationsRepositoryImpl.kt
  12. 84 0
      app/src/main/java/com/nextcloud/talk/openconversations/viewmodels/OpenConversationsViewModel.kt
  13. 22 0
      app/src/main/res/drawable/baseline_format_list_bulleted_24.xml
  14. 22 0
      app/src/main/res/drawable/baseline_info_24.xml
  15. 218 0
      app/src/main/res/layout/activity_contacts.xml
  16. 67 0
      app/src/main/res/layout/activity_open_conversations.xml
  17. 0 96
      app/src/main/res/layout/controller_contacts_rv.xml
  18. 0 85
      app/src/main/res/layout/conversation_privacy_toggle.xml
  19. 19 21
      app/src/main/res/layout/rv_item_open_conversation.xml
  20. 2 0
      app/src/main/res/values-night/colors.xml
  21. 2 0
      app/src/main/res/values/colors.xml
  22. 5 0
      app/src/main/res/values/strings.xml

+ 4 - 0
app/src/main/AndroidManifest.xml

@@ -218,6 +218,10 @@
             android:screenOrientation="portrait"
             android:screenOrientation="portrait"
             android:theme="@style/AppTheme" />
             android:theme="@style/AppTheme" />
 
 
+        <activity
+            android:name=".openconversations.ListOpenConversationsActivity"
+            android:theme="@style/AppTheme" />
+
         <activity
         <activity
             android:name=".conversationlist.ConversationsListActivity"
             android:name=".conversationlist.ConversationsListActivity"
             android:theme="@style/AppTheme"
             android:theme="@style/AppTheme"

+ 38 - 26
app/src/main/java/com/nextcloud/talk/contacts/ContactsActivity.kt

@@ -53,7 +53,7 @@ import com.nextcloud.talk.application.NextcloudTalkApplication
 import com.nextcloud.talk.chat.ChatActivity
 import com.nextcloud.talk.chat.ChatActivity
 import com.nextcloud.talk.controllers.bottomsheet.ConversationOperationEnum
 import com.nextcloud.talk.controllers.bottomsheet.ConversationOperationEnum
 import com.nextcloud.talk.data.user.model.User
 import com.nextcloud.talk.data.user.model.User
-import com.nextcloud.talk.databinding.ControllerContactsRvBinding
+import com.nextcloud.talk.databinding.ActivityContactsBinding
 import com.nextcloud.talk.events.OpenConversationEvent
 import com.nextcloud.talk.events.OpenConversationEvent
 import com.nextcloud.talk.jobs.AddParticipantsToConversation
 import com.nextcloud.talk.jobs.AddParticipantsToConversation
 import com.nextcloud.talk.models.RetrofitBucket
 import com.nextcloud.talk.models.RetrofitBucket
@@ -63,6 +63,7 @@ import com.nextcloud.talk.models.json.conversations.Conversation
 import com.nextcloud.talk.models.json.conversations.RoomOverall
 import com.nextcloud.talk.models.json.conversations.RoomOverall
 import com.nextcloud.talk.models.json.converters.EnumActorTypeConverter
 import com.nextcloud.talk.models.json.converters.EnumActorTypeConverter
 import com.nextcloud.talk.models.json.participants.Participant
 import com.nextcloud.talk.models.json.participants.Participant
+import com.nextcloud.talk.openconversations.ListOpenConversationsActivity
 import com.nextcloud.talk.ui.dialog.ContactsBottomDialog
 import com.nextcloud.talk.ui.dialog.ContactsBottomDialog
 import com.nextcloud.talk.users.UserManager
 import com.nextcloud.talk.users.UserManager
 import com.nextcloud.talk.utils.ApiUtils
 import com.nextcloud.talk.utils.ApiUtils
@@ -90,7 +91,7 @@ class ContactsActivity :
     BaseActivity(),
     BaseActivity(),
     SearchView.OnQueryTextListener,
     SearchView.OnQueryTextListener,
     FlexibleAdapter.OnItemClickListener {
     FlexibleAdapter.OnItemClickListener {
-    private lateinit var binding: ControllerContactsRvBinding
+    private lateinit var binding: ActivityContactsBinding
 
 
     @Inject
     @Inject
     lateinit var userManager: UserManager
     lateinit var userManager: UserManager
@@ -125,7 +126,7 @@ class ContactsActivity :
         super.onCreate(savedInstanceState)
         super.onCreate(savedInstanceState)
         NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this)
         NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this)
 
 
-        binding = ControllerContactsRvBinding.inflate(layoutInflater)
+        binding = ActivityContactsBinding.inflate(layoutInflater)
         setupActionBar()
         setupActionBar()
         setContentView(binding.root)
         setContentView(binding.root)
         setupSystemColors()
         setupSystemColors()
@@ -159,13 +160,16 @@ class ContactsActivity :
             toggleConversationPrivacyLayout(!isPublicCall)
             toggleConversationPrivacyLayout(!isPublicCall)
         }
         }
         if (isAddingParticipantsView) {
         if (isAddingParticipantsView) {
-            binding.joinConversationViaLink.joinConversationViaLinkRelativeLayout.visibility = View.GONE
-            binding.conversationPrivacyToggle.callHeaderLayout.visibility = View.GONE
+            binding.joinConversationViaLink.visibility = View.GONE
+            binding.callHeaderLayout.visibility = View.GONE
         } else {
         } else {
-            binding.joinConversationViaLink.joinConversationViaLinkRelativeLayout.setOnClickListener {
+            binding.joinConversationViaLink.setOnClickListener {
                 joinConversationViaLink()
                 joinConversationViaLink()
             }
             }
-            binding.conversationPrivacyToggle.callHeaderLayout.setOnClickListener {
+            binding.listOpenConversations.setOnClickListener {
+                listOpenConversations()
+            }
+            binding.callHeaderLayout.setOnClickListener {
                 toggleCallHeader()
                 toggleCallHeader()
             }
             }
         }
         }
@@ -662,14 +666,17 @@ class ContactsActivity :
 
 
         binding?.controllerGenericRv?.let { viewThemeUtils.androidx.themeSwipeRefreshLayout(it.swipeRefreshLayout) }
         binding?.controllerGenericRv?.let { viewThemeUtils.androidx.themeSwipeRefreshLayout(it.swipeRefreshLayout) }
 
 
-        binding?.joinConversationViaLink?.joinConversationViaLinkImageView
-            ?.background
-            ?.setColorFilter(
-                ResourcesCompat.getColor(resources!!, R.color.colorBackgroundDarker, null),
-                PorterDuff.Mode.SRC_IN
-            )
+        binding.listOpenConversationsImage.background?.setColorFilter(
+            ResourcesCompat.getColor(resources!!, R.color.colorBackgroundDarker, null),
+            PorterDuff.Mode.SRC_IN
+        )
 
 
-        binding?.conversationPrivacyToggle?.let {
+        binding.joinConversationViaLinkImage.background?.setColorFilter(
+            ResourcesCompat.getColor(resources!!, R.color.colorBackgroundDarker, null),
+            PorterDuff.Mode.SRC_IN
+        )
+
+        binding?.let {
             viewThemeUtils.platform.colorImageViewBackgroundAndIcon(it.publicCallLink)
             viewThemeUtils.platform.colorImageViewBackgroundAndIcon(it.publicCallLink)
         }
         }
         disengageProgressBar()
         disengageProgressBar()
@@ -677,11 +684,11 @@ class ContactsActivity :
 
 
     private fun disengageProgressBar() {
     private fun disengageProgressBar() {
         if (!alreadyFetching) {
         if (!alreadyFetching) {
-            binding?.loadingContent?.visibility = View.GONE
-            binding?.controllerGenericRv?.root?.visibility = View.VISIBLE
+            binding.loadingContent.visibility = View.GONE
+            binding.controllerGenericRv.root.visibility = View.VISIBLE
             if (isNewConversationView) {
             if (isNewConversationView) {
-                binding?.conversationPrivacyToggle?.callHeaderLayout?.visibility = View.VISIBLE
-                binding?.joinConversationViaLink?.joinConversationViaLinkRelativeLayout?.visibility = View.VISIBLE
+                binding.callHeaderLayout.visibility = View.VISIBLE
+                binding.joinConversationViaLink.visibility = View.VISIBLE
             }
             }
         }
         }
     }
     }
@@ -715,7 +722,7 @@ class ContactsActivity :
             adapter?.updateDataSet(contactItems as List<Nothing>?)
             adapter?.updateDataSet(contactItems as List<Nothing>?)
         }
         }
 
 
-        binding?.controllerGenericRv?.swipeRefreshLayout?.isEnabled = !adapter!!.hasFilter()
+        binding.controllerGenericRv?.swipeRefreshLayout?.isEnabled = !adapter!!.hasFilter()
 
 
         return true
         return true
     }
     }
@@ -877,6 +884,11 @@ class ContactsActivity :
         prepareAndShowBottomSheetWithBundle(bundle)
         prepareAndShowBottomSheetWithBundle(bundle)
     }
     }
 
 
+    private fun listOpenConversations() {
+        val intent = Intent(this, ListOpenConversationsActivity::class.java)
+        startActivity(intent)
+    }
+
     private fun toggleCallHeader() {
     private fun toggleCallHeader() {
         toggleConversationPrivacyLayout(isPublicCall)
         toggleConversationPrivacyLayout(isPublicCall)
         isPublicCall = !isPublicCall
         isPublicCall = !isPublicCall
@@ -917,25 +929,25 @@ class ContactsActivity :
 
 
     private fun toggleConversationPrivacyLayout(showInitialLayout: Boolean) {
     private fun toggleConversationPrivacyLayout(showInitialLayout: Boolean) {
         if (showInitialLayout) {
         if (showInitialLayout) {
-            binding?.conversationPrivacyToggle?.initialRelativeLayout?.visibility = View.VISIBLE
-            binding?.conversationPrivacyToggle?.secondaryRelativeLayout?.visibility = View.GONE
+            binding.initialRelativeLayout.visibility = View.VISIBLE
+            binding.secondaryRelativeLayout.visibility = View.GONE
         } else {
         } else {
-            binding?.conversationPrivacyToggle?.initialRelativeLayout?.visibility = View.GONE
-            binding?.conversationPrivacyToggle?.secondaryRelativeLayout?.visibility = View.VISIBLE
+            binding.initialRelativeLayout.visibility = View.GONE
+            binding.secondaryRelativeLayout.visibility = View.VISIBLE
         }
         }
     }
     }
 
 
     private fun toggleConversationViaLinkVisibility(isPublicCall: Boolean) {
     private fun toggleConversationViaLinkVisibility(isPublicCall: Boolean) {
         if (isPublicCall) {
         if (isPublicCall) {
-            binding?.joinConversationViaLink?.joinConversationViaLinkRelativeLayout?.visibility = View.GONE
+            binding.joinConversationViaLink.visibility = View.GONE
             updateGroupParticipantSelection()
             updateGroupParticipantSelection()
         } else {
         } else {
-            binding?.joinConversationViaLink?.joinConversationViaLinkRelativeLayout?.visibility = View.VISIBLE
+            binding.joinConversationViaLink.visibility = View.VISIBLE
         }
         }
     }
     }
 
 
     companion object {
     companion object {
-        const val TAG = "ContactsController"
+        private val TAG = ContactsActivity::class.simpleName
         const val RETRIES: Long = 3
         const val RETRIES: Long = 3
         const val CONTACTS_BATCH_SIZE: Int = 50
         const val CONTACTS_BATCH_SIZE: Int = 50
         const val HEADER_ELEVATION: Int = 5
         const val HEADER_ELEVATION: Int = 5

+ 8 - 0
app/src/main/java/com/nextcloud/talk/dagger/modules/RepositoryModule.kt

@@ -31,6 +31,8 @@ import com.nextcloud.talk.data.storage.ArbitraryStoragesRepository
 import com.nextcloud.talk.data.storage.ArbitraryStoragesRepositoryImpl
 import com.nextcloud.talk.data.storage.ArbitraryStoragesRepositoryImpl
 import com.nextcloud.talk.data.user.UsersRepository
 import com.nextcloud.talk.data.user.UsersRepository
 import com.nextcloud.talk.data.user.UsersRepositoryImpl
 import com.nextcloud.talk.data.user.UsersRepositoryImpl
+import com.nextcloud.talk.openconversations.data.OpenConversationsRepository
+import com.nextcloud.talk.openconversations.data.OpenConversationsRepositoryImpl
 import com.nextcloud.talk.polls.repositories.PollRepository
 import com.nextcloud.talk.polls.repositories.PollRepository
 import com.nextcloud.talk.polls.repositories.PollRepositoryImpl
 import com.nextcloud.talk.polls.repositories.PollRepositoryImpl
 import com.nextcloud.talk.raisehand.RequestAssistanceRepository
 import com.nextcloud.talk.raisehand.RequestAssistanceRepository
@@ -110,6 +112,12 @@ class RepositoryModule {
         return RequestAssistanceRepositoryImpl(ncApi, userProvider)
         return RequestAssistanceRepositoryImpl(ncApi, userProvider)
     }
     }
 
 
+    @Provides
+    fun provideOpenConversationsRepository(ncApi: NcApi, userProvider: CurrentUserProviderNew):
+        OpenConversationsRepository {
+        return OpenConversationsRepositoryImpl(ncApi, userProvider)
+    }
+
     @Provides
     @Provides
     fun translateRepository(ncApi: NcApi):
     fun translateRepository(ncApi: NcApi):
         TranslateRepository {
         TranslateRepository {

+ 6 - 0
app/src/main/java/com/nextcloud/talk/dagger/modules/ViewModelModule.kt

@@ -24,6 +24,7 @@ package com.nextcloud.talk.dagger.modules
 import androidx.lifecycle.ViewModel
 import androidx.lifecycle.ViewModel
 import androidx.lifecycle.ViewModelProvider
 import androidx.lifecycle.ViewModelProvider
 import com.nextcloud.talk.messagesearch.MessageSearchViewModel
 import com.nextcloud.talk.messagesearch.MessageSearchViewModel
+import com.nextcloud.talk.openconversations.viewmodels.OpenConversationsViewModel
 import com.nextcloud.talk.polls.viewmodels.PollCreateViewModel
 import com.nextcloud.talk.polls.viewmodels.PollCreateViewModel
 import com.nextcloud.talk.polls.viewmodels.PollMainViewModel
 import com.nextcloud.talk.polls.viewmodels.PollMainViewModel
 import com.nextcloud.talk.polls.viewmodels.PollResultsViewModel
 import com.nextcloud.talk.polls.viewmodels.PollResultsViewModel
@@ -107,4 +108,9 @@ abstract class ViewModelModule {
     @IntoMap
     @IntoMap
     @ViewModelKey(TranslateViewModel::class)
     @ViewModelKey(TranslateViewModel::class)
     abstract fun translateViewModel(viewModel: TranslateViewModel): ViewModel
     abstract fun translateViewModel(viewModel: TranslateViewModel): ViewModel
+
+    @Binds
+    @IntoMap
+    @ViewModelKey(OpenConversationsViewModel::class)
+    abstract fun openConversationsViewModelModel(viewModel: OpenConversationsViewModel): ViewModel
 }
 }

+ 135 - 0
app/src/main/java/com/nextcloud/talk/openconversations/ListOpenConversationsActivity.kt

@@ -0,0 +1,135 @@
+/*
+ * Nextcloud Talk application
+ *
+ * @author Marcel Hibbe
+ * Copyright (C) 2023 Marcel Hibbe <dev@mhibbe.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.nextcloud.talk.openconversations
+
+import android.content.Intent
+import android.graphics.drawable.ColorDrawable
+import android.os.Bundle
+import android.view.View
+import android.widget.Toast
+import androidx.lifecycle.ViewModelProvider
+import autodagger.AutoInjector
+import com.nextcloud.talk.R
+import com.nextcloud.talk.activities.BaseActivity
+import com.nextcloud.talk.api.NcApi
+import com.nextcloud.talk.application.NextcloudTalkApplication
+import com.nextcloud.talk.chat.ChatActivity
+import com.nextcloud.talk.databinding.ActivityOpenConversationsBinding
+import com.nextcloud.talk.openconversations.data.OpenConversation
+import com.nextcloud.talk.openconversations.viewmodels.OpenConversationsViewModel
+import com.nextcloud.talk.utils.bundle.BundleKeys
+import com.nextcloud.talk.utils.database.user.CurrentUserProviderNew
+import javax.inject.Inject
+
+@AutoInjector(NextcloudTalkApplication::class)
+class ListOpenConversationsActivity : BaseActivity() {
+
+    private lateinit var binding: ActivityOpenConversationsBinding
+
+    @Inject
+    lateinit var ncApi: NcApi
+
+    @Inject
+    lateinit var viewModelFactory: ViewModelProvider.Factory
+
+    @Inject
+    lateinit var userProvider: CurrentUserProviderNew
+
+    lateinit var openConversationsViewModel: OpenConversationsViewModel
+
+    lateinit var adapter: OpenConversationsAdapter
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this)
+
+        openConversationsViewModel = ViewModelProvider(this, viewModelFactory)[OpenConversationsViewModel::class.java]
+
+        openConversationsViewModel.fetchConversations()
+
+        binding = ActivityOpenConversationsBinding.inflate(layoutInflater)
+        setupActionBar()
+        setContentView(binding.root)
+        setupSystemColors()
+
+        val user = userProvider.currentUser.blockingGet()
+
+        adapter = OpenConversationsAdapter(user) { conversation -> adapterOnClick(conversation) }
+        binding.openConversationsRecyclerView.adapter = adapter
+
+        initObservers()
+    }
+
+    private fun adapterOnClick(conversation: OpenConversation) {
+        val user = userProvider.currentUser.blockingGet()
+
+        val bundle = Bundle()
+        bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, user)
+        bundle.putString(BundleKeys.KEY_ROOM_TOKEN, conversation.roomToken)
+
+        val chatIntent = Intent(context, ChatActivity::class.java)
+        chatIntent.putExtras(bundle)
+        chatIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
+        startActivity(chatIntent)
+    }
+
+    private fun initObservers() {
+        openConversationsViewModel.viewState.observe(this) { state ->
+            when (state) {
+                is OpenConversationsViewModel.FetchConversationsStartState -> {
+                    binding.openConversationsRecyclerView.visibility = View.GONE
+                    binding.progressBarWrapper.visibility = View.VISIBLE
+                }
+                is OpenConversationsViewModel.FetchConversationsSuccessState -> {
+                    binding.openConversationsRecyclerView.visibility = View.VISIBLE
+                    binding.progressBarWrapper.visibility = View.GONE
+                    adapter.submitList(state.conversations)
+                }
+                is OpenConversationsViewModel.FetchConversationsEmptyState -> {
+                    binding.openConversationsRecyclerView.visibility = View.GONE
+                    binding.progressBarWrapper.visibility = View.GONE
+
+                    binding.emptyList.emptyListView.visibility = View.VISIBLE
+                    binding.emptyList.emptyListViewHeadline.text = getString(R.string.nc_no_open_conversations_headline)
+                    binding.emptyList.emptyListViewText.text = getString(R.string.nc_no_open_conversations_text)
+                    binding.emptyList.emptyListIcon.setImageResource(R.drawable.baseline_info_24)
+                    binding.emptyList.emptyListIcon.visibility = View.VISIBLE
+                    binding.emptyList.emptyListViewText.visibility = View.VISIBLE
+                }
+                is OpenConversationsViewModel.FetchConversationsErrorState -> {
+                    Toast.makeText(context, R.string.nc_common_error_sorry, Toast.LENGTH_LONG).show()
+                }
+                else -> {}
+            }
+        }
+    }
+
+    private fun setupActionBar() {
+        setSupportActionBar(binding.openConversationsToolbar)
+        binding.openConversationsToolbar.setNavigationOnClickListener {
+            onBackPressedDispatcher.onBackPressed()
+        }
+        supportActionBar?.setDisplayHomeAsUpEnabled(true)
+        supportActionBar?.setDisplayShowHomeEnabled(true)
+        supportActionBar?.setIcon(ColorDrawable(resources!!.getColor(R.color.transparent, null)))
+        viewThemeUtils.material.themeToolbar(binding.openConversationsToolbar)
+    }
+}

+ 84 - 0
app/src/main/java/com/nextcloud/talk/openconversations/adapters/OpenConversationsAdapter.kt

@@ -0,0 +1,84 @@
+/*
+ * Nextcloud Talk application
+ *
+ * @author Marcel Hibbe
+ * Copyright (C) 2023 Marcel Hibbe <dev@mhibbe.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.nextcloud.talk.openconversations
+
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.recyclerview.widget.DiffUtil
+import androidx.recyclerview.widget.ListAdapter
+import androidx.recyclerview.widget.RecyclerView
+import com.nextcloud.talk.R
+import com.nextcloud.talk.data.user.model.User
+import com.nextcloud.talk.databinding.RvItemOpenConversationBinding
+import com.nextcloud.talk.extensions.loadUserAvatar
+import com.nextcloud.talk.openconversations.data.OpenConversation
+
+class OpenConversationsAdapter(val user: User, private val onClick: (OpenConversation) -> Unit) :
+    ListAdapter<OpenConversation, OpenConversationsAdapter.OpenConversationsViewHolder>(ConversationsCallback) {
+
+    inner class OpenConversationsViewHolder(val itemBinding: RvItemOpenConversationBinding) :
+        RecyclerView.ViewHolder(itemBinding.root) {
+
+        var currentConversation: OpenConversation? = null
+
+        init {
+            itemBinding.root.setOnClickListener {
+                currentConversation?.let {
+                    onClick(it)
+                }
+            }
+        }
+
+        fun bindItem(conversation: OpenConversation) {
+            currentConversation = conversation
+            itemBinding.nameText.text = conversation.displayName
+
+            // load avatar from server when https://github.com/nextcloud/spreed/issues/9600 is solved
+            // itemBinding.avatarView.loadUserAvatar(user, conversation.displayName, true, false)
+            itemBinding.avatarView.loadUserAvatar(R.drawable.ic_circular_group)
+        }
+    }
+
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): OpenConversationsViewHolder {
+        return OpenConversationsViewHolder(
+            RvItemOpenConversationBinding.inflate(
+                LayoutInflater.from(parent.context),
+                parent,
+                false
+            )
+        )
+    }
+
+    override fun onBindViewHolder(holder: OpenConversationsViewHolder, position: Int) {
+        val conversation = getItem(position)
+        holder.bindItem(conversation)
+    }
+}
+
+object ConversationsCallback : DiffUtil.ItemCallback<OpenConversation>() {
+    override fun areItemsTheSame(oldItem: OpenConversation, newItem: OpenConversation): Boolean {
+        return oldItem == newItem
+    }
+
+    override fun areContentsTheSame(oldItem: OpenConversation, newItem: OpenConversation): Boolean {
+        return oldItem.roomId == newItem.roomId
+    }
+}

+ 25 - 0
app/src/main/java/com/nextcloud/talk/openconversations/data/JoinConversationModel.kt

@@ -0,0 +1,25 @@
+/*
+ * Nextcloud Talk application
+ *
+ * @author Marcel Hibbe
+ * Copyright (C) 2023 Marcel Hibbe <dev@mhibbe.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.nextcloud.talk.openconversations.data
+
+data class JoinConversationModel(
+    var success: Boolean
+)

+ 27 - 0
app/src/main/java/com/nextcloud/talk/openconversations/data/OpenConversation.kt

@@ -0,0 +1,27 @@
+/*
+ * Nextcloud Talk application
+ *
+ * @author Marcel Hibbe
+ * Copyright (C) 2023 Marcel Hibbe <dev@mhibbe.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.nextcloud.talk.openconversations.data
+
+data class OpenConversation(
+    var roomId: String,
+    var roomToken: String,
+    var displayName: String
+)

+ 25 - 0
app/src/main/java/com/nextcloud/talk/openconversations/data/OpenConversationsModel.kt

@@ -0,0 +1,25 @@
+/*
+ * Nextcloud Talk application
+ *
+ * @author Marcel Hibbe
+ * Copyright (C) 2023 Marcel Hibbe <dev@mhibbe.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.nextcloud.talk.openconversations.data
+
+data class OpenConversationsModel(
+    var conversations: List<OpenConversation>
+)

+ 28 - 0
app/src/main/java/com/nextcloud/talk/openconversations/data/OpenConversationsRepository.kt

@@ -0,0 +1,28 @@
+/*
+ * Nextcloud Talk application
+ *
+ * @author Marcel Hibbe
+ * Copyright (C) 2023 Marcel Hibbe <dev@mhibbe.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.nextcloud.talk.openconversations.data
+
+import io.reactivex.Observable
+
+interface OpenConversationsRepository {
+
+    fun fetchConversations(): Observable<OpenConversationsModel>
+}

+ 58 - 0
app/src/main/java/com/nextcloud/talk/openconversations/data/OpenConversationsRepositoryImpl.kt

@@ -0,0 +1,58 @@
+/*
+ * Nextcloud Talk application
+ *
+ * @author Marcel Hibbe
+ * Copyright (C) 2023 Marcel Hibbe <dev@mhibbe.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.nextcloud.talk.openconversations.data
+
+import com.nextcloud.talk.api.NcApi
+import com.nextcloud.talk.data.user.model.User
+import com.nextcloud.talk.models.json.conversations.Conversation
+import com.nextcloud.talk.utils.ApiUtils
+import com.nextcloud.talk.utils.database.user.CurrentUserProviderNew
+import io.reactivex.Observable
+
+class OpenConversationsRepositoryImpl(private val ncApi: NcApi, currentUserProvider: CurrentUserProviderNew) :
+    OpenConversationsRepository {
+
+    val currentUser: User = currentUserProvider.currentUser.blockingGet()
+    val credentials: String = ApiUtils.getCredentials(currentUser.username, currentUser.token)
+
+    val apiVersion = ApiUtils.getConversationApiVersion(currentUser, intArrayOf(ApiUtils.APIv4, ApiUtils.APIv3, 1))
+
+    override fun fetchConversations(): Observable<OpenConversationsModel> {
+        return ncApi.getOpenConversations(
+            credentials,
+            ApiUtils.getUrlForOpenConversations(apiVersion, currentUser.baseUrl)
+        ).map { mapToOpenConversationsModel(it.ocs?.data!!) }
+    }
+
+    private fun mapToOpenConversationsModel(
+        conversations: List<Conversation>
+    ): OpenConversationsModel {
+        return OpenConversationsModel(
+            conversations.map { conversation ->
+                OpenConversation(
+                    conversation.roomId!!,
+                    conversation.token!!,
+                    conversation.name!!
+                )
+            }
+        )
+    }
+}

+ 84 - 0
app/src/main/java/com/nextcloud/talk/openconversations/viewmodels/OpenConversationsViewModel.kt

@@ -0,0 +1,84 @@
+/*
+ * Nextcloud Talk application
+ *
+ * @author Marcel Hibbe
+ * Copyright (C) 2023 Marcel Hibbe <dev@mhibbe.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.nextcloud.talk.openconversations.viewmodels
+
+import android.util.Log
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import com.nextcloud.talk.openconversations.data.OpenConversation
+import com.nextcloud.talk.openconversations.data.OpenConversationsModel
+import com.nextcloud.talk.openconversations.data.OpenConversationsRepository
+import io.reactivex.Observer
+import io.reactivex.android.schedulers.AndroidSchedulers
+import io.reactivex.disposables.Disposable
+import io.reactivex.schedulers.Schedulers
+import javax.inject.Inject
+
+class OpenConversationsViewModel @Inject constructor(private val repository: OpenConversationsRepository) :
+    ViewModel() {
+
+    sealed interface ViewState
+
+    object FetchConversationsStartState : ViewState
+    object FetchConversationsEmptyState : ViewState
+    object FetchConversationsErrorState : ViewState
+    open class FetchConversationsSuccessState(val conversations: List<OpenConversation>) : ViewState
+
+    private val _viewState: MutableLiveData<ViewState> = MutableLiveData(FetchConversationsStartState)
+    val viewState: LiveData<ViewState>
+        get() = _viewState
+
+    fun fetchConversations() {
+        _viewState.value = FetchConversationsStartState
+        repository.fetchConversations()
+            .subscribeOn(Schedulers.io())
+            ?.observeOn(AndroidSchedulers.mainThread())
+            ?.subscribe(FetchConversationsObserver())
+    }
+
+    inner class FetchConversationsObserver : Observer<OpenConversationsModel> {
+        override fun onSubscribe(d: Disposable) {
+            // unused atm
+        }
+
+        override fun onNext(model: OpenConversationsModel) {
+            if (model.conversations.isEmpty()) {
+                _viewState.value = FetchConversationsEmptyState
+            } else {
+                _viewState.value = FetchConversationsSuccessState(model.conversations)
+            }
+        }
+
+        override fun onError(e: Throwable) {
+            Log.e(TAG, "Error when fetching open conversations")
+            _viewState.value = FetchConversationsErrorState
+        }
+
+        override fun onComplete() {
+            // unused atm
+        }
+    }
+
+    companion object {
+        private val TAG = OpenConversationsViewModel::class.simpleName
+    }
+}

+ 22 - 0
app/src/main/res/drawable/baseline_format_list_bulleted_24.xml

@@ -0,0 +1,22 @@
+<!--
+    @author Google LLC
+    Copyright (C) 2023 Google LLC
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<vector android:autoMirrored="true" android:height="24dp"
+    android:tint="#000000" android:viewportHeight="24"
+    android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="@android:color/white" android:pathData="M4,10.5c-0.83,0 -1.5,0.67 -1.5,1.5s0.67,1.5 1.5,1.5 1.5,-0.67 1.5,-1.5 -0.67,-1.5 -1.5,-1.5zM4,4.5c-0.83,0 -1.5,0.67 -1.5,1.5S3.17,7.5 4,7.5 5.5,6.83 5.5,6 4.83,4.5 4,4.5zM4,16.5c-0.83,0 -1.5,0.68 -1.5,1.5s0.68,1.5 1.5,1.5 1.5,-0.68 1.5,-1.5 -0.67,-1.5 -1.5,-1.5zM7,19h14v-2L7,17v2zM7,13h14v-2L7,11v2zM7,5v2h14L21,5L7,5z"/>
+</vector>

+ 22 - 0
app/src/main/res/drawable/baseline_info_24.xml

@@ -0,0 +1,22 @@
+<!--
+    @author Google LLC
+    Copyright (C) 2023 Google LLC
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<vector android:height="24dp" android:tint="@color/icon_on_bg_default"
+    android:viewportHeight="24" android:viewportWidth="24"
+    android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="@android:color/white" android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,17h-2v-6h2v6zM13,9h-2L11,7h2v2z"/>
+</vector>

+ 218 - 0
app/src/main/res/layout/activity_contacts.xml

@@ -0,0 +1,218 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Nextcloud Talk application
+  ~
+  ~ @author Andy Scherzinger
+  ~ @author Marcel Hibbe
+  ~ Copyright (C) 2018 Andy Scherzinger
+  ~ Copyright (C) 2023 Marcel Hibbe <dev@mhibbe.de>
+  ~
+  ~ This program is free software: you can redistribute it and/or modify
+  ~ it under the terms of the GNU General Public License as published by
+  ~ the Free Software Foundation, either version 3 of the License, or
+  ~ at your option) any later version.
+  ~
+  ~ This program is distributed in the hope that it will be useful,
+  ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
+  ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  ~ GNU General Public License for more details.
+  ~
+  ~ You should have received a copy of the GNU General Public License
+  ~ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+  -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    android:animateLayoutChanges="true">
+
+    <com.google.android.material.appbar.AppBarLayout
+        android:id="@+id/contacts_appbar"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+        <com.google.android.material.appbar.MaterialToolbar
+            android:id="@+id/contacts_toolbar"
+            android:layout_width="match_parent"
+            android:layout_height="?attr/actionBarSize"
+            android:background="@color/appbar"
+            android:theme="?attr/actionBarPopupTheme"
+            app:layout_scrollFlags="scroll|enterAlways"
+            app:navigationIconTint="@color/fontAppbar"
+            app:popupTheme="@style/appActionBarPopupMenu"
+            app:titleTextColor="@color/fontAppbar"
+            tools:title="@string/nc_app_product_name" />
+    </com.google.android.material.appbar.AppBarLayout>
+
+    <LinearLayout
+        android:id="@+id/loading_content"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        tools:visibility="gone">
+
+        <include layout="@layout/rv_item_contact_shimmer" />
+        <include layout="@layout/rv_item_contact_shimmer" />
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical">
+
+            <com.elyeproj.loaderviewlibrary.LoaderTextView
+                android:id="@+id/title_text_view"
+                android:layout_width="16dp"
+                android:layout_height="32dp"
+                android:layout_marginStart="72dp"
+                android:layout_marginTop="@dimen/standard_half_margin"
+                android:layout_marginEnd="@dimen/standard_margin"
+                android:layout_marginBottom="@dimen/standard_half_margin"
+                app:custom_color="@color/nc_shimmer_default_color" />
+
+            <View
+                android:layout_width="match_parent"
+                android:layout_height="1px"
+                android:background="?android:attr/listDivider" />
+
+        </LinearLayout>
+
+        <include layout="@layout/rv_item_contact_shimmer" />
+        <include layout="@layout/rv_item_contact_shimmer" />
+        <include layout="@layout/rv_item_contact_shimmer" />
+
+    </LinearLayout>
+
+    <RelativeLayout
+        android:id="@+id/call_header_layout"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="@dimen/standard_margin"
+        android:layout_marginTop="@dimen/standard_half_margin"
+        android:layout_marginEnd="@dimen/standard_margin"
+        android:layout_marginBottom="@dimen/standard_half_margin"
+        android:orientation="vertical">
+
+        <RelativeLayout
+            android:id="@+id/initial_relative_layout"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content">
+
+            <ImageView
+                android:id="@+id/public_call_link"
+                android:layout_width="@dimen/avatar_size"
+                android:layout_height="@dimen/avatar_size"
+                android:layout_centerVertical="true"
+                android:layout_marginEnd="@dimen/standard_margin"
+                android:background="@drawable/round_bgnd"
+                android:contentDescription="@null"
+                android:padding="@dimen/standard_half_padding"
+                android:src="@drawable/ic_add_white_24px"
+                app:tint="@color/white" />
+
+            <TextView
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_centerVertical="true"
+                android:layout_toEndOf="@id/public_call_link"
+                android:ellipsize="middle"
+                android:singleLine="true"
+                android:text="@string/nc_public_call"
+                android:textAlignment="viewStart"
+                android:textAppearance="@style/ListItem"
+                tools:text="@string/nc_public_call" />
+
+        </RelativeLayout>
+
+        <RelativeLayout
+            android:id="@+id/secondary_relative_layout"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_centerInParent="true"
+            android:minHeight="@dimen/small_item_height"
+            android:visibility="gone"
+            tools:visibility="gone">
+
+            <TextView
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_centerInParent="true"
+                android:text="@string/nc_public_call_explanation"
+                android:textAlignment="center"
+                android:textAppearance="?android:attr/textAppearanceListItem"
+                tools:text="@string/nc_public_call_explanation" />
+
+        </RelativeLayout>
+    </RelativeLayout>
+
+    <RelativeLayout
+        android:id="@+id/list_open_conversations"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="@dimen/standard_margin"
+        android:layout_marginTop="@dimen/standard_half_margin"
+        android:layout_marginEnd="@dimen/standard_margin"
+        android:layout_marginBottom="@dimen/standard_half_margin"
+        android:orientation="vertical">
+
+        <ImageView
+            android:id="@+id/list_open_conversations_image"
+            android:layout_width="@dimen/avatar_size"
+            android:layout_height="@dimen/avatar_size"
+            android:layout_centerVertical="true"
+            android:layout_marginEnd="@dimen/standard_margin"
+            android:background="@drawable/round_bgnd"
+            android:contentDescription="@null"
+            android:padding="@dimen/standard_half_padding"
+            android:src="@drawable/baseline_format_list_bulleted_24"
+            app:tint="@color/white" />
+
+        <TextView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_centerVertical="true"
+            android:layout_toEndOf="@id/list_open_conversations_image"
+            android:ellipsize="middle"
+            android:singleLine="true"
+            android:text="@string/nc_list_open_conversations"
+            android:textAlignment="viewStart"
+            android:textAppearance="@style/ListItem" />
+    </RelativeLayout>
+
+    <RelativeLayout
+        android:id="@+id/join_conversation_via_link"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="@dimen/standard_margin"
+        android:layout_marginTop="@dimen/standard_half_margin"
+        android:layout_marginEnd="@dimen/standard_margin"
+        android:layout_marginBottom="@dimen/standard_half_margin"
+        android:orientation="vertical">
+
+        <ImageView
+            android:id="@+id/join_conversation_via_link_image"
+            android:layout_width="@dimen/avatar_size"
+            android:layout_height="@dimen/avatar_size"
+            android:layout_centerVertical="true"
+            android:layout_marginEnd="@dimen/standard_margin"
+            android:background="@drawable/round_bgnd"
+            android:contentDescription="@null"
+            android:padding="@dimen/standard_half_padding"
+            android:src="@drawable/ic_public_black_24px"
+            app:tint="@color/white" />
+
+        <TextView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_centerVertical="true"
+            android:layout_toEndOf="@id/join_conversation_via_link_image"
+            android:ellipsize="middle"
+            android:singleLine="true"
+            android:text="@string/nc_join_via_link"
+            android:textAlignment="viewStart"
+            android:textAppearance="@style/ListItem" />
+    </RelativeLayout>
+
+    <include
+        android:id="@+id/controller_generic_rv"
+        layout="@layout/controller_generic_rv" />
+</LinearLayout>

+ 67 - 0
app/src/main/res/layout/activity_open_conversations.xml

@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Nextcloud Talk application
+  ~
+  ~ @author Marcel Hibbe
+  ~ Copyright (C) 2023 Marcel Hibbe <dev@mhibbe.de>
+  ~
+  ~ This program is free software: you can redistribute it and/or modify
+  ~ it under the terms of the GNU General Public License as published by
+  ~ the Free Software Foundation, either version 3 of the License, or
+  ~ at your option) any later version.
+  ~
+  ~ This program is distributed in the hope that it will be useful,
+  ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
+  ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  ~ GNU General Public License for more details.
+  ~
+  ~ You should have received a copy of the GNU General Public License
+  ~ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+  -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    android:animateLayoutChanges="true">
+
+    <com.google.android.material.appbar.AppBarLayout
+        android:id="@+id/open_conversations_appbar"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+        <com.google.android.material.appbar.MaterialToolbar
+            android:id="@+id/open_conversations_toolbar"
+            android:layout_width="match_parent"
+            android:layout_height="?attr/actionBarSize"
+            android:background="@color/appbar"
+            android:theme="?attr/actionBarPopupTheme"
+            app:title="@string/openConversations"
+            app:layout_scrollFlags="scroll|enterAlways"
+            app:navigationIconTint="@color/fontAppbar"
+            app:popupTheme="@style/appActionBarPopupMenu"
+            app:titleTextColor="@color/fontAppbar" />
+    </com.google.android.material.appbar.AppBarLayout>
+
+    <androidx.recyclerview.widget.RecyclerView
+            android:id="@+id/open_conversations_recycler_view"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            app:layoutManager="LinearLayoutManager"/>
+
+    <LinearLayout
+        android:id="@+id/progress_bar_wrapper"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:gravity="center"
+        android:visibility="gone">
+        <ProgressBar
+            style="?android:attr/progressBarStyle"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:gravity="center"/>
+    </LinearLayout>
+
+    <include
+        android:id="@+id/emptyList"
+        layout="@layout/empty_list" />
+
+</LinearLayout>

+ 0 - 96
app/src/main/res/layout/controller_contacts_rv.xml

@@ -1,96 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
-  ~ Nextcloud Talk application
-  ~
-  ~ @author Andy Scherzinger
-  ~ Copyright (C) 2018 Andy Scherzinger
-  ~
-  ~ This program is free software: you can redistribute it and/or modify
-  ~ it under the terms of the GNU General Public License as published by
-  ~ the Free Software Foundation, either version 3 of the License, or
-  ~ at your option) any later version.
-  ~
-  ~ This program is distributed in the hope that it will be useful,
-  ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
-  ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  ~ GNU General Public License for more details.
-  ~
-  ~ You should have received a copy of the GNU General Public License
-  ~ along with this program.  If not, see <http://www.gnu.org/licenses/>.
-  -->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
-    xmlns:tools="http://schemas.android.com/tools"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:orientation="vertical"
-    android:animateLayoutChanges="true">
-
-    <com.google.android.material.appbar.AppBarLayout
-        android:id="@+id/contacts_appbar"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content">
-        <com.google.android.material.appbar.MaterialToolbar
-            android:id="@+id/contacts_toolbar"
-            android:layout_width="match_parent"
-            android:layout_height="?attr/actionBarSize"
-            android:background="@color/appbar"
-            android:theme="?attr/actionBarPopupTheme"
-            app:layout_scrollFlags="scroll|enterAlways"
-            app:navigationIconTint="@color/fontAppbar"
-            app:popupTheme="@style/appActionBarPopupMenu"
-            app:titleTextColor="@color/fontAppbar"
-            tools:title="@string/nc_app_product_name" />
-    </com.google.android.material.appbar.AppBarLayout>
-
-    <LinearLayout
-        android:id="@+id/loading_content"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:orientation="vertical">
-
-        <include layout="@layout/rv_item_contact_shimmer" />
-        <include layout="@layout/rv_item_contact_shimmer" />
-
-        <LinearLayout
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:orientation="vertical">
-
-            <com.elyeproj.loaderviewlibrary.LoaderTextView
-                android:id="@+id/title_text_view"
-                android:layout_width="16dp"
-                android:layout_height="32dp"
-                android:layout_marginStart="72dp"
-                android:layout_marginTop="@dimen/standard_half_margin"
-                android:layout_marginEnd="@dimen/standard_margin"
-                android:layout_marginBottom="@dimen/standard_half_margin"
-                app:custom_color="@color/nc_shimmer_default_color" />
-
-            <View
-                android:layout_width="match_parent"
-                android:layout_height="1px"
-                android:background="?android:attr/listDivider" />
-
-        </LinearLayout>
-
-        <include layout="@layout/rv_item_contact_shimmer" />
-        <include layout="@layout/rv_item_contact_shimmer" />
-        <include layout="@layout/rv_item_contact_shimmer" />
-
-    </LinearLayout>
-
-    <include
-        android:id="@+id/conversation_privacy_toggle"
-        layout="@layout/conversation_privacy_toggle"
-        android:visibility="gone" />
-
-    <include
-        android:id="@+id/join_conversation_via_link"
-        layout="@layout/join_conversation_via_link"
-        android:visibility="gone" />
-
-    <include
-        android:id="@+id/controller_generic_rv"
-        layout="@layout/controller_generic_rv"
-        android:visibility="gone" />
-</LinearLayout>

+ 0 - 85
app/src/main/res/layout/conversation_privacy_toggle.xml

@@ -1,85 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
-  ~ Nextcloud Talk application
-  ~
-  ~ @author Mario Danic
-  ~ @author Andy Scherzinger
-  ~ Copyright (C) 2021 Andy Scherzinger <info@andy-scherzinger.de>
-  ~ Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
-  ~
-  ~ This program is free software: you can redistribute it and/or modify
-  ~ it under the terms of the GNU General Public License as published by
-  ~ the Free Software Foundation, either version 3 of the License, or
-  ~ at your option) any later version.
-  ~
-  ~ This program is distributed in the hope that it will be useful,
-  ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
-  ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  ~ GNU General Public License for more details.
-  ~
-  ~ You should have received a copy of the GNU General Public License
-  ~ along with this program.  If not, see <http://www.gnu.org/licenses/>.
-  -->
-
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
-    xmlns:tools="http://schemas.android.com/tools"
-    android:id="@+id/call_header_layout"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:layout_marginStart="@dimen/standard_margin"
-    android:layout_marginTop="@dimen/standard_half_margin"
-    android:layout_marginEnd="@dimen/standard_margin"
-    android:layout_marginBottom="@dimen/standard_half_margin"
-    android:orientation="vertical">
-
-    <RelativeLayout
-        android:id="@+id/initial_relative_layout"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content">
-
-        <ImageView
-            android:id="@+id/public_call_link"
-            android:layout_width="@dimen/avatar_size"
-            android:layout_height="@dimen/avatar_size"
-            android:layout_centerVertical="true"
-            android:layout_marginEnd="@dimen/standard_margin"
-            android:background="@drawable/round_bgnd"
-            android:contentDescription="@null"
-            android:padding="@dimen/standard_half_padding"
-            android:src="@drawable/ic_add_white_24px"
-            app:tint="@color/white" />
-
-        <TextView
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_centerVertical="true"
-            android:layout_toEndOf="@id/public_call_link"
-            android:ellipsize="middle"
-            android:singleLine="true"
-            android:text="@string/nc_public_call"
-            android:textAlignment="viewStart"
-            android:textAppearance="@style/ListItem"
-            tools:text="@string/nc_public_call" />
-
-    </RelativeLayout>
-
-    <RelativeLayout
-        android:id="@+id/secondary_relative_layout"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_centerInParent="true"
-        android:minHeight="@dimen/small_item_height"
-        android:visibility="gone"
-        tools:visibility="gone">
-
-        <TextView
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_centerInParent="true"
-            android:text="@string/nc_public_call_explanation"
-            android:textAlignment="center"
-            android:textAppearance="?android:attr/textAppearanceListItem"
-            tools:text="@string/nc_public_call_explanation" />
-
-    </RelativeLayout>
-</RelativeLayout>

+ 19 - 21
app/src/main/res/layout/join_conversation_via_link.xml → app/src/main/res/layout/rv_item_open_conversation.xml

@@ -3,8 +3,10 @@
   ~
   ~
   ~ @author Mario Danic
   ~ @author Mario Danic
   ~ @author Andy Scherzinger
   ~ @author Andy Scherzinger
-  ~ Copyright (C) 2021 Andy Scherzinger <info@andy-scherzinger.de>
-  ~ Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
+  ~ @author Marcel Hibbe
+  ~ Copyright (C) 2023 Marcel Hibbe <dev@mhibbe.de>
+  ~ Copyright (C) 2021 Andy Scherzinger
+  ~ Copyright (C) 2017 Mario Danic
   ~
   ~
   ~ This program is free software: you can redistribute it and/or modify
   ~ This program is free software: you can redistribute it and/or modify
   ~ it under the terms of the GNU General Public License as published by
   ~ it under the terms of the GNU General Public License as published by
@@ -21,8 +23,7 @@
   -->
   -->
 
 
 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
-    android:id="@+id/joinConversationViaLinkRelativeLayout"
+    xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:layout_height="wrap_content"
     android:layout_marginStart="@dimen/standard_margin"
     android:layout_marginStart="@dimen/standard_margin"
@@ -31,27 +32,24 @@
     android:layout_marginBottom="@dimen/standard_half_margin"
     android:layout_marginBottom="@dimen/standard_half_margin"
     android:orientation="vertical">
     android:orientation="vertical">
 
 
+    <androidx.emoji2.widget.EmojiTextView
+        android:id="@+id/name_text"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_centerVertical="true"
+        android:layout_toEndOf="@id/avatar_view"
+        android:ellipsize="end"
+        android:lines="1"
+        android:textAlignment="viewStart"
+        android:textAppearance="@style/ListItem"
+        tools:text="Jane Doe" />
+
     <ImageView
     <ImageView
-        android:id="@+id/joinConversationViaLinkImageView"
+        android:id="@+id/avatar_view"
         android:layout_width="@dimen/avatar_size"
         android:layout_width="@dimen/avatar_size"
         android:layout_height="@dimen/avatar_size"
         android:layout_height="@dimen/avatar_size"
         android:layout_centerVertical="true"
         android:layout_centerVertical="true"
         android:layout_marginEnd="@dimen/standard_margin"
         android:layout_marginEnd="@dimen/standard_margin"
-        android:background="@drawable/round_bgnd"
-        android:contentDescription="@null"
-        android:padding="@dimen/standard_half_padding"
-        android:src="@drawable/ic_public_black_24px"
-        app:tint="@color/white" />
-
-    <TextView
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_centerVertical="true"
-        android:layout_toEndOf="@id/joinConversationViaLinkImageView"
-        android:ellipsize="middle"
-        android:singleLine="true"
-        android:text="@string/nc_join_via_link"
-        android:textAlignment="viewStart"
-        android:textAppearance="@style/ListItem" />
+        android:contentDescription="@string/avatar" />
 
 
 </RelativeLayout>
 </RelativeLayout>

+ 2 - 0
app/src/main/res/values-night/colors.xml

@@ -76,4 +76,6 @@
 
 
     <color name="dialog_background">#353535</color>
     <color name="dialog_background">#353535</color>
 
 
+    <color name="icon_on_bg_default">#99FFFFFF</color>
+
 </resources>
 </resources>

+ 2 - 0
app/src/main/res/values/colors.xml

@@ -105,4 +105,6 @@
     possible?! don't use this to set the background of dialogs -->
     possible?! don't use this to set the background of dialogs -->
     <color name="dialog_background">#FFFFFF</color>
     <color name="dialog_background">#FFFFFF</color>
 
 
+    <color name="icon_on_bg_default">#99000000</color>
+
 </resources>
 </resources>

+ 5 - 0
app/src/main/res/values/strings.xml

@@ -197,6 +197,7 @@ How to translate with transifex:
 
 
     <string name="nc_new_conversation">New conversation</string>
     <string name="nc_new_conversation">New conversation</string>
     <string name="nc_join_via_link">Join with a link</string>
     <string name="nc_join_via_link">Join with a link</string>
+    <string name="nc_list_open_conversations">List open conversations</string>
     <string name="nc_join_via_web">Join via web</string>
     <string name="nc_join_via_web">Join via web</string>
     <string name="nc_mark_as_read">Mark as read</string>
     <string name="nc_mark_as_read">Mark as read</string>
     <string name="nc_mark_as_unread">Mark as unread</string>
     <string name="nc_mark_as_unread">Mark as unread</string>
@@ -205,6 +206,10 @@ How to translate with transifex:
 
 
     <string name="nc_forward_to_three_dots">Forward to …</string>
     <string name="nc_forward_to_three_dots">Forward to …</string>
 
 
+    <!-- Open conversations -->
+    <string name="nc_no_open_conversations_headline">No open conversations</string>
+    <string name="nc_no_open_conversations_text">No open conversations that you can join.\nEither there are no open conversations or you already joined all of them.</string>
+
     <!-- Contacts -->
     <!-- Contacts -->
     <string name="nc_select_participants">Select participants</string>
     <string name="nc_select_participants">Select participants</string>
     <string name="nc_add_participants">Add participants</string>
     <string name="nc_add_participants">Add participants</string>