diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 256e2b2b5..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" />
-
-
-
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 31bdd4109..b0031a27f 100644
--- a/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivity.kt
+++ b/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivity.kt
@@ -1,900 +1,83 @@
/*
* Nextcloud Talk - Android Client
*
- * SPDX-FileCopyrightText: 2022 Andy Scherzinger
- * SPDX-FileCopyrightText: 2022 Marcel Hibbe
- * SPDX-FileCopyrightText: 2017 Mario Danic
+ * SPDX-FileCopyrightText: 2024 Sowjanya Kota
+ * SPDX-FileCopyrightText: 2025 Marcel Hibbe
* 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.annotation.SuppressLint
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 androidx.activity.compose.setContent
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.runtime.remember
+import androidx.lifecycle.ViewModelProvider
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
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.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
-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
+class ContactsActivity : BaseActivity() {
@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
+ lateinit var viewModelFactory: ViewModelProvider.Factory
+ private lateinit var contactsViewModel: ContactsViewModel
+ @SuppressLint("UnrememberedMutableState")
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!!
+ 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(
+ ShareType.Group.shareType,
+ ShareType.Email.shareType,
+ ShareType.Circle.shareType
+ )
)
+ contactsViewModel.getContactsFromSearchParams()
}
- }
+ val colorScheme = viewThemeUtils.getColorScheme(this)
+ val uiState = contactsViewModel.contactsViewState.collectAsStateWithLifecycle()
- checkAndHandleDoneMenuItem()
- if (adapter?.hasFilter() == true) {
- searchItem!!.expandActionView()
- searchView!!.setQuery(adapter!!.getFilter(String::class.java) as CharSequence, false)
- }
- return true
- }
+ val selectedParticipants = remember {
+ intent?.getParcelableArrayListExtraProvider("selectedParticipants")
+ ?: emptyList()
+ }.toSet().toMutableList()
+ contactsViewModel.updateSelectedParticipants(selectedParticipants)
- 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)
+ MaterialTheme(
+ colorScheme = colorScheme
) {
- 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
+ ContactsScreen(
+ contactsViewModel = contactsViewModel,
+ uiState = uiState.value
)
- if (!contactItems!!.contains(newContactItem)) {
- newUserItemList.add(newContactItem)
- }
+ SetupSystemBars()
}
}
- 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
+ }
+}
+
+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/ContactsActivityCompose.kt b/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityCompose.kt
deleted file mode 100644
index e5366d608..000000000
--- a/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivityCompose.kt
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Nextcloud Talk - Android Client
- *
- * SPDX-FileCopyrightText: 2024 Sowjanya Kota
- * SPDX-FileCopyrightText: 2025 Marcel Hibbe
- * SPDX-License-Identifier: GPL-3.0-or-later
- */
-
-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
-import androidx.compose.runtime.remember
-import androidx.lifecycle.ViewModelProvider
-import androidx.lifecycle.compose.collectAsStateWithLifecycle
-import autodagger.AutoInjector
-import com.nextcloud.talk.activities.BaseActivity
-import com.nextcloud.talk.application.NextcloudTalkApplication
-import com.nextcloud.talk.components.SetupSystemBars
-import com.nextcloud.talk.models.json.autocomplete.AutocompleteUser
-import javax.inject.Inject
-
-@AutoInjector(NextcloudTalkApplication::class)
-class ContactsActivityCompose : BaseActivity() {
-
- @Inject
- lateinit var viewModelFactory: ViewModelProvider.Factory
- private lateinit var contactsViewModel: ContactsViewModel
-
- @SuppressLint("UnrememberedMutableState")
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this)
- contactsViewModel = ViewModelProvider(this, viewModelFactory)[ContactsViewModel::class.java]
- setContent {
- val isAddParticipants = intent.getBooleanExtra("isAddParticipants", false)
- contactsViewModel.updateIsAddParticipants(isAddParticipants)
- if (isAddParticipants) {
- contactsViewModel.updateShareTypes(
- listOf(
- ShareType.Group.shareType,
- ShareType.Email.shareType,
- ShareType.Circle.shareType
- )
- )
- contactsViewModel.getContactsFromSearchParams()
- }
- 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()
- }
- }.toSet().toMutableList()
- contactsViewModel.updateSelectedParticipants(selectedParticipants)
-
- MaterialTheme(
- colorScheme = colorScheme
- ) {
- ContactsScreen(
- contactsViewModel = contactsViewModel,
- uiState = uiState.value
- )
- SetupSystemBars()
- }
- }
- }
-}
-
-class CompanionClass {
- companion object {
- internal val TAG = ContactsActivityCompose::class.simpleName
- internal const val ROOM_TYPE_ONE_ONE = "1"
- }
-}
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/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/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..215ffde5f 100644
--- a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt
+++ b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt
@@ -84,9 +84,10 @@ import com.nextcloud.talk.R
import com.nextcloud.talk.activities.BaseActivity
import com.nextcloud.talk.application.NextcloudTalkApplication
import com.nextcloud.talk.chat.ChatActivity
+import com.nextcloud.talk.contacts.ContactsActivity
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)
@@ -378,12 +379,12 @@ fun AddParticipants(
modifier = Modifier
.padding(start = 16.dp, bottom = 16.dp)
.clickable {
- val intent = Intent(context, ContactsActivityCompose::class.java)
+ val intent = Intent(context, ContactsActivity::class.java)
intent.putParcelableArrayListExtra(
"selectedParticipants",
participants as ArrayList
)
- intent.putExtra("isAddParticipants", true)
+ intent.putExtra(BundleKeys.KEY_ADD_PARTICIPANTS, true)
intent.putExtra("isAddParticipantsEdit", true)
launcher.launch(intent)
},
@@ -416,8 +417,8 @@ fun AddParticipants(
modifier = Modifier
.fillMaxWidth()
.clickable {
- val intent = Intent(context, ContactsActivityCompose::class.java)
- intent.putExtra("isAddParticipants", true)
+ val intent = Intent(context, ContactsActivity::class.java)
+ 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..b4909ff6f 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,6 +52,8 @@ 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.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
@@ -56,14 +61,17 @@ 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 +134,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
@@ -147,10 +152,21 @@ class ConversationInfoActivity :
data.putLong(BundleKeys.KEY_INTERNAL_USER_ID, conversationUser.id!!)
return data.build()
}
-
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)
@@ -319,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)
}
}
@@ -383,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)
) {
@@ -652,23 +672,89 @@ 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()
+ 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)
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 +779,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 +796,7 @@ class ConversationInfoActivity :
).show()
}
}
+
else -> {
}
}
@@ -769,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() }
@@ -779,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
@@ -1011,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", "")
@@ -1034,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
@@ -1069,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/java/com/nextcloud/talk/conversationlist/ConversationsListActivity.kt b/app/src/main/java/com/nextcloud/talk/conversationlist/ConversationsListActivity.kt
index eb9b25ec1..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
@@ -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
@@ -1253,8 +1252,7 @@ class ConversationsListActivity :
conversation.type === ConversationEnums.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL
private fun showNewConversationsScreen() {
- val intent = Intent(context, ContactsActivityCompose::class.java)
- intent.putExtra(KEY_NEW_CONVERSATION, true)
+ val intent = Intent(context, ContactsActivity::class.java)
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)
+ }
+}
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..dcbfa5238 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
@@ -481,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