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 <dev@mhibbe.de>
This commit is contained in:
Marcel Hibbe 2025-03-06 16:38:03 +01:00
parent d6d7a11573
commit 1c7d84f90b
No known key found for this signature in database
GPG Key ID: C793F8B59F43CE7B
10 changed files with 199 additions and 996 deletions

View File

@ -246,10 +246,6 @@
android:name=".conversationinfoedit.ConversationInfoEditActivity"
android:theme="@style/AppTheme" />
<activity
android:name=".contacts.ContactsActivity"
android:theme="@style/AppTheme" />
<activity
android:name=".openconversations.ListOpenConversationsActivity"
android:theme="@style/AppTheme" />

View File

@ -1,900 +0,0 @@
/*
* Nextcloud Talk - Android Client
*
* SPDX-FileCopyrightText: 2022 Andy Scherzinger <info@andy-scherzinger.de>
* SPDX-FileCopyrightText: 2022 Marcel Hibbe <dev@mhibbe.de>
* SPDX-FileCopyrightText: 2017 Mario Danic <mario@lovelyhq.com>
* 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<AbstractFlexibleItem<*>>? = 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<String, GenericTextHeaderItem> = HashMap<String, GenericTextHeaderItem>()
private var alreadyFetching = false
private var doneMenuItem: MenuItem? = null
private var selectedUserIds: MutableSet<String> = HashSet()
private var selectedGroupIds: MutableSet<String> = HashSet()
private var selectedCircleIds: MutableSet<String> = HashSet()
private var selectedEmails: MutableSet<String> = HashSet()
private var existingParticipants: List<String>? = 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<AbstractFlexibleItem<*>>()
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<RoomOverall> {
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<String> = selectedUserIds.toTypedArray()
val groupIdsArray: Array<String> = selectedGroupIds.toTypedArray()
val emailsArray: Array<String> = selectedEmails.toTypedArray()
val circleIdsArray: Array<String> = 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<String, Any?> = HashMap(retrofitBucket.queryMap)
modifiedQueryMap["limit"] = CONTACTS_BATCH_SIZE
if (isAddingParticipantsView) {
modifiedQueryMap["itemId"] = conversationToken
}
val shareTypesList: ArrayList<String> = 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<ResponseBody> {
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<Nothing>?)
} else {
adapter?.filterItems()
}
}
override fun onError(e: Throwable) {
dispose(contactsQueryDisposable)
}
override fun onComplete() {
dispose(contactsQueryDisposable)
alreadyFetching = false
disengageProgressBar()
}
})
}
private fun processAutocompleteUserList(responseBody: ResponseBody): MutableList<AbstractFlexibleItem<*>> {
try {
val autocompleteOverall: AutocompleteOverall = LoganSquare.parse(
responseBody.string(),
AutocompleteOverall::class.java
)
val autocompleteUsersList: ArrayList<AutocompleteUser> = 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<AutocompleteUser>
): MutableList<AbstractFlexibleItem<*>> {
var participant: Participant
val actorTypeConverter = EnumActorTypeConverter()
val newUserItemList: MutableList<AbstractFlexibleItem<*>> = 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<AbstractFlexibleItem<*>>) {
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<Nothing>?)
}
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<ContactItem> = adapter?.currentItems as List<ContactItem>
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<RoomOverall> {
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
}
}

View File

@ -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<AutocompleteUser>("selectedParticipants")
?: emptyList()
}.toSet().toMutableList()
contactsViewModel.updateSelectedParticipants(selectedParticipants)

View File

@ -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
)
}
}

View File

@ -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(

View File

@ -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
)
}
}
}

View File

@ -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<AutocompleteUser>("selectedParticipants")
data?.getParcelableArrayListExtraProvider<AutocompleteUser>("selectedParticipants")
?: emptyList()
val participants = selectedParticipants.toMutableList()
conversationCreationViewModel.updateSelectedParticipants(participants)
@ -383,7 +384,7 @@ fun AddParticipants(
"selectedParticipants",
participants as ArrayList<AutocompleteUser>
)
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

View File

@ -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<ParticipantItem>? = null
@ -151,6 +155,18 @@ class ConversationInfoActivity :
return null
}
private val addParticipantsResult = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) {
executeIfResultOk(it) { intent ->
val selectedParticipants =
intent?.getParcelableArrayListExtraProvider<AutocompleteUser>("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<String>()
@ -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<AutocompleteUser>) {
val groupIdsArray: MutableSet<String> = HashSet()
val emailIdsArray: MutableSet<String> = HashSet()
val circleIdsArray: MutableSet<String> = HashSet()
val userIdsArray: MutableSet<String> = 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 -> {
}
}

View File

@ -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)
}

View File

@ -0,0 +1,32 @@
/*
* Nextcloud Talk - Android Client
*
* SPDX-FileCopyrightText: 2025 Marcel Hibbe <dev@mhibbe.de>
* 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 <reified T : Parcelable> 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 <reified T : Parcelable> Intent.getParcelableArrayListExtraProvider(
identifierParameter: String
): java.util.ArrayList<T>? {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
this.getParcelableArrayListExtra(identifierParameter, T::class.java)
} else {
this.getParcelableArrayListExtra(identifierParameter)
}
}