mirror of
https://github.com/nextcloud/talk-android
synced 2025-02-02 20:53:09 +00:00
Merge commit 'dc705ccf4027f61ebaefe625a99087cf5641d6a7'
This commit is contained in:
commit
8bc3f80667
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,964 @@
|
||||
/*
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Mario Danic
|
||||
* @author Marcel Hibbe
|
||||
* @author Andy Scherzinger
|
||||
* Copyright (C) 2017 Mario Danic <mario@lovelyhq.com>
|
||||
* Copyright (C) 2022 Marcel Hibbe <dev@mhibbe.de>
|
||||
* Copyright (C) 2022 Andy Scherzinger <info@andy-scherzinger.de>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.nextcloud.talk.controllers
|
||||
|
||||
import android.app.SearchManager
|
||||
import android.content.Context
|
||||
import android.graphics.PorterDuff
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.text.InputType
|
||||
import android.util.Log
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
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.WorkManager
|
||||
import autodagger.AutoInjector
|
||||
import com.bluelinelabs.logansquare.LoganSquare
|
||||
import com.nextcloud.talk.R
|
||||
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.application.NextcloudTalkApplication.Companion.sharedApplication
|
||||
import com.nextcloud.talk.controllers.base.NewBaseController
|
||||
import com.nextcloud.talk.controllers.bottomsheet.ConversationOperationEnum
|
||||
import com.nextcloud.talk.controllers.util.viewBinding
|
||||
import com.nextcloud.talk.databinding.ControllerContactsRvBinding
|
||||
import com.nextcloud.talk.events.OpenConversationEvent
|
||||
import com.nextcloud.talk.jobs.AddParticipantsToConversation
|
||||
import com.nextcloud.talk.models.RetrofitBucket
|
||||
import com.nextcloud.talk.models.database.CapabilitiesUtil
|
||||
import com.nextcloud.talk.models.database.UserEntity
|
||||
import com.nextcloud.talk.models.json.autocomplete.AutocompleteOverall
|
||||
import com.nextcloud.talk.models.json.autocomplete.AutocompleteUser
|
||||
import com.nextcloud.talk.models.json.conversations.Conversation
|
||||
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.ui.dialog.ContactsBottomDialog
|
||||
import com.nextcloud.talk.utils.ApiUtils
|
||||
import com.nextcloud.talk.utils.ConductorRemapping
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys
|
||||
import com.nextcloud.talk.utils.database.user.UserUtils
|
||||
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.EventBus
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
import org.parceler.Parcels
|
||||
import java.io.IOException
|
||||
import java.util.ArrayList
|
||||
import java.util.Collections
|
||||
import java.util.HashMap
|
||||
import java.util.HashSet
|
||||
import java.util.Locale
|
||||
import javax.inject.Inject
|
||||
|
||||
@AutoInjector(NextcloudTalkApplication::class)
|
||||
class ContactsController(args: Bundle) :
|
||||
NewBaseController(R.layout.controller_contacts_rv),
|
||||
SearchView.OnQueryTextListener,
|
||||
FlexibleAdapter.OnItemClickListener {
|
||||
private val binding: ControllerContactsRvBinding by viewBinding(ControllerContactsRvBinding::bind)
|
||||
|
||||
@Inject
|
||||
lateinit var userUtils: UserUtils
|
||||
|
||||
@Inject
|
||||
lateinit var eventBus: EventBus
|
||||
|
||||
@Inject
|
||||
lateinit var ncApi: NcApi
|
||||
|
||||
private var credentials: String? = null
|
||||
private var currentUser: UserEntity? = 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
|
||||
private var contactsBottomDialog: ContactsBottomDialog? = null
|
||||
|
||||
init {
|
||||
setHasOptionsMenu(true)
|
||||
sharedApplication!!.componentApplication.inject(this)
|
||||
|
||||
if (args.containsKey(BundleKeys.KEY_NEW_CONVERSATION)) {
|
||||
isNewConversationView = true
|
||||
existingParticipants = ArrayList()
|
||||
} else if (args.containsKey(BundleKeys.KEY_ADD_PARTICIPANTS)) {
|
||||
isAddingParticipantsView = true
|
||||
conversationToken = args.getString(BundleKeys.KEY_TOKEN)
|
||||
existingParticipants = ArrayList()
|
||||
if (args.containsKey(BundleKeys.KEY_EXISTING_PARTICIPANTS)) {
|
||||
existingParticipants = args.getStringArrayList(BundleKeys.KEY_EXISTING_PARTICIPANTS)
|
||||
}
|
||||
}
|
||||
selectedUserIds = HashSet()
|
||||
selectedGroupIds = HashSet()
|
||||
selectedEmails = HashSet()
|
||||
selectedCircleIds = HashSet()
|
||||
}
|
||||
|
||||
override fun onAttach(view: View) {
|
||||
super.onAttach(view)
|
||||
eventBus.register(this)
|
||||
if (isNewConversationView) {
|
||||
toggleNewCallHeaderVisibility(!isPublicCall)
|
||||
}
|
||||
if (isAddingParticipantsView) {
|
||||
binding.joinConversationViaLink.joinConversationViaLinkRelativeLayout.visibility = View.GONE
|
||||
binding.conversationPrivacyToggle.callHeaderLayout.visibility = View.GONE
|
||||
} else {
|
||||
binding.joinConversationViaLink.joinConversationViaLinkRelativeLayout.setOnClickListener {
|
||||
joinConversationViaLink()
|
||||
}
|
||||
binding.conversationPrivacyToggle.callHeaderLayout.setOnClickListener {
|
||||
toggleCallHeader()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onViewBound(view: View) {
|
||||
super.onViewBound(view)
|
||||
currentUser = userUtils.currentUser
|
||||
if (currentUser != null) {
|
||||
credentials = ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token)
|
||||
}
|
||||
if (adapter == null) {
|
||||
contactItems = ArrayList<AbstractFlexibleItem<*>>()
|
||||
adapter = FlexibleAdapter(contactItems, activity, false)
|
||||
if (currentUser != null) {
|
||||
fetchData()
|
||||
}
|
||||
}
|
||||
setupAdapter()
|
||||
prepareViews()
|
||||
}
|
||||
|
||||
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) {
|
||||
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)
|
||||
} else {
|
||||
val bundle = Bundle()
|
||||
val roomType: Conversation.ConversationType = if (isPublicCall) {
|
||||
Conversation.ConversationType.ROOM_PUBLIC_CALL
|
||||
} else {
|
||||
Conversation.ConversationType.ROOM_GROUP_CALL
|
||||
}
|
||||
val userIdsArray = ArrayList(selectedUserIds)
|
||||
val groupIdsArray = ArrayList(selectedGroupIds)
|
||||
val emailsArray = ArrayList(selectedEmails)
|
||||
val circleIdsArray = ArrayList(selectedCircleIds)
|
||||
bundle.putParcelable(BundleKeys.KEY_CONVERSATION_TYPE, Parcels.wrap(roomType))
|
||||
bundle.putStringArrayList(BundleKeys.KEY_INVITED_PARTICIPANTS, userIdsArray)
|
||||
bundle.putStringArrayList(BundleKeys.KEY_INVITED_GROUP, groupIdsArray)
|
||||
bundle.putStringArrayList(BundleKeys.KEY_INVITED_EMAIL, emailsArray)
|
||||
bundle.putStringArrayList(BundleKeys.KEY_INVITED_CIRCLE, circleIdsArray)
|
||||
bundle.putSerializable(BundleKeys.KEY_OPERATION_CODE, ConversationOperationEnum.OPS_CODE_INVITE_USERS)
|
||||
prepareAndShowBottomSheetWithBundle(bundle)
|
||||
}
|
||||
} else {
|
||||
addParticipantsToConversation()
|
||||
}
|
||||
}
|
||||
|
||||
private fun createRoom(roomType: String, sourceType: String?, userId: String) {
|
||||
val apiVersion: Int = ApiUtils.getConversationApiVersion(currentUser, intArrayOf(ApiUtils.APIv4, 1))
|
||||
val retrofitBucket: RetrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(
|
||||
apiVersion,
|
||||
currentUser!!.baseUrl,
|
||||
roomType,
|
||||
sourceType,
|
||||
userId,
|
||||
null
|
||||
)
|
||||
ncApi.createRoom(
|
||||
credentials,
|
||||
retrofitBucket.getUrl(), retrofitBucket.getQueryMap()
|
||||
)
|
||||
.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.putParcelable(BundleKeys.KEY_USER_ENTITY, currentUser)
|
||||
bundle.putString(BundleKeys.KEY_ROOM_TOKEN, roomOverall.getOcs().getData().getToken())
|
||||
bundle.putString(BundleKeys.KEY_ROOM_ID, roomOverall.getOcs().getData().getRoomId())
|
||||
|
||||
// FIXME once APIv2 or later is used only, the createRoom already returns all the data
|
||||
ncApi.getRoom(
|
||||
credentials,
|
||||
ApiUtils.getUrlForRoom(
|
||||
apiVersion, currentUser!!.baseUrl,
|
||||
roomOverall.getOcs().getData().getToken()
|
||||
)
|
||||
)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(object : Observer<RoomOverall> {
|
||||
override fun onSubscribe(d: Disposable) {
|
||||
// unused atm
|
||||
}
|
||||
override fun onNext(roomOverall: RoomOverall) {
|
||||
bundle.putParcelable(
|
||||
BundleKeys.KEY_ACTIVE_CONVERSATION,
|
||||
Parcels.wrap(roomOverall.getOcs().getData())
|
||||
)
|
||||
ConductorRemapping.remapChatController(
|
||||
router, currentUser!!.id,
|
||||
roomOverall.getOcs().getData().getToken(), bundle, true
|
||||
)
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
// unused atm
|
||||
}
|
||||
|
||||
override fun onComplete() {
|
||||
// unused atm
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
// unused atm
|
||||
}
|
||||
|
||||
override fun onComplete() {
|
||||
// unused atm
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun addParticipantsToConversation() {
|
||||
val userIdsArray: Array<String> = selectedUserIds.toTypedArray<String>()
|
||||
val groupIdsArray: Array<String> = selectedGroupIds.toTypedArray<String>()
|
||||
val emailsArray: Array<String> = selectedEmails.toTypedArray<String>()
|
||||
val circleIdsArray: Array<String> = selectedCircleIds.toTypedArray<String>()
|
||||
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)
|
||||
router.popCurrentController()
|
||||
}
|
||||
|
||||
private fun initSearchView() {
|
||||
if (activity != null) {
|
||||
val searchManager: SearchManager? = activity?.getSystemService(Context.SEARCH_SERVICE) as SearchManager?
|
||||
if (searchItem != null) {
|
||||
searchView = MenuItemCompat.getActionView(searchItem) as 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 (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O &&
|
||||
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(activity?.componentName))
|
||||
}
|
||||
searchView!!.setOnQueryTextListener(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
return when (item.itemId) {
|
||||
R.id.home -> {
|
||||
router.popCurrentController()
|
||||
}
|
||||
R.id.contacts_selection_done -> {
|
||||
selectionDone()
|
||||
true
|
||||
}
|
||||
else -> {
|
||||
super.onOptionsItemSelected(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
super.onCreateOptionsMenu(menu, inflater)
|
||||
inflater.inflate(R.menu.menu_contacts, menu)
|
||||
searchItem = menu.findItem(R.id.action_search)
|
||||
doneMenuItem = menu.findItem(R.id.contacts_selection_done)
|
||||
initSearchView()
|
||||
}
|
||||
|
||||
override fun onPrepareOptionsMenu(menu: Menu) {
|
||||
super.onPrepareOptionsMenu(menu)
|
||||
checkAndHandleDoneMenuItem()
|
||||
if (adapter?.hasFilter() == true) {
|
||||
searchItem!!.expandActionView()
|
||||
searchView!!.setQuery(adapter!!.getFilter(String::class.java) as CharSequence, false)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("Detekt.TooGenericExceptionCaught")
|
||||
private fun fetchData() {
|
||||
dispose(null)
|
||||
alreadyFetching = true
|
||||
userHeaderItems = HashMap<String, GenericTextHeaderItem>()
|
||||
val query = adapter!!.getFilter(String::class.java) as String?
|
||||
val retrofitBucket: RetrofitBucket =
|
||||
ApiUtils.getRetrofitBucketForContactsSearchFor14(currentUser!!.baseUrl, query)
|
||||
val modifiedQueryMap: HashMap<String, Any?> = HashMap<String, Any?>(retrofitBucket.getQueryMap())
|
||||
modifiedQueryMap.put("limit", CONTACTS_BATCH_SIZE)
|
||||
if (isAddingParticipantsView) {
|
||||
modifiedQueryMap.put("itemId", conversationToken)
|
||||
}
|
||||
val shareTypesList: ArrayList<String> = ArrayList()
|
||||
// users
|
||||
shareTypesList.add("0")
|
||||
if (!isAddingParticipantsView) {
|
||||
// groups
|
||||
shareTypesList.add("1")
|
||||
} else if (CapabilitiesUtil.hasSpreedFeatureCapability(currentUser, "invite-groups-and-mails")) {
|
||||
// groups
|
||||
shareTypesList.add("1")
|
||||
// emails
|
||||
shareTypesList.add("4")
|
||||
}
|
||||
if (CapabilitiesUtil.hasSpreedFeatureCapability(currentUser, "circles-support")) {
|
||||
// circles
|
||||
shareTypesList.add("7")
|
||||
}
|
||||
modifiedQueryMap.put("shareTypes[]", shareTypesList)
|
||||
ncApi.getContactsWithSearchParam(
|
||||
credentials,
|
||||
retrofitBucket.getUrl(), 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) {
|
||||
val newUserItemList = processAutocompleteUserList(responseBody)
|
||||
|
||||
userHeaderItems = HashMap<String, GenericTextHeaderItem>()
|
||||
contactItems!!.addAll(newUserItemList)
|
||||
|
||||
sortUserItems(newUserItemList)
|
||||
|
||||
if (newUserItemList.size > 0) {
|
||||
adapter?.updateDataSet(newUserItemList as List<Nothing>?)
|
||||
} else {
|
||||
adapter?.filterItems()
|
||||
}
|
||||
|
||||
try {
|
||||
binding.controllerGenericRv.swipeRefreshLayout.isRefreshing = false
|
||||
} catch (npe: NullPointerException) {
|
||||
// view binding can be null
|
||||
// since this is called asynchronously and UI might have been destroyed in the meantime
|
||||
Log.i(TAG, "UI destroyed - view binding already gone")
|
||||
}
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
try {
|
||||
binding.controllerGenericRv.swipeRefreshLayout.isRefreshing = false
|
||||
} catch (npe: NullPointerException) {
|
||||
// view binding can be null
|
||||
// since this is called asynchronously and UI might have been destroyed in the meantime
|
||||
Log.i(TAG, "UI destroyed - view binding already gone")
|
||||
}
|
||||
dispose(contactsQueryDisposable)
|
||||
}
|
||||
|
||||
override fun onComplete() {
|
||||
try {
|
||||
binding.controllerGenericRv.swipeRefreshLayout.isRefreshing = false
|
||||
} catch (npe: NullPointerException) {
|
||||
// view binding can be null
|
||||
// since this is called asynchronously and UI might have been destroyed in the meantime
|
||||
Log.i(TAG, "UI destroyed - view binding already gone")
|
||||
}
|
||||
dispose(contactsQueryDisposable)
|
||||
alreadyFetching = false
|
||||
disengageProgressBar()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun processAutocompleteUserList(responseBody: ResponseBody): MutableList<AbstractFlexibleItem<*>> {
|
||||
try {
|
||||
val autocompleteOverall: AutocompleteOverall = LoganSquare.parse<AutocompleteOverall>(
|
||||
responseBody.string(),
|
||||
AutocompleteOverall::class.java
|
||||
)
|
||||
val autocompleteUsersList: ArrayList<AutocompleteUser> = ArrayList<AutocompleteUser>()
|
||||
autocompleteUsersList.addAll(autocompleteOverall.ocs!!.data!!)
|
||||
return processAutocompleteUserList(autocompleteUsersList)
|
||||
} catch (ioe: IOException) {
|
||||
Log.e(TAG, "Parsing response body failed while getting contacts", ioe)
|
||||
}
|
||||
|
||||
return ArrayList<AbstractFlexibleItem<*>>()
|
||||
}
|
||||
|
||||
private fun processAutocompleteUserList(
|
||||
autocompleteUsersList: ArrayList<AutocompleteUser>
|
||||
): MutableList<AbstractFlexibleItem<*>> {
|
||||
var participant: Participant
|
||||
val actorTypeConverter = EnumActorTypeConverter()
|
||||
val newUserItemList: MutableList<AbstractFlexibleItem<*>> = ArrayList<AbstractFlexibleItem<*>>()
|
||||
for (autocompleteUser in autocompleteUsersList) {
|
||||
if (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)
|
||||
userHeaderItems.put(headerTitle, genericTextHeaderItem)
|
||||
}
|
||||
val newContactItem = ContactItem(
|
||||
participant,
|
||||
currentUser,
|
||||
userHeaderItems[headerTitle]
|
||||
)
|
||||
if (!contactItems!!.contains(newContactItem)) {
|
||||
newUserItemList.add(newContactItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
return newUserItemList
|
||||
}
|
||||
|
||||
private fun getHeaderTitle(participant: Participant): String {
|
||||
return when {
|
||||
participant.getActorType() == Participant.ActorType.GROUPS -> {
|
||||
resources!!.getString(R.string.nc_groups)
|
||||
}
|
||||
participant.getActorType() == Participant.ActorType.CIRCLES -> {
|
||||
resources!!.getString(R.string.nc_circles)
|
||||
}
|
||||
else -> {
|
||||
participant.getDisplayName().substring(0, 1).toUpperCase(Locale.getDefault())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun createParticipant(
|
||||
autocompleteUser: AutocompleteUser,
|
||||
actorTypeConverter: EnumActorTypeConverter
|
||||
): Participant {
|
||||
val participant = Participant()
|
||||
participant.setActorId(autocompleteUser.id)
|
||||
participant.setActorType(actorTypeConverter.getFromString(autocompleteUser.source))
|
||||
participant.setDisplayName(autocompleteUser.label)
|
||||
participant.setSource(autocompleteUser.source)
|
||||
|
||||
return participant
|
||||
}
|
||||
|
||||
private fun sortUserItems(newUserItemList: MutableList<AbstractFlexibleItem<*>>) {
|
||||
Collections.sort(
|
||||
newUserItemList,
|
||||
{ o1: AbstractFlexibleItem<*>, o2: AbstractFlexibleItem<*> ->
|
||||
val firstName: String = if (o1 is ContactItem) {
|
||||
(o1 as ContactItem).model.getDisplayName()
|
||||
} else {
|
||||
(o1 as GenericTextHeaderItem).model
|
||||
}
|
||||
val secondName: String = if (o2 is ContactItem) {
|
||||
(o2 as ContactItem).model.getDisplayName()
|
||||
} else {
|
||||
(o2 as GenericTextHeaderItem).model
|
||||
}
|
||||
if (o1 is ContactItem && o2 is ContactItem) {
|
||||
val firstSource: String = (o1 as ContactItem).model.getSource()
|
||||
val secondSource: String = (o2 as ContactItem).model.getSource()
|
||||
if (firstSource == secondSource) {
|
||||
return@sort firstName.compareTo(secondName, ignoreCase = true)
|
||||
}
|
||||
|
||||
// First users
|
||||
if ("users" == firstSource) {
|
||||
return@sort -1
|
||||
} else if ("users" == secondSource) {
|
||||
return@sort 1
|
||||
}
|
||||
|
||||
// Then groups
|
||||
if ("groups" == firstSource) {
|
||||
return@sort -1
|
||||
} else if ("groups" == secondSource) {
|
||||
return@sort 1
|
||||
}
|
||||
|
||||
// Then circles
|
||||
if ("circles" == firstSource) {
|
||||
return@sort -1
|
||||
} else if ("circles" == secondSource) {
|
||||
return@sort 1
|
||||
}
|
||||
|
||||
// Otherwise fall back to name sorting
|
||||
return@sort firstName.compareTo(secondName, ignoreCase = true)
|
||||
}
|
||||
firstName.compareTo(secondName, ignoreCase = true)
|
||||
}
|
||||
)
|
||||
|
||||
Collections.sort(
|
||||
contactItems
|
||||
) { o1: AbstractFlexibleItem<*>, o2: AbstractFlexibleItem<*> ->
|
||||
val firstName: String = if (o1 is ContactItem) {
|
||||
(o1 as ContactItem).model.getDisplayName()
|
||||
} else {
|
||||
(o1 as GenericTextHeaderItem).model
|
||||
}
|
||||
val secondName: String = if (o2 is ContactItem) {
|
||||
(o2 as ContactItem).model.getDisplayName()
|
||||
} else {
|
||||
(o2 as GenericTextHeaderItem).model
|
||||
}
|
||||
if (o1 is ContactItem && o2 is ContactItem) {
|
||||
if ("groups" == (o1 as ContactItem).model.getSource() &&
|
||||
"groups" == (o2 as ContactItem).model.getSource()
|
||||
) {
|
||||
return@sort firstName.compareTo(secondName, ignoreCase = true)
|
||||
} else if ("groups" == (o1 as ContactItem).model.getSource()) {
|
||||
return@sort -1
|
||||
} else if ("groups" == (o2 as ContactItem).model.getSource()) {
|
||||
return@sort 1
|
||||
}
|
||||
}
|
||||
firstName.compareTo(secondName, ignoreCase = true)
|
||||
}
|
||||
}
|
||||
|
||||
private fun prepareViews() {
|
||||
layoutManager = SmoothScrollLinearLayoutManager(activity)
|
||||
binding.controllerGenericRv.recyclerView.layoutManager = layoutManager
|
||||
binding.controllerGenericRv.recyclerView.setHasFixedSize(true)
|
||||
binding.controllerGenericRv.recyclerView.adapter = adapter
|
||||
binding.controllerGenericRv.swipeRefreshLayout.setOnRefreshListener { fetchData() }
|
||||
binding.controllerGenericRv.swipeRefreshLayout.setColorSchemeResources(R.color.colorPrimary)
|
||||
binding.controllerGenericRv.swipeRefreshLayout
|
||||
.setProgressBackgroundColorSchemeResource(R.color.refresh_spinner_background)
|
||||
binding.joinConversationViaLink.joinConversationViaLinkImageView
|
||||
.background
|
||||
.setColorFilter(
|
||||
ResourcesCompat.getColor(resources!!, R.color.colorBackgroundDarker, null),
|
||||
PorterDuff.Mode.SRC_IN
|
||||
)
|
||||
binding.conversationPrivacyToggle.publicCallLink
|
||||
.background
|
||||
.setColorFilter(
|
||||
ResourcesCompat.getColor(resources!!, R.color.colorPrimary, null),
|
||||
PorterDuff.Mode.SRC_IN
|
||||
)
|
||||
disengageProgressBar()
|
||||
}
|
||||
|
||||
private fun disengageProgressBar() {
|
||||
if (!alreadyFetching) {
|
||||
binding.loadingContent.visibility = View.GONE
|
||||
binding.controllerGenericRv.root.visibility = View.VISIBLE
|
||||
if (isNewConversationView) {
|
||||
binding.conversationPrivacyToggle.callHeaderLayout.visibility = View.VISIBLE
|
||||
binding.joinConversationViaLink.joinConversationViaLinkRelativeLayout.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 onSaveViewState(view: View, outState: Bundle) {
|
||||
adapter?.onSaveInstanceState(outState)
|
||||
super.onSaveViewState(view, outState)
|
||||
}
|
||||
|
||||
override fun onRestoreViewState(view: View, savedViewState: Bundle) {
|
||||
super.onRestoreViewState(view, savedViewState)
|
||||
if (adapter != null) {
|
||||
adapter?.onRestoreInstanceState(savedViewState)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
dispose(null)
|
||||
}
|
||||
|
||||
@Suppress("Detekt.TooGenericExceptionCaught")
|
||||
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>?)
|
||||
}
|
||||
|
||||
try {
|
||||
binding.controllerGenericRv.swipeRefreshLayout.isEnabled = !adapter!!.hasFilter()
|
||||
} catch (npe: NullPointerException) {
|
||||
// view binding can be null
|
||||
// since this is called asynchronously and UI might have been destroyed in the meantime
|
||||
Log.i(TAG, "UI destroyed - view binding already gone")
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
override val title: String
|
||||
get() = 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)
|
||||
}
|
||||
}
|
||||
|
||||
private fun prepareAndShowBottomSheetWithBundle(bundle: Bundle) {
|
||||
// 11: create conversation-enter name for new conversation
|
||||
// 10: get&join room when enter link
|
||||
contactsBottomDialog = ContactsBottomDialog(activity!!, bundle)
|
||||
contactsBottomDialog?.show()
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onMessageEvent(openConversationEvent: OpenConversationEvent) {
|
||||
ConductorRemapping.remapChatController(
|
||||
router, currentUser!!.id,
|
||||
openConversationEvent.conversation!!.getToken(),
|
||||
openConversationEvent.bundle!!, true
|
||||
)
|
||||
contactsBottomDialog?.dismiss()
|
||||
}
|
||||
|
||||
override fun onDetach(view: View) {
|
||||
super.onDetach(view)
|
||||
eventBus.unregister(this)
|
||||
}
|
||||
|
||||
override fun onItemClick(view: View, position: Int): Boolean {
|
||||
if (adapter?.getItem(position) is ContactItem) {
|
||||
if (!isNewConversationView && !isAddingParticipantsView) {
|
||||
createRoom(adapter?.getItem(position) as ContactItem)
|
||||
} else {
|
||||
val participant: Participant = (adapter?.getItem(position) as ContactItem).model
|
||||
updateSelection((adapter?.getItem(position) as ContactItem))
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private fun updateSelection(contactItem: ContactItem) {
|
||||
contactItem.model.isSelected = !contactItem.model.isSelected
|
||||
updateSelectionLists(contactItem.model)
|
||||
if (CapabilitiesUtil.hasSpreedFeatureCapability(currentUser, "last-room-activity") &&
|
||||
!CapabilitiesUtil.hasSpreedFeatureCapability(currentUser, "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.getActorId() == contactItem.model.getActorId() &&
|
||||
internalParticipant.getActorType() == Participant.ActorType.GROUPS &&
|
||||
internalParticipant.isSelected
|
||||
) {
|
||||
internalParticipant.isSelected = false
|
||||
selectedGroupIds.remove(internalParticipant.getActorId())
|
||||
}
|
||||
}
|
||||
}
|
||||
adapter?.notifyDataSetChanged()
|
||||
checkAndHandleDoneMenuItem()
|
||||
}
|
||||
|
||||
private fun createRoom(contactItem: ContactItem) {
|
||||
var roomType = "1"
|
||||
if ("groups" == contactItem.model.getSource()) {
|
||||
roomType = "2"
|
||||
}
|
||||
val apiVersion: Int = ApiUtils.getConversationApiVersion(currentUser, intArrayOf(ApiUtils.APIv4, 1))
|
||||
val retrofitBucket: RetrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(
|
||||
apiVersion,
|
||||
currentUser!!.baseUrl,
|
||||
roomType,
|
||||
null,
|
||||
contactItem.model.getActorId(),
|
||||
null
|
||||
)
|
||||
ncApi.createRoom(
|
||||
credentials,
|
||||
retrofitBucket.getUrl(), retrofitBucket.getQueryMap()
|
||||
)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(object : Observer<RoomOverall> {
|
||||
override fun onSubscribe(d: Disposable) {
|
||||
// unused atm
|
||||
}
|
||||
override fun onNext(roomOverall: RoomOverall) {
|
||||
if (activity != null) {
|
||||
val bundle = Bundle()
|
||||
bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, currentUser)
|
||||
bundle.putString(BundleKeys.KEY_ROOM_TOKEN, roomOverall.getOcs().getData().getToken())
|
||||
bundle.putString(BundleKeys.KEY_ROOM_ID, roomOverall.getOcs().getData().getRoomId())
|
||||
bundle.putParcelable(
|
||||
BundleKeys.KEY_ACTIVE_CONVERSATION,
|
||||
Parcels.wrap(roomOverall.getOcs().getData())
|
||||
)
|
||||
ConductorRemapping.remapChatController(
|
||||
router,
|
||||
currentUser!!.id,
|
||||
roomOverall.getOcs().getData().getToken(),
|
||||
bundle,
|
||||
true
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
// unused atm
|
||||
}
|
||||
override fun onComplete() {
|
||||
// unused atm
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun updateSelectionLists(participant: Participant) {
|
||||
if ("groups" == participant.getSource()) {
|
||||
if (participant.isSelected) {
|
||||
selectedGroupIds.add(participant.getActorId())
|
||||
} else {
|
||||
selectedGroupIds.remove(participant.getActorId())
|
||||
}
|
||||
} else if ("emails" == participant.getSource()) {
|
||||
if (participant.isSelected) {
|
||||
selectedEmails.add(participant.getActorId())
|
||||
} else {
|
||||
selectedEmails.remove(participant.getActorId())
|
||||
}
|
||||
} else if ("circles" == participant.getSource()) {
|
||||
if (participant.isSelected) {
|
||||
selectedCircleIds.add(participant.getActorId())
|
||||
} else {
|
||||
selectedCircleIds.remove(participant.getActorId())
|
||||
}
|
||||
} else {
|
||||
if (participant.isSelected) {
|
||||
selectedUserIds.add(participant.getActorId())
|
||||
} else {
|
||||
selectedUserIds.remove(participant.getActorId())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun isValidGroupSelection(
|
||||
contactItem: ContactItem,
|
||||
participant: Participant,
|
||||
adapter: FlexibleAdapter<*>?
|
||||
): Boolean {
|
||||
return "groups" == contactItem.model.getSource() && participant.isSelected && adapter?.selectedItemCount!! > 1
|
||||
}
|
||||
|
||||
private fun joinConversationViaLink() {
|
||||
val bundle = Bundle()
|
||||
bundle.putSerializable(BundleKeys.KEY_OPERATION_CODE, ConversationOperationEnum.OPS_CODE_GET_AND_JOIN_ROOM)
|
||||
prepareAndShowBottomSheetWithBundle(bundle)
|
||||
}
|
||||
|
||||
private fun toggleCallHeader() {
|
||||
toggleNewCallHeaderVisibility(isPublicCall)
|
||||
isPublicCall = !isPublicCall
|
||||
|
||||
if (isPublicCall) {
|
||||
binding.joinConversationViaLink.joinConversationViaLinkRelativeLayout.visibility = View.GONE
|
||||
updateGroupParticipantSelection()
|
||||
} else {
|
||||
binding.joinConversationViaLink.joinConversationViaLinkRelativeLayout.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
enableContactForNonPublicCall()
|
||||
checkAndHandleDoneMenuItem()
|
||||
adapter?.notifyDataSetChanged()
|
||||
}
|
||||
|
||||
private fun updateGroupParticipantSelection() {
|
||||
val currentItems: List<AbstractFlexibleItem<*>> = adapter?.currentItems as
|
||||
List<AbstractFlexibleItem<*>>
|
||||
var internalParticipant: Participant
|
||||
for (i in currentItems.indices) {
|
||||
if (currentItems[i] is ContactItem) {
|
||||
internalParticipant = (currentItems[i] as ContactItem).model
|
||||
if (internalParticipant.getActorType() == Participant.ActorType.GROUPS &&
|
||||
internalParticipant.isSelected
|
||||
) {
|
||||
internalParticipant.isSelected = false
|
||||
selectedGroupIds.remove(internalParticipant.getActorId())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 ("groups" == contactItem.model.getSource()) {
|
||||
contactItem.isEnabled = !isPublicCall
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("Detekt.TooGenericExceptionCaught")
|
||||
private fun toggleNewCallHeaderVisibility(showInitialLayout: Boolean) {
|
||||
try {
|
||||
if (showInitialLayout) {
|
||||
binding.conversationPrivacyToggle.initialRelativeLayout.visibility = View.VISIBLE
|
||||
binding.conversationPrivacyToggle.secondaryRelativeLayout.visibility = View.GONE
|
||||
} else {
|
||||
binding.conversationPrivacyToggle.initialRelativeLayout.visibility = View.GONE
|
||||
binding.conversationPrivacyToggle.secondaryRelativeLayout.visibility = View.VISIBLE
|
||||
}
|
||||
} catch (npe: NullPointerException) {
|
||||
// view binding can be null
|
||||
// since this is called asynchronously and UI might have been destroyed in the meantime
|
||||
Log.i(TAG, "UI destroyed - view binding already gone")
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TAG = "ContactsController"
|
||||
const val RETRIES: Long = 3
|
||||
const val CONTACTS_BATCH_SIZE: Int = 50
|
||||
const val HEADER_ELEVATION: Int = 5
|
||||
}
|
||||
}
|
@ -357,6 +357,7 @@ class ProfileController : NewBaseController(R.layout.controller_profile) {
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("Detekt.LongMethod")
|
||||
private fun createUserInfoDetails(userInfo: UserProfileData?): List<UserInfoDetailsItem> {
|
||||
val result: MutableList<UserInfoDetailsItem> = LinkedList()
|
||||
|
||||
|
@ -32,7 +32,7 @@ import com.nextcloud.talk.models.json.participants.Participant.ActorType.GUESTS
|
||||
import com.nextcloud.talk.models.json.participants.Participant.ActorType.USERS
|
||||
|
||||
class EnumActorTypeConverter : StringBasedTypeConverter<Participant.ActorType>() {
|
||||
override fun getFromString(string: String): Participant.ActorType {
|
||||
override fun getFromString(string: String?): Participant.ActorType {
|
||||
return when (string) {
|
||||
"emails" -> EMAILS
|
||||
"groups" -> GROUPS
|
||||
|
@ -121,53 +121,53 @@ import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.USER_RE
|
||||
*/
|
||||
class EnumSystemMessageTypeConverter : StringBasedTypeConverter<ChatMessage.SystemMessageType>() {
|
||||
override fun getFromString(string: String): ChatMessage.SystemMessageType {
|
||||
when (string) {
|
||||
"conversation_created" -> return CONVERSATION_CREATED
|
||||
"conversation_renamed" -> return CONVERSATION_RENAMED
|
||||
"description_set" -> return DESCRIPTION_SET
|
||||
"description_removed" -> return DESCRIPTION_REMOVED
|
||||
"call_started" -> return CALL_STARTED
|
||||
"call_joined" -> return CALL_JOINED
|
||||
"call_left" -> return CALL_LEFT
|
||||
"call_ended" -> return CALL_ENDED
|
||||
"call_ended_everyone" -> return CALL_ENDED_EVERYONE
|
||||
"call_missed" -> return CALL_MISSED
|
||||
"call_tried" -> return CALL_TRIED
|
||||
"read_only_off" -> return READ_ONLY_OFF
|
||||
"read_only" -> return READ_ONLY
|
||||
"listable_none" -> return LISTABLE_NONE
|
||||
"listable_users" -> return LISTABLE_USERS
|
||||
"listable_all" -> return LISTABLE_ALL
|
||||
"lobby_none" -> return LOBBY_NONE
|
||||
"lobby_non_moderators" -> return LOBBY_NON_MODERATORS
|
||||
"lobby_timer_reached" -> return LOBBY_OPEN_TO_EVERYONE
|
||||
"guests_allowed" -> return GUESTS_ALLOWED
|
||||
"guests_disallowed" -> return GUESTS_DISALLOWED
|
||||
"password_set" -> return PASSWORD_SET
|
||||
"password_removed" -> return PASSWORD_REMOVED
|
||||
"user_added" -> return USER_ADDED
|
||||
"user_removed" -> return USER_REMOVED
|
||||
"group_added" -> return GROUP_ADDED
|
||||
"group_removed" -> return GROUP_REMOVED
|
||||
"circle_added" -> return CIRCLE_ADDED
|
||||
"circle_removed" -> return CIRCLE_REMOVED
|
||||
"moderator_promoted" -> return MODERATOR_PROMOTED
|
||||
"moderator_demoted" -> return MODERATOR_DEMOTED
|
||||
"guest_moderator_promoted" -> return GUEST_MODERATOR_PROMOTED
|
||||
"guest_moderator_demoted" -> return GUEST_MODERATOR_DEMOTED
|
||||
"message_deleted" -> return MESSAGE_DELETED
|
||||
"file_shared" -> return FILE_SHARED
|
||||
"object_shared" -> return OBJECT_SHARED
|
||||
"matterbridge_config_added" -> return MATTERBRIDGE_CONFIG_ADDED
|
||||
"matterbridge_config_edited" -> return MATTERBRIDGE_CONFIG_EDITED
|
||||
"matterbridge_config_removed" -> return MATTERBRIDGE_CONFIG_REMOVED
|
||||
"matterbridge_config_enabled" -> return MATTERBRIDGE_CONFIG_ENABLED
|
||||
"matterbridge_config_disabled" -> return MATTERBRIDGE_CONFIG_DISABLED
|
||||
"history_cleared" -> return CLEARED_CHAT
|
||||
"reaction" -> return REACTION
|
||||
"reaction_deleted" -> return REACTION_DELETED
|
||||
"reaction_revoked" -> return REACTION_REVOKED
|
||||
else -> return DUMMY
|
||||
return when (string) {
|
||||
"conversation_created" -> CONVERSATION_CREATED
|
||||
"conversation_renamed" -> CONVERSATION_RENAMED
|
||||
"description_set" -> DESCRIPTION_SET
|
||||
"description_removed" -> DESCRIPTION_REMOVED
|
||||
"call_started" -> CALL_STARTED
|
||||
"call_joined" -> CALL_JOINED
|
||||
"call_left" -> CALL_LEFT
|
||||
"call_ended" -> CALL_ENDED
|
||||
"call_ended_everyone" -> CALL_ENDED_EVERYONE
|
||||
"call_missed" -> CALL_MISSED
|
||||
"call_tried" -> CALL_TRIED
|
||||
"read_only_off" -> READ_ONLY_OFF
|
||||
"read_only" -> READ_ONLY
|
||||
"listable_none" -> LISTABLE_NONE
|
||||
"listable_users" -> LISTABLE_USERS
|
||||
"listable_all" -> LISTABLE_ALL
|
||||
"lobby_none" -> LOBBY_NONE
|
||||
"lobby_non_moderators" -> LOBBY_NON_MODERATORS
|
||||
"lobby_timer_reached" -> LOBBY_OPEN_TO_EVERYONE
|
||||
"guests_allowed" -> GUESTS_ALLOWED
|
||||
"guests_disallowed" -> GUESTS_DISALLOWED
|
||||
"password_set" -> PASSWORD_SET
|
||||
"password_removed" -> PASSWORD_REMOVED
|
||||
"user_added" -> USER_ADDED
|
||||
"user_removed" -> USER_REMOVED
|
||||
"group_added" -> GROUP_ADDED
|
||||
"group_removed" -> GROUP_REMOVED
|
||||
"circle_added" -> CIRCLE_ADDED
|
||||
"circle_removed" -> CIRCLE_REMOVED
|
||||
"moderator_promoted" -> MODERATOR_PROMOTED
|
||||
"moderator_demoted" -> MODERATOR_DEMOTED
|
||||
"guest_moderator_promoted" -> GUEST_MODERATOR_PROMOTED
|
||||
"guest_moderator_demoted" -> GUEST_MODERATOR_DEMOTED
|
||||
"message_deleted" -> MESSAGE_DELETED
|
||||
"file_shared" -> FILE_SHARED
|
||||
"object_shared" -> OBJECT_SHARED
|
||||
"matterbridge_config_added" -> MATTERBRIDGE_CONFIG_ADDED
|
||||
"matterbridge_config_edited" -> MATTERBRIDGE_CONFIG_EDITED
|
||||
"matterbridge_config_removed" -> MATTERBRIDGE_CONFIG_REMOVED
|
||||
"matterbridge_config_enabled" -> MATTERBRIDGE_CONFIG_ENABLED
|
||||
"matterbridge_config_disabled" -> MATTERBRIDGE_CONFIG_DISABLED
|
||||
"history_cleared" -> CLEARED_CHAT
|
||||
"reaction" -> REACTION
|
||||
"reaction_deleted" -> REACTION_DELETED
|
||||
"reaction_revoked" -> REACTION_REVOKED
|
||||
else -> DUMMY
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -162,10 +162,8 @@ class MessageActionsDialog(
|
||||
}
|
||||
|
||||
private fun initEmojiBar(hasChatPermission: Boolean) {
|
||||
if (hasChatPermission &&
|
||||
CapabilitiesUtil.hasSpreedFeatureCapability(user, "reactions") &&
|
||||
Conversation.ConversationReadOnlyState.CONVERSATION_READ_ONLY !=
|
||||
currentConversation?.conversationReadOnlyState &&
|
||||
if (CapabilitiesUtil.hasSpreedFeatureCapability(user, "reactions") &&
|
||||
isPermitted(hasChatPermission) &&
|
||||
isReactableMessageType(message)
|
||||
) {
|
||||
checkAndSetEmojiSelfReaction(dialogMessageActionsBinding.emojiThumbsUp)
|
||||
@ -203,6 +201,11 @@ class MessageActionsDialog(
|
||||
}
|
||||
}
|
||||
|
||||
private fun isPermitted(hasChatPermission: Boolean): Boolean {
|
||||
return hasChatPermission && Conversation.ConversationReadOnlyState.CONVERSATION_READ_ONLY !=
|
||||
currentConversation?.conversationReadOnlyState
|
||||
}
|
||||
|
||||
private fun isReactableMessageType(message: ChatMessage): Boolean {
|
||||
return !(message.isCommandMessage || message.isDeletedCommentMessage || message.isDeleted)
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ import java.util.HashMap
|
||||
|
||||
object DrawableUtils {
|
||||
|
||||
@Suppress("Detekt.LongMethod")
|
||||
fun getDrawableResourceIdForMimeType(mimetype: String?): Int {
|
||||
var localMimetype = mimetype
|
||||
val drawableMap = HashMap<String, Int>()
|
||||
|
@ -62,14 +62,17 @@
|
||||
</LinearLayout>
|
||||
|
||||
<include
|
||||
android:id="@+id/conversation_privacy_toggle"
|
||||
layout="@layout/conversation_privacy_toggle"
|
||||
android:visibility="gone" />
|
||||
|
||||
<include
|
||||
android:id="@+id/join_conversation_via_link"
|
||||
layout="@layout/join_conversation_via_link"
|
||||
android:visibility="gone" />
|
||||
|
||||
<include
|
||||
android:id="@+id/controller_generic_rv"
|
||||
layout="@layout/controller_generic_rv"
|
||||
android:visibility="gone" />
|
||||
</LinearLayout>
|
||||
|
Loading…
Reference in New Issue
Block a user