diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 256e2b2b5..0d014dfc1 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -246,10 +246,6 @@
android:name=".conversationinfoedit.ConversationInfoEditActivity"
android:theme="@style/AppTheme" />
-
-
diff --git a/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivity.kt b/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivity.kt
deleted file mode 100644
index 31bdd4109..000000000
--- a/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivity.kt
+++ /dev/null
@@ -1,900 +0,0 @@
-/*
- * Nextcloud Talk - Android Client
- *
- * SPDX-FileCopyrightText: 2022 Andy Scherzinger
- * SPDX-FileCopyrightText: 2022 Marcel Hibbe
- * SPDX-FileCopyrightText: 2017 Mario Danic
- * SPDX-License-Identifier: GPL-3.0-or-later
- */
-package com.nextcloud.talk.contacts
-
-import android.app.SearchManager
-import android.content.Context
-import android.content.Intent
-import android.graphics.PorterDuff
-import android.graphics.drawable.ColorDrawable
-import android.os.Bundle
-import android.text.InputType
-import android.util.Log
-import android.view.Menu
-import android.view.MenuItem
-import android.view.View
-import android.view.inputmethod.EditorInfo
-import androidx.appcompat.widget.SearchView
-import androidx.core.content.res.ResourcesCompat
-import androidx.core.view.MenuItemCompat
-import androidx.work.Data
-import androidx.work.OneTimeWorkRequest
-import androidx.work.WorkInfo
-import androidx.work.WorkManager
-import autodagger.AutoInjector
-import com.bluelinelabs.logansquare.LoganSquare
-import com.nextcloud.talk.R
-import com.nextcloud.talk.activities.BaseActivity
-import com.nextcloud.talk.adapters.items.ContactItem
-import com.nextcloud.talk.adapters.items.GenericTextHeaderItem
-import com.nextcloud.talk.api.NcApi
-import com.nextcloud.talk.application.NextcloudTalkApplication
-import com.nextcloud.talk.chat.ChatActivity
-import com.nextcloud.talk.conversation.CreateConversationDialogFragment
-import com.nextcloud.talk.data.user.model.User
-import com.nextcloud.talk.databinding.ActivityContactsBinding
-import com.nextcloud.talk.events.EventStatus
-import com.nextcloud.talk.events.OpenConversationEvent
-import com.nextcloud.talk.jobs.AddParticipantsToConversation
-import com.nextcloud.talk.models.RetrofitBucket
-import com.nextcloud.talk.models.json.autocomplete.AutocompleteOverall
-import com.nextcloud.talk.models.json.autocomplete.AutocompleteUser
-import com.nextcloud.talk.models.json.conversations.ConversationEnums
-import com.nextcloud.talk.models.json.conversations.RoomOverall
-import com.nextcloud.talk.models.json.converters.EnumActorTypeConverter
-import com.nextcloud.talk.models.json.participants.Participant
-import com.nextcloud.talk.openconversations.ListOpenConversationsActivity
-import com.nextcloud.talk.users.UserManager
-import com.nextcloud.talk.utils.ApiUtils
-import com.nextcloud.talk.utils.CapabilitiesUtil
-import com.nextcloud.talk.utils.SpreedFeatures
-import com.nextcloud.talk.utils.UserIdUtils.getIdForUser
-import com.nextcloud.talk.utils.bundle.BundleKeys
-import eu.davidea.flexibleadapter.FlexibleAdapter
-import eu.davidea.flexibleadapter.SelectableAdapter
-import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
-import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
-import io.reactivex.Observer
-import io.reactivex.android.schedulers.AndroidSchedulers
-import io.reactivex.disposables.Disposable
-import io.reactivex.schedulers.Schedulers
-import okhttp3.ResponseBody
-import org.greenrobot.eventbus.Subscribe
-import org.greenrobot.eventbus.ThreadMode
-import org.parceler.Parcels
-import java.io.IOException
-import java.util.Locale
-import javax.inject.Inject
-
-@AutoInjector(NextcloudTalkApplication::class)
-class ContactsActivity :
- BaseActivity(),
- SearchView.OnQueryTextListener,
- FlexibleAdapter.OnItemClickListener {
- private lateinit var binding: ActivityContactsBinding
-
- @Inject
- lateinit var userManager: UserManager
-
- @Inject
- lateinit var ncApi: NcApi
-
- private var credentials: String? = null
- private var currentUser: User? = null
- private var contactsQueryDisposable: Disposable? = null
- private var cacheQueryDisposable: Disposable? = null
- private var adapter: FlexibleAdapter<*>? = null
- private var contactItems: MutableList>? = null
- private var layoutManager: SmoothScrollLinearLayoutManager? = null
- private var searchItem: MenuItem? = null
- private var searchView: SearchView? = null
- private var isNewConversationView = false
- private var isPublicCall = false
- private var userHeaderItems: HashMap = HashMap()
- private var alreadyFetching = false
- private var doneMenuItem: MenuItem? = null
- private var selectedUserIds: MutableSet = HashSet()
- private var selectedGroupIds: MutableSet = HashSet()
- private var selectedCircleIds: MutableSet = HashSet()
- private var selectedEmails: MutableSet = HashSet()
- private var existingParticipants: List? = null
- private var isAddingParticipantsView = false
- private var conversationToken: String? = null
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this)
-
- binding = ActivityContactsBinding.inflate(layoutInflater)
- setupActionBar()
- setContentView(binding.root)
- setupSystemColors()
-
- if (savedInstanceState != null) {
- if (adapter != null) {
- adapter?.onRestoreInstanceState(savedInstanceState)
- }
- }
-
- existingParticipants = ArrayList()
- if (intent.hasExtra(BundleKeys.KEY_NEW_CONVERSATION)) {
- // adding a new conversation, setting a flag.
- isNewConversationView = true
- } else if (intent.hasExtra(BundleKeys.KEY_ADD_PARTICIPANTS)) {
- // adding the participants in the conversation also opens this activity, setting a flag for it.
- isAddingParticipantsView = true
- conversationToken = intent.getStringExtra(BundleKeys.KEY_TOKEN)
- if (intent.hasExtra(BundleKeys.KEY_EXISTING_PARTICIPANTS)) {
- existingParticipants = intent.getStringArrayListExtra(BundleKeys.KEY_EXISTING_PARTICIPANTS)
- }
- }
- selectedUserIds = HashSet()
- selectedGroupIds = HashSet()
- selectedEmails = HashSet()
- selectedCircleIds = HashSet()
- }
-
- override fun onResume() {
- super.onResume()
-
- if (isNewConversationView) {
- toggleConversationPrivacyLayout(!isPublicCall)
- }
- if (isAddingParticipantsView) {
- binding.callHeaderLayout.visibility = View.GONE
- binding.listOpenConversations.visibility = View.GONE
- } else {
- binding.listOpenConversations.setOnClickListener {
- listOpenConversations()
- }
- binding.callHeaderLayout.setOnClickListener {
- toggleCallHeader()
- }
- }
-
- currentUser = currentUserProvider.currentUser.blockingGet()
- if (currentUser != null) {
- credentials = ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token)
- }
- if (adapter == null) {
- contactItems = ArrayList>()
- adapter = FlexibleAdapter(contactItems, this, false)
- if (currentUser != null) {
- fetchData()
- }
- }
- setupAdapter()
- prepareViews()
- }
-
- private fun setupActionBar() {
- setSupportActionBar(binding.contactsToolbar)
- binding.contactsToolbar.setNavigationOnClickListener {
- onBackPressedDispatcher.onBackPressed()
- }
- supportActionBar?.setDisplayHomeAsUpEnabled(true)
- supportActionBar?.setDisplayShowHomeEnabled(true)
- supportActionBar?.setIcon(ColorDrawable(resources!!.getColor(android.R.color.transparent, null)))
- supportActionBar?.title = when {
- isAddingParticipantsView -> {
- resources!!.getString(R.string.nc_add_participants)
- }
-
- isNewConversationView -> {
- resources!!.getString(R.string.nc_select_participants)
- }
-
- else -> {
- resources!!.getString(R.string.nc_app_product_name)
- }
- }
- viewThemeUtils.material.themeToolbar(binding.contactsToolbar)
- }
-
- override fun onSaveInstanceState(bundle: Bundle) {
- super.onSaveInstanceState(bundle)
- adapter?.onSaveInstanceState(bundle)
- }
-
- override fun onCreateOptionsMenu(menu: Menu): Boolean {
- super.onCreateOptionsMenu(menu)
- menuInflater.inflate(R.menu.menu_contacts, menu)
- searchItem = menu.findItem(R.id.action_search)
- doneMenuItem = menu.findItem(R.id.contacts_selection_done)
- initSearchView()
- return true
- }
-
- override fun onPrepareOptionsMenu(menu: Menu): Boolean {
- super.onPrepareOptionsMenu(menu)
- if (searchItem != null) {
- binding.titleTextView.let {
- viewThemeUtils.platform.colorToolbarMenuIcon(
- it.context,
- searchItem!!
- )
- }
- }
-
- checkAndHandleDoneMenuItem()
- if (adapter?.hasFilter() == true) {
- searchItem!!.expandActionView()
- searchView!!.setQuery(adapter!!.getFilter(String::class.java) as CharSequence, false)
- }
- return true
- }
-
- override fun onOptionsItemSelected(item: MenuItem): Boolean {
- return when (item.itemId) {
- R.id.home -> {
- finish()
- true
- }
-
- R.id.contacts_selection_done -> {
- selectionDone()
- true
- }
-
- else -> {
- super.onOptionsItemSelected(item)
- }
- }
- }
-
- private fun setupAdapter() {
- adapter?.setNotifyChangeOfUnfilteredItems(true)?.mode = SelectableAdapter.Mode.MULTI
- adapter?.setStickyHeaderElevation(HEADER_ELEVATION)
- ?.setUnlinkAllItemsOnRemoveHeaders(true)
- ?.setDisplayHeadersAtStartUp(true)
- ?.setStickyHeaders(true)
- adapter?.addListener(this)
- }
-
- private fun selectionDone() {
- if (isAddingParticipantsView) {
- // add participants in the view
- addParticipantsToConversation()
- } else {
- // if there is only 1 participant, directly add him while creating room (which can only add 'one')
- if (!isPublicCall && selectedCircleIds.size + selectedGroupIds.size + selectedUserIds.size == 1) {
- val userId: String
- var sourceType: String? = null
- var roomType = "1"
- when {
- selectedGroupIds.size == 1 -> {
- roomType = "2"
- userId = selectedGroupIds.iterator().next()
- }
-
- selectedCircleIds.size == 1 -> {
- roomType = "2"
- sourceType = "circles"
- userId = selectedCircleIds.iterator().next()
- }
-
- else -> {
- userId = selectedUserIds.iterator().next()
- }
- }
- createRoom(roomType, sourceType, userId)
-
- // if there are more participants to add, ask for roomName and add them one after another
- } else {
- val roomType: ConversationEnums.ConversationType = if (isPublicCall) {
- ConversationEnums.ConversationType.ROOM_PUBLIC_CALL
- } else {
- ConversationEnums.ConversationType.ROOM_GROUP_CALL
- }
- val userIdsArray = ArrayList(selectedUserIds)
- val groupIdsArray = ArrayList(selectedGroupIds)
- val emailsArray = ArrayList(selectedEmails)
- val circleIdsArray = ArrayList(selectedCircleIds)
-
- val createConversationDialog = CreateConversationDialogFragment.newInstance(
- userIdsArray,
- groupIdsArray,
- emailsArray,
- circleIdsArray,
- Parcels.wrap(roomType)
- )
- createConversationDialog.show(
- supportFragmentManager,
- TAG
- )
- }
- }
- }
-
- private fun createRoom(roomType: String, sourceType: String?, userId: String) {
- val apiVersion: Int = ApiUtils.getConversationApiVersion(currentUser!!, intArrayOf(ApiUtils.API_V4, 1))
- val retrofitBucket: RetrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(
- apiVersion,
- currentUser!!.baseUrl!!,
- roomType,
- sourceType,
- userId,
- null
- )
- ncApi.createRoom(
- credentials,
- retrofitBucket.url,
- retrofitBucket.queryMap
- )
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(object : Observer {
- override fun onSubscribe(d: Disposable) {
- // unused atm
- }
-
- override fun onNext(roomOverall: RoomOverall) {
- val bundle = Bundle()
- bundle.putString(BundleKeys.KEY_ROOM_TOKEN, roomOverall.ocs!!.data!!.token)
- // bundle.putString(BundleKeys.KEY_ROOM_ID, roomOverall.ocs!!.data!!.roomId)
-
- val chatIntent = Intent(context, ChatActivity::class.java)
- chatIntent.putExtras(bundle)
- chatIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
- startActivity(chatIntent)
- }
-
- override fun onError(e: Throwable) {
- // unused atm
- }
-
- override fun onComplete() {
- // unused atm
- }
- })
- }
-
- private fun addParticipantsToConversation() {
- val userIdsArray: Array = selectedUserIds.toTypedArray()
- val groupIdsArray: Array = selectedGroupIds.toTypedArray()
- val emailsArray: Array = selectedEmails.toTypedArray()
- val circleIdsArray: Array = selectedCircleIds.toTypedArray()
- val data = Data.Builder()
- data.putLong(BundleKeys.KEY_INTERNAL_USER_ID, currentUser!!.id!!)
- data.putString(BundleKeys.KEY_TOKEN, conversationToken)
- data.putStringArray(BundleKeys.KEY_SELECTED_USERS, userIdsArray)
- data.putStringArray(BundleKeys.KEY_SELECTED_GROUPS, groupIdsArray)
- data.putStringArray(BundleKeys.KEY_SELECTED_EMAILS, emailsArray)
- data.putStringArray(BundleKeys.KEY_SELECTED_CIRCLES, circleIdsArray)
- val addParticipantsToConversationWorker: OneTimeWorkRequest = OneTimeWorkRequest.Builder(
- AddParticipantsToConversation::class.java
- ).setInputData(data.build()).build()
- WorkManager.getInstance().enqueue(addParticipantsToConversationWorker)
-
- WorkManager.getInstance(context).getWorkInfoByIdLiveData(addParticipantsToConversationWorker.id)
- .observeForever { workInfo: WorkInfo? ->
- if (workInfo != null) {
- when (workInfo.state) {
- WorkInfo.State.RUNNING -> {
- Log.d(TAG, "running AddParticipantsToConversation")
- }
-
- WorkInfo.State.SUCCEEDED -> {
- Log.d(TAG, "success AddParticipantsToConversation")
-
- eventBus.post(
- EventStatus(
- getIdForUser(currentUser),
- EventStatus.EventType.PARTICIPANTS_UPDATE,
- true
- )
- )
-
- finish()
- }
-
- WorkInfo.State.FAILED -> {
- Log.d(TAG, "failed AddParticipantsToConversation")
- }
-
- else -> {
- }
- }
- }
- }
- }
-
- private fun initSearchView() {
- val searchManager: SearchManager? = getSystemService(Context.SEARCH_SERVICE) as SearchManager?
- if (searchItem != null) {
- searchView = MenuItemCompat.getActionView(searchItem) as SearchView
- viewThemeUtils.talk.themeSearchView(searchView!!)
- searchView!!.maxWidth = Int.MAX_VALUE
- searchView!!.inputType = InputType.TYPE_TEXT_VARIATION_FILTER
- var imeOptions: Int = EditorInfo.IME_ACTION_DONE or EditorInfo.IME_FLAG_NO_FULLSCREEN
- if (appPreferences.isKeyboardIncognito == true) {
- imeOptions = imeOptions or EditorInfo.IME_FLAG_NO_PERSONALIZED_LEARNING
- }
- searchView!!.imeOptions = imeOptions
- searchView!!.queryHint = resources!!.getString(R.string.nc_search)
- if (searchManager != null) {
- searchView!!.setSearchableInfo(searchManager.getSearchableInfo(componentName))
- }
- searchView!!.setOnQueryTextListener(this)
- }
- }
-
- private fun fetchData() {
- dispose(null)
- alreadyFetching = true
- userHeaderItems = HashMap()
- val query = adapter!!.getFilter(String::class.java)
- val retrofitBucket: RetrofitBucket =
- ApiUtils.getRetrofitBucketForContactsSearchFor14(currentUser!!.baseUrl!!, query)
- val modifiedQueryMap: HashMap = HashMap(retrofitBucket.queryMap)
- modifiedQueryMap["limit"] = CONTACTS_BATCH_SIZE
- if (isAddingParticipantsView) {
- modifiedQueryMap["itemId"] = conversationToken
- }
- val shareTypesList: ArrayList = ArrayList()
- // users
- shareTypesList.add("0")
- if (!isAddingParticipantsView) {
- // groups
- shareTypesList.add("1")
- } else if (CapabilitiesUtil.hasSpreedFeatureCapability(
- currentUser?.capabilities?.spreedCapability!!,
- SpreedFeatures.INVITE_GROUPS_AND_MAILS
- )
- ) {
- // groups
- shareTypesList.add("1")
- // emails
- shareTypesList.add("4")
- }
- if (CapabilitiesUtil.hasSpreedFeatureCapability(
- currentUser?.capabilities?.spreedCapability!!,
- SpreedFeatures.CIRCLES_SUPPORT
- )
- ) {
- // circles
- shareTypesList.add("7")
- }
- modifiedQueryMap["shareTypes[]"] = shareTypesList
- ncApi.getContactsWithSearchParam(
- credentials,
- retrofitBucket.url,
- shareTypesList,
- modifiedQueryMap
- )
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .retry(RETRIES)
- .subscribe(object : Observer {
- override fun onSubscribe(d: Disposable) {
- contactsQueryDisposable = d
- }
-
- override fun onNext(responseBody: ResponseBody) {
- // getting contacts
- val newUserItemList = processAutocompleteUserList(responseBody)
-
- userHeaderItems = HashMap()
- // getting the contact list from the endpoints.
- contactItems!!.addAll(newUserItemList)
-
- sortUserItems(newUserItemList)
-
- if (newUserItemList.size > 0) {
- adapter?.updateDataSet(newUserItemList as List?)
- } else {
- adapter?.filterItems()
- }
- }
-
- override fun onError(e: Throwable) {
- dispose(contactsQueryDisposable)
- }
-
- override fun onComplete() {
- dispose(contactsQueryDisposable)
- alreadyFetching = false
- disengageProgressBar()
- }
- })
- }
-
- private fun processAutocompleteUserList(responseBody: ResponseBody): MutableList> {
- try {
- val autocompleteOverall: AutocompleteOverall = LoganSquare.parse(
- responseBody.string(),
- AutocompleteOverall::class.java
- )
- val autocompleteUsersList: ArrayList = ArrayList()
- autocompleteUsersList.addAll(autocompleteOverall.ocs!!.data!!)
- return processAutocompleteUserList(autocompleteUsersList)
- } catch (ioe: IOException) {
- Log.e(TAG, "Parsing response body failed while getting contacts", ioe)
- }
-
- return ArrayList()
- }
-
- private fun processAutocompleteUserList(
- autocompleteUsersList: ArrayList
- ): MutableList> {
- var participant: Participant
- val actorTypeConverter = EnumActorTypeConverter()
- val newUserItemList: MutableList> = ArrayList()
- for (autocompleteUser in autocompleteUsersList) {
- if (autocompleteUser.id != null &&
- autocompleteUser.id != currentUser!!.userId &&
- !existingParticipants!!.contains(autocompleteUser.id)
- ) {
- participant = createParticipant(autocompleteUser, actorTypeConverter)
- val headerTitle = getHeaderTitle(participant)
- var genericTextHeaderItem: GenericTextHeaderItem
- if (!userHeaderItems.containsKey(headerTitle)) {
- genericTextHeaderItem = GenericTextHeaderItem(headerTitle, viewThemeUtils)
- userHeaderItems.put(headerTitle, genericTextHeaderItem)
- }
- val newContactItem = ContactItem(
- participant,
- currentUser!!,
- userHeaderItems[headerTitle],
- viewThemeUtils
- )
- if (!contactItems!!.contains(newContactItem)) {
- newUserItemList.add(newContactItem)
- }
- }
- }
- return newUserItemList
- }
-
- // this function displays the title of the contacts activity
- private fun getHeaderTitle(participant: Participant): String {
- return when {
- participant.calculatedActorType == Participant.ActorType.GROUPS -> {
- resources!!.getString(R.string.nc_groups)
- }
-
- participant.calculatedActorType == Participant.ActorType.CIRCLES -> {
- resources!!.getString(R.string.nc_teams)
- }
-
- else -> {
- participant.displayName!!.substring(0, 1).uppercase(Locale.getDefault())
- }
- }
- }
-
- private fun createParticipant(
- autocompleteUser: AutocompleteUser,
- actorTypeConverter: EnumActorTypeConverter
- ): Participant {
- val participant = Participant()
- participant.actorId = autocompleteUser.id
- participant.actorType = actorTypeConverter.getFromString(autocompleteUser.source)
- participant.displayName = autocompleteUser.label
-
- return participant
- }
-
- @Suppress("LongMethod")
- private fun sortUserItems(newUserItemList: MutableList>) {
- newUserItemList.sortWith sort@{ o1: AbstractFlexibleItem<*>, o2: AbstractFlexibleItem<*> ->
- val firstName: String = if (o1 is ContactItem) {
- o1.model.displayName!!
- } else {
- (o1 as GenericTextHeaderItem).model
- }
- val secondName: String = if (o2 is ContactItem) {
- o2.model.displayName!!
- } else {
- (o2 as GenericTextHeaderItem).model
- }
- if (o1 is ContactItem && o2 is ContactItem) {
- val firstSource: Participant.ActorType = o1.model.actorType!!
- val secondSource: Participant.ActorType = o2.model.actorType!!
- if (firstSource == secondSource) {
- return@sort firstName.compareTo(secondName, ignoreCase = true)
- }
-
- // First users
- if (Participant.ActorType.USERS == firstSource) {
- return@sort -1
- } else if (Participant.ActorType.USERS == secondSource) {
- return@sort 1
- }
-
- // Then groups
- if (Participant.ActorType.GROUPS == firstSource) {
- return@sort -1
- } else if (Participant.ActorType.GROUPS == secondSource) {
- return@sort 1
- }
-
- // Then circles
- if (Participant.ActorType.CIRCLES == firstSource) {
- return@sort -1
- } else if (Participant.ActorType.CIRCLES == secondSource) {
- return@sort 1
- }
-
- // Otherwise fall back to name sorting
- return@sort firstName.compareTo(secondName, ignoreCase = true)
- }
- firstName.compareTo(secondName, ignoreCase = true)
- }
-
- contactItems?.sortWith sort@{ o1: AbstractFlexibleItem<*>, o2: AbstractFlexibleItem<*> ->
- val firstName: String = if (o1 is ContactItem) {
- o1.model.displayName!!
- } else {
- (o1 as GenericTextHeaderItem).model
- }
- val secondName: String = if (o2 is ContactItem) {
- o2.model.displayName!!
- } else {
- (o2 as GenericTextHeaderItem).model
- }
- if (o1 is ContactItem && o2 is ContactItem) {
- if (Participant.ActorType.GROUPS == o1.model.actorType &&
- Participant.ActorType.GROUPS == o2.model.actorType
- ) {
- return@sort firstName.compareTo(secondName, ignoreCase = true)
- } else if (Participant.ActorType.GROUPS == o1.model.actorType) {
- return@sort -1
- } else if (Participant.ActorType.GROUPS == o2.model.actorType) {
- return@sort 1
- }
- }
- firstName.compareTo(secondName, ignoreCase = true)
- }
- }
-
- private fun prepareViews() {
- layoutManager = SmoothScrollLinearLayoutManager(this)
- binding.contactsRv.layoutManager = layoutManager
- binding.contactsRv.setHasFixedSize(true)
- binding.contactsRv.adapter = adapter
-
- binding.listOpenConversationsImage.background?.setColorFilter(
- ResourcesCompat.getColor(resources!!, R.color.colorBackgroundDarker, null),
- PorterDuff.Mode.SRC_IN
- )
-
- binding.let {
- viewThemeUtils.platform.colorImageViewBackgroundAndIcon(it.publicCallLink)
- }
- disengageProgressBar()
- }
-
- private fun disengageProgressBar() {
- if (!alreadyFetching) {
- binding.contactsRv.visibility = View.VISIBLE
- binding.loadingContent.visibility = View.GONE
- binding.root.visibility = View.VISIBLE
- if (isNewConversationView) {
- binding.callHeaderLayout.visibility = View.VISIBLE
- }
- }
- }
-
- private fun dispose(disposable: Disposable?) {
- if (disposable != null && !disposable.isDisposed) {
- disposable.dispose()
- } else if (disposable == null) {
- if (contactsQueryDisposable != null && !contactsQueryDisposable!!.isDisposed) {
- contactsQueryDisposable!!.dispose()
- contactsQueryDisposable = null
- }
- if (cacheQueryDisposable != null && !cacheQueryDisposable!!.isDisposed) {
- cacheQueryDisposable!!.dispose()
- cacheQueryDisposable = null
- }
- }
- }
-
- override fun onDestroy() {
- super.onDestroy()
- dispose(null)
- }
-
- override fun onQueryTextChange(newText: String): Boolean {
- if (newText != "" && adapter?.hasNewFilter(newText) == true) {
- adapter?.setFilter(newText)
- fetchData()
- } else if (newText == "") {
- adapter?.setFilter("")
- adapter?.updateDataSet(contactItems as List?)
- }
-
- return true
- }
-
- override fun onQueryTextSubmit(query: String): Boolean {
- return onQueryTextChange(query)
- }
-
- private fun checkAndHandleDoneMenuItem() {
- if (adapter != null && doneMenuItem != null) {
- doneMenuItem!!.isVisible =
- selectedCircleIds.size + selectedEmails.size + selectedGroupIds.size + selectedUserIds.size > 0 ||
- isPublicCall
- } else if (doneMenuItem != null) {
- doneMenuItem!!.isVisible = false
- }
- }
-
- @Subscribe(threadMode = ThreadMode.MAIN)
- fun onMessageEvent(openConversationEvent: OpenConversationEvent) {
- val chatIntent = Intent(context, ChatActivity::class.java)
- chatIntent.putExtras(openConversationEvent.bundle!!)
- chatIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
- startActivity(chatIntent)
- }
-
- override fun onItemClick(view: View, position: Int): Boolean {
- if (adapter?.getItem(position) is ContactItem) {
- if (!isNewConversationView && !isAddingParticipantsView) {
- createRoom(adapter?.getItem(position) as ContactItem)
- } else {
- updateSelection((adapter?.getItem(position) as ContactItem))
- }
- }
- return true
- }
-
- private fun updateSelection(contactItem: ContactItem) {
- contactItem.model.selected = !contactItem.model.selected
- updateSelectionLists(contactItem.model)
- if (CapabilitiesUtil.hasSpreedFeatureCapability(
- currentUser?.capabilities?.spreedCapability!!, SpreedFeatures.LAST_ROOM_ACTIVITY
- ) &&
- !CapabilitiesUtil.hasSpreedFeatureCapability(
- currentUser?.capabilities?.spreedCapability!!, SpreedFeatures.INVITE_GROUPS_AND_MAILS
- ) &&
- isValidGroupSelection(contactItem, contactItem.model, adapter)
- ) {
- val currentItems: List = adapter?.currentItems as List
- var internalParticipant: Participant
- for (i in currentItems.indices) {
- internalParticipant = currentItems[i].model
- if (internalParticipant.calculatedActorId == contactItem.model.calculatedActorId &&
- internalParticipant.calculatedActorType == Participant.ActorType.GROUPS &&
- internalParticipant.selected
- ) {
- internalParticipant.selected = false
- selectedGroupIds.remove(internalParticipant.calculatedActorId!!)
- }
- }
- }
- adapter?.notifyDataSetChanged()
- checkAndHandleDoneMenuItem()
- }
-
- private fun createRoom(contactItem: ContactItem) {
- var roomType = "1"
- if (Participant.ActorType.GROUPS == contactItem.model.actorType) {
- roomType = "2"
- }
- val apiVersion: Int = ApiUtils.getConversationApiVersion(currentUser!!, intArrayOf(ApiUtils.API_V4, 1))
- val retrofitBucket: RetrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(
- apiVersion,
- currentUser!!.baseUrl!!,
- roomType,
- null,
- contactItem.model.calculatedActorId,
- null
- )
- ncApi.createRoom(credentials, retrofitBucket.url, retrofitBucket.queryMap)
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(object : Observer {
- override fun onSubscribe(d: Disposable) {
- // unused atm
- }
-
- override fun onNext(roomOverall: RoomOverall) {
- val bundle = Bundle()
- bundle.putString(BundleKeys.KEY_ROOM_TOKEN, roomOverall.ocs!!.data!!.token)
- // bundle.putString(BundleKeys.KEY_ROOM_ID, roomOverall.ocs!!.data!!.roomId)
-
- val chatIntent = Intent(context, ChatActivity::class.java)
- chatIntent.putExtras(bundle)
- chatIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
- startActivity(chatIntent)
- }
-
- override fun onError(e: Throwable) {
- // unused atm
- }
-
- override fun onComplete() {
- // unused atm
- }
- })
- }
-
- private fun updateSelectionLists(participant: Participant) {
- if (Participant.ActorType.GROUPS == participant.actorType) {
- if (participant.selected) {
- selectedGroupIds.add(participant.calculatedActorId!!)
- } else {
- selectedGroupIds.remove(participant.calculatedActorId!!)
- }
- } else if (Participant.ActorType.EMAILS == participant.actorType) {
- if (participant.selected) {
- selectedEmails.add(participant.calculatedActorId!!)
- } else {
- selectedEmails.remove(participant.calculatedActorId!!)
- }
- } else if (Participant.ActorType.CIRCLES == participant.actorType) {
- if (participant.selected) {
- selectedCircleIds.add(participant.calculatedActorId!!)
- } else {
- selectedCircleIds.remove(participant.calculatedActorId!!)
- }
- } else {
- if (participant.selected) {
- selectedUserIds.add(participant.calculatedActorId!!)
- } else {
- selectedUserIds.remove(participant.calculatedActorId!!)
- }
- }
- }
-
- private fun isValidGroupSelection(
- contactItem: ContactItem,
- participant: Participant,
- adapter: FlexibleAdapter<*>?
- ): Boolean {
- return Participant.ActorType.GROUPS == contactItem.model.actorType &&
- participant.selected && adapter?.selectedItemCount!! > 1
- }
-
- private fun listOpenConversations() {
- val intent = Intent(this, ListOpenConversationsActivity::class.java)
- startActivity(intent)
- }
-
- private fun toggleCallHeader() {
- toggleConversationPrivacyLayout(isPublicCall)
- isPublicCall = !isPublicCall
- enableContactForNonPublicCall()
- checkAndHandleDoneMenuItem()
- adapter?.notifyDataSetChanged()
- }
-
- private fun enableContactForNonPublicCall() {
- for (i in 0 until adapter!!.itemCount) {
- if (adapter?.getItem(i) is ContactItem) {
- val contactItem: ContactItem = adapter?.getItem(i) as ContactItem
- if (Participant.ActorType.GROUPS == contactItem.model.actorType) {
- contactItem.isEnabled = !isPublicCall
- }
- }
- }
- }
-
- private fun toggleConversationPrivacyLayout(showInitialLayout: Boolean) {
- if (showInitialLayout) {
- binding.publicConversationCreate.visibility = View.VISIBLE
- binding.publicConversationInfo.visibility = View.GONE
- } else {
- binding.publicConversationCreate.visibility = View.GONE
- binding.publicConversationInfo.visibility = View.VISIBLE
- binding.listOpenConversations.visibility = View.GONE
- }
- }
-
- companion object {
- private val TAG = ContactsActivity::class.simpleName
- const val RETRIES: Long = 3
- const val CONTACTS_BATCH_SIZE: Int = 50
- const val HEADER_ELEVATION: Int = 5
- }
-}
diff --git a/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityCompose.kt b/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityCompose.kt
index e5366d608..9805a7e05 100644
--- a/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityCompose.kt
+++ b/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityCompose.kt
@@ -9,7 +9,6 @@
package com.nextcloud.talk.contacts
import android.annotation.SuppressLint
-import android.os.Build
import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.compose.material3.MaterialTheme
@@ -19,8 +18,10 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import autodagger.AutoInjector
import com.nextcloud.talk.activities.BaseActivity
import com.nextcloud.talk.application.NextcloudTalkApplication
+import com.nextcloud.talk.extensions.getParcelableArrayListExtraProvider
import com.nextcloud.talk.components.SetupSystemBars
import com.nextcloud.talk.models.json.autocomplete.AutocompleteUser
+import com.nextcloud.talk.utils.bundle.BundleKeys
import javax.inject.Inject
@AutoInjector(NextcloudTalkApplication::class)
@@ -36,7 +37,7 @@ class ContactsActivityCompose : BaseActivity() {
NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this)
contactsViewModel = ViewModelProvider(this, viewModelFactory)[ContactsViewModel::class.java]
setContent {
- val isAddParticipants = intent.getBooleanExtra("isAddParticipants", false)
+ val isAddParticipants = intent.getBooleanExtra(BundleKeys.KEY_ADD_PARTICIPANTS, false)
contactsViewModel.updateIsAddParticipants(isAddParticipants)
if (isAddParticipants) {
contactsViewModel.updateShareTypes(
@@ -50,14 +51,10 @@ class ContactsActivityCompose : BaseActivity() {
}
val colorScheme = viewThemeUtils.getColorScheme(this)
val uiState = contactsViewModel.contactsViewState.collectAsStateWithLifecycle()
+
val selectedParticipants = remember {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
- intent.getParcelableArrayListExtra("selectedParticipants", AutocompleteUser::class.java)
- ?: emptyList()
- } else {
- @Suppress("DEPRECATION")
- intent.getParcelableArrayListExtra("selectedParticipants") ?: emptyList()
- }
+ intent?.getParcelableArrayListExtraProvider("selectedParticipants")
+ ?: emptyList()
}.toSet().toMutableList()
contactsViewModel.updateSelectedParticipants(selectedParticipants)
diff --git a/app/src/main/java/com/nextcloud/talk/contacts/ContactsScreen.kt b/app/src/main/java/com/nextcloud/talk/contacts/ContactsScreen.kt
index 7aefffdb6..69d70306d 100644
--- a/app/src/main/java/com/nextcloud/talk/contacts/ContactsScreen.kt
+++ b/app/src/main/java/com/nextcloud/talk/contacts/ContactsScreen.kt
@@ -15,7 +15,6 @@ import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
-import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.stringResource
import androidx.lifecycle.compose.collectAsStateWithLifecycle
@@ -26,8 +25,6 @@ import com.nextcloud.talk.contacts.components.ConversationCreationOptions
@Composable
fun ContactsScreen(contactsViewModel: ContactsViewModel, uiState: ContactsUiState) {
- val context = LocalContext.current
-
val searchQuery by contactsViewModel.searchQuery.collectAsStateWithLifecycle()
val isSearchActive by contactsViewModel.isSearchActive.collectAsStateWithLifecycle()
val isAddParticipants by contactsViewModel.isAddParticipantsView.collectAsStateWithLifecycle()
@@ -57,17 +54,17 @@ fun ContactsScreen(contactsViewModel: ContactsViewModel, uiState: ContactsUiStat
},
content = {
Column(
- Modifier.padding(it)
+ Modifier
+ .padding(it)
.background(colorResource(id = R.color.bg_default))
) {
- ConversationCreationOptions(
- context = context,
- contactsViewModel = contactsViewModel
- )
+ if (!isAddParticipants) {
+ ConversationCreationOptions()
+ }
+
ContactsList(
contactsUiState = uiState,
- contactsViewModel = contactsViewModel,
- context = context
+ contactsViewModel = contactsViewModel
)
}
}
diff --git a/app/src/main/java/com/nextcloud/talk/contacts/components/ContactsList.kt b/app/src/main/java/com/nextcloud/talk/contacts/components/ContactsList.kt
index df856c9c2..560a79482 100644
--- a/app/src/main/java/com/nextcloud/talk/contacts/components/ContactsList.kt
+++ b/app/src/main/java/com/nextcloud/talk/contacts/components/ContactsList.kt
@@ -8,7 +8,6 @@
package com.nextcloud.talk.contacts.components
-import android.content.Context
import android.util.Log
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
@@ -18,15 +17,18 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.LocalContext
import com.nextcloud.talk.contacts.CompanionClass
import com.nextcloud.talk.contacts.ContactsUiState
import com.nextcloud.talk.contacts.ContactsViewModel
@Composable
-fun ContactsList(contactsUiState: ContactsUiState, contactsViewModel: ContactsViewModel, context: Context) {
+fun ContactsList(contactsUiState: ContactsUiState, contactsViewModel: ContactsViewModel) {
+ val context = LocalContext.current
when (contactsUiState) {
is ContactsUiState.None -> {
}
+
is ContactsUiState.Loading -> {
Box(
modifier = Modifier.fillMaxSize(),
@@ -35,6 +37,7 @@ fun ContactsList(contactsUiState: ContactsUiState, contactsViewModel: ContactsVi
CircularProgressIndicator()
}
}
+
is ContactsUiState.Success -> {
val contacts = contactsUiState.contacts
Log.d(CompanionClass.TAG, "Contacts:$contacts")
@@ -42,6 +45,7 @@ fun ContactsList(contactsUiState: ContactsUiState, contactsViewModel: ContactsVi
ContactsItem(contacts, contactsViewModel, context)
}
}
+
is ContactsUiState.Error -> {
val errorMessage = contactsUiState.message
Box(
diff --git a/app/src/main/java/com/nextcloud/talk/contacts/components/ConversationCreationOptions.kt b/app/src/main/java/com/nextcloud/talk/contacts/components/ConversationCreationOptions.kt
index 6793aa0b6..4f060d01e 100644
--- a/app/src/main/java/com/nextcloud/talk/contacts/components/ConversationCreationOptions.kt
+++ b/app/src/main/java/com/nextcloud/talk/contacts/components/ConversationCreationOptions.kt
@@ -8,7 +8,6 @@
package com.nextcloud.talk.contacts.components
-import android.content.Context
import android.content.Intent
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
@@ -23,75 +22,71 @@ import androidx.compose.material.icons.automirrored.filled.List
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
-import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.nextcloud.talk.R
-import com.nextcloud.talk.contacts.ContactsViewModel
import com.nextcloud.talk.conversationcreation.ConversationCreationActivity
import com.nextcloud.talk.openconversations.ListOpenConversationsActivity
@Composable
-fun ConversationCreationOptions(context: Context, contactsViewModel: ContactsViewModel) {
- val isAddParticipants by contactsViewModel.isAddParticipantsView.collectAsState()
- if (!isAddParticipants) {
- Column {
- Row(
+fun ConversationCreationOptions() {
+ val context = LocalContext.current
+ Column {
+ Row(
+ modifier = Modifier
+ .padding(start = 16.dp, end = 16.dp, top = 16.dp, bottom = 8.dp)
+ .clickable {
+ val intent = Intent(context, ConversationCreationActivity::class.java)
+ context.startActivity(intent)
+ },
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Icon(
+ painter = painterResource(id = R.drawable.baseline_chat_bubble_outline_24),
modifier = Modifier
- .padding(start = 16.dp, end = 16.dp, top = 16.dp, bottom = 8.dp)
- .clickable {
- val intent = Intent(context, ConversationCreationActivity::class.java)
- context.startActivity(intent)
- },
- verticalAlignment = Alignment.CenterVertically
- ) {
- Icon(
- painter = painterResource(id = R.drawable.baseline_chat_bubble_outline_24),
- modifier = Modifier
- .width(40.dp)
- .height(40.dp)
- .padding(8.dp),
- contentDescription = null
- )
- Text(
- modifier = Modifier
- .fillMaxWidth()
- .wrapContentHeight(),
- text = stringResource(R.string.nc_create_new_conversation),
- maxLines = 1,
- fontSize = 16.sp
- )
- }
- Row(
+ .width(40.dp)
+ .height(40.dp)
+ .padding(8.dp),
+ contentDescription = null
+ )
+ Text(
modifier = Modifier
- .padding(start = 16.dp, end = 16.dp, top = 8.dp, bottom = 8.dp)
- .clickable {
- val intent = Intent(context, ListOpenConversationsActivity::class.java)
- context.startActivity(intent)
- },
- verticalAlignment = Alignment.CenterVertically
- ) {
- Icon(
- Icons.AutoMirrored.Filled.List,
- modifier = Modifier
- .width(40.dp)
- .height(40.dp)
- .padding(8.dp),
- contentDescription = null
- )
- Text(
- modifier = Modifier
- .fillMaxWidth()
- .wrapContentHeight(),
- text = stringResource(R.string.nc_join_open_conversations),
- fontSize = 16.sp
- )
- }
+ .fillMaxWidth()
+ .wrapContentHeight(),
+ text = stringResource(R.string.nc_create_new_conversation),
+ maxLines = 1,
+ fontSize = 16.sp
+ )
+ }
+ Row(
+ modifier = Modifier
+ .padding(start = 16.dp, end = 16.dp, top = 8.dp, bottom = 8.dp)
+ .clickable {
+ val intent = Intent(context, ListOpenConversationsActivity::class.java)
+ context.startActivity(intent)
+ },
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Icon(
+ Icons.AutoMirrored.Filled.List,
+ modifier = Modifier
+ .width(40.dp)
+ .height(40.dp)
+ .padding(8.dp),
+ contentDescription = null
+ )
+ Text(
+ modifier = Modifier
+ .fillMaxWidth()
+ .wrapContentHeight(),
+ text = stringResource(R.string.nc_join_open_conversations),
+ fontSize = 16.sp
+ )
}
}
}
diff --git a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt
index cb1b1e834..1212833d7 100644
--- a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt
+++ b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt
@@ -87,6 +87,7 @@ import com.nextcloud.talk.chat.ChatActivity
import com.nextcloud.talk.components.SetupSystemBars
import com.nextcloud.talk.contacts.ContactsActivityCompose
import com.nextcloud.talk.contacts.loadImage
+import com.nextcloud.talk.extensions.getParcelableArrayListExtraProvider
import com.nextcloud.talk.models.json.autocomplete.AutocompleteUser
import com.nextcloud.talk.utils.CapabilitiesUtil
import com.nextcloud.talk.utils.PickImage
@@ -163,7 +164,7 @@ fun ConversationCreationScreen(
if (result.resultCode == Activity.RESULT_OK) {
val data = result.data
val selectedParticipants =
- data?.getParcelableArrayListExtra("selectedParticipants")
+ data?.getParcelableArrayListExtraProvider("selectedParticipants")
?: emptyList()
val participants = selectedParticipants.toMutableList()
conversationCreationViewModel.updateSelectedParticipants(participants)
@@ -383,7 +384,7 @@ fun AddParticipants(
"selectedParticipants",
participants as ArrayList
)
- intent.putExtra("isAddParticipants", true)
+ intent.putExtra(BundleKeys.KEY_ADD_PARTICIPANTS, true)
intent.putExtra("isAddParticipantsEdit", true)
launcher.launch(intent)
},
@@ -417,7 +418,7 @@ fun AddParticipants(
.fillMaxWidth()
.clickable {
val intent = Intent(context, ContactsActivityCompose::class.java)
- intent.putExtra("isAddParticipants", true)
+ intent.putExtra(BundleKeys.KEY_ADD_PARTICIPANTS, true)
launcher.launch(intent)
},
verticalAlignment = Alignment.CenterVertically
@@ -608,7 +609,8 @@ fun ShowChangePassword(onDismiss: () -> Unit, conversationCreationViewModel: Con
Spacer(modifier = Modifier.height(16.dp))
Column(
- modifier = Modifier.fillMaxWidth()
+ modifier = Modifier
+ .fillMaxWidth()
.padding(vertical = 8.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
diff --git a/app/src/main/java/com/nextcloud/talk/conversationinfo/ConversationInfoActivity.kt b/app/src/main/java/com/nextcloud/talk/conversationinfo/ConversationInfoActivity.kt
index c6928a559..104e3af76 100644
--- a/app/src/main/java/com/nextcloud/talk/conversationinfo/ConversationInfoActivity.kt
+++ b/app/src/main/java/com/nextcloud/talk/conversationinfo/ConversationInfoActivity.kt
@@ -11,6 +11,7 @@
package com.nextcloud.talk.conversationinfo
import android.annotation.SuppressLint
+import android.app.Activity
import android.content.Intent
import android.graphics.drawable.ColorDrawable
import android.os.Bundle
@@ -21,6 +22,8 @@ import android.view.MenuItem
import android.view.View
import android.view.View.GONE
import android.view.View.VISIBLE
+import androidx.activity.result.ActivityResult
+import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.DrawableRes
import androidx.appcompat.app.AlertDialog
import androidx.core.content.res.ResourcesCompat
@@ -49,21 +52,25 @@ import com.nextcloud.talk.api.NcApi
import com.nextcloud.talk.application.NextcloudTalkApplication
import com.nextcloud.talk.bottomsheet.items.BasicListItemWithImage
import com.nextcloud.talk.bottomsheet.items.listItemsWithImage
-import com.nextcloud.talk.contacts.ContactsActivity
+import com.nextcloud.talk.chat.ChatActivity
+import com.nextcloud.talk.contacts.ContactsActivityCompose
import com.nextcloud.talk.conversationinfo.viewmodel.ConversationInfoViewModel
import com.nextcloud.talk.conversationinfoedit.ConversationInfoEditActivity
import com.nextcloud.talk.data.user.model.User
import com.nextcloud.talk.databinding.ActivityConversationInfoBinding
import com.nextcloud.talk.databinding.DialogBanParticipantBinding
import com.nextcloud.talk.events.EventStatus
+import com.nextcloud.talk.extensions.getParcelableArrayListExtraProvider
import com.nextcloud.talk.extensions.loadConversationAvatar
import com.nextcloud.talk.extensions.loadNoteToSelfAvatar
import com.nextcloud.talk.extensions.loadSystemAvatar
import com.nextcloud.talk.extensions.loadUserAvatar
+import com.nextcloud.talk.jobs.AddParticipantsToConversation
import com.nextcloud.talk.jobs.DeleteConversationWorker
import com.nextcloud.talk.jobs.LeaveConversationWorker
import com.nextcloud.talk.models.domain.ConversationModel
import com.nextcloud.talk.models.domain.converters.DomainEnumNotificationLevelConverter
+import com.nextcloud.talk.models.json.autocomplete.AutocompleteUser
import com.nextcloud.talk.models.json.capabilities.SpreedCapability
import com.nextcloud.talk.models.json.conversations.ConversationEnums
import com.nextcloud.talk.models.json.converters.EnumActorTypeConverter
@@ -126,12 +133,9 @@ class ConversationInfoActivity :
private lateinit var conversationUser: User
private var hasAvatarSpacing: Boolean = false
private lateinit var credentials: String
- private var roomDisposable: Disposable? = null
private var participantsDisposable: Disposable? = null
private var databaseStorageModule: DatabaseStorageModule? = null
-
- // private var conversation: Conversation? = null
private var conversation: ConversationModel? = null
private var adapter: FlexibleAdapter? = null
@@ -151,6 +155,18 @@ class ConversationInfoActivity :
return null
}
+ private val addParticipantsResult = registerForActivityResult(
+ ActivityResultContracts.StartActivityForResult()
+ ) {
+ executeIfResultOk(it) { intent ->
+ val selectedParticipants =
+ intent?.getParcelableArrayListExtraProvider("selectedParticipants")
+ ?: emptyList()
+ val participants = selectedParticipants.toMutableList()
+ addParticipantsToConversation(participants)
+ }
+ }
+
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this)
@@ -190,7 +206,7 @@ class ConversationInfoActivity :
binding.deleteConversationAction.setOnClickListener { showDeleteConversationDialog() }
binding.leaveConversationAction.setOnClickListener { leaveConversation() }
binding.clearConversationHistory.setOnClickListener { showClearHistoryDialog() }
- binding.addParticipantsAction.setOnClickListener { addParticipants() }
+ binding.addParticipantsAction.setOnClickListener { selectParticipantsToAdd() }
binding.listBansButton.setOnClickListener { listBans() }
viewModel.getRoom(conversationUser, conversationToken)
@@ -237,9 +253,11 @@ class ConversationInfoActivity :
when (state) {
is ConversationInfoViewModel.SetConversationReadOnlyViewState.Success -> {
}
+
is ConversationInfoViewModel.SetConversationReadOnlyViewState.Error -> {
Snackbar.make(binding.root, R.string.conversation_read_only_failed, Snackbar.LENGTH_LONG).show()
}
+
is ConversationInfoViewModel.SetConversationReadOnlyViewState.None -> {
}
}
@@ -249,6 +267,7 @@ class ConversationInfoActivity :
when (uiState) {
is ConversationInfoViewModel.ClearChatHistoryViewState.None -> {
}
+
is ConversationInfoViewModel.ClearChatHistoryViewState.Success -> {
Snackbar.make(
binding.root,
@@ -256,6 +275,7 @@ class ConversationInfoActivity :
Snackbar.LENGTH_LONG
).show()
}
+
is ConversationInfoViewModel.ClearChatHistoryViewState.Error -> {
Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show()
Log.e(TAG, "failed to clear chat history", uiState.exception)
@@ -652,7 +672,15 @@ class ConversationInfoActivity :
.commit()
}
- private fun addParticipants() {
+ private fun executeIfResultOk(result: ActivityResult, onResult: (intent: Intent?) -> Unit) {
+ if (result.resultCode == Activity.RESULT_OK) {
+ onResult(result.data)
+ } else {
+ Log.e(ChatActivity.TAG, "resultCode for received intent was != ok")
+ }
+ }
+
+ private fun selectParticipantsToAdd() {
val bundle = Bundle()
val existingParticipantsId = arrayListOf()
@@ -666,9 +694,61 @@ class ConversationInfoActivity :
bundle.putStringArrayList(BundleKeys.KEY_EXISTING_PARTICIPANTS, existingParticipantsId)
bundle.putString(BundleKeys.KEY_TOKEN, conversation!!.token)
- val intent = Intent(this, ContactsActivity::class.java)
+ val intent = Intent(this, ContactsActivityCompose::class.java)
intent.putExtras(bundle)
- startActivity(intent)
+
+ addParticipantsResult.launch(intent)
+ }
+
+ private fun addParticipantsToConversation(participants: List) {
+ val groupIdsArray: MutableSet = HashSet()
+ val emailIdsArray: MutableSet = HashSet()
+ val circleIdsArray: MutableSet = HashSet()
+ val userIdsArray: MutableSet = HashSet()
+
+ participants.forEach { participant ->
+ when (participant.source) {
+ Participant.ActorType.GROUPS.name.lowercase() -> groupIdsArray.add(participant.id!!)
+ Participant.ActorType.EMAILS.name.lowercase() -> emailIdsArray.add(participant.id!!)
+ Participant.ActorType.CIRCLES.name.lowercase() -> circleIdsArray.add(participant.id!!)
+ else -> userIdsArray.add(participant.id!!)
+ }
+ }
+
+ val data = Data.Builder()
+ data.putLong(BundleKeys.KEY_INTERNAL_USER_ID, conversationUser.id!!)
+ data.putString(BundleKeys.KEY_TOKEN, conversationToken)
+ data.putStringArray(BundleKeys.KEY_SELECTED_USERS, userIdsArray.toTypedArray())
+ data.putStringArray(BundleKeys.KEY_SELECTED_GROUPS, groupIdsArray.toTypedArray())
+ data.putStringArray(BundleKeys.KEY_SELECTED_EMAILS, emailIdsArray.toTypedArray())
+ data.putStringArray(BundleKeys.KEY_SELECTED_CIRCLES, circleIdsArray.toTypedArray())
+ val addParticipantsToConversationWorker: OneTimeWorkRequest = OneTimeWorkRequest.Builder(
+ AddParticipantsToConversation::class.java
+ ).setInputData(data.build()).build()
+ WorkManager.getInstance().enqueue(addParticipantsToConversationWorker)
+
+ WorkManager.getInstance(context).getWorkInfoByIdLiveData(addParticipantsToConversationWorker.id)
+ .observeForever { workInfo: WorkInfo? ->
+ if (workInfo != null) {
+ when (workInfo.state) {
+ WorkInfo.State.RUNNING -> {
+ Log.d(TAG, "running AddParticipantsToConversation")
+ }
+
+ WorkInfo.State.SUCCEEDED -> {
+ Log.d(TAG, "success AddParticipantsToConversation")
+ getListOfParticipants()
+ }
+
+ WorkInfo.State.FAILED -> {
+ Log.d(TAG, "failed AddParticipantsToConversation")
+ }
+
+ else -> {
+ }
+ }
+ }
+ }
}
private fun leaveConversation() {
@@ -693,6 +773,7 @@ class ConversationInfoActivity :
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
startActivity(intent)
}
+
WorkInfo.State.FAILED -> {
val errorType = workInfo.outputData.getString("error_type")
if (errorType == LeaveConversationWorker.ERROR_NO_OTHER_MODERATORS_OR_OWNERS_LEFT) {
@@ -709,6 +790,7 @@ class ConversationInfoActivity :
).show()
}
}
+
else -> {
}
}
diff --git a/app/src/main/java/com/nextcloud/talk/conversationlist/ConversationsListActivity.kt b/app/src/main/java/com/nextcloud/talk/conversationlist/ConversationsListActivity.kt
index eb9b25ec1..189dd62cc 100644
--- a/app/src/main/java/com/nextcloud/talk/conversationlist/ConversationsListActivity.kt
+++ b/app/src/main/java/com/nextcloud/talk/conversationlist/ConversationsListActivity.kt
@@ -131,7 +131,6 @@ import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_FORWARD_HIDE_SOURCE_ROOM
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_FORWARD_MSG_FLAG
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_FORWARD_MSG_TEXT
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_INTERNAL_USER_ID
-import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_NEW_CONVERSATION
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_TOKEN
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_SCROLL_TO_NOTIFICATION_CATEGORY
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_SHARED_TEXT
@@ -1254,7 +1253,6 @@ class ConversationsListActivity :
private fun showNewConversationsScreen() {
val intent = Intent(context, ContactsActivityCompose::class.java)
- intent.putExtra(KEY_NEW_CONVERSATION, true)
startActivity(intent)
}
diff --git a/app/src/main/java/com/nextcloud/talk/extensions/ParcelableExtensions.kt b/app/src/main/java/com/nextcloud/talk/extensions/ParcelableExtensions.kt
new file mode 100644
index 000000000..b3e2b0c05
--- /dev/null
+++ b/app/src/main/java/com/nextcloud/talk/extensions/ParcelableExtensions.kt
@@ -0,0 +1,32 @@
+/*
+ * Nextcloud Talk - Android Client
+ *
+ * SPDX-FileCopyrightText: 2025 Marcel Hibbe
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+package com.nextcloud.talk.extensions
+
+import android.content.Intent
+import android.os.Build
+import android.os.Parcelable
+
+@Suppress("DEPRECATION")
+inline fun Intent.getParcelableExtraProvider(identifierParameter: String): T? {
+ return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ this.getParcelableExtra(identifierParameter, T::class.java)
+ } else {
+ this.getParcelableExtra(identifierParameter)
+ }
+}
+
+@Suppress("DEPRECATION")
+inline fun Intent.getParcelableArrayListExtraProvider(
+ identifierParameter: String
+): java.util.ArrayList? {
+ return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ this.getParcelableArrayListExtra(identifierParameter, T::class.java)
+ } else {
+ this.getParcelableArrayListExtra(identifierParameter)
+ }
+}