mirror of
https://github.com/nextcloud/talk-android.git
synced 2024-11-26 06:55:42 +03:00
Remove EntryMenuController and OperationMenuController
The "operation view" at the bottom is replaced by snackbars. I have removed the join via public link feature for now. This was buggy, complex and incomplete. This feature must be reimplemented in a more useful place (login screen, so you can use it as a guest without using an existing instance). Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
This commit is contained in:
parent
da541d3fc3
commit
32dbe70399
29 changed files with 1663 additions and 2063 deletions
|
@ -41,6 +41,7 @@ import androidx.core.content.res.ResourcesCompat
|
|||
import androidx.core.view.MenuItemCompat
|
||||
import androidx.work.Data
|
||||
import androidx.work.OneTimeWorkRequest
|
||||
import androidx.work.WorkInfo
|
||||
import androidx.work.WorkManager
|
||||
import autodagger.AutoInjector
|
||||
import com.bluelinelabs.logansquare.LoganSquare
|
||||
|
@ -51,9 +52,10 @@ 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.controllers.bottomsheet.ConversationOperationEnum
|
||||
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
|
||||
|
@ -64,9 +66,9 @@ 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.ui.dialog.ContactsBottomDialog
|
||||
import com.nextcloud.talk.users.UserManager
|
||||
import com.nextcloud.talk.utils.ApiUtils
|
||||
import com.nextcloud.talk.utils.UserIdUtils.getIdForUser
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys
|
||||
import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
|
@ -82,7 +84,6 @@ import org.greenrobot.eventbus.Subscribe
|
|||
import org.greenrobot.eventbus.ThreadMode
|
||||
import org.parceler.Parcels
|
||||
import java.io.IOException
|
||||
import java.util.Collections
|
||||
import java.util.Locale
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -120,7 +121,6 @@ class ContactsActivity :
|
|||
private var existingParticipants: List<String>? = null
|
||||
private var isAddingParticipantsView = false
|
||||
private var conversationToken: String? = null
|
||||
private var contactsBottomDialog: ContactsBottomDialog? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
@ -160,12 +160,8 @@ class ContactsActivity :
|
|||
toggleConversationPrivacyLayout(!isPublicCall)
|
||||
}
|
||||
if (isAddingParticipantsView) {
|
||||
binding.joinConversationViaLink.visibility = View.GONE
|
||||
binding.callHeaderLayout.visibility = View.GONE
|
||||
} else {
|
||||
binding.joinConversationViaLink.setOnClickListener {
|
||||
joinConversationViaLink()
|
||||
}
|
||||
binding.listOpenConversations.setOnClickListener {
|
||||
listOpenConversations()
|
||||
}
|
||||
|
@ -228,7 +224,7 @@ class ContactsActivity :
|
|||
override fun onPrepareOptionsMenu(menu: Menu): Boolean {
|
||||
super.onPrepareOptionsMenu(menu)
|
||||
if (searchItem != null) {
|
||||
binding?.titleTextView?.let {
|
||||
binding.titleTextView.let {
|
||||
viewThemeUtils.platform.colorToolbarMenuIcon(
|
||||
it.context,
|
||||
searchItem!!
|
||||
|
@ -270,7 +266,10 @@ class ContactsActivity :
|
|||
}
|
||||
|
||||
private fun selectionDone() {
|
||||
if (!isAddingParticipantsView) {
|
||||
if (isAddingParticipantsView) {
|
||||
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
|
||||
|
@ -290,8 +289,9 @@ class ContactsActivity :
|
|||
}
|
||||
}
|
||||
createRoom(roomType, sourceType, userId)
|
||||
|
||||
// if there are more participants to add, ask for roomName and add them one after another
|
||||
} else {
|
||||
val bundle = Bundle()
|
||||
val roomType: Conversation.ConversationType = if (isPublicCall) {
|
||||
Conversation.ConversationType.ROOM_PUBLIC_CALL
|
||||
} else {
|
||||
|
@ -301,16 +301,19 @@ class ContactsActivity :
|
|||
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)
|
||||
|
||||
val createConversationDialog = CreateConversationDialogFragment.newInstance(
|
||||
userIdsArray,
|
||||
groupIdsArray,
|
||||
emailsArray,
|
||||
circleIdsArray,
|
||||
Parcels.wrap(roomType)
|
||||
)
|
||||
createConversationDialog.show(
|
||||
supportFragmentManager,
|
||||
TAG
|
||||
)
|
||||
}
|
||||
} else {
|
||||
addParticipantsToConversation()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -373,9 +376,40 @@ class ContactsActivity :
|
|||
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) {
|
||||
|
@ -401,14 +435,14 @@ class ContactsActivity :
|
|||
private fun fetchData() {
|
||||
dispose(null)
|
||||
alreadyFetching = true
|
||||
userHeaderItems = HashMap<String, GenericTextHeaderItem>()
|
||||
val query = adapter!!.getFilter(String::class.java) as String?
|
||||
userHeaderItems = HashMap()
|
||||
val query = adapter!!.getFilter(String::class.java)
|
||||
val retrofitBucket: RetrofitBucket =
|
||||
ApiUtils.getRetrofitBucketForContactsSearchFor14(currentUser!!.baseUrl, query)
|
||||
val modifiedQueryMap: HashMap<String, Any?> = HashMap<String, Any?>(retrofitBucket.queryMap)
|
||||
modifiedQueryMap.put("limit", CONTACTS_BATCH_SIZE)
|
||||
val modifiedQueryMap: HashMap<String, Any?> = HashMap(retrofitBucket.queryMap)
|
||||
modifiedQueryMap["limit"] = CONTACTS_BATCH_SIZE
|
||||
if (isAddingParticipantsView) {
|
||||
modifiedQueryMap.put("itemId", conversationToken)
|
||||
modifiedQueryMap["itemId"] = conversationToken
|
||||
}
|
||||
val shareTypesList: ArrayList<String> = ArrayList()
|
||||
// users
|
||||
|
@ -426,7 +460,7 @@ class ContactsActivity :
|
|||
// circles
|
||||
shareTypesList.add("7")
|
||||
}
|
||||
modifiedQueryMap.put("shareTypes[]", shareTypesList)
|
||||
modifiedQueryMap["shareTypes[]"] = shareTypesList
|
||||
ncApi.getContactsWithSearchParam(
|
||||
credentials,
|
||||
retrofitBucket.url,
|
||||
|
@ -444,7 +478,7 @@ class ContactsActivity :
|
|||
override fun onNext(responseBody: ResponseBody) {
|
||||
val newUserItemList = processAutocompleteUserList(responseBody)
|
||||
|
||||
userHeaderItems = HashMap<String, GenericTextHeaderItem>()
|
||||
userHeaderItems = HashMap()
|
||||
contactItems!!.addAll(newUserItemList)
|
||||
|
||||
sortUserItems(newUserItemList)
|
||||
|
@ -455,16 +489,16 @@ class ContactsActivity :
|
|||
adapter?.filterItems()
|
||||
}
|
||||
|
||||
binding?.controllerGenericRv?.swipeRefreshLayout?.isRefreshing = false
|
||||
binding.controllerGenericRv.swipeRefreshLayout.isRefreshing = false
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
binding?.controllerGenericRv?.swipeRefreshLayout?.isRefreshing = false
|
||||
binding.controllerGenericRv.swipeRefreshLayout.isRefreshing = false
|
||||
dispose(contactsQueryDisposable)
|
||||
}
|
||||
|
||||
override fun onComplete() {
|
||||
binding?.controllerGenericRv?.swipeRefreshLayout?.isRefreshing = false
|
||||
binding.controllerGenericRv.swipeRefreshLayout.isRefreshing = false
|
||||
dispose(contactsQueryDisposable)
|
||||
alreadyFetching = false
|
||||
disengageProgressBar()
|
||||
|
@ -474,18 +508,18 @@ class ContactsActivity :
|
|||
|
||||
private fun processAutocompleteUserList(responseBody: ResponseBody): MutableList<AbstractFlexibleItem<*>> {
|
||||
try {
|
||||
val autocompleteOverall: AutocompleteOverall = LoganSquare.parse<AutocompleteOverall>(
|
||||
val autocompleteOverall: AutocompleteOverall = LoganSquare.parse(
|
||||
responseBody.string(),
|
||||
AutocompleteOverall::class.java
|
||||
)
|
||||
val autocompleteUsersList: ArrayList<AutocompleteUser> = ArrayList<AutocompleteUser>()
|
||||
val autocompleteUsersList: ArrayList<AutocompleteUser> = ArrayList()
|
||||
autocompleteUsersList.addAll(autocompleteOverall.ocs!!.data!!)
|
||||
return processAutocompleteUserList(autocompleteUsersList)
|
||||
} catch (ioe: IOException) {
|
||||
Log.e(TAG, "Parsing response body failed while getting contacts", ioe)
|
||||
}
|
||||
|
||||
return ArrayList<AbstractFlexibleItem<*>>()
|
||||
return ArrayList()
|
||||
}
|
||||
|
||||
private fun processAutocompleteUserList(
|
||||
|
@ -493,7 +527,7 @@ class ContactsActivity :
|
|||
): MutableList<AbstractFlexibleItem<*>> {
|
||||
var participant: Participant
|
||||
val actorTypeConverter = EnumActorTypeConverter()
|
||||
val newUserItemList: MutableList<AbstractFlexibleItem<*>> = ArrayList<AbstractFlexibleItem<*>>()
|
||||
val newUserItemList: MutableList<AbstractFlexibleItem<*>> = ArrayList()
|
||||
for (autocompleteUser in autocompleteUsersList) {
|
||||
if (autocompleteUser.id != null &&
|
||||
autocompleteUser.id != currentUser!!.userId &&
|
||||
|
@ -529,7 +563,7 @@ class ContactsActivity :
|
|||
resources!!.getString(R.string.nc_circles)
|
||||
}
|
||||
else -> {
|
||||
participant.displayName!!.substring(0, 1).toUpperCase(Locale.getDefault())
|
||||
participant.displayName!!.substring(0, 1).uppercase(Locale.getDefault())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -547,23 +581,22 @@ class ContactsActivity :
|
|||
return participant
|
||||
}
|
||||
|
||||
@Suppress("LongMethod")
|
||||
private fun sortUserItems(newUserItemList: MutableList<AbstractFlexibleItem<*>>) {
|
||||
Collections.sort(
|
||||
newUserItemList,
|
||||
{ o1: AbstractFlexibleItem<*>, o2: AbstractFlexibleItem<*> ->
|
||||
newUserItemList.sortWith sort@{ o1: AbstractFlexibleItem<*>, o2: AbstractFlexibleItem<*> ->
|
||||
val firstName: String = if (o1 is ContactItem) {
|
||||
(o1 as ContactItem).model.displayName!!
|
||||
o1.model.displayName!!
|
||||
} else {
|
||||
(o1 as GenericTextHeaderItem).model
|
||||
}
|
||||
val secondName: String = if (o2 is ContactItem) {
|
||||
(o2 as ContactItem).model.displayName!!
|
||||
o2.model.displayName!!
|
||||
} else {
|
||||
(o2 as GenericTextHeaderItem).model
|
||||
}
|
||||
if (o1 is ContactItem && o2 is ContactItem) {
|
||||
val firstSource: String = (o1 as ContactItem).model.source!!
|
||||
val secondSource: String = (o2 as ContactItem).model.source!!
|
||||
val firstSource: String = o1.model.source!!
|
||||
val secondSource: String = o2.model.source!!
|
||||
if (firstSource == secondSource) {
|
||||
return@sort firstName.compareTo(secondName, ignoreCase = true)
|
||||
}
|
||||
|
@ -594,29 +627,26 @@ class ContactsActivity :
|
|||
}
|
||||
firstName.compareTo(secondName, ignoreCase = true)
|
||||
}
|
||||
)
|
||||
|
||||
Collections.sort(
|
||||
contactItems
|
||||
) { o1: AbstractFlexibleItem<*>, o2: AbstractFlexibleItem<*> ->
|
||||
contactItems?.sortWith sort@{ o1: AbstractFlexibleItem<*>, o2: AbstractFlexibleItem<*> ->
|
||||
val firstName: String = if (o1 is ContactItem) {
|
||||
(o1 as ContactItem).model.displayName!!
|
||||
o1.model.displayName!!
|
||||
} else {
|
||||
(o1 as GenericTextHeaderItem).model
|
||||
}
|
||||
val secondName: String = if (o2 is ContactItem) {
|
||||
(o2 as ContactItem).model.displayName!!
|
||||
o2.model.displayName!!
|
||||
} else {
|
||||
(o2 as GenericTextHeaderItem).model
|
||||
}
|
||||
if (o1 is ContactItem && o2 is ContactItem) {
|
||||
if ("groups" == (o1 as ContactItem).model.source &&
|
||||
"groups" == (o2 as ContactItem).model.source
|
||||
if ("groups" == o1.model.source &&
|
||||
"groups" == o2.model.source
|
||||
) {
|
||||
return@sort firstName.compareTo(secondName, ignoreCase = true)
|
||||
} else if ("groups" == (o1 as ContactItem).model.source) {
|
||||
} else if ("groups" == o1.model.source) {
|
||||
return@sort -1
|
||||
} else if ("groups" == (o2 as ContactItem).model.source) {
|
||||
} else if ("groups" == o2.model.source) {
|
||||
return@sort 1
|
||||
}
|
||||
}
|
||||
|
@ -626,24 +656,19 @@ class ContactsActivity :
|
|||
|
||||
private fun prepareViews() {
|
||||
layoutManager = SmoothScrollLinearLayoutManager(this)
|
||||
binding?.controllerGenericRv?.recyclerView?.layoutManager = layoutManager
|
||||
binding?.controllerGenericRv?.recyclerView?.setHasFixedSize(true)
|
||||
binding?.controllerGenericRv?.recyclerView?.adapter = adapter
|
||||
binding?.controllerGenericRv?.swipeRefreshLayout?.setOnRefreshListener { fetchData() }
|
||||
binding.controllerGenericRv.recyclerView.layoutManager = layoutManager
|
||||
binding.controllerGenericRv.recyclerView.setHasFixedSize(true)
|
||||
binding.controllerGenericRv.recyclerView.adapter = adapter
|
||||
binding.controllerGenericRv.swipeRefreshLayout.setOnRefreshListener { fetchData() }
|
||||
|
||||
binding?.controllerGenericRv?.let { viewThemeUtils.androidx.themeSwipeRefreshLayout(it.swipeRefreshLayout) }
|
||||
binding.controllerGenericRv.let { viewThemeUtils.androidx.themeSwipeRefreshLayout(it.swipeRefreshLayout) }
|
||||
|
||||
binding.listOpenConversationsImage.background?.setColorFilter(
|
||||
ResourcesCompat.getColor(resources!!, R.color.colorBackgroundDarker, null),
|
||||
PorterDuff.Mode.SRC_IN
|
||||
)
|
||||
|
||||
binding.joinConversationViaLinkImage.background?.setColorFilter(
|
||||
ResourcesCompat.getColor(resources!!, R.color.colorBackgroundDarker, null),
|
||||
PorterDuff.Mode.SRC_IN
|
||||
)
|
||||
|
||||
binding?.let {
|
||||
binding.let {
|
||||
viewThemeUtils.platform.colorImageViewBackgroundAndIcon(it.publicCallLink)
|
||||
}
|
||||
disengageProgressBar()
|
||||
|
@ -655,7 +680,6 @@ class ContactsActivity :
|
|||
binding.controllerGenericRv.root.visibility = View.VISIBLE
|
||||
if (isNewConversationView) {
|
||||
binding.callHeaderLayout.visibility = View.VISIBLE
|
||||
binding.joinConversationViaLink.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -708,21 +732,12 @@ class ContactsActivity :
|
|||
}
|
||||
}
|
||||
|
||||
private fun prepareAndShowBottomSheetWithBundle(bundle: Bundle) {
|
||||
// 11: create conversation-enter name for new conversation
|
||||
// 10: get&join room when enter link
|
||||
contactsBottomDialog = ContactsBottomDialog(this, bundle)
|
||||
contactsBottomDialog?.show()
|
||||
}
|
||||
|
||||
@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)
|
||||
|
||||
contactsBottomDialog?.dismiss()
|
||||
}
|
||||
|
||||
override fun onItemClick(view: View, position: Int): Boolean {
|
||||
|
@ -730,7 +745,6 @@ class ContactsActivity :
|
|||
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))
|
||||
}
|
||||
}
|
||||
|
@ -840,12 +854,6 @@ class ContactsActivity :
|
|||
return "groups" == contactItem.model.source && participant.selected && 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 listOpenConversations() {
|
||||
val intent = Intent(this, ListOpenConversationsActivity::class.java)
|
||||
startActivity(intent)
|
||||
|
@ -854,30 +862,11 @@ class ContactsActivity :
|
|||
private fun toggleCallHeader() {
|
||||
toggleConversationPrivacyLayout(isPublicCall)
|
||||
isPublicCall = !isPublicCall
|
||||
toggleConversationViaLinkVisibility(isPublicCall)
|
||||
|
||||
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.calculatedActorType == Participant.ActorType.GROUPS &&
|
||||
internalParticipant.selected
|
||||
) {
|
||||
internalParticipant.selected = false
|
||||
selectedGroupIds.remove(internalParticipant.calculatedActorId)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun enableContactForNonPublicCall() {
|
||||
for (i in 0 until adapter!!.itemCount) {
|
||||
if (adapter?.getItem(i) is ContactItem) {
|
||||
|
@ -891,20 +880,12 @@ class ContactsActivity :
|
|||
|
||||
private fun toggleConversationPrivacyLayout(showInitialLayout: Boolean) {
|
||||
if (showInitialLayout) {
|
||||
binding.initialRelativeLayout.visibility = View.VISIBLE
|
||||
binding.secondaryRelativeLayout.visibility = View.GONE
|
||||
binding.publicConversationCreate.visibility = View.VISIBLE
|
||||
binding.publicConversationInfo.visibility = View.GONE
|
||||
} else {
|
||||
binding.initialRelativeLayout.visibility = View.GONE
|
||||
binding.secondaryRelativeLayout.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
|
||||
private fun toggleConversationViaLinkVisibility(isPublicCall: Boolean) {
|
||||
if (isPublicCall) {
|
||||
binding.joinConversationViaLink.visibility = View.GONE
|
||||
updateGroupParticipantSelection()
|
||||
} else {
|
||||
binding.joinConversationViaLink.visibility = View.VISIBLE
|
||||
binding.publicConversationCreate.visibility = View.GONE
|
||||
binding.publicConversationInfo.visibility = View.VISIBLE
|
||||
binding.listOpenConversations.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
/*
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Marcel Hibbe
|
||||
* Copyright (C) 2022 Marcel Hibbe <dev@mhibbe.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.bottomsheet
|
||||
|
||||
enum class ConversationOperationEnum {
|
||||
OPS_CODE_RENAME_ROOM,
|
||||
OPS_CODE_GET_AND_JOIN_ROOM,
|
||||
OPS_CODE_INVITE_USERS,
|
||||
OPS_CODE_MARK_AS_READ,
|
||||
OPS_CODE_MARK_AS_UNREAD,
|
||||
OPS_CODE_REMOVE_FAVORITE,
|
||||
OPS_CODE_ADD_FAVORITE,
|
||||
OPS_CODE_JOIN_ROOM
|
||||
}
|
|
@ -1,377 +0,0 @@
|
|||
/*
|
||||
* 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.bottomsheet
|
||||
|
||||
import android.content.res.ColorStateList
|
||||
import android.os.Bundle
|
||||
import android.text.Editable
|
||||
import android.text.InputType
|
||||
import android.text.TextUtils
|
||||
import android.text.TextWatcher
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import android.view.inputmethod.EditorInfo
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import autodagger.AutoInjector
|
||||
import com.bluelinelabs.conductor.RouterTransaction
|
||||
import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler
|
||||
import com.google.android.material.textfield.TextInputLayout
|
||||
import com.nextcloud.android.common.ui.theme.utils.ColorRole
|
||||
import com.nextcloud.talk.R
|
||||
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.BaseController
|
||||
import com.nextcloud.talk.controllers.util.viewBinding
|
||||
import com.nextcloud.talk.data.user.model.User
|
||||
import com.nextcloud.talk.databinding.ControllerEntryMenuBinding
|
||||
import com.nextcloud.talk.models.json.conversations.Conversation
|
||||
import com.nextcloud.talk.models.json.conversations.RoomOverall
|
||||
import com.nextcloud.talk.users.UserManager
|
||||
import com.nextcloud.talk.utils.ApiUtils
|
||||
import com.nextcloud.talk.utils.UriUtils
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_TOKEN
|
||||
import com.nextcloud.talk.utils.singletons.ApplicationWideMessageHolder
|
||||
import com.vanniktech.emoji.EmojiPopup
|
||||
import io.reactivex.Observer
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import okhttp3.internal.immutableListOf
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import javax.inject.Inject
|
||||
|
||||
@AutoInjector(NextcloudTalkApplication::class)
|
||||
class EntryMenuController(args: Bundle) :
|
||||
BaseController(
|
||||
R.layout.controller_entry_menu,
|
||||
args
|
||||
) {
|
||||
private val binding: ControllerEntryMenuBinding? by viewBinding(ControllerEntryMenuBinding::bind)
|
||||
|
||||
@Inject
|
||||
lateinit var ncApi: NcApi
|
||||
|
||||
@Inject
|
||||
lateinit var eventBus: EventBus
|
||||
|
||||
@Inject
|
||||
lateinit var userManager: UserManager
|
||||
|
||||
private val operation: ConversationOperationEnum
|
||||
private var conversation: Conversation? = null
|
||||
private val packageName: String
|
||||
private val name: String
|
||||
|
||||
private var emojiPopup: EmojiPopup? = null
|
||||
private val originalBundle: Bundle
|
||||
private var currentUser: User? = null
|
||||
private val roomToken: String
|
||||
|
||||
override val appBarLayoutType: AppBarLayoutType
|
||||
get() = AppBarLayoutType.SEARCH_BAR
|
||||
|
||||
override fun onAttach(view: View) {
|
||||
super.onAttach(view)
|
||||
if (ApplicationWideMessageHolder.MessageType.CALL_PASSWORD_WRONG ==
|
||||
ApplicationWideMessageHolder.getInstance().messageType
|
||||
) {
|
||||
binding?.textInputLayout?.error = resources?.getString(R.string.nc_wrong_password)
|
||||
ApplicationWideMessageHolder.getInstance().messageType = null
|
||||
if (binding?.okButton?.isEnabled == true) {
|
||||
binding?.okButton?.isEnabled = false
|
||||
binding?.okButton?.alpha = OPACITY_BUTTON_DISABLED
|
||||
}
|
||||
}
|
||||
|
||||
emojiPopup = binding?.let {
|
||||
EmojiPopup(
|
||||
rootView = view,
|
||||
editText = it.textEdit,
|
||||
onEmojiPopupShownListener = {
|
||||
viewThemeUtils.platform.colorImageView(it.smileyButton, ColorRole.PRIMARY)
|
||||
},
|
||||
onEmojiPopupDismissListener = {
|
||||
it.smileyButton.imageTintList = ColorStateList.valueOf(
|
||||
ResourcesCompat.getColor(
|
||||
resources!!,
|
||||
R.color.medium_emphasis_text,
|
||||
context.theme
|
||||
)
|
||||
)
|
||||
},
|
||||
onEmojiClickListener = {
|
||||
binding?.textEdit?.editableText?.append(" ")
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onViewBound(view: View) {
|
||||
super.onViewBound(view)
|
||||
|
||||
currentUser = userManager.currentUser.blockingGet()
|
||||
|
||||
if (operation == ConversationOperationEnum.OPS_CODE_GET_AND_JOIN_ROOM) {
|
||||
binding?.textEdit?.inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_URI
|
||||
|
||||
textEditAddChangedListener()
|
||||
|
||||
binding?.textInputLayout?.let { viewThemeUtils.material.colorTextInputLayout(it) }
|
||||
binding?.okButton?.let { viewThemeUtils.material.colorMaterialButtonText(it) }
|
||||
|
||||
binding?.textInputLayout?.hint = resources!!.getString(R.string.nc_conversation_link)
|
||||
|
||||
binding?.textInputLayout?.requestFocus()
|
||||
|
||||
binding?.smileyButton?.setOnClickListener { onSmileyClick() }
|
||||
binding?.okButton?.setOnClickListener { onOkButtonClick() }
|
||||
} else if (operation == ConversationOperationEnum.OPS_CODE_INVITE_USERS) {
|
||||
binding?.textEdit?.inputType = InputType.TYPE_CLASS_TEXT
|
||||
|
||||
textEditAddChangedListener()
|
||||
binding?.smileyButton?.visibility = View.VISIBLE
|
||||
|
||||
binding?.textInputLayout?.let { viewThemeUtils.material.colorTextInputLayout(it) }
|
||||
binding?.okButton?.let { viewThemeUtils.material.colorMaterialButtonText(it) }
|
||||
|
||||
binding?.textInputLayout?.requestFocus()
|
||||
|
||||
binding?.smileyButton?.setOnClickListener { onSmileyClick() }
|
||||
binding?.okButton?.setOnClickListener { onOkButtonClick() }
|
||||
} else {
|
||||
val apiVersion = ApiUtils.getConversationApiVersion(currentUser, intArrayOf(ApiUtils.APIv4, ApiUtils.APIv1))
|
||||
ncApi.getRoom(
|
||||
ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token),
|
||||
ApiUtils.getUrlForRoom(apiVersion, currentUser!!.baseUrl, roomToken)
|
||||
)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.retry(1)
|
||||
.subscribe(object : Observer<RoomOverall> {
|
||||
override fun onSubscribe(d: Disposable) {
|
||||
// unused atm
|
||||
}
|
||||
|
||||
@Suppress("Detekt.LongMethod")
|
||||
override fun onNext(roomOverall: RoomOverall) {
|
||||
conversation = roomOverall.ocs!!.data
|
||||
|
||||
if (conversation != null && operation === ConversationOperationEnum.OPS_CODE_RENAME_ROOM) {
|
||||
binding?.textEdit?.setText(conversation!!.name)
|
||||
}
|
||||
|
||||
binding?.textEdit?.setOnEditorActionListener { v, actionId, event ->
|
||||
@Suppress("IMPLICIT_BOXING_IN_IDENTITY_EQUALS")
|
||||
if (actionId === EditorInfo.IME_ACTION_DONE && binding?.okButton?.isEnabled == true) {
|
||||
binding?.okButton?.callOnClick()
|
||||
return@setOnEditorActionListener true
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
textEditAddChangedListener()
|
||||
|
||||
var labelText = ""
|
||||
when (operation) {
|
||||
ConversationOperationEnum.OPS_CODE_RENAME_ROOM -> {
|
||||
labelText = resources!!.getString(R.string.nc_call_name)
|
||||
binding?.textEdit?.inputType = InputType.TYPE_CLASS_TEXT
|
||||
binding?.smileyButton?.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
ConversationOperationEnum.OPS_CODE_JOIN_ROOM -> {
|
||||
// 99 is joining a conversation via password
|
||||
labelText = resources!!.getString(R.string.nc_password)
|
||||
binding?.textEdit?.inputType =
|
||||
InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_PASSWORD
|
||||
}
|
||||
|
||||
else -> {
|
||||
}
|
||||
}
|
||||
if (PASSWORD_ENTRY_OPERATIONS.contains(operation)) {
|
||||
binding?.textInputLayout?.endIconMode = TextInputLayout.END_ICON_PASSWORD_TOGGLE
|
||||
} else {
|
||||
binding?.textInputLayout?.endIconMode = TextInputLayout.END_ICON_NONE
|
||||
}
|
||||
|
||||
binding?.textInputLayout?.let { viewThemeUtils.material.colorTextInputLayout(it) }
|
||||
binding?.okButton?.let { viewThemeUtils.material.colorMaterialButtonText(it) }
|
||||
|
||||
binding?.textInputLayout?.hint = labelText
|
||||
binding?.textInputLayout?.requestFocus()
|
||||
|
||||
binding?.smileyButton?.setOnClickListener { onSmileyClick() }
|
||||
binding?.okButton?.setOnClickListener { onOkButtonClick() }
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
Log.e("EntryMenuController", "error")
|
||||
}
|
||||
|
||||
override fun onComplete() {
|
||||
// unused atm
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private fun textEditAddChangedListener() {
|
||||
binding?.textEdit?.addTextChangedListener(object : TextWatcher {
|
||||
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
|
||||
// unused atm
|
||||
}
|
||||
|
||||
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
|
||||
// unused atm
|
||||
}
|
||||
|
||||
override fun afterTextChanged(s: Editable) {
|
||||
if (!TextUtils.isEmpty(s)) {
|
||||
if (operation === ConversationOperationEnum.OPS_CODE_RENAME_ROOM) {
|
||||
if (conversation!!.name == null || !conversation!!.name.equals(s.toString())) {
|
||||
if (!binding?.okButton?.isEnabled!!) {
|
||||
binding?.okButton?.isEnabled = true
|
||||
binding?.okButton?.alpha = OPACITY_ENABLED
|
||||
}
|
||||
binding?.textInputLayout?.isErrorEnabled = false
|
||||
} else {
|
||||
if (binding?.okButton?.isEnabled == true) {
|
||||
binding?.okButton?.isEnabled = false
|
||||
binding?.okButton?.alpha = OPACITY_DISABLED
|
||||
}
|
||||
binding?.textInputLayout?.error = resources?.getString(R.string.nc_call_name_is_same)
|
||||
}
|
||||
} else if (operation !== ConversationOperationEnum.OPS_CODE_GET_AND_JOIN_ROOM) {
|
||||
if (!binding?.okButton?.isEnabled!!) {
|
||||
binding?.okButton?.isEnabled = true
|
||||
binding?.okButton?.alpha = OPACITY_ENABLED
|
||||
}
|
||||
binding?.textInputLayout?.isErrorEnabled = false
|
||||
} else if (
|
||||
UriUtils.hasHttpProtocolPrefixed(binding?.textEdit?.text.toString()) &&
|
||||
binding?.textEdit?.text.toString().contains("/call/")
|
||||
) {
|
||||
if (!binding?.okButton?.isEnabled!!) {
|
||||
binding?.okButton?.isEnabled = true
|
||||
binding?.okButton?.alpha = OPACITY_ENABLED
|
||||
}
|
||||
binding?.textInputLayout?.isErrorEnabled = false
|
||||
} else {
|
||||
if (binding?.okButton?.isEnabled == true) {
|
||||
binding?.okButton?.isEnabled = false
|
||||
binding?.okButton?.alpha = OPACITY_DISABLED
|
||||
}
|
||||
binding?.textInputLayout?.error = resources?.getString(R.string.nc_wrong_link)
|
||||
}
|
||||
} else {
|
||||
if (binding?.okButton?.isEnabled == true) {
|
||||
binding?.okButton?.isEnabled = false
|
||||
binding?.okButton?.alpha = OPACITY_DISABLED
|
||||
}
|
||||
binding?.textInputLayout?.isErrorEnabled = false
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun onSmileyClick() {
|
||||
emojiPopup?.toggle()
|
||||
}
|
||||
|
||||
private fun onOkButtonClick() {
|
||||
if (operation === ConversationOperationEnum.OPS_CODE_JOIN_ROOM) {
|
||||
joinRoom()
|
||||
} else if (
|
||||
operation !== ConversationOperationEnum.OPS_CODE_GET_AND_JOIN_ROOM &&
|
||||
operation !== ConversationOperationEnum.OPS_CODE_INVITE_USERS
|
||||
) {
|
||||
val bundle = Bundle()
|
||||
conversation!!.name = binding?.textEdit?.text.toString()
|
||||
bundle.putString(BundleKeys.KEY_ROOM_TOKEN, roomToken)
|
||||
bundle.putString(BundleKeys.KEY_NEW_ROOM_NAME, binding?.textEdit?.text.toString())
|
||||
bundle.putSerializable(BundleKeys.KEY_OPERATION_CODE, operation)
|
||||
router.pushController(
|
||||
RouterTransaction.with(OperationsMenuController(bundle))
|
||||
.pushChangeHandler(HorizontalChangeHandler())
|
||||
.popChangeHandler(HorizontalChangeHandler())
|
||||
)
|
||||
} else if (operation !== ConversationOperationEnum.OPS_CODE_INVITE_USERS) {
|
||||
val bundle = Bundle()
|
||||
bundle.putSerializable(BundleKeys.KEY_OPERATION_CODE, operation)
|
||||
bundle.putString(BundleKeys.KEY_CALL_URL, binding?.textEdit?.text.toString())
|
||||
router.pushController(
|
||||
RouterTransaction.with(OperationsMenuController(bundle))
|
||||
.pushChangeHandler(HorizontalChangeHandler())
|
||||
.popChangeHandler(HorizontalChangeHandler())
|
||||
)
|
||||
} else if (operation === ConversationOperationEnum.OPS_CODE_INVITE_USERS) {
|
||||
originalBundle.putString(BundleKeys.KEY_CONVERSATION_NAME, binding?.textEdit?.text.toString())
|
||||
router.pushController(
|
||||
RouterTransaction.with(
|
||||
OperationsMenuController(
|
||||
originalBundle
|
||||
)
|
||||
)
|
||||
.pushChangeHandler(HorizontalChangeHandler())
|
||||
.popChangeHandler(HorizontalChangeHandler())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun joinRoom() {
|
||||
val bundle = Bundle()
|
||||
bundle.putString(BundleKeys.KEY_ROOM_TOKEN, roomToken)
|
||||
bundle.putString(BundleKeys.KEY_CONVERSATION_PASSWORD, binding?.textEdit?.text.toString())
|
||||
bundle.putSerializable(BundleKeys.KEY_OPERATION_CODE, operation)
|
||||
router.pushController(
|
||||
RouterTransaction.with(OperationsMenuController(bundle))
|
||||
.pushChangeHandler(HorizontalChangeHandler())
|
||||
.popChangeHandler(HorizontalChangeHandler())
|
||||
)
|
||||
}
|
||||
|
||||
init {
|
||||
sharedApplication!!.componentApplication.inject(this)
|
||||
|
||||
originalBundle = args
|
||||
operation = args.getSerializable(BundleKeys.KEY_OPERATION_CODE) as ConversationOperationEnum
|
||||
roomToken = args.getString(KEY_ROOM_TOKEN, "")
|
||||
name = args.getString(BundleKeys.KEY_APP_ITEM_NAME, "")
|
||||
packageName = args.getString(BundleKeys.KEY_APP_ITEM_PACKAGE_NAME, "")
|
||||
// callUrl = args.getString(BundleKeys.KEY_CALL_URL, "")
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val PASSWORD_ENTRY_OPERATIONS: List<ConversationOperationEnum> =
|
||||
immutableListOf(
|
||||
ConversationOperationEnum.OPS_CODE_JOIN_ROOM
|
||||
)
|
||||
const val OPACITY_DISABLED = 0.38f
|
||||
const val OPACITY_BUTTON_DISABLED = 0.7f
|
||||
const val OPACITY_ENABLED = 1.0f
|
||||
}
|
||||
}
|
|
@ -1,753 +0,0 @@
|
|||
/*
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Mario Danic
|
||||
* @author Andy Scherzinger
|
||||
* Copyright (C) 2022 Andy Scherzinger <info@andy-scherzinger.de>
|
||||
* Copyright (C) 2017 Mario Danic <mario@lovelyhq.com>
|
||||
*
|
||||
* 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.bottomsheet
|
||||
|
||||
import android.os.Bundle
|
||||
import android.text.TextUtils
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import autodagger.AutoInjector
|
||||
import com.bluelinelabs.conductor.RouterTransaction
|
||||
import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler
|
||||
import com.nextcloud.android.common.ui.theme.utils.ColorRole
|
||||
import com.nextcloud.talk.R
|
||||
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.BaseController
|
||||
import com.nextcloud.talk.controllers.util.viewBinding
|
||||
import com.nextcloud.talk.data.user.model.User
|
||||
import com.nextcloud.talk.databinding.ControllerOperationsMenuBinding
|
||||
import com.nextcloud.talk.events.ConversationsListFetchDataEvent
|
||||
import com.nextcloud.talk.events.OpenConversationEvent
|
||||
import com.nextcloud.talk.models.RetrofitBucket
|
||||
import com.nextcloud.talk.models.json.capabilities.CapabilitiesOverall
|
||||
import com.nextcloud.talk.models.json.conversations.Conversation
|
||||
import com.nextcloud.talk.models.json.conversations.Conversation.ConversationType
|
||||
import com.nextcloud.talk.models.json.conversations.RoomOverall
|
||||
import com.nextcloud.talk.models.json.generic.GenericOverall
|
||||
import com.nextcloud.talk.models.json.participants.AddParticipantOverall
|
||||
import com.nextcloud.talk.users.UserManager
|
||||
import com.nextcloud.talk.utils.ApiUtils
|
||||
import com.nextcloud.talk.utils.DisplayUtils
|
||||
import com.nextcloud.talk.utils.NoSupportedApiException
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_CALL_URL
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_CONVERSATION_NAME
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_CONVERSATION_PASSWORD
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_CONVERSATION_TYPE
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_INVITED_GROUP
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_INVITED_PARTICIPANTS
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_NEW_ROOM_NAME
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_OPERATION_CODE
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_ID
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_TOKEN
|
||||
import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew
|
||||
import com.nextcloud.talk.utils.singletons.ApplicationWideMessageHolder
|
||||
import io.reactivex.Observer
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.parceler.Parcels
|
||||
import retrofit2.HttpException
|
||||
import java.io.IOException
|
||||
import java.util.Collections
|
||||
import javax.inject.Inject
|
||||
|
||||
@AutoInjector(NextcloudTalkApplication::class)
|
||||
class OperationsMenuController(args: Bundle) : BaseController(
|
||||
R.layout.controller_operations_menu,
|
||||
args
|
||||
) {
|
||||
private val binding: ControllerOperationsMenuBinding? by viewBinding(ControllerOperationsMenuBinding::bind)
|
||||
|
||||
@Inject
|
||||
lateinit var ncApi: NcApi
|
||||
|
||||
@Inject
|
||||
lateinit var userManager: UserManager
|
||||
|
||||
@Inject
|
||||
lateinit var eventBus: EventBus
|
||||
|
||||
private val operation: ConversationOperationEnum?
|
||||
private var conversation: Conversation? = null
|
||||
private var currentUser: User? = null
|
||||
private val callPassword: String
|
||||
private val callUrl: String
|
||||
private var roomToken: String
|
||||
private val roomNameNew: String
|
||||
private var baseUrl: String? = null
|
||||
private var conversationToken: String? = null
|
||||
private var disposable: Disposable? = null
|
||||
private var conversationType: ConversationType? = null
|
||||
private var invitedUsers: ArrayList<String>? = ArrayList()
|
||||
private var invitedGroups: ArrayList<String>? = ArrayList()
|
||||
private var credentials: String? = null
|
||||
private val conversationName: String
|
||||
|
||||
override val appBarLayoutType: AppBarLayoutType
|
||||
get() = AppBarLayoutType.SEARCH_BAR
|
||||
|
||||
override fun onViewBound(view: View) {
|
||||
super.onViewBound(view)
|
||||
sharedApplication!!.componentApplication.inject(this)
|
||||
currentUser = userManager.currentUser.blockingGet()
|
||||
|
||||
binding?.progressBar?.let { viewThemeUtils.platform.colorCircularProgressBar(it, ColorRole.PRIMARY) }
|
||||
|
||||
if (!TextUtils.isEmpty(callUrl) && callUrl.contains("/call")) {
|
||||
conversationToken = callUrl.substring(callUrl.lastIndexOf("/") + 1)
|
||||
if (callUrl.contains("/index.php")) {
|
||||
baseUrl = callUrl.substring(0, callUrl.indexOf("/index.php"))
|
||||
} else {
|
||||
baseUrl = callUrl.substring(0, callUrl.indexOf("/call"))
|
||||
}
|
||||
}
|
||||
|
||||
if (roomToken.isNotEmpty()) {
|
||||
val apiVersion = apiVersion()
|
||||
ncApi.getRoom(
|
||||
credentials,
|
||||
ApiUtils.getUrlForRoom(apiVersion, currentUser!!.baseUrl, roomToken)
|
||||
)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.retry(1)
|
||||
.subscribe(object : Observer<RoomOverall> {
|
||||
override fun onSubscribe(d: Disposable) {
|
||||
disposable = d
|
||||
}
|
||||
|
||||
override fun onNext(roomOverall: RoomOverall) {
|
||||
conversation = roomOverall.ocs!!.data
|
||||
|
||||
if (!TextUtils.isEmpty(baseUrl) && baseUrl != currentUser!!.baseUrl) {
|
||||
fetchCapabilitiesForGuest()
|
||||
} else {
|
||||
processOperation()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
Log.e(TAG, "error while fetching room", e)
|
||||
}
|
||||
|
||||
override fun onComplete() {
|
||||
// unused atm
|
||||
}
|
||||
})
|
||||
} else {
|
||||
processOperation()
|
||||
}
|
||||
}
|
||||
|
||||
private fun fetchCapabilitiesForGuest() {
|
||||
ncApi.getCapabilities(null, ApiUtils.getUrlForCapabilities(baseUrl))
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(object : Observer<CapabilitiesOverall> {
|
||||
override fun onSubscribe(d: Disposable) {
|
||||
// unused atm
|
||||
}
|
||||
|
||||
override fun onNext(capabilitiesOverall: CapabilitiesOverall) {
|
||||
currentUser = User()
|
||||
currentUser!!.baseUrl = baseUrl
|
||||
currentUser!!.userId = "?"
|
||||
try {
|
||||
currentUser!!.capabilities = capabilitiesOverall.ocs!!.data!!.capabilities
|
||||
} catch (e: IOException) {
|
||||
Log.e("OperationsMenu", "Failed to serialize capabilities")
|
||||
}
|
||||
try {
|
||||
checkCapabilities(currentUser!!)
|
||||
processOperation()
|
||||
} catch (e: NoSupportedApiException) {
|
||||
showResultImage(everythingOK = false, isGuestSupportError = false)
|
||||
Log.d(TAG, "No supported server version found", e)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
showResultImage(everythingOK = false, isGuestSupportError = false)
|
||||
Log.e(TAG, "Error fetching capabilities for guest", e)
|
||||
}
|
||||
|
||||
override fun onComplete() {
|
||||
// unused atm
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@Suppress("Detekt.ComplexMethod")
|
||||
private fun processOperation() {
|
||||
if (currentUser == null) {
|
||||
showResultImage(everythingOK = false, isGuestSupportError = true)
|
||||
Log.e(TAG, "Ended up in processOperation without a valid currentUser")
|
||||
return
|
||||
}
|
||||
credentials = ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token)
|
||||
when (operation) {
|
||||
ConversationOperationEnum.OPS_CODE_RENAME_ROOM -> operationRenameRoom()
|
||||
ConversationOperationEnum.OPS_CODE_GET_AND_JOIN_ROOM -> operationGetAndJoinRoom()
|
||||
ConversationOperationEnum.OPS_CODE_INVITE_USERS -> operationInviteUsers()
|
||||
ConversationOperationEnum.OPS_CODE_MARK_AS_READ -> operationMarkAsRead()
|
||||
ConversationOperationEnum.OPS_CODE_MARK_AS_UNREAD -> operationMarkAsUnread()
|
||||
ConversationOperationEnum.OPS_CODE_REMOVE_FAVORITE,
|
||||
ConversationOperationEnum.OPS_CODE_ADD_FAVORITE -> operationToggleFavorite()
|
||||
|
||||
ConversationOperationEnum.OPS_CODE_JOIN_ROOM -> operationJoinRoom()
|
||||
else -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun apiVersion(): Int {
|
||||
return ApiUtils.getConversationApiVersion(currentUser, intArrayOf(ApiUtils.APIv4, ApiUtils.APIv1))
|
||||
}
|
||||
|
||||
private fun chatApiVersion(): Int {
|
||||
return ApiUtils.getChatApiVersion(currentUser, intArrayOf(ApiUtils.APIv1))
|
||||
}
|
||||
|
||||
private fun operationJoinRoom() {
|
||||
ncApi.joinRoom(
|
||||
credentials,
|
||||
ApiUtils.getUrlForParticipantsActive(
|
||||
apiVersion(),
|
||||
baseUrl,
|
||||
conversationToken
|
||||
),
|
||||
callPassword
|
||||
)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.retry(1)
|
||||
.subscribe(RoomOperationsObserver())
|
||||
}
|
||||
|
||||
private fun operationMarkAsRead() {
|
||||
ncApi.setChatReadMarker(
|
||||
credentials,
|
||||
ApiUtils.getUrlForChatReadMarker(
|
||||
chatApiVersion(),
|
||||
currentUser!!.baseUrl,
|
||||
conversation!!.token
|
||||
),
|
||||
conversation!!.lastMessage!!.jsonMessageId
|
||||
)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.retry(1)
|
||||
.subscribe(GenericOperationsObserver())
|
||||
}
|
||||
|
||||
private fun operationMarkAsUnread() {
|
||||
ncApi.markRoomAsUnread(
|
||||
credentials,
|
||||
ApiUtils.getUrlForChatReadMarker(
|
||||
chatApiVersion(),
|
||||
currentUser!!.baseUrl,
|
||||
conversation!!.token
|
||||
)
|
||||
)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.retry(1)
|
||||
.subscribe(GenericOperationsObserver())
|
||||
}
|
||||
|
||||
private fun operationRenameRoom() {
|
||||
ncApi.renameRoom(
|
||||
credentials,
|
||||
ApiUtils.getUrlForRoom(
|
||||
apiVersion(),
|
||||
currentUser!!.baseUrl,
|
||||
conversation!!.token
|
||||
),
|
||||
roomNameNew
|
||||
)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.retry(1)
|
||||
.subscribe(GenericOperationsObserver())
|
||||
}
|
||||
|
||||
private fun operationToggleFavorite() {
|
||||
val genericOperationsObserver = GenericOperationsObserver()
|
||||
val apiVersion = apiVersion()
|
||||
if (operation === ConversationOperationEnum.OPS_CODE_REMOVE_FAVORITE) {
|
||||
ncApi.removeConversationFromFavorites(
|
||||
credentials,
|
||||
ApiUtils.getUrlForRoomFavorite(
|
||||
apiVersion,
|
||||
currentUser!!.baseUrl,
|
||||
conversation!!.token
|
||||
)
|
||||
)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.retry(1)
|
||||
.subscribe(genericOperationsObserver)
|
||||
} else {
|
||||
ncApi.addConversationToFavorites(
|
||||
credentials,
|
||||
ApiUtils.getUrlForRoomFavorite(
|
||||
apiVersion,
|
||||
currentUser!!.baseUrl,
|
||||
conversation!!.token
|
||||
)
|
||||
)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.retry(1)
|
||||
.subscribe(genericOperationsObserver)
|
||||
}
|
||||
}
|
||||
|
||||
private fun operationInviteUsers() {
|
||||
val retrofitBucket: RetrofitBucket
|
||||
val apiVersion = apiVersion()
|
||||
var invite: String? = null
|
||||
if (invitedGroups!!.size > 0) {
|
||||
invite = invitedGroups!![0]
|
||||
}
|
||||
retrofitBucket = if (conversationType == ConversationType.ROOM_PUBLIC_CALL) {
|
||||
ApiUtils.getRetrofitBucketForCreateRoom(
|
||||
apiVersion,
|
||||
currentUser!!.baseUrl,
|
||||
"3",
|
||||
null,
|
||||
invite,
|
||||
conversationName
|
||||
)
|
||||
} else {
|
||||
ApiUtils.getRetrofitBucketForCreateRoom(
|
||||
apiVersion,
|
||||
currentUser!!.baseUrl,
|
||||
"2",
|
||||
null,
|
||||
invite,
|
||||
conversationName
|
||||
)
|
||||
}
|
||||
ncApi.createRoom(credentials, retrofitBucket.url, retrofitBucket.queryMap)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.retry(1)
|
||||
.subscribe(object : Observer<RoomOverall> {
|
||||
override fun onSubscribe(d: Disposable) {
|
||||
// unused atm
|
||||
}
|
||||
|
||||
override fun onNext(roomOverall: RoomOverall) {
|
||||
conversation = roomOverall.ocs!!.data
|
||||
ncApi.getRoom(
|
||||
credentials,
|
||||
ApiUtils.getUrlForRoom(
|
||||
apiVersion,
|
||||
currentUser!!.baseUrl,
|
||||
conversation!!.token
|
||||
)
|
||||
)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(object : Observer<RoomOverall> {
|
||||
override fun onSubscribe(d: Disposable) {
|
||||
// unused atm
|
||||
}
|
||||
|
||||
override fun onNext(
|
||||
roomOverall: RoomOverall
|
||||
) {
|
||||
conversation = roomOverall.ocs!!.data
|
||||
inviteUsersToAConversation()
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
showResultImage(everythingOK = false, isGuestSupportError = false)
|
||||
dispose()
|
||||
}
|
||||
|
||||
override fun onComplete() {
|
||||
// unused atm
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
showResultImage(everythingOK = false, isGuestSupportError = false)
|
||||
dispose()
|
||||
}
|
||||
|
||||
override fun onComplete() {
|
||||
dispose()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun operationGetAndJoinRoom() {
|
||||
val apiVersion = apiVersion()
|
||||
ncApi.getRoom(
|
||||
credentials,
|
||||
ApiUtils.getUrlForRoom(apiVersion, baseUrl, conversationToken)
|
||||
)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.retry(1)
|
||||
.subscribe(object : Observer<RoomOverall> {
|
||||
override fun onSubscribe(d: Disposable) {
|
||||
disposable = d
|
||||
}
|
||||
|
||||
override fun onNext(roomOverall: RoomOverall) {
|
||||
conversation = roomOverall.ocs!!.data
|
||||
if (conversation!!.hasPassword && conversation!!.isGuest) {
|
||||
eventBus.post(ConversationsListFetchDataEvent())
|
||||
val bundle = Bundle()
|
||||
bundle.putString(BundleKeys.KEY_ROOM_TOKEN, roomToken)
|
||||
bundle.putSerializable(KEY_OPERATION_CODE, ConversationOperationEnum.OPS_CODE_JOIN_ROOM)
|
||||
router.pushController(
|
||||
RouterTransaction.with(EntryMenuController(bundle))
|
||||
.pushChangeHandler(HorizontalChangeHandler())
|
||||
.popChangeHandler(HorizontalChangeHandler())
|
||||
)
|
||||
} else if (conversation!!.isGuest) {
|
||||
ncApi.joinRoom(
|
||||
credentials,
|
||||
ApiUtils.getUrlForParticipantsActive(
|
||||
apiVersion,
|
||||
baseUrl,
|
||||
conversationToken
|
||||
),
|
||||
null
|
||||
)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(object : Observer<RoomOverall> {
|
||||
override fun onSubscribe(d: Disposable) {
|
||||
// unused atm
|
||||
}
|
||||
|
||||
override fun onNext(roomOverall: RoomOverall) {
|
||||
conversation = roomOverall.ocs!!.data
|
||||
initiateConversation()
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
showResultImage(everythingOK = false, isGuestSupportError = false)
|
||||
dispose()
|
||||
}
|
||||
|
||||
override fun onComplete() {
|
||||
// unused atm
|
||||
}
|
||||
})
|
||||
} else {
|
||||
initiateConversation()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
showResultImage(everythingOK = false, isGuestSupportError = false)
|
||||
dispose()
|
||||
}
|
||||
|
||||
override fun onComplete() {
|
||||
dispose()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@Suppress("Detekt.TooGenericExceptionCaught")
|
||||
private fun showResultImage(everythingOK: Boolean, isGuestSupportError: Boolean) {
|
||||
try {
|
||||
binding?.progressBar?.visibility = View.GONE
|
||||
if (resources != null) {
|
||||
if (everythingOK) {
|
||||
binding?.resultImageView?.setImageDrawable(
|
||||
DisplayUtils.getTintedDrawable(
|
||||
resources,
|
||||
R.drawable.ic_check_circle_black_24dp,
|
||||
R.color.nc_darkGreen
|
||||
)
|
||||
)
|
||||
} else {
|
||||
binding?.resultImageView?.setImageDrawable(
|
||||
DisplayUtils.getTintedDrawable(
|
||||
resources,
|
||||
R.drawable.ic_cancel_black_24dp,
|
||||
R.color.nc_darkRed
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
binding?.resultImageView?.visibility = View.VISIBLE
|
||||
if (everythingOK) {
|
||||
binding?.resultTextView?.setText(R.string.nc_all_ok_operation)
|
||||
} else {
|
||||
binding?.resultTextView?.setTextColor(resources!!.getColor(R.color.nc_darkRed, null))
|
||||
binding?.resultTextView?.setText(R.string.nc_failed_to_perform_operation)
|
||||
}
|
||||
binding?.resultTextView?.visibility = View.VISIBLE
|
||||
if (everythingOK) {
|
||||
eventBus.post(ConversationsListFetchDataEvent())
|
||||
} else {
|
||||
binding?.resultImageView?.setImageDrawable(
|
||||
DisplayUtils.getTintedDrawable(
|
||||
resources,
|
||||
R.drawable.ic_cancel_black_24dp,
|
||||
R.color.nc_darkRed
|
||||
)
|
||||
)
|
||||
binding?.okButton?.setOnClickListener { v: View? -> eventBus.post(ConversationsListFetchDataEvent()) }
|
||||
binding?.okButton?.visibility = View.VISIBLE
|
||||
}
|
||||
} catch (npe: NullPointerException) {
|
||||
Log.i(TAG, "Controller already closed", npe)
|
||||
}
|
||||
}
|
||||
|
||||
private fun dispose() {
|
||||
if (disposable != null && !disposable!!.isDisposed) {
|
||||
disposable!!.dispose()
|
||||
}
|
||||
disposable = null
|
||||
}
|
||||
|
||||
public override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
dispose()
|
||||
}
|
||||
|
||||
@kotlin.Throws(NoSupportedApiException::class)
|
||||
private fun checkCapabilities(currentUser: User) {
|
||||
ApiUtils.getConversationApiVersion(currentUser, intArrayOf(ApiUtils.APIv4, 1))
|
||||
ApiUtils.getCallApiVersion(currentUser, intArrayOf(ApiUtils.APIv4, 1))
|
||||
ApiUtils.getChatApiVersion(currentUser, intArrayOf(1))
|
||||
ApiUtils.getSignalingApiVersion(currentUser, intArrayOf(ApiUtils.APIv3, 2, 1))
|
||||
}
|
||||
|
||||
private fun inviteUsersToAConversation() {
|
||||
val localInvitedUsers = invitedUsers
|
||||
val localInvitedGroups = invitedGroups
|
||||
if (localInvitedGroups!!.size > 0) {
|
||||
localInvitedGroups.removeAt(0)
|
||||
}
|
||||
val apiVersion = ApiUtils.getConversationApiVersion(currentUser, API_CONVERSATION_VERSIONS)
|
||||
if (localInvitedUsers!!.size > 0 || localInvitedGroups.size > 0 &&
|
||||
CapabilitiesUtilNew.hasSpreedFeatureCapability(currentUser, "invite-groups-and-mails")
|
||||
) {
|
||||
addGroupsToConversation(localInvitedUsers, localInvitedGroups, apiVersion)
|
||||
addUsersToConversation(localInvitedUsers, localInvitedGroups, apiVersion)
|
||||
} else {
|
||||
initiateConversation()
|
||||
}
|
||||
}
|
||||
|
||||
private fun addUsersToConversation(
|
||||
localInvitedUsers: ArrayList<String>?,
|
||||
localInvitedGroups: ArrayList<String>?,
|
||||
apiVersion: Int
|
||||
) {
|
||||
var retrofitBucket: RetrofitBucket
|
||||
for (i in localInvitedUsers!!.indices) {
|
||||
val userId = invitedUsers!![i]
|
||||
retrofitBucket = ApiUtils.getRetrofitBucketForAddParticipant(
|
||||
apiVersion,
|
||||
currentUser!!.baseUrl,
|
||||
conversation!!.token,
|
||||
userId
|
||||
)
|
||||
ncApi.addParticipant(credentials, retrofitBucket.url, retrofitBucket.queryMap)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.retry(1)
|
||||
.subscribe(object : Observer<AddParticipantOverall> {
|
||||
override fun onSubscribe(d: Disposable) {
|
||||
// unused atm
|
||||
}
|
||||
|
||||
override fun onNext(addParticipantOverall: AddParticipantOverall) {
|
||||
// unused atm
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
dispose()
|
||||
}
|
||||
|
||||
override fun onComplete() {
|
||||
Collections.synchronizedList(localInvitedUsers).remove(userId)
|
||||
if (localInvitedGroups!!.size == 0 && localInvitedUsers.size == 0) {
|
||||
initiateConversation()
|
||||
}
|
||||
dispose()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private fun addGroupsToConversation(
|
||||
localInvitedUsers: ArrayList<String>?,
|
||||
localInvitedGroups: ArrayList<String>?,
|
||||
apiVersion: Int
|
||||
) {
|
||||
var retrofitBucket: RetrofitBucket
|
||||
if (localInvitedGroups!!.size > 0 &&
|
||||
CapabilitiesUtilNew.hasSpreedFeatureCapability(currentUser, "invite-groups-and-mails")
|
||||
) {
|
||||
for (i in localInvitedGroups.indices) {
|
||||
val groupId = localInvitedGroups[i]
|
||||
retrofitBucket = ApiUtils.getRetrofitBucketForAddParticipantWithSource(
|
||||
apiVersion,
|
||||
currentUser!!.baseUrl,
|
||||
conversation!!.token,
|
||||
"groups",
|
||||
groupId
|
||||
)
|
||||
ncApi.addParticipant(credentials, retrofitBucket.url, retrofitBucket.queryMap)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.retry(1)
|
||||
.subscribe(object : Observer<AddParticipantOverall> {
|
||||
override fun onSubscribe(d: Disposable) {
|
||||
// unused atm
|
||||
}
|
||||
|
||||
override fun onNext(addParticipantOverall: AddParticipantOverall) {
|
||||
// unused atm
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
dispose()
|
||||
}
|
||||
|
||||
override fun onComplete() {
|
||||
Collections.synchronizedList(localInvitedGroups).remove(groupId)
|
||||
if (localInvitedGroups.size == 0 && localInvitedUsers!!.size == 0) {
|
||||
initiateConversation()
|
||||
}
|
||||
dispose()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun initiateConversation() {
|
||||
eventBus.post(ConversationsListFetchDataEvent())
|
||||
val bundle = Bundle()
|
||||
bundle.putString(KEY_ROOM_TOKEN, conversation!!.token)
|
||||
bundle.putString(KEY_ROOM_ID, conversation!!.roomId)
|
||||
bundle.putString(KEY_CONVERSATION_NAME, conversation!!.displayName)
|
||||
bundle.putString(KEY_CONVERSATION_PASSWORD, callPassword)
|
||||
eventBus.post(OpenConversationEvent(conversation, bundle))
|
||||
}
|
||||
|
||||
private fun handleObserverError(e: Throwable) {
|
||||
if (operation !== ConversationOperationEnum.OPS_CODE_JOIN_ROOM || e !is HttpException) {
|
||||
showResultImage(everythingOK = false, isGuestSupportError = false)
|
||||
} else {
|
||||
val response = e.response()
|
||||
if (response != null && response.code() == FORBIDDEN) {
|
||||
ApplicationWideMessageHolder.getInstance()
|
||||
.setMessageType(ApplicationWideMessageHolder.MessageType.CALL_PASSWORD_WRONG)
|
||||
router.popCurrentController()
|
||||
} else {
|
||||
showResultImage(everythingOK = false, isGuestSupportError = false)
|
||||
}
|
||||
}
|
||||
dispose()
|
||||
}
|
||||
|
||||
private inner class GenericOperationsObserver : Observer<GenericOverall> {
|
||||
override fun onSubscribe(d: Disposable) {
|
||||
disposable = d
|
||||
}
|
||||
|
||||
override fun onNext(genericOverall: GenericOverall) {
|
||||
if (operation !== ConversationOperationEnum.OPS_CODE_JOIN_ROOM) {
|
||||
showResultImage(everythingOK = true, isGuestSupportError = false)
|
||||
} else {
|
||||
throw IllegalArgumentException("Unsupported operation code observed!")
|
||||
}
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
handleObserverError(e)
|
||||
}
|
||||
|
||||
override fun onComplete() {
|
||||
dispose()
|
||||
}
|
||||
}
|
||||
|
||||
private inner class RoomOperationsObserver : Observer<RoomOverall> {
|
||||
override fun onSubscribe(d: Disposable) {
|
||||
disposable = d
|
||||
}
|
||||
|
||||
override fun onNext(roomOverall: RoomOverall) {
|
||||
conversation = roomOverall.ocs!!.data
|
||||
if (operation !== ConversationOperationEnum.OPS_CODE_JOIN_ROOM) {
|
||||
showResultImage(everythingOK = true, isGuestSupportError = false)
|
||||
} else {
|
||||
conversation = roomOverall.ocs!!.data
|
||||
initiateConversation()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
handleObserverError(e)
|
||||
}
|
||||
|
||||
override fun onComplete() {
|
||||
dispose()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "OperationsMenu"
|
||||
private const val FORBIDDEN = 403
|
||||
private val API_CONVERSATION_VERSIONS = intArrayOf(4, 1)
|
||||
}
|
||||
|
||||
init {
|
||||
operation = args.getSerializable(KEY_OPERATION_CODE) as ConversationOperationEnum?
|
||||
callPassword = args.getString(KEY_CONVERSATION_PASSWORD, "")
|
||||
callUrl = args.getString(KEY_CALL_URL, "")
|
||||
roomToken = args.getString(KEY_ROOM_TOKEN, "")
|
||||
roomNameNew = args.getString(KEY_NEW_ROOM_NAME, "")
|
||||
if (args.containsKey(KEY_INVITED_PARTICIPANTS)) {
|
||||
invitedUsers = args.getStringArrayList(KEY_INVITED_PARTICIPANTS)
|
||||
}
|
||||
if (args.containsKey(KEY_INVITED_GROUP)) {
|
||||
invitedGroups = args.getStringArrayList(KEY_INVITED_GROUP)
|
||||
}
|
||||
if (args.containsKey(KEY_CONVERSATION_TYPE)) {
|
||||
conversationType = Parcels.unwrap(args.getParcelable(KEY_CONVERSATION_TYPE))
|
||||
}
|
||||
conversationName = args.getString(KEY_CONVERSATION_NAME, "")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,310 @@
|
|||
/*
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Marcel Hibbe
|
||||
* Copyright (C) 2023 Marcel Hibbe <dev@mhibbe.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.conversation
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Dialog
|
||||
import android.content.Intent
|
||||
import android.content.res.ColorStateList
|
||||
import android.os.Bundle
|
||||
import android.os.Parcelable
|
||||
import android.text.Editable
|
||||
import android.text.TextUtils
|
||||
import android.text.TextWatcher
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.work.Data
|
||||
import androidx.work.OneTimeWorkRequest
|
||||
import androidx.work.WorkInfo
|
||||
import androidx.work.WorkManager
|
||||
import autodagger.AutoInjector
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.nextcloud.android.common.ui.theme.utils.ColorRole
|
||||
import com.nextcloud.talk.R
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication
|
||||
import com.nextcloud.talk.chat.ChatActivity
|
||||
import com.nextcloud.talk.conversation.viewmodel.ConversationViewModel
|
||||
import com.nextcloud.talk.databinding.DialogCreateConversationBinding
|
||||
import com.nextcloud.talk.jobs.AddParticipantsToConversation
|
||||
import com.nextcloud.talk.models.json.conversations.Conversation
|
||||
import com.nextcloud.talk.ui.theme.ViewThemeUtils
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys
|
||||
import com.nextcloud.talk.utils.database.user.CurrentUserProviderNew
|
||||
import com.vanniktech.emoji.EmojiPopup
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.parceler.Parcels
|
||||
import javax.inject.Inject
|
||||
|
||||
@AutoInjector(NextcloudTalkApplication::class)
|
||||
class CreateConversationDialogFragment : DialogFragment() {
|
||||
|
||||
@Inject
|
||||
lateinit var viewModelFactory: ViewModelProvider.Factory
|
||||
|
||||
@Inject
|
||||
lateinit var viewThemeUtils: ViewThemeUtils
|
||||
|
||||
@Inject
|
||||
lateinit var eventBus: EventBus
|
||||
|
||||
@Inject
|
||||
lateinit var currentUserProvider: CurrentUserProviderNew
|
||||
|
||||
private lateinit var binding: DialogCreateConversationBinding
|
||||
private lateinit var viewModel: ConversationViewModel
|
||||
|
||||
private var emojiPopup: EmojiPopup? = null
|
||||
|
||||
private var conversationType: Conversation.ConversationType? = null
|
||||
private var usersToInvite: ArrayList<String> = ArrayList()
|
||||
private var groupsToInvite: ArrayList<String> = ArrayList()
|
||||
private var emailsToInvite: ArrayList<String> = ArrayList()
|
||||
private var circlesToInvite: ArrayList<String> = ArrayList()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this)
|
||||
|
||||
viewModel = ViewModelProvider(this, viewModelFactory)[ConversationViewModel::class.java]
|
||||
|
||||
if (arguments?.containsKey(USERS_TO_INVITE) == true) {
|
||||
usersToInvite = arguments?.getStringArrayList(USERS_TO_INVITE)!!
|
||||
}
|
||||
if (arguments?.containsKey(GROUPS_TO_INVITE) == true) {
|
||||
groupsToInvite = arguments?.getStringArrayList(GROUPS_TO_INVITE)!!
|
||||
}
|
||||
if (arguments?.containsKey(EMAILS_TO_INVITE) == true) {
|
||||
emailsToInvite = arguments?.getStringArrayList(EMAILS_TO_INVITE)!!
|
||||
}
|
||||
if (arguments?.containsKey(CIRCLES_TO_INVITE) == true) {
|
||||
circlesToInvite = arguments?.getStringArrayList(CIRCLES_TO_INVITE)!!
|
||||
}
|
||||
if (arguments?.containsKey(KEY_CONVERSATION_TYPE) == true) {
|
||||
conversationType = Parcels.unwrap(arguments?.getParcelable(KEY_CONVERSATION_TYPE))
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("InflateParams")
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
binding = DialogCreateConversationBinding.inflate(LayoutInflater.from(context))
|
||||
|
||||
val dialogBuilder = MaterialAlertDialogBuilder(binding.root.context)
|
||||
.setTitle(resources.getString(R.string.nc_call_name))
|
||||
// listener is null for now to avoid closing after button was clicked.
|
||||
// listener is set later in onStart
|
||||
.setPositiveButton(R.string.nc_common_create, null)
|
||||
.setNegativeButton(R.string.nc_common_dismiss, null)
|
||||
.setView(binding.root)
|
||||
viewThemeUtils.dialog.colorMaterialAlertDialogBackground(binding.root.context, dialogBuilder)
|
||||
|
||||
return dialogBuilder.create()
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
setupListeners()
|
||||
setupStateObserver()
|
||||
|
||||
setupEmojiPopup()
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
|
||||
val positiveButton = (dialog as AlertDialog).getButton(AlertDialog.BUTTON_POSITIVE)
|
||||
positiveButton.isEnabled = false
|
||||
positiveButton.setOnClickListener {
|
||||
viewModel.createConversation(
|
||||
binding.textEdit.text.toString(),
|
||||
conversationType
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupEmojiPopup() {
|
||||
emojiPopup = binding.let {
|
||||
EmojiPopup(
|
||||
rootView = requireView(),
|
||||
editText = it.textEdit,
|
||||
onEmojiPopupShownListener = {
|
||||
viewThemeUtils.platform.colorImageView(it.smileyButton, ColorRole.PRIMARY)
|
||||
},
|
||||
onEmojiPopupDismissListener = {
|
||||
it.smileyButton.imageTintList = ColorStateList.valueOf(
|
||||
ResourcesCompat.getColor(
|
||||
resources,
|
||||
R.color.medium_emphasis_text,
|
||||
context?.theme
|
||||
)
|
||||
)
|
||||
},
|
||||
onEmojiClickListener = {
|
||||
binding.textEdit.editableText?.append(" ")
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupListeners() {
|
||||
binding.smileyButton.setOnClickListener { emojiPopup?.toggle() }
|
||||
binding.textEdit.addTextChangedListener(object : TextWatcher {
|
||||
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
|
||||
// unused atm
|
||||
}
|
||||
|
||||
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
|
||||
// unused atm
|
||||
}
|
||||
|
||||
override fun afterTextChanged(s: Editable) {
|
||||
val positiveButton = (dialog as AlertDialog).getButton(AlertDialog.BUTTON_POSITIVE)
|
||||
|
||||
if (!TextUtils.isEmpty(s)) {
|
||||
if (!positiveButton.isEnabled) {
|
||||
positiveButton.isEnabled = true
|
||||
}
|
||||
} else {
|
||||
if (positiveButton.isEnabled) {
|
||||
positiveButton.isEnabled = false
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun setupStateObserver() {
|
||||
viewModel.viewState.observe(viewLifecycleOwner) { state ->
|
||||
when (state) {
|
||||
is ConversationViewModel.InitialState -> {}
|
||||
is ConversationViewModel.CreatingState -> {}
|
||||
is ConversationViewModel.CreatingSuccessState -> addParticipants(state.roomToken)
|
||||
is ConversationViewModel.CreatingFailedState -> {
|
||||
Log.e(TAG, "Failed to create conversation")
|
||||
showError()
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun addParticipants(roomToken: String) {
|
||||
val data = Data.Builder()
|
||||
data.putLong(BundleKeys.KEY_INTERNAL_USER_ID, currentUserProvider.currentUser.blockingGet().id!!)
|
||||
data.putString(BundleKeys.KEY_TOKEN, roomToken)
|
||||
data.putStringArray(BundleKeys.KEY_SELECTED_USERS, usersToInvite.toTypedArray())
|
||||
data.putStringArray(BundleKeys.KEY_SELECTED_GROUPS, groupsToInvite.toTypedArray())
|
||||
data.putStringArray(BundleKeys.KEY_SELECTED_EMAILS, emailsToInvite.toTypedArray())
|
||||
data.putStringArray(BundleKeys.KEY_SELECTED_CIRCLES, circlesToInvite.toTypedArray())
|
||||
|
||||
val addParticipantsToConversationWorker: OneTimeWorkRequest = OneTimeWorkRequest.Builder(
|
||||
AddParticipantsToConversation::class.java
|
||||
)
|
||||
.setInputData(data.build())
|
||||
.build()
|
||||
|
||||
WorkManager.getInstance(requireContext()).enqueue(addParticipantsToConversationWorker)
|
||||
|
||||
WorkManager.getInstance(requireContext()).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")
|
||||
initiateConversation(roomToken)
|
||||
}
|
||||
|
||||
WorkInfo.State.FAILED -> {
|
||||
Log.e(TAG, "failed to AddParticipantsToConversation")
|
||||
showError()
|
||||
}
|
||||
|
||||
else -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun initiateConversation(roomToken: String) {
|
||||
val bundle = Bundle()
|
||||
bundle.putString(BundleKeys.KEY_ROOM_TOKEN, roomToken)
|
||||
|
||||
val chatIntent = Intent(context, ChatActivity::class.java)
|
||||
chatIntent.putExtras(bundle)
|
||||
chatIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||
startActivity(chatIntent)
|
||||
|
||||
dismiss()
|
||||
}
|
||||
|
||||
private fun showError() {
|
||||
dismiss()
|
||||
Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show()
|
||||
}
|
||||
|
||||
/**
|
||||
* Fragment creator
|
||||
*/
|
||||
companion object {
|
||||
private val TAG = CreateConversationDialogFragment::class.java.simpleName
|
||||
private const val USERS_TO_INVITE = "usersToInvite"
|
||||
private const val GROUPS_TO_INVITE = "groupsToInvite"
|
||||
private const val EMAILS_TO_INVITE = "emailsToInvite"
|
||||
private const val CIRCLES_TO_INVITE = "circlesToInvite"
|
||||
private const val KEY_CONVERSATION_TYPE = "keyConversationType"
|
||||
|
||||
@JvmStatic
|
||||
fun newInstance(
|
||||
usersToInvite: ArrayList<String>?,
|
||||
groupsToInvite: ArrayList<String>?,
|
||||
emailsToInvite: ArrayList<String>?,
|
||||
circlesToInvite: ArrayList<String>?,
|
||||
conversationType: Parcelable
|
||||
): CreateConversationDialogFragment {
|
||||
val args = Bundle()
|
||||
args.putStringArrayList(USERS_TO_INVITE, usersToInvite)
|
||||
args.putStringArrayList(GROUPS_TO_INVITE, groupsToInvite)
|
||||
args.putStringArrayList(EMAILS_TO_INVITE, emailsToInvite)
|
||||
args.putStringArrayList(CIRCLES_TO_INVITE, circlesToInvite)
|
||||
args.putParcelable(KEY_CONVERSATION_TYPE, conversationType)
|
||||
val fragment = CreateConversationDialogFragment()
|
||||
fragment.arguments = args
|
||||
return fragment
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,229 @@
|
|||
/*
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Marcel Hibbe
|
||||
* Copyright (C) 2023 Marcel Hibbe <dev@mhibbe.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.conversation
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Dialog
|
||||
import android.content.res.ColorStateList
|
||||
import android.os.Bundle
|
||||
import android.text.Editable
|
||||
import android.text.TextUtils
|
||||
import android.text.TextWatcher
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import autodagger.AutoInjector
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.nextcloud.android.common.ui.theme.utils.ColorRole
|
||||
import com.nextcloud.talk.R
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication
|
||||
import com.nextcloud.talk.conversation.viewmodel.RenameConversationViewModel
|
||||
import com.nextcloud.talk.conversationlist.ConversationsListActivity
|
||||
import com.nextcloud.talk.databinding.DialogRenameConversationBinding
|
||||
import com.nextcloud.talk.events.ConversationsListFetchDataEvent
|
||||
import com.nextcloud.talk.ui.theme.ViewThemeUtils
|
||||
import com.vanniktech.emoji.EmojiPopup
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import javax.inject.Inject
|
||||
|
||||
@AutoInjector(NextcloudTalkApplication::class)
|
||||
class RenameConversationDialogFragment : DialogFragment() {
|
||||
|
||||
@Inject
|
||||
lateinit var viewModelFactory: ViewModelProvider.Factory
|
||||
|
||||
@Inject
|
||||
lateinit var viewThemeUtils: ViewThemeUtils
|
||||
|
||||
@Inject
|
||||
lateinit var eventBus: EventBus
|
||||
|
||||
private lateinit var binding: DialogRenameConversationBinding
|
||||
private lateinit var viewModel: RenameConversationViewModel
|
||||
|
||||
private var emojiPopup: EmojiPopup? = null
|
||||
|
||||
private var roomToken = ""
|
||||
private var initialName = ""
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this)
|
||||
|
||||
viewModel = ViewModelProvider(this, viewModelFactory)[RenameConversationViewModel::class.java]
|
||||
roomToken = arguments?.getString(KEY_ROOM_TOKEN)!!
|
||||
initialName = arguments?.getString(INITIAL_NAME)!!
|
||||
}
|
||||
|
||||
@SuppressLint("InflateParams")
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
binding = DialogRenameConversationBinding.inflate(LayoutInflater.from(context))
|
||||
|
||||
val dialogBuilder = MaterialAlertDialogBuilder(binding.root.context)
|
||||
.setTitle(resources.getString(R.string.nc_call_name))
|
||||
// listener is null for now to avoid closing after button was clicked.
|
||||
// listener is set later in onStart
|
||||
.setPositiveButton(R.string.nc_rename_confirm, null)
|
||||
.setNegativeButton(R.string.nc_common_dismiss, null)
|
||||
.setView(binding.root)
|
||||
viewThemeUtils.dialog.colorMaterialAlertDialogBackground(binding.root.context, dialogBuilder)
|
||||
|
||||
return dialogBuilder.create()
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
setupListeners()
|
||||
setupStateObserver()
|
||||
|
||||
setupEmojiPopup()
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
binding.textEdit.setText(initialName)
|
||||
|
||||
val positiveButton = (dialog as AlertDialog).getButton(AlertDialog.BUTTON_POSITIVE)
|
||||
positiveButton.isEnabled = false
|
||||
positiveButton.setOnClickListener {
|
||||
viewModel.renameConversation(roomToken, binding.textEdit.text.toString())
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupEmojiPopup() {
|
||||
emojiPopup = binding.let {
|
||||
EmojiPopup(
|
||||
rootView = requireView(),
|
||||
editText = it.textEdit,
|
||||
onEmojiPopupShownListener = {
|
||||
viewThemeUtils.platform.colorImageView(it.smileyButton, ColorRole.PRIMARY)
|
||||
},
|
||||
onEmojiPopupDismissListener = {
|
||||
it.smileyButton.imageTintList = ColorStateList.valueOf(
|
||||
ResourcesCompat.getColor(
|
||||
resources,
|
||||
R.color.medium_emphasis_text,
|
||||
context?.theme
|
||||
)
|
||||
)
|
||||
},
|
||||
onEmojiClickListener = {
|
||||
binding.textEdit.editableText?.append(" ")
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupListeners() {
|
||||
binding.smileyButton.setOnClickListener { emojiPopup?.toggle() }
|
||||
binding.textEdit.addTextChangedListener(object : TextWatcher {
|
||||
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
|
||||
// unused atm
|
||||
}
|
||||
|
||||
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
|
||||
// unused atm
|
||||
}
|
||||
|
||||
override fun afterTextChanged(s: Editable) {
|
||||
val positiveButton = (dialog as AlertDialog).getButton(AlertDialog.BUTTON_POSITIVE)
|
||||
|
||||
if (!TextUtils.isEmpty(s)) {
|
||||
if (initialName == s.toString()) {
|
||||
positiveButton.isEnabled = false
|
||||
} else if (!positiveButton.isEnabled) {
|
||||
positiveButton.isEnabled = true
|
||||
}
|
||||
} else {
|
||||
if (positiveButton.isEnabled) {
|
||||
positiveButton.isEnabled = false
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun setupStateObserver() {
|
||||
viewModel.viewState.observe(viewLifecycleOwner) { state ->
|
||||
when (state) {
|
||||
is RenameConversationViewModel.InitialState -> {}
|
||||
is RenameConversationViewModel.RenamingState -> {}
|
||||
is RenameConversationViewModel.RenamingSuccessState -> handleSuccess()
|
||||
is RenameConversationViewModel.RenamingFailedState -> showError()
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleSuccess() {
|
||||
eventBus.post(ConversationsListFetchDataEvent())
|
||||
|
||||
context?.resources?.let {
|
||||
String.format(
|
||||
it.getString(R.string.renamed_conversation),
|
||||
initialName
|
||||
)
|
||||
}?.let {
|
||||
(activity as ConversationsListActivity?)?.showSnackbar(
|
||||
it
|
||||
)
|
||||
}
|
||||
|
||||
dismiss()
|
||||
}
|
||||
|
||||
private fun showError() {
|
||||
dismiss()
|
||||
Log.e(TAG, "Failed to rename conversation")
|
||||
Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show()
|
||||
}
|
||||
|
||||
/**
|
||||
* Fragment creator
|
||||
*/
|
||||
companion object {
|
||||
private val TAG = RenameConversationDialogFragment::class.java.simpleName
|
||||
private const val KEY_ROOM_TOKEN = "keyRoomToken"
|
||||
private const val INITIAL_NAME = "initialName"
|
||||
|
||||
@JvmStatic
|
||||
fun newInstance(roomTokenParam: String, initialName: String): RenameConversationDialogFragment {
|
||||
val args = Bundle()
|
||||
args.putString(KEY_ROOM_TOKEN, roomTokenParam)
|
||||
args.putString(INITIAL_NAME, initialName)
|
||||
val fragment = RenameConversationDialogFragment()
|
||||
fragment.arguments = args
|
||||
return fragment
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Marcel Hibbe
|
||||
* Copyright (C) 2023 Marcel Hibbe <dev@mhibbe.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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.nextcloud.talk.conversation.repository
|
||||
|
||||
import com.nextcloud.talk.models.json.conversations.Conversation
|
||||
import com.nextcloud.talk.models.json.conversations.RoomOverall
|
||||
import com.nextcloud.talk.models.json.generic.GenericOverall
|
||||
import io.reactivex.Observable
|
||||
|
||||
interface ConversationRepository {
|
||||
|
||||
fun renameConversation(
|
||||
roomToken: String,
|
||||
roomNameNew: String
|
||||
): Observable<GenericOverall>
|
||||
|
||||
fun createConversation(
|
||||
roomName: String,
|
||||
conversationType: Conversation.ConversationType?
|
||||
): Observable<RoomOverall>
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Marcel Hibbe
|
||||
* Copyright (C) 2023 Marcel Hibbe <dev@mhibbe.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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.nextcloud.talk.conversation.repository
|
||||
|
||||
import com.nextcloud.talk.api.NcApi
|
||||
import com.nextcloud.talk.data.user.model.User
|
||||
import com.nextcloud.talk.models.RetrofitBucket
|
||||
import com.nextcloud.talk.models.json.conversations.Conversation
|
||||
import com.nextcloud.talk.models.json.conversations.RoomOverall
|
||||
import com.nextcloud.talk.models.json.generic.GenericOverall
|
||||
import com.nextcloud.talk.utils.ApiUtils
|
||||
import com.nextcloud.talk.utils.database.user.CurrentUserProviderNew
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
|
||||
class ConversationRepositoryImpl(private val ncApi: NcApi, currentUserProvider: CurrentUserProviderNew) :
|
||||
ConversationRepository {
|
||||
|
||||
val currentUser: User = currentUserProvider.currentUser.blockingGet()
|
||||
val credentials: String = ApiUtils.getCredentials(currentUser.username, currentUser.token)
|
||||
|
||||
override fun renameConversation(
|
||||
roomToken: String,
|
||||
roomNameNew: String
|
||||
): Observable<GenericOverall> {
|
||||
val apiVersion = ApiUtils.getConversationApiVersion(currentUser, intArrayOf(ApiUtils.APIv4, ApiUtils.APIv1))
|
||||
|
||||
return ncApi.renameRoom(
|
||||
credentials,
|
||||
ApiUtils.getUrlForRoom(
|
||||
apiVersion,
|
||||
currentUser.baseUrl,
|
||||
roomToken
|
||||
),
|
||||
roomNameNew
|
||||
)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.retry(API_RETRIES)
|
||||
}
|
||||
|
||||
override fun createConversation(
|
||||
roomName: String,
|
||||
conversationType: Conversation.ConversationType?
|
||||
): Observable<RoomOverall> {
|
||||
val apiVersion = ApiUtils.getConversationApiVersion(currentUser, intArrayOf(ApiUtils.APIv4, ApiUtils.APIv1))
|
||||
|
||||
val retrofitBucket: RetrofitBucket = if (conversationType == Conversation.ConversationType.ROOM_PUBLIC_CALL) {
|
||||
ApiUtils.getRetrofitBucketForCreateRoom(
|
||||
apiVersion,
|
||||
currentUser.baseUrl,
|
||||
ROOM_TYPE_PUBLIC,
|
||||
null,
|
||||
null,
|
||||
roomName
|
||||
)
|
||||
} else {
|
||||
ApiUtils.getRetrofitBucketForCreateRoom(
|
||||
apiVersion,
|
||||
currentUser.baseUrl,
|
||||
ROOM_TYPE_GROUP,
|
||||
null,
|
||||
null,
|
||||
roomName
|
||||
)
|
||||
}
|
||||
return ncApi.createRoom(credentials, retrofitBucket.url, retrofitBucket.queryMap)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.retry(1)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val ROOM_TYPE_PUBLIC = "3"
|
||||
private const val ROOM_TYPE_GROUP = "2"
|
||||
const val API_RETRIES: Long = 3
|
||||
}
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Marcel Hibbe
|
||||
* Copyright (C) 2023 Marcel Hibbe <dev@mhibbe.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.conversation.viewmodel
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.nextcloud.talk.conversation.repository.ConversationRepository
|
||||
import com.nextcloud.talk.models.json.conversations.Conversation
|
||||
import com.nextcloud.talk.models.json.conversations.RoomOverall
|
||||
import io.reactivex.Observer
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import javax.inject.Inject
|
||||
|
||||
class ConversationViewModel @Inject constructor(private val repository: ConversationRepository) : ViewModel() {
|
||||
|
||||
sealed class ViewState
|
||||
object InitialState : ViewState()
|
||||
|
||||
object CreatingState : ViewState()
|
||||
class CreatingSuccessState(val roomToken: String) : ViewState()
|
||||
object CreatingFailedState : ViewState()
|
||||
|
||||
private val _viewState: MutableLiveData<ViewState> = MutableLiveData(
|
||||
InitialState
|
||||
)
|
||||
val viewState: LiveData<ViewState>
|
||||
get() = _viewState
|
||||
|
||||
private var disposable: Disposable? = null
|
||||
|
||||
override fun onCleared() {
|
||||
super.onCleared()
|
||||
disposable?.dispose()
|
||||
}
|
||||
|
||||
fun createConversation(
|
||||
roomName: String,
|
||||
conversationType: Conversation.ConversationType?
|
||||
) {
|
||||
_viewState.value = CreatingState
|
||||
|
||||
repository.createConversation(
|
||||
roomName,
|
||||
conversationType
|
||||
)
|
||||
.doOnSubscribe { disposable = it }
|
||||
?.subscribeOn(Schedulers.io())
|
||||
?.observeOn(AndroidSchedulers.mainThread())
|
||||
?.subscribe(CreateConversationObserver())
|
||||
}
|
||||
|
||||
inner class CreateConversationObserver : Observer<RoomOverall> {
|
||||
override fun onSubscribe(d: Disposable) {
|
||||
// unused atm
|
||||
}
|
||||
|
||||
override fun onNext(roomOverall: RoomOverall) {
|
||||
val conversation = roomOverall.ocs!!.data
|
||||
_viewState.value = CreatingSuccessState(conversation?.token!!)
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
// dispose()
|
||||
}
|
||||
|
||||
override fun onComplete() {
|
||||
// dispose()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val TAG = ConversationViewModel::class.java.simpleName
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Marcel Hibbe
|
||||
* Copyright (C) 2023 Marcel Hibbe <dev@mhibbe.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.conversation.viewmodel
|
||||
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.nextcloud.talk.conversation.repository.ConversationRepository
|
||||
import com.nextcloud.talk.models.json.generic.GenericOverall
|
||||
import io.reactivex.Observer
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import javax.inject.Inject
|
||||
|
||||
class RenameConversationViewModel @Inject constructor(private val repository: ConversationRepository) : ViewModel() {
|
||||
|
||||
sealed class ViewState
|
||||
object InitialState : ViewState()
|
||||
object RenamingState : ViewState()
|
||||
object RenamingSuccessState : ViewState()
|
||||
object RenamingFailedState : ViewState()
|
||||
|
||||
private val _viewState: MutableLiveData<ViewState> = MutableLiveData(
|
||||
InitialState
|
||||
)
|
||||
val viewState: LiveData<ViewState>
|
||||
get() = _viewState
|
||||
|
||||
fun renameConversation(roomToken: String, roomNameNew: String) {
|
||||
_viewState.value = RenamingState
|
||||
|
||||
repository.renameConversation(
|
||||
roomToken,
|
||||
roomNameNew
|
||||
)
|
||||
.subscribeOn(Schedulers.io())
|
||||
?.observeOn(AndroidSchedulers.mainThread())
|
||||
?.subscribe(RenameConversationObserver())
|
||||
}
|
||||
|
||||
inner class RenameConversationObserver : Observer<GenericOverall> {
|
||||
|
||||
lateinit var genericOverall: GenericOverall
|
||||
|
||||
override fun onSubscribe(d: Disposable) = Unit
|
||||
|
||||
override fun onNext(response: GenericOverall) {
|
||||
genericOverall = response
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
Log.e(TAG, "Failed to rename conversation", e)
|
||||
_viewState.value = RenamingFailedState
|
||||
}
|
||||
|
||||
override fun onComplete() {
|
||||
_viewState.value = RenamingSuccessState
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val TAG = RenameConversationViewModel::class.java.simpleName
|
||||
}
|
||||
}
|
|
@ -58,6 +58,7 @@ import androidx.fragment.app.DialogFragment
|
|||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.work.Data
|
||||
import androidx.work.OneTimeWorkRequest
|
||||
import androidx.work.WorkInfo
|
||||
import androidx.work.WorkManager
|
||||
import autodagger.AutoInjector
|
||||
import coil.imageLoader
|
||||
|
@ -86,7 +87,6 @@ import com.nextcloud.talk.data.user.model.User
|
|||
import com.nextcloud.talk.databinding.ControllerConversationsRvBinding
|
||||
import com.nextcloud.talk.events.ConversationsListFetchDataEvent
|
||||
import com.nextcloud.talk.events.EventStatus
|
||||
import com.nextcloud.talk.interfaces.ConversationMenuInterface
|
||||
import com.nextcloud.talk.jobs.AccountRemovalWorker
|
||||
import com.nextcloud.talk.jobs.ContactAddressBookWorker.Companion.run
|
||||
import com.nextcloud.talk.jobs.DeleteConversationWorker
|
||||
|
@ -143,8 +143,7 @@ import javax.inject.Inject
|
|||
class ConversationsListActivity :
|
||||
BaseActivity(),
|
||||
FlexibleAdapter.OnItemClickListener,
|
||||
FlexibleAdapter.OnItemLongClickListener,
|
||||
ConversationMenuInterface {
|
||||
FlexibleAdapter.OnItemLongClickListener {
|
||||
|
||||
private lateinit var binding: ControllerConversationsRvBinding
|
||||
|
||||
|
@ -181,7 +180,6 @@ class ConversationsListActivity :
|
|||
private var credentials: String? = null
|
||||
private var adapterWasNull = true
|
||||
private var isRefreshing = false
|
||||
private var conversationMenuBundle: Bundle? = null
|
||||
private var showShareToScreen = false
|
||||
private var filesToShare: ArrayList<String>? = null
|
||||
private var selectedConversation: Conversation? = null
|
||||
|
@ -600,6 +598,9 @@ class ConversationsListActivity :
|
|||
searchItem!!.expandActionView()
|
||||
}
|
||||
|
||||
fun showSnackbar(text: String) {
|
||||
Snackbar.make(binding.root, text, Snackbar.LENGTH_LONG).show()
|
||||
}
|
||||
fun fetchRooms() {
|
||||
val includeStatus = isUserStatusAvailable(userManager.currentUser.blockingGet())
|
||||
|
||||
|
@ -999,7 +1000,7 @@ class ConversationsListActivity :
|
|||
|
||||
@SuppressLint("CheckResult") // handled by helper
|
||||
private fun loadMoreMessages() {
|
||||
binding?.swipeRefreshLayoutView?.isRefreshing = true
|
||||
binding.swipeRefreshLayoutView.isRefreshing = true
|
||||
val observable = searchHelper!!.loadMore()
|
||||
observable?.observeOn(AndroidSchedulers.mainThread())
|
||||
?.subscribe({ results: MessageSearchResults -> onMessageSearchResult(results) }) { throwable: Throwable ->
|
||||
|
@ -1302,12 +1303,8 @@ class ConversationsListActivity :
|
|||
}, BOTTOM_SHEET_DELAY)
|
||||
}
|
||||
|
||||
override fun showDeleteConversationDialog(bundle: Bundle) {
|
||||
conversationMenuBundle = bundle
|
||||
if (conversationMenuBundle != null &&
|
||||
isInternalUserEqualsCurrentUser(currentUser, conversationMenuBundle)
|
||||
) {
|
||||
binding?.floatingActionButton?.let {
|
||||
fun showDeleteConversationDialog(conversation: Conversation) {
|
||||
binding.floatingActionButton.let {
|
||||
val dialogBuilder = MaterialAlertDialogBuilder(it.context)
|
||||
.setIcon(
|
||||
viewThemeUtils.dialog
|
||||
|
@ -1316,17 +1313,9 @@ class ConversationsListActivity :
|
|||
.setTitle(R.string.nc_delete_call)
|
||||
.setMessage(R.string.nc_delete_conversation_more)
|
||||
.setPositiveButton(R.string.nc_delete) { _, _ ->
|
||||
val data = Data.Builder()
|
||||
data.putLong(
|
||||
KEY_INTERNAL_USER_ID,
|
||||
conversationMenuBundle!!.getLong(KEY_INTERNAL_USER_ID)
|
||||
)
|
||||
data.putString(KEY_ROOM_TOKEN, bundle.getString(KEY_ROOM_TOKEN))
|
||||
conversationMenuBundle = null
|
||||
deleteConversation(data.build())
|
||||
deleteConversation(conversation)
|
||||
}
|
||||
.setNegativeButton(R.string.nc_cancel) { _, _ ->
|
||||
conversationMenuBundle = null
|
||||
}
|
||||
|
||||
viewThemeUtils.dialog
|
||||
|
@ -1338,14 +1327,9 @@ class ConversationsListActivity :
|
|||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun isInternalUserEqualsCurrentUser(currentUser: User?, conversationMenuBundle: Bundle?): Boolean {
|
||||
return currentUser != null && conversationMenuBundle!!.getLong(KEY_INTERNAL_USER_ID) == currentUser.id
|
||||
}
|
||||
|
||||
private fun showUnauthorizedDialog() {
|
||||
binding?.floatingActionButton?.let {
|
||||
binding.floatingActionButton.let {
|
||||
val dialogBuilder = MaterialAlertDialogBuilder(it.context)
|
||||
.setIcon(
|
||||
viewThemeUtils.dialog.colorMaterialAlertDialogIcon(
|
||||
|
@ -1517,10 +1501,40 @@ class ConversationsListActivity :
|
|||
Runtime.getRuntime().exit(0)
|
||||
}
|
||||
|
||||
private fun deleteConversation(data: Data) {
|
||||
private fun deleteConversation(conversation: Conversation) {
|
||||
val data = Data.Builder()
|
||||
data.putLong(
|
||||
KEY_INTERNAL_USER_ID,
|
||||
currentUser?.id!!
|
||||
)
|
||||
data.putString(KEY_ROOM_TOKEN, conversation.token)
|
||||
|
||||
val deleteConversationWorker =
|
||||
OneTimeWorkRequest.Builder(DeleteConversationWorker::class.java).setInputData(data).build()
|
||||
OneTimeWorkRequest.Builder(DeleteConversationWorker::class.java).setInputData(data.build()).build()
|
||||
WorkManager.getInstance().enqueue(deleteConversationWorker)
|
||||
|
||||
WorkManager.getInstance(context).getWorkInfoByIdLiveData(deleteConversationWorker.id)
|
||||
.observeForever { workInfo: WorkInfo? ->
|
||||
if (workInfo != null) {
|
||||
when (workInfo.state) {
|
||||
WorkInfo.State.SUCCEEDED -> {
|
||||
showSnackbar(
|
||||
String.format(
|
||||
context.resources.getString(R.string.deleted_conversation),
|
||||
conversation.displayName
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
WorkInfo.State.FAILED -> {
|
||||
showSnackbar(context.resources.getString(R.string.nc_common_error_sorry))
|
||||
}
|
||||
|
||||
else -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun onMessageSearchResult(results: MessageSearchResults) {
|
||||
|
|
|
@ -28,6 +28,8 @@ package com.nextcloud.talk.dagger.modules
|
|||
import com.nextcloud.talk.api.NcApi
|
||||
import com.nextcloud.talk.chat.data.ChatRepository
|
||||
import com.nextcloud.talk.chat.data.ChatRepositoryImpl
|
||||
import com.nextcloud.talk.conversation.repository.ConversationRepository
|
||||
import com.nextcloud.talk.conversation.repository.ConversationRepositoryImpl
|
||||
import com.nextcloud.talk.conversationinfoedit.data.ConversationInfoEditRepository
|
||||
import com.nextcloud.talk.conversationinfoedit.data.ConversationInfoEditRepositoryImpl
|
||||
import com.nextcloud.talk.data.source.local.TalkDatabase
|
||||
|
@ -139,4 +141,10 @@ class RepositoryModule {
|
|||
ConversationInfoEditRepository {
|
||||
return ConversationInfoEditRepositoryImpl(ncApi, userProvider)
|
||||
}
|
||||
|
||||
@Provides
|
||||
fun provideConversationRepository(ncApi: NcApi, userProvider: CurrentUserProviderNew):
|
||||
ConversationRepository {
|
||||
return ConversationRepositoryImpl(ncApi, userProvider)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,8 @@ import androidx.lifecycle.ViewModel
|
|||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.nextcloud.talk.callnotification.viewmodel.CallNotificationViewModel
|
||||
import com.nextcloud.talk.chat.viewmodels.ChatViewModel
|
||||
import com.nextcloud.talk.conversation.viewmodel.ConversationViewModel
|
||||
import com.nextcloud.talk.conversation.viewmodel.RenameConversationViewModel
|
||||
import com.nextcloud.talk.conversationinfoedit.viewmodel.ConversationInfoEditViewModel
|
||||
import com.nextcloud.talk.messagesearch.MessageSearchViewModel
|
||||
import com.nextcloud.talk.openconversations.viewmodels.OpenConversationsViewModel
|
||||
|
@ -132,4 +134,14 @@ abstract class ViewModelModule {
|
|||
@IntoMap
|
||||
@ViewModelKey(ConversationInfoEditViewModel::class)
|
||||
abstract fun conversationInfoEditViewModel(viewModel: ConversationInfoEditViewModel): ViewModel
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ViewModelKey(RenameConversationViewModel::class)
|
||||
abstract fun renameConversationViewModel(viewModel: RenameConversationViewModel): ViewModel
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ViewModelKey(ConversationViewModel::class)
|
||||
abstract fun conversationViewModel(viewModel: ConversationViewModel): ViewModel
|
||||
}
|
||||
|
|
|
@ -25,11 +25,9 @@ import android.content.Context;
|
|||
import com.nextcloud.talk.api.NcApi;
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication;
|
||||
import com.nextcloud.talk.data.user.model.User;
|
||||
import com.nextcloud.talk.events.EventStatus;
|
||||
import com.nextcloud.talk.models.RetrofitBucket;
|
||||
import com.nextcloud.talk.users.UserManager;
|
||||
import com.nextcloud.talk.utils.ApiUtils;
|
||||
import com.nextcloud.talk.utils.UserIdUtils;
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys;
|
||||
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
|
@ -138,9 +136,6 @@ public class AddParticipantsToConversation extends Worker {
|
|||
}
|
||||
}
|
||||
|
||||
eventBus.post(new EventStatus(UserIdUtils.INSTANCE.getIdForUser(user),
|
||||
EventStatus.EventType.PARTICIPANTS_UPDATE,
|
||||
true));
|
||||
return Result.success();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,86 +0,0 @@
|
|||
/*
|
||||
* Nextcloud Talk application
|
||||
*
|
||||
* @author Marcel Hibbe
|
||||
* Copyright (C) 2022 Marcel Hibbe <dev@mhibbe.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.ui.dialog
|
||||
|
||||
import android.app.Activity
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import autodagger.AutoInjector
|
||||
import com.bluelinelabs.conductor.Conductor
|
||||
import com.bluelinelabs.conductor.Router
|
||||
import com.bluelinelabs.conductor.RouterTransaction
|
||||
import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import com.nextcloud.talk.R
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication
|
||||
import com.nextcloud.talk.controllers.bottomsheet.EntryMenuController
|
||||
import com.nextcloud.talk.databinding.DialogBottomContactsBinding
|
||||
import com.nextcloud.talk.ui.theme.ViewThemeUtils
|
||||
import javax.inject.Inject
|
||||
|
||||
@AutoInjector(NextcloudTalkApplication::class)
|
||||
class ContactsBottomDialog(
|
||||
val activity: Activity,
|
||||
val bundle: Bundle
|
||||
) : BottomSheetDialog(activity) {
|
||||
|
||||
@Inject
|
||||
lateinit var viewThemeUtils: ViewThemeUtils
|
||||
|
||||
private var dialogRouter: Router? = null
|
||||
|
||||
private lateinit var binding: DialogBottomContactsBinding
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
NextcloudTalkApplication.sharedApplication?.componentApplication?.inject(this)
|
||||
|
||||
binding = DialogBottomContactsBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
window?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
|
||||
|
||||
viewThemeUtils.platform.themeDialog(binding.root)
|
||||
executeEntryMenuController(bundle)
|
||||
}
|
||||
|
||||
private fun executeEntryMenuController(bundle: Bundle) {
|
||||
dialogRouter = Conductor.attachRouter(activity, binding.root, null)
|
||||
|
||||
dialogRouter!!.pushController(
|
||||
RouterTransaction.with(EntryMenuController(bundle))
|
||||
.pushChangeHandler(HorizontalChangeHandler())
|
||||
.popChangeHandler(HorizontalChangeHandler())
|
||||
)
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
val bottomSheet = findViewById<View>(R.id.design_bottom_sheet)
|
||||
val behavior = BottomSheetBehavior.from(bottomSheet as View)
|
||||
behavior.state = BottomSheetBehavior.STATE_COLLAPSED
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "ContactsBottomDialog"
|
||||
}
|
||||
}
|
|
@ -26,36 +26,31 @@ import android.view.View
|
|||
import android.view.ViewGroup
|
||||
import androidx.work.Data
|
||||
import androidx.work.OneTimeWorkRequest
|
||||
import androidx.work.WorkInfo
|
||||
import androidx.work.WorkManager
|
||||
import autodagger.AutoInjector
|
||||
import com.bluelinelabs.conductor.Conductor
|
||||
import com.bluelinelabs.conductor.Router
|
||||
import com.bluelinelabs.conductor.RouterTransaction
|
||||
import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import com.nextcloud.talk.R
|
||||
import com.nextcloud.talk.api.NcApi
|
||||
import com.nextcloud.talk.application.NextcloudTalkApplication
|
||||
import com.nextcloud.talk.controllers.bottomsheet.ConversationOperationEnum
|
||||
import com.nextcloud.talk.controllers.bottomsheet.ConversationOperationEnum.OPS_CODE_ADD_FAVORITE
|
||||
import com.nextcloud.talk.controllers.bottomsheet.ConversationOperationEnum.OPS_CODE_MARK_AS_READ
|
||||
import com.nextcloud.talk.controllers.bottomsheet.ConversationOperationEnum.OPS_CODE_MARK_AS_UNREAD
|
||||
import com.nextcloud.talk.controllers.bottomsheet.ConversationOperationEnum.OPS_CODE_REMOVE_FAVORITE
|
||||
import com.nextcloud.talk.controllers.bottomsheet.ConversationOperationEnum.OPS_CODE_RENAME_ROOM
|
||||
import com.nextcloud.talk.controllers.bottomsheet.EntryMenuController
|
||||
import com.nextcloud.talk.controllers.bottomsheet.OperationsMenuController
|
||||
import com.nextcloud.talk.conversation.RenameConversationDialogFragment
|
||||
import com.nextcloud.talk.conversationlist.ConversationsListActivity
|
||||
import com.nextcloud.talk.data.user.model.User
|
||||
import com.nextcloud.talk.databinding.DialogConversationOperationsBinding
|
||||
import com.nextcloud.talk.jobs.LeaveConversationWorker
|
||||
import com.nextcloud.talk.models.json.conversations.Conversation
|
||||
import com.nextcloud.talk.models.json.generic.GenericOverall
|
||||
import com.nextcloud.talk.ui.theme.ViewThemeUtils
|
||||
import com.nextcloud.talk.users.UserManager
|
||||
import com.nextcloud.talk.utils.ApiUtils
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_INTERNAL_USER_ID
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_OPERATION_CODE
|
||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_TOKEN
|
||||
import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew
|
||||
import io.reactivex.Observer
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import javax.inject.Inject
|
||||
|
||||
@AutoInjector(NextcloudTalkApplication::class)
|
||||
|
@ -65,8 +60,6 @@ class ConversationsListBottomDialog(
|
|||
val conversation: Conversation
|
||||
) : BottomSheetDialog(activity) {
|
||||
|
||||
private var dialogRouter: Router? = null
|
||||
|
||||
private lateinit var binding: DialogConversationOperationsBinding
|
||||
|
||||
@Inject
|
||||
|
@ -78,6 +71,8 @@ class ConversationsListBottomDialog(
|
|||
@Inject
|
||||
lateinit var userManager: UserManager
|
||||
|
||||
lateinit var credentials: String
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
NextcloudTalkApplication.sharedApplication?.componentApplication?.inject(this)
|
||||
|
@ -90,6 +85,15 @@ class ConversationsListBottomDialog(
|
|||
initHeaderDescription()
|
||||
initItemsVisibility()
|
||||
initClickListeners()
|
||||
|
||||
credentials = ApiUtils.getCredentials(currentUser.username, currentUser.token)
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
val bottomSheet = findViewById<View>(R.id.design_bottom_sheet)
|
||||
val behavior = BottomSheetBehavior.from(bottomSheet as View)
|
||||
behavior.state = BottomSheetBehavior.STATE_COLLAPSED
|
||||
}
|
||||
|
||||
private fun initHeaderDescription() {
|
||||
|
@ -104,18 +108,18 @@ class ConversationsListBottomDialog(
|
|||
val hasFavoritesCapability = CapabilitiesUtilNew.hasSpreedFeatureCapability(currentUser, "favorites")
|
||||
val canModerate = conversation.canModerate(currentUser)
|
||||
|
||||
binding.conversationOperationRemoveFavorite.visibility = setVisibleIf(
|
||||
binding.conversationRemoveFromFavorites.visibility = setVisibleIf(
|
||||
hasFavoritesCapability && conversation.favorite
|
||||
)
|
||||
binding.conversationOperationAddFavorite.visibility = setVisibleIf(
|
||||
binding.conversationAddToFavorites.visibility = setVisibleIf(
|
||||
hasFavoritesCapability && !conversation.favorite
|
||||
)
|
||||
|
||||
binding.conversationOperationMarkAsRead.visibility = setVisibleIf(
|
||||
binding.conversationMarkAsRead.visibility = setVisibleIf(
|
||||
conversation.unreadMessages > 0 && CapabilitiesUtilNew.canSetChatReadMarker(currentUser)
|
||||
)
|
||||
|
||||
binding.conversationOperationMarkAsUnread.visibility = setVisibleIf(
|
||||
binding.conversationMarkAsUnread.visibility = setVisibleIf(
|
||||
conversation.unreadMessages <= 0 && CapabilitiesUtilNew.canMarkRoomAsUnread(currentUser)
|
||||
)
|
||||
|
||||
|
@ -144,15 +148,208 @@ class ConversationsListBottomDialog(
|
|||
}
|
||||
|
||||
private fun initClickListeners() {
|
||||
binding.conversationOperationAddFavorite.setOnClickListener {
|
||||
executeOperationsMenuController(OPS_CODE_ADD_FAVORITE)
|
||||
binding.conversationAddToFavorites.setOnClickListener {
|
||||
addConversationToFavorites()
|
||||
}
|
||||
|
||||
binding.conversationOperationRemoveFavorite.setOnClickListener {
|
||||
executeOperationsMenuController(OPS_CODE_REMOVE_FAVORITE)
|
||||
binding.conversationRemoveFromFavorites.setOnClickListener {
|
||||
removeConversationFromFavorites()
|
||||
}
|
||||
|
||||
binding.conversationMarkAsRead.setOnClickListener {
|
||||
markConversationAsRead()
|
||||
}
|
||||
|
||||
binding.conversationMarkAsUnread.setOnClickListener {
|
||||
markConversationAsUnread()
|
||||
}
|
||||
|
||||
binding.conversationOperationRename.setOnClickListener {
|
||||
renameConversation()
|
||||
}
|
||||
|
||||
binding.conversationOperationLeave.setOnClickListener {
|
||||
leaveConversation()
|
||||
}
|
||||
|
||||
binding.conversationOperationDelete.setOnClickListener {
|
||||
deleteConversation()
|
||||
}
|
||||
}
|
||||
|
||||
private fun addConversationToFavorites() {
|
||||
val apiVersion = ApiUtils.getConversationApiVersion(currentUser, intArrayOf(ApiUtils.APIv4, ApiUtils.APIv1))
|
||||
ncApi.addConversationToFavorites(
|
||||
credentials,
|
||||
ApiUtils.getUrlForRoomFavorite(
|
||||
apiVersion,
|
||||
currentUser.baseUrl,
|
||||
conversation.token
|
||||
)
|
||||
)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.retry(1)
|
||||
.subscribe(object : Observer<GenericOverall> {
|
||||
override fun onSubscribe(d: Disposable) {
|
||||
// unused atm
|
||||
}
|
||||
|
||||
override fun onNext(genericOverall: GenericOverall) {
|
||||
activity.fetchRooms()
|
||||
activity.showSnackbar(
|
||||
String.format(
|
||||
context.resources.getString(R.string.added_to_favorites),
|
||||
conversation.displayName
|
||||
)
|
||||
)
|
||||
dismiss()
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
activity.showSnackbar(context.resources.getString(R.string.nc_common_error_sorry))
|
||||
dismiss()
|
||||
}
|
||||
|
||||
override fun onComplete() {
|
||||
// unused atm
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun removeConversationFromFavorites() {
|
||||
val apiVersion = ApiUtils.getConversationApiVersion(currentUser, intArrayOf(ApiUtils.APIv4, ApiUtils.APIv1))
|
||||
ncApi.removeConversationFromFavorites(
|
||||
credentials,
|
||||
ApiUtils.getUrlForRoomFavorite(
|
||||
apiVersion,
|
||||
currentUser.baseUrl,
|
||||
conversation.token
|
||||
)
|
||||
)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.retry(1)
|
||||
.subscribe(object : Observer<GenericOverall> {
|
||||
override fun onSubscribe(d: Disposable) {
|
||||
// unused atm
|
||||
}
|
||||
|
||||
override fun onNext(genericOverall: GenericOverall) {
|
||||
activity.fetchRooms()
|
||||
activity.showSnackbar(
|
||||
String.format(
|
||||
context.resources.getString(R.string.removed_from_favorites),
|
||||
conversation.displayName
|
||||
)
|
||||
)
|
||||
dismiss()
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
activity.showSnackbar(context.resources.getString(R.string.nc_common_error_sorry))
|
||||
dismiss()
|
||||
}
|
||||
|
||||
override fun onComplete() {
|
||||
// unused atm
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun markConversationAsUnread() {
|
||||
ncApi.markRoomAsUnread(
|
||||
credentials,
|
||||
ApiUtils.getUrlForChatReadMarker(
|
||||
chatApiVersion(),
|
||||
currentUser.baseUrl,
|
||||
conversation.token
|
||||
)
|
||||
)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.retry(1)
|
||||
.subscribe(object : Observer<GenericOverall> {
|
||||
override fun onSubscribe(d: Disposable) {
|
||||
// unused atm
|
||||
}
|
||||
|
||||
override fun onNext(genericOverall: GenericOverall) {
|
||||
activity.fetchRooms()
|
||||
activity.showSnackbar(
|
||||
String.format(
|
||||
context.resources.getString(R.string.marked_as_unread),
|
||||
conversation.displayName
|
||||
)
|
||||
)
|
||||
dismiss()
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
activity.showSnackbar(context.resources.getString(R.string.nc_common_error_sorry))
|
||||
dismiss()
|
||||
}
|
||||
|
||||
override fun onComplete() {
|
||||
// unused atm
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun markConversationAsRead() {
|
||||
ncApi.setChatReadMarker(
|
||||
credentials,
|
||||
ApiUtils.getUrlForChatReadMarker(
|
||||
chatApiVersion(),
|
||||
currentUser.baseUrl,
|
||||
conversation.token
|
||||
),
|
||||
conversation.lastMessage!!.jsonMessageId
|
||||
)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.retry(1)
|
||||
.subscribe(object : Observer<GenericOverall> {
|
||||
override fun onSubscribe(d: Disposable) {
|
||||
// unused atm
|
||||
}
|
||||
|
||||
override fun onNext(genericOverall: GenericOverall) {
|
||||
activity.fetchRooms()
|
||||
activity.showSnackbar(
|
||||
String.format(
|
||||
context.resources.getString(R.string.marked_as_read),
|
||||
conversation.displayName
|
||||
)
|
||||
)
|
||||
dismiss()
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
activity.showSnackbar(context.resources.getString(R.string.nc_common_error_sorry))
|
||||
dismiss()
|
||||
}
|
||||
|
||||
override fun onComplete() {
|
||||
// unused atm
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun renameConversation() {
|
||||
if (!TextUtils.isEmpty(conversation.token)) {
|
||||
dismiss()
|
||||
val conversationDialog = RenameConversationDialogFragment.newInstance(
|
||||
conversation.token!!,
|
||||
conversation.displayName!!
|
||||
)
|
||||
conversationDialog.show(
|
||||
activity.supportFragmentManager,
|
||||
TAG
|
||||
)
|
||||
}
|
||||
}
|
||||
private fun leaveConversation() {
|
||||
val dataBuilder = Data.Builder()
|
||||
dataBuilder.putString(KEY_ROOM_TOKEN, conversation.token)
|
||||
dataBuilder.putLong(KEY_INTERNAL_USER_ID, currentUser.id!!)
|
||||
|
@ -164,78 +361,45 @@ class ConversationsListBottomDialog(
|
|||
).build()
|
||||
WorkManager.getInstance().enqueue(leaveConversationWorker)
|
||||
|
||||
WorkManager.getInstance(context).getWorkInfoByIdLiveData(leaveConversationWorker.id)
|
||||
.observeForever { workInfo: WorkInfo? ->
|
||||
if (workInfo != null) {
|
||||
when (workInfo.state) {
|
||||
WorkInfo.State.SUCCEEDED -> {
|
||||
activity.showSnackbar(
|
||||
String.format(
|
||||
context.resources.getString(R.string.left_conversation),
|
||||
conversation.displayName
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
WorkInfo.State.FAILED -> {
|
||||
activity.showSnackbar(context.resources.getString(R.string.nc_common_error_sorry))
|
||||
}
|
||||
|
||||
else -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dismiss()
|
||||
}
|
||||
|
||||
binding.conversationOperationDelete.setOnClickListener {
|
||||
private fun deleteConversation() {
|
||||
if (!TextUtils.isEmpty(conversation.token)) {
|
||||
val bundle = Bundle()
|
||||
bundle.putLong(KEY_INTERNAL_USER_ID, currentUser.id!!)
|
||||
bundle.putString(KEY_ROOM_TOKEN, conversation.token)
|
||||
activity.showDeleteConversationDialog(bundle)
|
||||
activity.showDeleteConversationDialog(conversation)
|
||||
}
|
||||
|
||||
dismiss()
|
||||
}
|
||||
|
||||
binding.conversationOperationRename.setOnClickListener {
|
||||
executeEntryMenuController(OPS_CODE_RENAME_ROOM)
|
||||
private fun chatApiVersion(): Int {
|
||||
return ApiUtils.getChatApiVersion(currentUser, intArrayOf(ApiUtils.APIv1))
|
||||
}
|
||||
|
||||
binding.conversationOperationMarkAsRead.setOnClickListener {
|
||||
executeOperationsMenuController(OPS_CODE_MARK_AS_READ)
|
||||
}
|
||||
|
||||
binding.conversationOperationMarkAsUnread.setOnClickListener {
|
||||
executeOperationsMenuController(OPS_CODE_MARK_AS_UNREAD)
|
||||
}
|
||||
}
|
||||
|
||||
private fun executeOperationsMenuController(operation: ConversationOperationEnum) {
|
||||
val bundle = Bundle()
|
||||
bundle.putSerializable(KEY_OPERATION_CODE, operation)
|
||||
bundle.putString(KEY_ROOM_TOKEN, conversation.token)
|
||||
|
||||
binding.operationItemsLayout.visibility = View.GONE
|
||||
|
||||
dialogRouter = Conductor.attachRouter(activity, binding.root, null)
|
||||
|
||||
dialogRouter!!.pushController(
|
||||
RouterTransaction.with(OperationsMenuController(bundle))
|
||||
.pushChangeHandler(HorizontalChangeHandler())
|
||||
.popChangeHandler(HorizontalChangeHandler())
|
||||
)
|
||||
|
||||
activity.fetchRooms()
|
||||
}
|
||||
|
||||
private fun executeEntryMenuController(operation: ConversationOperationEnum) {
|
||||
val bundle = Bundle()
|
||||
bundle.putSerializable(KEY_OPERATION_CODE, operation)
|
||||
bundle.putString(KEY_ROOM_TOKEN, conversation.token)
|
||||
|
||||
binding.operationItemsLayout.visibility = View.GONE
|
||||
|
||||
dialogRouter = Conductor.attachRouter(activity, binding.root, null)
|
||||
|
||||
dialogRouter!!.pushController(
|
||||
|
||||
// TODO refresh conversation list after EntryMenuController finished (throw event? / pass controller
|
||||
// into EntryMenuController to execute fetch data... ?!)
|
||||
// for example if you set a password, the dialog items should be refreshed for the next time you open it
|
||||
// without to manually have to refresh the conversations list
|
||||
// also see BottomSheetLockEvent ??
|
||||
|
||||
RouterTransaction.with(EntryMenuController(bundle))
|
||||
.pushChangeHandler(HorizontalChangeHandler())
|
||||
.popChangeHandler(HorizontalChangeHandler())
|
||||
)
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
val bottomSheet = findViewById<View>(R.id.design_bottom_sheet)
|
||||
val behavior = BottomSheetBehavior.from(bottomSheet as View)
|
||||
behavior.state = BottomSheetBehavior.STATE_COLLAPSED
|
||||
companion object {
|
||||
val TAG = ConversationsListBottomDialog::class.simpleName
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
package com.nextcloud.talk.utils.remapchat
|
||||
|
||||
import android.os.Bundle
|
||||
import com.bluelinelabs.conductor.ControllerChangeHandler
|
||||
import com.bluelinelabs.conductor.Router
|
||||
|
||||
data class RemapChatModel(
|
||||
val router: Router,
|
||||
val controllerChangeHandler: ControllerChangeHandler,
|
||||
val chatControllerTag: String,
|
||||
val bundle: Bundle
|
||||
)
|
|
@ -1,25 +0,0 @@
|
|||
<!--
|
||||
~ Nextcloud Talk application
|
||||
~
|
||||
~ @author Mario Danic
|
||||
~ Copyright (C) 2017 Mario Danic <mario@lovelyhq.com>
|
||||
~
|
||||
~ 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/>.
|
||||
-->
|
||||
|
||||
<vector android:autoMirrored="true" android:height="24dp"
|
||||
android:viewportHeight="24.0" android:viewportWidth="24.0"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FF000000" android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM10,17l-5,-5 1.41,-1.41L10,14.17l7.59,-7.59L19,8l-9,9z"/>
|
||||
</vector>
|
|
@ -1,25 +0,0 @@
|
|||
<!--
|
||||
~ Nextcloud Talk application
|
||||
~
|
||||
~ @author Mario Danic
|
||||
~ Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
|
||||
~
|
||||
~ 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/>.
|
||||
-->
|
||||
|
||||
<vector android:autoMirrored="true" android:height="24dp"
|
||||
android:viewportHeight="16.0" android:viewportWidth="16.0"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FF000000" android:pathData="m9.236,2.166 l-3.182,3.184c-0.707,0.707 -1.038,1.618 -0.988,2.457 0.05,0.839 0.433,1.584 0.988,2.139l1.412,-1.416c-0.567,-0.567 -0.544,-1.219 0.002,-1.766l3.181,-3.182c0.525,-0.525 1.251,-0.523 1.772,-0.002 0.482,0.556 0.527,1.238 -0.004,1.77l-0.82,0.82c0.555,0.785 0.645,1.366 0.593,2.234l1.641,-1.641c1.237,-1.237 1.237,-3.365 0,-4.602 -1.236,-1.236 -3.342,-1.211 -4.596,0.004zM9.943,6.051 L8.529,7.469c0,0 0.003,0 0.004,0 0.55,0.55 0.507,1.258 -0.004,1.77l-3.182,3.182c-0.696,0.592 -1.298,0.471 -1.77,0 -0.626,-0.626 -0.5,-1.268 0,-1.768l0.85,-0.847c-0.556,-0.784 -0.648,-1.365 -0.598,-2.232l-1.666,1.666c-1.239,1.239 -1.236,3.36 0,4.596 1.235,1.235 3.362,1.236 4.598,0l3.182,-3.182c0.709,-0.708 1.04,-1.618 0.991,-2.459 -0.048,-0.84 -0.432,-1.586 -0.989,-2.141z"/>
|
||||
</vector>
|
|
@ -93,7 +93,7 @@
|
|||
android:orientation="vertical">
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/initial_relative_layout"
|
||||
android:id="@+id/public_conversation_create"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
|
@ -124,7 +124,7 @@
|
|||
</RelativeLayout>
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/secondary_relative_layout"
|
||||
android:id="@+id/public_conversation_info"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true"
|
||||
|
@ -178,40 +178,6 @@
|
|||
android:textAppearance="@style/ListItem" />
|
||||
</RelativeLayout>
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/join_conversation_via_link"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/standard_margin"
|
||||
android:layout_marginTop="@dimen/standard_half_margin"
|
||||
android:layout_marginEnd="@dimen/standard_margin"
|
||||
android:layout_marginBottom="@dimen/standard_half_margin"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/join_conversation_via_link_image"
|
||||
android:layout_width="@dimen/avatar_size"
|
||||
android:layout_height="@dimen/avatar_size"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginEnd="@dimen/standard_margin"
|
||||
android:background="@drawable/round_bgnd"
|
||||
android:contentDescription="@null"
|
||||
android:padding="@dimen/standard_half_padding"
|
||||
android:src="@drawable/ic_public_black_24px"
|
||||
app:tint="@color/white" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_toEndOf="@id/join_conversation_via_link_image"
|
||||
android:ellipsize="middle"
|
||||
android:singleLine="true"
|
||||
android:text="@string/nc_join_via_link"
|
||||
android:textAlignment="viewStart"
|
||||
android:textAppearance="@style/ListItem" />
|
||||
</RelativeLayout>
|
||||
|
||||
<include
|
||||
android:id="@+id/controller_generic_rv"
|
||||
layout="@layout/controller_generic_rv" />
|
||||
|
|
|
@ -78,19 +78,19 @@
|
|||
android:id="@+id/conversation_info_name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/standard_margin"
|
||||
android:layout_marginTop="@dimen/standard_quarter_margin"
|
||||
android:layout_marginEnd="@dimen/standard_margin"
|
||||
android:animateLayoutChanges="true"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"
|
||||
android:layout_marginStart="@dimen/standard_margin"
|
||||
android:layout_marginEnd="@dimen/standard_margin">
|
||||
tools:visibility="visible">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/avatar_image"
|
||||
android:layout_width="@dimen/avatar_size_big"
|
||||
android:layout_height="@dimen/avatar_size_big"
|
||||
android:layout_marginTop="@dimen/standard_margin"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_marginTop="@dimen/standard_margin"
|
||||
android:contentDescription="@string/avatar"
|
||||
tools:src="@drawable/account_circle_48dp" />
|
||||
|
||||
|
@ -119,10 +119,10 @@
|
|||
android:id="@+id/description_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/standard_half_margin"
|
||||
android:layout_marginBottom="@dimen/standard_half_margin"
|
||||
android:layout_marginStart="@dimen/standard_margin"
|
||||
android:layout_marginTop="@dimen/standard_half_margin"
|
||||
android:layout_marginEnd="@dimen/standard_margin"
|
||||
android:layout_marginBottom="@dimen/standard_half_margin"
|
||||
android:autoLink="web"
|
||||
tools:text="Hello world!" />
|
||||
</LinearLayout>
|
||||
|
@ -137,7 +137,6 @@
|
|||
android:visibility="gone"
|
||||
tools:visibility="gone">
|
||||
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
|
@ -154,7 +153,6 @@
|
|||
android:text="@string/nc_add_to_favorites"
|
||||
android:textSize="@dimen/two_line_primary_text_size" />
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
|
@ -191,25 +189,24 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/standard_quarter_margin"
|
||||
android:orientation="vertical"
|
||||
>
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/shared_items_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="@dimen/standard_padding"
|
||||
android:text="@string/nc_shared_items"
|
||||
android:textSize="@dimen/headline_text_size"
|
||||
android:textStyle="bold"
|
||||
android:padding="@dimen/standard_padding"/>
|
||||
android:textStyle="bold" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/shared_items_button"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:orientation="horizontal"
|
||||
android:padding="@dimen/standard_padding"
|
||||
android:background="?android:attr/selectableItemBackground">
|
||||
android:padding="@dimen/standard_padding">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="24dp"
|
||||
|
@ -235,25 +232,24 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/standard_quarter_margin"
|
||||
android:orientation="vertical"
|
||||
>
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/conversation_settings_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="@dimen/standard_padding"
|
||||
android:text="@string/nc_conversation_settings"
|
||||
android:textSize="@dimen/headline_text_size"
|
||||
android:textStyle="bold"
|
||||
android:padding="@dimen/standard_padding"/>
|
||||
android:textStyle="bold" />
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/conversation_info_chat_settings_input_layout"
|
||||
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/standard_half_margin"
|
||||
android:layout_marginHorizontal="@dimen/standard_margin"
|
||||
android:layout_marginTop="@dimen/standard_half_margin"
|
||||
android:hint="@string/nc_expire_messages">
|
||||
|
||||
<com.google.android.material.textfield.MaterialAutoCompleteTextView
|
||||
|
@ -283,10 +279,10 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/standard_quarter_margin"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone"
|
||||
tools:ignore="UnknownIdInLayout"
|
||||
tools:visibility="visible"
|
||||
android:orientation="vertical">
|
||||
tools:visibility="visible">
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/participants_list_category"
|
||||
|
@ -303,9 +299,9 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/standard_quarter_margin"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:orientation="horizontal"
|
||||
android:padding="@dimen/standard_padding"
|
||||
android:background="?android:attr/selectableItemBackground">
|
||||
android:padding="@dimen/standard_padding">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="24dp"
|
||||
|
@ -337,26 +333,25 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/standard_quarter_margin"
|
||||
android:orientation="vertical"
|
||||
>
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="@dimen/standard_padding"
|
||||
android:text="@string/danger_zone"
|
||||
android:textColor="@color/design_default_color_error"
|
||||
android:textSize="@dimen/headline_text_size"
|
||||
android:textStyle="bold"
|
||||
android:padding="@dimen/standard_padding"/>
|
||||
android:textStyle="bold" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/leaveConversationAction"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/standard_quarter_margin"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:orientation="horizontal"
|
||||
android:padding="@dimen/standard_padding"
|
||||
android:background="?android:attr/selectableItemBackground">
|
||||
android:padding="@dimen/standard_padding">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="24dp"
|
||||
|
@ -373,10 +368,6 @@
|
|||
android:text="@string/nc_leave"
|
||||
android:textColor="@color/design_default_color_error"
|
||||
android:textSize="@dimen/two_line_primary_text_size" />
|
||||
|
||||
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
|
@ -384,9 +375,9 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/standard_quarter_margin"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:orientation="horizontal"
|
||||
android:padding="@dimen/standard_padding"
|
||||
android:background="?android:attr/selectableItemBackground">
|
||||
android:padding="@dimen/standard_padding">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="24dp"
|
||||
|
@ -410,9 +401,9 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/standard_quarter_margin"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:orientation="horizontal"
|
||||
android:padding="@dimen/standard_padding"
|
||||
android:background="?android:attr/selectableItemBackground">
|
||||
android:padding="@dimen/standard_padding">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="24dp"
|
||||
|
|
|
@ -1,93 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Nextcloud Talk application
|
||||
~
|
||||
~ @author Mario Danic
|
||||
~ Copyright (C) 2017 Mario Danic <mario@lovelyhq.com>
|
||||
~
|
||||
~ 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/>.
|
||||
-->
|
||||
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/controller_operations_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/result_image_view"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_marginStart="24dp"
|
||||
android:layout_marginTop="24dp"
|
||||
android:layout_marginEnd="24dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:contentDescription="@null"
|
||||
android:tintMode="src_in"
|
||||
android:visibility="gone" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progress_bar"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_centerInParent="true"
|
||||
android:layout_marginTop="24dp"
|
||||
android:layout_marginBottom="24dp"
|
||||
android:indeterminate="true"
|
||||
android:indeterminateTint="@color/colorPrimary"
|
||||
android:indeterminateTintMode="src_in"
|
||||
android:keepScreenOn="true" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/result_text_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/result_image_view"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_marginStart="24dp"
|
||||
android:layout_marginEnd="24dp"
|
||||
android:layout_marginBottom="12dp"
|
||||
android:maxLines="3"
|
||||
android:textAlignment="center"
|
||||
android:textColor="@color/colorPrimary"
|
||||
android:visibility="gone" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/ok_button"
|
||||
style="@style/Widget.MaterialComponents.Button.TextButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/result_text_view"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginBottom="12dp"
|
||||
android:background="@color/bg_inverse"
|
||||
android:text="@string/nc_ok"
|
||||
android:textColor="@color/colorPrimary"
|
||||
android:visibility="gone" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/web_button"
|
||||
style="@style/Widget.MaterialComponents.Button.TextButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/result_text_view"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginBottom="12dp"
|
||||
android:layout_toStartOf="@id/ok_button"
|
||||
android:background="@color/bg_inverse"
|
||||
android:text="@string/nc_join_via_web"
|
||||
android:textColor="@color/nc_darkGreen"
|
||||
android:visibility="gone" />
|
||||
|
||||
</RelativeLayout>
|
|
@ -1,31 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Nextcloud Talk application
|
||||
~
|
||||
~ @author Marcel Hibbe
|
||||
~ @author Andy Scherzinger
|
||||
~ Copyright (C) 2022 Andy Scherzinger <info@andy-scherzinger.de>
|
||||
~ Copyright (C) 2022 Marcel Hibbe <dev@mhibbe.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/>.
|
||||
-->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="@dimen/standard_padding"
|
||||
android:paddingEnd="@dimen/standard_padding"
|
||||
android:paddingBottom="@dimen/standard_half_padding">
|
||||
|
||||
</LinearLayout>
|
|
@ -47,7 +47,7 @@
|
|||
tools:text="conversation name" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/conversation_operation_remove_favorite"
|
||||
android:id="@+id/conversation_remove_from_favorites"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/bottom_sheet_item_height"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
|
@ -77,7 +77,7 @@
|
|||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/conversation_operation_add_favorite"
|
||||
android:id="@+id/conversation_add_to_favorites"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/bottom_sheet_item_height"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
|
@ -107,7 +107,7 @@
|
|||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/conversation_operation_mark_as_read"
|
||||
android:id="@+id/conversation_mark_as_read"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/bottom_sheet_item_height"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
|
@ -137,7 +137,7 @@
|
|||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/conversation_operation_mark_as_unread"
|
||||
android:id="@+id/conversation_mark_as_unread"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/bottom_sheet_item_height"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
|
|
|
@ -27,22 +27,6 @@
|
|||
android:paddingTop="@dimen/standard_padding"
|
||||
android:paddingEnd="@dimen/standard_half_padding">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/ok_button"
|
||||
style="@style/Widget.MaterialComponents.Button.TextButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/text_input_layout"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_marginTop="@dimen/standard_half_margin"
|
||||
android:layout_marginBottom="@dimen/standard_half_margin"
|
||||
android:alpha="0.7"
|
||||
android:background="@color/bg_default"
|
||||
android:enabled="false"
|
||||
android:text="@string/nc_proceed"
|
||||
android:textAllCaps="false"
|
||||
android:textColor="@color/colorPrimary" />
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/text_input_layout"
|
||||
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
|
||||
|
@ -77,7 +61,6 @@
|
|||
android:background="@color/transparent"
|
||||
android:contentDescription="@string/nc_add_emojis"
|
||||
android:src="@drawable/ic_insert_emoticon_black_24dp"
|
||||
android:visibility="gone"
|
||||
app:tint="@color/medium_emphasis_text"
|
||||
tools:visibility="visible" />
|
||||
|
69
app/src/main/res/layout/dialog_rename_conversation.xml
Normal file
69
app/src/main/res/layout/dialog_rename_conversation.xml
Normal file
|
@ -0,0 +1,69 @@
|
|||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Nextcloud Talk application
|
||||
~
|
||||
~ @author Mario Danic
|
||||
~ @author Marcel Hibbe
|
||||
~ Copyright (C) 2023 Marcel Hibbe <dev@mhibbe.de>
|
||||
~ Copyright (C) 2017 Mario Danic <mario@lovelyhq.com>
|
||||
~
|
||||
~ 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/>.
|
||||
-->
|
||||
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="@dimen/standard_padding"
|
||||
android:paddingTop="@dimen/standard_padding"
|
||||
android:paddingEnd="@dimen/standard_half_padding">
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/text_input_layout"
|
||||
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="@dimen/standard_half_margin"
|
||||
android:layout_toStartOf="@id/smileyButton"
|
||||
app:errorTextAppearance="@style/ErrorAppearance"
|
||||
app:passwordToggleTint="@color/grey_600"
|
||||
app:boxStrokeColor="@color/colorPrimary"
|
||||
app:hintTextColor="@color/colorPrimary">
|
||||
|
||||
<com.nextcloud.talk.utils.EmojiTextInputEditText
|
||||
android:id="@+id/text_edit"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:imeOptions="actionDone"
|
||||
android:inputType="textUri"
|
||||
android:singleLine="true"
|
||||
android:textAlignment="viewStart"
|
||||
android:textColor="@color/high_emphasis_text" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/smileyButton"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_alignBottom="@id/text_input_layout"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_marginStart="-4dp"
|
||||
android:background="@color/transparent"
|
||||
android:contentDescription="@string/nc_add_emojis"
|
||||
android:src="@drawable/ic_insert_emoticon_black_24dp"
|
||||
app:tint="@color/medium_emphasis_text"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</RelativeLayout>
|
|
@ -44,7 +44,6 @@
|
|||
|
||||
<color name="bg_default">#121212</color>
|
||||
<color name="bg_default_semitransparent">#99121212</color>
|
||||
<color name="bg_inverse">@color/grey950</color>
|
||||
|
||||
<color name="fg_default">#FFFFFF</color>
|
||||
<color name="fg_inverse">#121212</color>
|
||||
|
|
|
@ -74,7 +74,6 @@
|
|||
|
||||
<color name="bg_default">#FFFFFF</color>
|
||||
<color name="bg_default_semitransparent">#99FFFFFF</color>
|
||||
<color name="bg_inverse">@color/grey950</color>
|
||||
<color name="bg_dark_mention_chips">#333333</color>
|
||||
|
||||
<color name="bg_message_list_incoming_bubble">#EFEFEF</color>
|
||||
|
|
|
@ -49,6 +49,8 @@ How to translate with transifex:
|
|||
<string name="nc_common_set">Set</string>
|
||||
<string name="nc_common_dismiss">Dismiss</string>
|
||||
<string name="nc_common_error_sorry">Sorry, something went wrong!</string>
|
||||
<string name="nc_common_create">Create</string>
|
||||
|
||||
|
||||
<!-- Bottom Navigation -->
|
||||
<string name="nc_settings">Settings</string>
|
||||
|
@ -168,10 +170,6 @@ How to translate with transifex:
|
|||
<string name="nc_cancel">Cancel</string>
|
||||
|
||||
<string name="nc_no_proxy">No proxy</string>
|
||||
<string name="nc_password">Password</string>
|
||||
<string name="nc_conversation_link">Conversation link</string>
|
||||
<string name="nc_new_password">New password</string>
|
||||
<string name="nc_wrong_password">Wrong password</string>
|
||||
<string name="nc_about">About</string>
|
||||
<string name="nc_privacy">Privacy</string>
|
||||
<string name="nc_get_source_code">Get source code</string>
|
||||
|
@ -190,20 +188,27 @@ How to translate with transifex:
|
|||
<string name="nc_clear_history_warning">Do you really want to delete all messages in this conversation?</string>
|
||||
<string name="nc_clear_history_success">All messages were deleted</string>
|
||||
<string name="nc_rename">Rename conversation</string>
|
||||
<string name="nc_rename_confirm">Rename</string>
|
||||
<string name="nc_delete_call">Delete conversation</string>
|
||||
<string name="nc_delete">Delete</string>
|
||||
<string name="nc_delete_all">Delete all</string>
|
||||
<string name="nc_delete_conversation_more">If you delete the conversation, it will also be deleted for all other participants.</string>
|
||||
|
||||
<string name="nc_new_conversation">New conversation</string>
|
||||
<string name="nc_join_via_link">Join with a link</string>
|
||||
<string name="nc_list_open_conversations">List open conversations</string>
|
||||
<string name="nc_join_via_web">Join via web</string>
|
||||
<string name="nc_mark_as_read">Mark as read</string>
|
||||
<string name="nc_mark_as_unread">Mark as unread</string>
|
||||
<string name="nc_add_to_favorites">Add to favorites</string>
|
||||
<string name="nc_remove_from_favorites">Remove from favorites</string>
|
||||
|
||||
<string name="added_to_favorites">Added conversation %1$s to favorites</string>
|
||||
<string name="removed_from_favorites">Removed conversation %1$s from favorites</string>
|
||||
<string name="marked_as_unread">Marked conversation %1$s as unread</string>
|
||||
<string name="marked_as_read">Marked conversation %1$s as read</string>
|
||||
<string name="deleted_conversation">Deleted conversation %1$s</string>
|
||||
<string name="left_conversation">You left the conversation %1$s</string>
|
||||
<string name="renamed_conversation">Conversation %1$s was renamed</string>
|
||||
|
||||
<string name="nc_forward_to_three_dots">Forward to …</string>
|
||||
|
||||
<!-- Open conversations -->
|
||||
|
@ -279,16 +284,10 @@ How to translate with transifex:
|
|||
<string name="nc_important_conversation">Important conversation</string>
|
||||
<string name="nc_important_conversation_desc">Notifications in this conversation will override Do Not Disturb settings</string>
|
||||
|
||||
<!-- Bottom sheet menu -->
|
||||
<string name="nc_failed_to_perform_operation">Sorry, something went wrong!</string>
|
||||
<string name="nc_failed_signaling_settings">Target server does not support joining public conversations via mobile phones. You may attempt to join the conversation via web browser.</string>
|
||||
<string name="nc_all_ok_operation">OK, all done!</string>
|
||||
<string name="nc_ok">OK</string>
|
||||
<string name="nc_call_name">Conversation name</string>
|
||||
<string name="nc_proceed">Proceed</string>
|
||||
<string name="nc_add_emojis">Add emojis</string>
|
||||
<string name="nc_call_name_is_same">The name you entered is the same as the existing one</string>
|
||||
<string name="nc_wrong_link">Conversation link is not valid</string>
|
||||
<string name="nc_share_text">Join the conversation at %1$s/index.php/call/%2$s</string>
|
||||
<string name="nc_share_subject">%1$s invitation</string>
|
||||
<string name="nc_share_text_pass">\nPassword: %1$s</string>
|
||||
|
|
Loading…
Reference in a new issue