From 1c7d84f90bea798ad4e6470f55e6b60aae908010 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Thu, 6 Mar 2025 16:38:03 +0100 Subject: [PATCH 1/4] remove old ContactsActivity and use ContactsActivityCompose for ConversationInfoActivity, the old ContactsActivity was still used to add participants. This is now replaced by the ContactsActivityCompose, so ContactsActivity is now deleted Signed-off-by: Marcel Hibbe --- app/src/main/AndroidManifest.xml | 4 - .../talk/contacts/ContactsActivity.kt | 900 ------------------ .../talk/contacts/ContactsActivityCompose.kt | 15 +- .../nextcloud/talk/contacts/ContactsScreen.kt | 17 +- .../talk/contacts/components/ContactsList.kt | 8 +- .../components/ConversationCreationOptions.kt | 109 +-- .../ConversationCreationActivity.kt | 10 +- .../ConversationInfoActivity.kt | 98 +- .../ConversationsListActivity.kt | 2 - .../talk/extensions/ParcelableExtensions.kt | 32 + 10 files changed, 199 insertions(+), 996 deletions(-) delete mode 100644 app/src/main/java/com/nextcloud/talk/contacts/ContactsActivity.kt create mode 100644 app/src/main/java/com/nextcloud/talk/extensions/ParcelableExtensions.kt 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) + } +} From 23b03eead8be9c2149ec59ad0cad8fdf92f8aceb Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Fri, 7 Mar 2025 15:12:40 +0100 Subject: [PATCH 2/4] rename ContactsActivityCompose to ContactsActivity the old ContactsActivity was deleted, so ContactsActivityCompose becomes ContactsActivity now Signed-off-by: Marcel Hibbe --- app/src/main/AndroidManifest.xml | 2 +- .../{ContactsActivityCompose.kt => ContactsActivity.kt} | 4 ++-- .../conversationcreation/ConversationCreationActivity.kt | 6 +++--- .../talk/conversationinfo/ConversationInfoActivity.kt | 4 ++-- .../talk/conversationlist/ConversationsListActivity.kt | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) rename app/src/main/java/com/nextcloud/talk/contacts/{ContactsActivityCompose.kt => ContactsActivity.kt} (96%) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 0d014dfc1..b82355363 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -126,7 +126,7 @@ android:name=".account.WebViewLoginActivity" android:theme="@style/AppTheme" /> - @@ -417,7 +417,7 @@ fun AddParticipants( modifier = Modifier .fillMaxWidth() .clickable { - val intent = Intent(context, ContactsActivityCompose::class.java) + val intent = Intent(context, ContactsActivity::class.java) intent.putExtra(BundleKeys.KEY_ADD_PARTICIPANTS, true) launcher.launch(intent) }, 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 104e3af76..1c97e4efb 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationinfo/ConversationInfoActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationinfo/ConversationInfoActivity.kt @@ -53,7 +53,7 @@ import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.bottomsheet.items.BasicListItemWithImage import com.nextcloud.talk.bottomsheet.items.listItemsWithImage import com.nextcloud.talk.chat.ChatActivity -import com.nextcloud.talk.contacts.ContactsActivityCompose +import com.nextcloud.talk.contacts.ContactsActivity import com.nextcloud.talk.conversationinfo.viewmodel.ConversationInfoViewModel import com.nextcloud.talk.conversationinfoedit.ConversationInfoEditActivity import com.nextcloud.talk.data.user.model.User @@ -694,7 +694,7 @@ class ConversationInfoActivity : bundle.putStringArrayList(BundleKeys.KEY_EXISTING_PARTICIPANTS, existingParticipantsId) bundle.putString(BundleKeys.KEY_TOKEN, conversation!!.token) - val intent = Intent(this, ContactsActivityCompose::class.java) + val intent = Intent(this, ContactsActivity::class.java) intent.putExtras(bundle) addParticipantsResult.launch(intent) 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 189dd62cc..eaf024c5a 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationlist/ConversationsListActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationlist/ConversationsListActivity.kt @@ -82,7 +82,7 @@ import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.arbitrarystorage.ArbitraryStorageManager import com.nextcloud.talk.chat.ChatActivity import com.nextcloud.talk.chat.viewmodels.ChatViewModel -import com.nextcloud.talk.contacts.ContactsActivityCompose +import com.nextcloud.talk.contacts.ContactsActivity import com.nextcloud.talk.contacts.ContactsUiState import com.nextcloud.talk.contacts.ContactsViewModel import com.nextcloud.talk.contacts.RoomUiState @@ -1252,7 +1252,7 @@ class ConversationsListActivity : conversation.type === ConversationEnums.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL private fun showNewConversationsScreen() { - val intent = Intent(context, ContactsActivityCompose::class.java) + val intent = Intent(context, ContactsActivity::class.java) startActivity(intent) } From 1fa0496b94767831abb3f1e1398e9484cff8a423 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Fri, 7 Mar 2025 15:37:25 +0100 Subject: [PATCH 3/4] remove unused resources Signed-off-by: Marcel Hibbe --- .../main/res/drawable/ic_add_white_24px.xml | 16 -- app/src/main/res/drawable/round_corner.xml | 11 - app/src/main/res/layout/activity_contacts.xml | 196 ------------------ .../layout/library_fast_scroller_layout.xml | 55 ----- app/src/main/res/layout/progress_layout.xml | 21 -- .../res/layout/rv_item_contact_shimmer.xml | 35 ---- app/src/main/res/menu/menu_contacts.xml | 25 --- app/src/main/res/values-night/colors.xml | 2 - app/src/main/res/values/colors.xml | 1 - app/src/main/res/values/strings.xml | 4 - 10 files changed, 366 deletions(-) delete mode 100644 app/src/main/res/drawable/ic_add_white_24px.xml delete mode 100644 app/src/main/res/drawable/round_corner.xml delete mode 100644 app/src/main/res/layout/activity_contacts.xml delete mode 100644 app/src/main/res/layout/library_fast_scroller_layout.xml delete mode 100644 app/src/main/res/layout/progress_layout.xml delete mode 100644 app/src/main/res/layout/rv_item_contact_shimmer.xml delete mode 100644 app/src/main/res/menu/menu_contacts.xml diff --git a/app/src/main/res/drawable/ic_add_white_24px.xml b/app/src/main/res/drawable/ic_add_white_24px.xml deleted file mode 100644 index 2c73d8735..000000000 --- a/app/src/main/res/drawable/ic_add_white_24px.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/round_corner.xml b/app/src/main/res/drawable/round_corner.xml deleted file mode 100644 index 0d70bb289..000000000 --- a/app/src/main/res/drawable/round_corner.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - diff --git a/app/src/main/res/layout/activity_contacts.xml b/app/src/main/res/layout/activity_contacts.xml deleted file mode 100644 index 9d53cf002..000000000 --- a/app/src/main/res/layout/activity_contacts.xml +++ /dev/null @@ -1,196 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/library_fast_scroller_layout.xml b/app/src/main/res/layout/library_fast_scroller_layout.xml deleted file mode 100644 index eba9d84ef..000000000 --- a/app/src/main/res/layout/library_fast_scroller_layout.xml +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/progress_layout.xml b/app/src/main/res/layout/progress_layout.xml deleted file mode 100644 index 748885aec..000000000 --- a/app/src/main/res/layout/progress_layout.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - diff --git a/app/src/main/res/layout/rv_item_contact_shimmer.xml b/app/src/main/res/layout/rv_item_contact_shimmer.xml deleted file mode 100644 index 87988c25d..000000000 --- a/app/src/main/res/layout/rv_item_contact_shimmer.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - diff --git a/app/src/main/res/menu/menu_contacts.xml b/app/src/main/res/menu/menu_contacts.xml deleted file mode 100644 index 641fee63c..000000000 --- a/app/src/main/res/menu/menu_contacts.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/values-night/colors.xml b/app/src/main/res/values-night/colors.xml index 087fee614..e433c4160 100644 --- a/app/src/main/res/values-night/colors.xml +++ b/app/src/main/res/values-night/colors.xml @@ -39,8 +39,6 @@ #484848 - #2C2C2C - #121212 #2A2A2A diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 3253644f1..7c1c45969 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -53,7 +53,6 @@ #E9FFFFFF #111111 #767676 - #DBDBDB #666666 #FFFFFF diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 90ad23282..6483bc303 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -241,7 +241,6 @@ How to translate with transifex: If you delete the conversation, it will also be deleted for all other participants. New conversation - List open conversations Mark as read Mark as unread Add to favorites @@ -267,7 +266,6 @@ How to translate with transifex: No open conversations that you can join.\nEither there are no open conversations or you already joined all of them. - Select participants Add participants Done User avatar @@ -291,8 +289,6 @@ How to translate with transifex: Connecting … Guest Public conversation - New public conversation - Public conversations let you invite people from outside through a specially crafted link. No response in 45 seconds, tap to try again Reconnecting … Currently offline, please check your connectivity From 743d4a0a4e8cee3a53fdc0e45814950edd5b3bb1 Mon Sep 17 00:00:00 2001 From: github-actions Date: Fri, 7 Mar 2025 14:53:06 +0000 Subject: [PATCH 4/4] hide already selected participants Signed-off-by: Marcel Hibbe Signed-off-by: github-actions --- .../talk/contacts/ContactsActivity.kt | 4 +++ .../talk/contacts/ContactsViewModel.kt | 13 ++++++++- .../ConversationInfoActivity.kt | 28 +++++++++++-------- app/src/main/res/values/strings.xml | 1 + scripts/analysis/lint-results.txt | 2 +- 5 files changed, 35 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivity.kt b/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivity.kt index 65682dd61..b0031a27f 100644 --- a/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivity.kt @@ -18,6 +18,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import autodagger.AutoInjector import com.nextcloud.talk.activities.BaseActivity import com.nextcloud.talk.application.NextcloudTalkApplication +import com.nextcloud.talk.contacts.CompanionClass.Companion.KEY_HIDE_ALREADY_EXISTING_PARTICIPANTS import com.nextcloud.talk.extensions.getParcelableArrayListExtraProvider import com.nextcloud.talk.components.SetupSystemBars import com.nextcloud.talk.models.json.autocomplete.AutocompleteUser @@ -38,7 +39,9 @@ class ContactsActivity : BaseActivity() { contactsViewModel = ViewModelProvider(this, viewModelFactory)[ContactsViewModel::class.java] setContent { val isAddParticipants = intent.getBooleanExtra(BundleKeys.KEY_ADD_PARTICIPANTS, false) + val hideAlreadyAddedParticipants = intent.getBooleanExtra(KEY_HIDE_ALREADY_EXISTING_PARTICIPANTS, false) contactsViewModel.updateIsAddParticipants(isAddParticipants) + contactsViewModel.hideAlreadyAddedParticipants(hideAlreadyAddedParticipants) if (isAddParticipants) { contactsViewModel.updateShareTypes( listOf( @@ -75,5 +78,6 @@ class CompanionClass { companion object { internal val TAG = ContactsActivity::class.simpleName internal const val ROOM_TYPE_ONE_ONE = "1" + const val KEY_HIDE_ALREADY_EXISTING_PARTICIPANTS: String = "KEY_HIDE_ALREADY_EXISTING_PARTICIPANTS" } } diff --git a/app/src/main/java/com/nextcloud/talk/contacts/ContactsViewModel.kt b/app/src/main/java/com/nextcloud/talk/contacts/ContactsViewModel.kt index 18c4ea2b3..7d495226b 100644 --- a/app/src/main/java/com/nextcloud/talk/contacts/ContactsViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/contacts/ContactsViewModel.kt @@ -36,6 +36,8 @@ class ContactsViewModel @Inject constructor( private val _isAddParticipantsView = MutableStateFlow(false) val isAddParticipantsView: StateFlow = _isAddParticipantsView + private var hideAlreadyAddedParticipants: Boolean = false + init { getContactsFromSearchParams() } @@ -69,6 +71,10 @@ class ContactsViewModel @Inject constructor( _isAddParticipantsView.value = value } + fun hideAlreadyAddedParticipants(value: Boolean) { + hideAlreadyAddedParticipants = value + } + @Suppress("Detekt.TooGenericExceptionCaught") fun getContactsFromSearchParams() { _contactsViewState.value = ContactsUiState.Loading @@ -78,7 +84,12 @@ class ContactsViewModel @Inject constructor( searchQuery.value, shareTypeList ) - val contactsList: List? = contacts.ocs!!.data + val contactsList: MutableList? = contacts.ocs!!.data?.toMutableList() + + if (hideAlreadyAddedParticipants) { + contactsList?.removeAll(selectedParticipants.value) + } + _contactsViewState.value = ContactsUiState.Success(contactsList) } catch (exception: Exception) { _contactsViewState.value = ContactsUiState.Error(exception.message ?: "") 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 1c97e4efb..b4909ff6f 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationinfo/ConversationInfoActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationinfo/ConversationInfoActivity.kt @@ -53,6 +53,7 @@ import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.bottomsheet.items.BasicListItemWithImage import com.nextcloud.talk.bottomsheet.items.listItemsWithImage import com.nextcloud.talk.chat.ChatActivity +import com.nextcloud.talk.contacts.CompanionClass.Companion.KEY_HIDE_ALREADY_EXISTING_PARTICIPANTS import com.nextcloud.talk.contacts.ContactsActivity import com.nextcloud.talk.conversationinfo.viewmodel.ConversationInfoViewModel import com.nextcloud.talk.conversationinfoedit.ConversationInfoEditActivity @@ -151,7 +152,6 @@ class ConversationInfoActivity : data.putLong(BundleKeys.KEY_INTERNAL_USER_ID, conversationUser.id!!) return data.build() } - return null } @@ -339,7 +339,7 @@ class ConversationInfoActivity : fun showOptionsMenu() { if (::optionsMenu.isInitialized) { optionsMenu.clear() - if (CapabilitiesUtil.hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.AVATAR)) { + if (hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.AVATAR)) { menuInflater.inflate(R.menu.menu_conversation_info, optionsMenu) } } @@ -403,7 +403,7 @@ class ConversationInfoActivity : } private fun setupWebinaryView() { - if (CapabilitiesUtil.hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.WEBINARY_LOBBY) && + if (hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.WEBINARY_LOBBY) && webinaryRoomType(conversation!!) && ConversationUtils.canModerate(conversation!!, spreedCapabilities) ) { @@ -682,16 +682,22 @@ class ConversationInfoActivity : private fun selectParticipantsToAdd() { val bundle = Bundle() - val existingParticipantsId = arrayListOf() + val existingParticipants = ArrayList() for (userItem in userItems) { if (userItem.model.calculatedActorType == USERS) { - existingParticipantsId.add(userItem.model.calculatedActorId!!) + val user = AutocompleteUser( + userItem.model.calculatedActorId!!, + userItem.model.displayName, + userItem.model.calculatedActorType.name.lowercase() + ) + existingParticipants.add(user) } } bundle.putBoolean(BundleKeys.KEY_ADD_PARTICIPANTS, true) - bundle.putStringArrayList(BundleKeys.KEY_EXISTING_PARTICIPANTS, existingParticipantsId) + bundle.putParcelableArrayList("selectedParticipants", existingParticipants) + bundle.putBoolean(KEY_HIDE_ALREADY_EXISTING_PARTICIPANTS, true) bundle.putString(BundleKeys.KEY_TOKEN, conversation!!.token) val intent = Intent(this, ContactsActivity::class.java) @@ -851,7 +857,7 @@ class ConversationInfoActivity : setUpNotificationSettings(databaseStorageModule!!) - if (CapabilitiesUtil.hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.RICH_OBJECT_LIST_MEDIA) && + if (hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.RICH_OBJECT_LIST_MEDIA) && conversationCopy.remoteServer.isNullOrEmpty() ) { binding.sharedItemsButton.setOnClickListener { showSharedItems() } @@ -861,7 +867,7 @@ class ConversationInfoActivity : if (ConversationUtils.canModerate(conversationCopy, spreedCapabilities)) { binding.addParticipantsAction.visibility = VISIBLE - if (CapabilitiesUtil.hasSpreedFeatureCapability( + if (hasSpreedFeatureCapability( spreedCapabilities, SpreedFeatures.CLEAR_HISTORY ) && conversationCopy.canDeleteConversation @@ -1093,7 +1099,7 @@ class ConversationInfoActivity : private fun initExpiringMessageOption() { if (ConversationUtils.isParticipantOwnerOrModerator(conversation!!) && - CapabilitiesUtil.hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.MESSAGE_EXPIRATION) + hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.MESSAGE_EXPIRATION) ) { databaseStorageModule?.setMessageExpiration(conversation!!.messageExpiration) val value = databaseStorageModule!!.getString("conversation_settings_dropdown", "") @@ -1116,7 +1122,7 @@ class ConversationInfoActivity : private fun adjustNotificationLevelUI() { if (conversation != null) { - if (CapabilitiesUtil.hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.NOTIFICATION_LEVELS)) { + if (hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.NOTIFICATION_LEVELS)) { binding.notificationSettingsView.conversationInfoMessageNotificationsDropdown.isEnabled = true binding.notificationSettingsView.conversationInfoMessageNotificationsDropdown.alpha = 1.0f @@ -1151,7 +1157,7 @@ class ConversationInfoActivity : private fun setProperNotificationValue(conversation: ConversationModel?) { if (conversation!!.type == ConversationEnums.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL) { - if (CapabilitiesUtil.hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.MENTION_FLAG)) { + if (hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.MENTION_FLAG)) { binding.notificationSettingsView.conversationInfoMessageNotificationsDropdown.setText( resources.getString(R.string.nc_notify_me_always) ) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6483bc303..dcbfa5238 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -477,6 +477,7 @@ How to translate with transifex: Teams Participants Add participants + Start group chat Owner Moderator diff --git a/scripts/analysis/lint-results.txt b/scripts/analysis/lint-results.txt index 26b9b13e0..beaf9459b 100644 --- a/scripts/analysis/lint-results.txt +++ b/scripts/analysis/lint-results.txt @@ -1,2 +1,2 @@ DO NOT TOUCH; GENERATED BY DRONE - Lint Report: 9 errors and 100 warnings + Lint Report: 9 errors and 98 warnings