From 754b825096c593014614f7062ede0a12d789e901 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Fri, 23 Feb 2024 16:54:31 +0100 Subject: [PATCH] pass spreedCapabilities instead user to CapabilitiesUtil To support federated rooms, capabilities have to be checked from the room which now also has capabilities. If room is not federated, capabilities fromuser are still checked. This is why CapabilitiesUtil had to be refactored to accept SpreedCapabilities which can come from room or user. Other than that, many other changes were made as a result of this change. Signed-off-by: Marcel Hibbe --- .../account/AccountVerificationActivity.kt | 6 +- .../talk/account/ServerSelectionActivity.kt | 4 +- .../nextcloud/talk/activities/CallActivity.kt | 65 +- .../nextcloud/talk/activities/MainActivity.kt | 6 +- .../talk/adapters/items/ConversationItem.kt | 5 +- .../messages/CallStartedViewHolder.kt | 4 +- .../IncomingLinkPreviewMessageViewHolder.kt | 2 +- .../IncomingLocationMessageViewHolder.kt | 2 +- .../messages/IncomingPollMessageViewHolder.kt | 2 +- .../messages/IncomingTextMessageViewHolder.kt | 2 +- .../IncomingVoiceMessageViewHolder.kt | 2 +- .../talk/adapters/messages/LinkPreview.kt | 4 +- .../OutcomingLinkPreviewMessageViewHolder.kt | 2 +- .../OutcomingLocationMessageViewHolder.kt | 2 +- .../OutcomingPollMessageViewHolder.kt | 2 +- .../OutcomingTextMessageViewHolder.kt | 2 +- .../OutcomingVoiceMessageViewHolder.kt | 2 +- .../java/com/nextcloud/talk/api/NcApi.java | 5 + .../CallNotificationActivity.kt | 17 +- .../com/nextcloud/talk/chat/ChatActivity.kt | 302 +++++---- .../talk/chat/data/ChatRepository.kt | 14 +- .../data/network/NetworkChatRepositoryImpl.kt | 60 +- .../talk/chat/viewmodels/ChatViewModel.kt | 115 ++-- .../webdav/ReadFolderListingOperation.kt | 2 +- .../talk/contacts/ContactsActivity.kt | 33 +- .../repository/ConversationRepositoryImpl.kt | 12 +- .../ConversationInfoActivity.kt | 361 ++++++----- .../conversationinfo/GuestAccessHelper.kt | 12 +- .../viewmodel/ConversationInfoViewModel.kt | 142 +++++ .../ConversationInfoEditActivity.kt | 21 +- .../ConversationInfoEditRepositoryImpl.kt | 8 +- .../ConversationsListActivity.kt | 45 +- .../talk/dagger/modules/ViewModelModule.kt | 6 + .../nextcloud/talk/data/user/UserMapper.kt | 6 +- .../nextcloud/talk/data/user/model/User.kt | 2 +- .../talk/extensions/ImageViewExtensions.kt | 8 +- .../data/InvitationsRepositoryImpl.kt | 12 +- .../jobs/AddParticipantsToConversation.java | 2 +- .../talk/jobs/ContactAddressBookWorker.kt | 2 +- .../talk/jobs/DeleteConversationWorker.java | 2 +- .../talk/jobs/LeaveConversationWorker.java | 2 +- .../nextcloud/talk/jobs/NotificationWorker.kt | 28 +- .../talk/jobs/ShareOperationWorker.kt | 4 +- .../talk/jobs/SignalingSettingsWorker.java | 2 +- .../talk/jobs/UploadAndShareFilesWorker.kt | 6 +- .../talk/location/GeocodingActivity.kt | 5 + .../talk/location/LocationPickerActivity.kt | 7 +- .../nextcloud/talk/models/RetrofitBucket.kt | 2 +- .../talk/models/domain/ConversationModel.kt | 14 +- .../DomainEnumNotificationLevelConverter.kt | 45 ++ .../json/capabilities/RoomCapabilitiesOCS.kt | 42 ++ .../capabilities/RoomCapabilitiesOverall.kt | 37 ++ .../talk/models/json/chat/ChatMessage.kt | 14 +- .../models/json/conversations/Conversation.kt | 22 +- .../data/OpenConversationsRepositoryImpl.kt | 6 +- .../polls/repositories/PollRepositoryImpl.kt | 10 +- .../MentionAutocompletePresenter.java | 8 +- .../nextcloud/talk/profile/ProfileActivity.kt | 27 +- .../RequestAssistanceRepositoryImpl.kt | 2 +- .../talk/receivers/DirectReplyReceiver.kt | 6 +- .../talk/receivers/MarkAsReadReceiver.kt | 6 +- .../RemoteFileBrowserItemsListViewHolder.kt | 2 +- .../CallRecordingRepositoryImpl.kt | 6 +- .../ConversationsRepositoryImpl.kt | 6 +- .../reactions/ReactionsRepositoryImpl.kt | 6 +- .../UnifiedSearchRepositoryImpl.kt | 2 +- .../talk/settings/SettingsActivity.kt | 35 +- .../repositories/SharedItemsRepositoryImpl.kt | 6 +- .../viewmodels/TranslateViewModel.kt | 8 +- .../ui/bottom/sheet/ProfileBottomSheet.kt | 6 +- .../talk/ui/dialog/AttachmentDialog.kt | 13 +- .../dialog/ChooseAccountDialogFragment.java | 4 +- .../ChooseAccountShareToDialogFragment.kt | 2 +- .../dialog/ConversationsListBottomDialog.kt | 39 +- .../talk/ui/dialog/DateTimePickerFragment.kt | 20 +- .../talk/ui/dialog/MessageActionsDialog.kt | 25 +- .../talk/ui/dialog/MoreCallActionsDialog.kt | 6 +- .../talk/ui/dialog/SetStatusDialogFragment.kt | 12 +- .../talk/ui/dialog/ShowReactionsDialog.kt | 4 +- .../upload/chunked/ChunkedFileUploader.kt | 8 +- .../talk/upload/normal/FileUploader.kt | 2 +- .../com/nextcloud/talk/utils/AccountUtils.kt | 2 +- .../com/nextcloud/talk/utils/ApiUtils.java | 559 ----------------- .../java/com/nextcloud/talk/utils/ApiUtils.kt | 577 ++++++++++++++++++ .../nextcloud/talk/utils/CapabilitiesUtil.kt | 275 +++++++++ .../nextcloud/talk/utils/ConversationUtils.kt | 23 +- .../nextcloud/talk/utils/FileViewerUtils.kt | 3 +- .../talk/utils/ParticipantPermissions.kt | 15 +- .../com/nextcloud/talk/utils/PushUtils.kt | 2 +- .../nextcloud/talk/utils/RemoteFileUtils.kt | 4 +- .../com/nextcloud/talk/utils/ShareUtils.kt | 4 +- .../nextcloud/talk/utils/bundle/BundleKeys.kt | 1 + .../database/user/CapabilitiesUtilNew.kt | 267 -------- .../DatabaseStorageModule.java | 7 +- .../talk/viewmodels/GeoCodingViewModel.kt | 7 +- .../webrtc/WebSocketConnectionHelper.java | 2 +- .../talk/utils/ParticipantPermissionsTest.kt | 6 +- .../nextcloud/talk/utils/ShareUtilsTest.kt | 8 +- detekt.yml | 2 +- gradle/verification-keyring.keys | 68 +-- gradle/verification-metadata.xml | 29 + 101 files changed, 2115 insertions(+), 1556 deletions(-) create mode 100644 app/src/main/java/com/nextcloud/talk/conversationinfo/viewmodel/ConversationInfoViewModel.kt create mode 100644 app/src/main/java/com/nextcloud/talk/models/domain/converters/DomainEnumNotificationLevelConverter.kt create mode 100644 app/src/main/java/com/nextcloud/talk/models/json/capabilities/RoomCapabilitiesOCS.kt create mode 100644 app/src/main/java/com/nextcloud/talk/models/json/capabilities/RoomCapabilitiesOverall.kt delete mode 100644 app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java create mode 100644 app/src/main/java/com/nextcloud/talk/utils/ApiUtils.kt create mode 100644 app/src/main/java/com/nextcloud/talk/utils/CapabilitiesUtil.kt delete mode 100644 app/src/main/java/com/nextcloud/talk/utils/database/user/CapabilitiesUtilNew.kt diff --git a/app/src/main/java/com/nextcloud/talk/account/AccountVerificationActivity.kt b/app/src/main/java/com/nextcloud/talk/account/AccountVerificationActivity.kt index 958c1f3ca..4f623c8f2 100644 --- a/app/src/main/java/com/nextcloud/talk/account/AccountVerificationActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/account/AccountVerificationActivity.kt @@ -199,7 +199,7 @@ class AccountVerificationActivity : BaseActivity() { val credentials = ApiUtils.getCredentials(username, token) cookieManager.cookieStore.removeAll() - ncApi.getCapabilities(credentials, ApiUtils.getUrlForCapabilities(baseUrl)) + ncApi.getCapabilities(credentials, ApiUtils.getUrlForCapabilities(baseUrl!!)) .subscribeOn(Schedulers.io()) .subscribe(object : Observer { override fun onSubscribe(d: Disposable) { @@ -213,7 +213,7 @@ class AccountVerificationActivity : BaseActivity() { capabilitiesOverall.ocs!!.data!!.capabilities!!.spreedCapability!!.features != null && !capabilitiesOverall.ocs!!.data!!.capabilities!!.spreedCapability!!.features!!.isEmpty() if (hasTalk) { - fetchProfile(credentials, capabilitiesOverall) + fetchProfile(credentials!!, capabilitiesOverall) } else { if (resources != null) { runOnUiThread { @@ -305,7 +305,7 @@ class AccountVerificationActivity : BaseActivity() { private fun fetchProfile(credentials: String, capabilitiesOverall: CapabilitiesOverall) { ncApi.getUserProfile( credentials, - ApiUtils.getUrlForUserProfile(baseUrl) + ApiUtils.getUrlForUserProfile(baseUrl!!) ) .subscribeOn(Schedulers.io()) .subscribe(object : Observer { diff --git a/app/src/main/java/com/nextcloud/talk/account/ServerSelectionActivity.kt b/app/src/main/java/com/nextcloud/talk/account/ServerSelectionActivity.kt index fb7d2de7a..5869ac18f 100644 --- a/app/src/main/java/com/nextcloud/talk/account/ServerSelectionActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/account/ServerSelectionActivity.kt @@ -52,7 +52,7 @@ import com.nextcloud.talk.utils.UriUtils import com.nextcloud.talk.utils.bundle.BundleKeys import com.nextcloud.talk.utils.bundle.BundleKeys.ADD_ADDITIONAL_ACCOUNT import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_IS_ACCOUNT_IMPORT -import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew +import com.nextcloud.talk.utils.CapabilitiesUtil import com.nextcloud.talk.utils.singletons.ApplicationWideMessageHolder import io.reactivex.Observer import io.reactivex.android.schedulers.AndroidSchedulers @@ -336,7 +336,7 @@ class ServerSelectionActivity : BaseActivity() { if (hasTalk) { runOnUiThread { - if (CapabilitiesUtilNew.isServerEOL(capabilities)) { + if (CapabilitiesUtil.isServerEOL(capabilitiesOverall.ocs?.data?.serverVersion?.major!!)) { if (resources != null) { runOnUiThread { setErrorText(resources!!.getString(R.string.nc_settings_server_eol)) diff --git a/app/src/main/java/com/nextcloud/talk/activities/CallActivity.kt b/app/src/main/java/com/nextcloud/talk/activities/CallActivity.kt index b80cf974d..e0b599724 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/CallActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/activities/CallActivity.kt @@ -110,6 +110,7 @@ import com.nextcloud.talk.ui.dialog.AudioOutputDialog import com.nextcloud.talk.ui.dialog.MoreCallActionsDialog import com.nextcloud.talk.users.UserManager import com.nextcloud.talk.utils.ApiUtils +import com.nextcloud.talk.utils.SpreedFeatures import com.nextcloud.talk.utils.DisplayUtils import com.nextcloud.talk.utils.NotificationUtils.cancelExistingNotificationsForRoom import com.nextcloud.talk.utils.NotificationUtils.getCallRingtoneUri @@ -131,9 +132,9 @@ 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.bundle.BundleKeys.KEY_START_CALL_AFTER_ROOM_SWITCH import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_SWITCH_TO_ROOM -import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew -import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew.hasSpreedFeatureCapability -import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew.isCallRecordingAvailable +import com.nextcloud.talk.utils.CapabilitiesUtil +import com.nextcloud.talk.utils.CapabilitiesUtil.hasSpreedFeatureCapability +import com.nextcloud.talk.utils.CapabilitiesUtil.isCallRecordingAvailable import com.nextcloud.talk.utils.database.user.CurrentUserProviderNew import com.nextcloud.talk.utils.permissions.PlatformPermissionUtil import com.nextcloud.talk.utils.power.PowerManagerUtils @@ -234,7 +235,7 @@ class CallActivity : CallBaseActivity() { private var iceServers: MutableList? = null private var cameraEnumerator: CameraEnumerator? = null private var roomToken: String? = null - var conversationUser: User? = null + lateinit var conversationUser: User private var conversationName: String? = null private var callSession: String? = null private var localStream: MediaStream? = null @@ -530,13 +531,13 @@ class CallActivity : CallBaseActivity() { ) } - when (CapabilitiesUtilNew.getRecordingConsentType(conversationUser)) { - CapabilitiesUtilNew.RECORDING_CONSENT_NOT_REQUIRED -> initiateCall() - CapabilitiesUtilNew.RECORDING_CONSENT_REQUIRED -> askForRecordingConsent() - CapabilitiesUtilNew.RECORDING_CONSENT_DEPEND_ON_CONVERSATION -> { + when (CapabilitiesUtil.getRecordingConsentType(conversationUser!!.capabilities!!.spreedCapability!!)) { + CapabilitiesUtil.RECORDING_CONSENT_NOT_REQUIRED -> initiateCall() + CapabilitiesUtil.RECORDING_CONSENT_REQUIRED -> askForRecordingConsent() + CapabilitiesUtil.RECORDING_CONSENT_DEPEND_ON_CONVERSATION -> { val getRoomApiVersion = ApiUtils.getConversationApiVersion( - conversationUser, - intArrayOf(ApiUtils.APIv4, 1) + conversationUser!!, + intArrayOf(ApiUtils.API_V4, 1) ) ncApi!!.getRoom(credentials, ApiUtils.getUrlForRoom(getRoomApiVersion, baseUrl, roomToken)) .retry(API_RETRIES) @@ -571,7 +572,10 @@ class CallActivity : CallBaseActivity() { override fun onResume() { super.onResume() - if (hasSpreedFeatureCapability(conversationUser, "recording-v1") && + if (hasSpreedFeatureCapability( + conversationUser.capabilities!!.spreedCapability!!, + SpreedFeatures.RECORDING_V1 + ) && othersInCall && elapsedSeconds.toInt() >= CALL_TIME_ONE_HOUR ) { @@ -1468,7 +1472,7 @@ class CallActivity : CallBaseActivity() { private fun fetchSignalingSettings() { Log.d(TAG, "fetchSignalingSettings") - val apiVersion = ApiUtils.getSignalingApiVersion(conversationUser, intArrayOf(ApiUtils.APIv3, 2, 1)) + val apiVersion = ApiUtils.getSignalingApiVersion(conversationUser, intArrayOf(ApiUtils.API_V3, 2, 1)) ncApi!!.getSignalingSettings(credentials, ApiUtils.getUrlForSignalingSettings(apiVersion, baseUrl)) .subscribeOn(Schedulers.io()) .retry(API_RETRIES) @@ -1531,7 +1535,7 @@ class CallActivity : CallBaseActivity() { private fun addIceServers(signalingSettingsOverall: SignalingSettingsOverall, apiVersion: Int) { if (signalingSettingsOverall.ocs!!.settings!!.stunServers != null) { val stunServers = signalingSettingsOverall.ocs!!.settings!!.stunServers - if (apiVersion == ApiUtils.APIv3) { + if (apiVersion == ApiUtils.API_V3) { for ((_, urls) in stunServers!!) { if (urls != null) { for (url in urls) { @@ -1564,7 +1568,7 @@ class CallActivity : CallBaseActivity() { } private fun checkCapabilities() { - ncApi!!.getCapabilities(credentials, ApiUtils.getUrlForCapabilities(baseUrl)) + ncApi!!.getCapabilities(credentials, ApiUtils.getUrlForCapabilities(baseUrl!!)) .retry(API_RETRIES) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) @@ -1600,7 +1604,7 @@ class CallActivity : CallBaseActivity() { private fun joinRoomAndCall() { callSession = ApplicationWideCurrentRoomHolder.getInstance().session - val apiVersion = ApiUtils.getConversationApiVersion(conversationUser, intArrayOf(ApiUtils.APIv4, 1)) + val apiVersion = ApiUtils.getConversationApiVersion(conversationUser, intArrayOf(ApiUtils.API_V4, 1)) Log.d(TAG, "joinRoomAndCall") Log.d(TAG, " baseUrl= $baseUrl") Log.d(TAG, " roomToken= $roomToken") @@ -1656,7 +1660,7 @@ class CallActivity : CallBaseActivity() { fun getRoomAndContinue() { val getRoomApiVersion = ApiUtils.getConversationApiVersion( conversationUser, - intArrayOf(ApiUtils.APIv4, 1) + intArrayOf(ApiUtils.API_V4, 1) ) ncApi!!.getRoom(credentials, ApiUtils.getUrlForRoom(getRoomApiVersion, baseUrl, roomToken)) .retry(API_RETRIES) @@ -1715,10 +1719,10 @@ class CallActivity : CallBaseActivity() { callParticipantList = CallParticipantList(signalingMessageReceiver) callParticipantList!!.addObserver(callParticipantListObserver) - val apiVersion = ApiUtils.getCallApiVersion(conversationUser, intArrayOf(ApiUtils.APIv4, 1)) + val apiVersion = ApiUtils.getCallApiVersion(conversationUser, intArrayOf(ApiUtils.API_V4, 1)) ncApi!!.joinCall( credentials, - ApiUtils.getUrlForCall(apiVersion, baseUrl, roomToken), + ApiUtils.getUrlForCall(apiVersion, baseUrl, roomToken!!), inCallFlag, isCallWithoutNotification, recordingConsentGiven @@ -1756,7 +1760,10 @@ class CallActivity : CallBaseActivity() { } private fun startCallTimeCounter(callStartTime: Long?) { - if (callStartTime != null && hasSpreedFeatureCapability(conversationUser, "recording-v1")) { + if (callStartTime != null && hasSpreedFeatureCapability( + conversationUser!!.capabilities!!.spreedCapability!!, SpreedFeatures.RECORDING_V1 + ) + ) { binding!!.callDuration.visibility = View.VISIBLE val currentTimeInSec = System.currentTimeMillis() / SECOND_IN_MILLIES elapsedSeconds = currentTimeInSec - callStartTime @@ -1793,7 +1800,7 @@ class CallActivity : CallBaseActivity() { } private fun pullSignalingMessages() { - val signalingApiVersion = ApiUtils.getSignalingApiVersion(conversationUser, intArrayOf(ApiUtils.APIv3, 2, 1)) + val signalingApiVersion = ApiUtils.getSignalingApiVersion(conversationUser, intArrayOf(ApiUtils.API_V3, 2, 1)) val delayOnError = AtomicInteger(0) ncApi!!.pullSignalingMessages( @@ -1801,7 +1808,7 @@ class CallActivity : CallBaseActivity() { ApiUtils.getUrlForSignaling( signalingApiVersion, baseUrl, - roomToken + roomToken!! ) ) .subscribeOn(Schedulers.io()) @@ -2031,12 +2038,12 @@ class CallActivity : CallBaseActivity() { private fun hangupNetworkCalls(shutDownView: Boolean) { Log.d(TAG, "hangupNetworkCalls. shutDownView=$shutDownView") - val apiVersion = ApiUtils.getCallApiVersion(conversationUser, intArrayOf(ApiUtils.APIv4, 1)) + val apiVersion = ApiUtils.getCallApiVersion(conversationUser, intArrayOf(ApiUtils.API_V4, 1)) if (callParticipantList != null) { callParticipantList!!.removeObserver(callParticipantListObserver) callParticipantList!!.destroy() } - ncApi!!.leaveCall(credentials, ApiUtils.getUrlForCall(apiVersion, baseUrl, roomToken)) + ncApi!!.leaveCall(credentials, ApiUtils.getUrlForCall(apiVersion, baseUrl, roomToken!!)) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(object : Observer { @@ -2919,10 +2926,10 @@ class CallActivity : CallBaseActivity() { val strings: MutableList = ArrayList() val stringToSend = stringBuilder.toString() strings.add(stringToSend) - val apiVersion = ApiUtils.getSignalingApiVersion(conversationUser, intArrayOf(ApiUtils.APIv3, 2, 1)) + val apiVersion = ApiUtils.getSignalingApiVersion(conversationUser, intArrayOf(ApiUtils.API_V3, 2, 1)) ncApi!!.sendSignalingMessages( credentials, - ApiUtils.getUrlForSignaling(apiVersion, baseUrl, roomToken), + ApiUtils.getUrlForSignaling(apiVersion, baseUrl, roomToken!!), strings.toString() ) .retry(API_RETRIES) @@ -3099,12 +3106,14 @@ class CallActivity : CallBaseActivity() { val isAllowedToStartOrStopRecording: Boolean get() = ( - isCallRecordingAvailable(conversationUser!!) && + isCallRecordingAvailable(conversationUser!!.capabilities!!.spreedCapability!!) && isModerator ) val isAllowedToRaiseHand: Boolean - get() = hasSpreedFeatureCapability(conversationUser, "raise-hand") || - isBreakoutRoom + get() = hasSpreedFeatureCapability( + conversationUser.capabilities!!.spreedCapability!!, + SpreedFeatures.RAISE_HAND + ) || isBreakoutRoom private inner class SelfVideoTouchListener : OnTouchListener { @SuppressLint("ClickableViewAccessibility") diff --git a/app/src/main/java/com/nextcloud/talk/activities/MainActivity.kt b/app/src/main/java/com/nextcloud/talk/activities/MainActivity.kt index 81d918448..ecc2627ec 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/MainActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/activities/MainActivity.kt @@ -181,7 +181,7 @@ class MainActivity : BaseActivity(), ActionBarProvider { val user = userId.substringBeforeLast("@") val baseUrl = userId.substringAfterLast("@") - if (userManager.currentUser.blockingGet()?.baseUrl?.endsWith(baseUrl) == true) { + if (userManager.currentUser.blockingGet()?.baseUrl!!.endsWith(baseUrl) == true) { startConversation(user) } else { Snackbar.make( @@ -200,11 +200,11 @@ class MainActivity : BaseActivity(), ActionBarProvider { val currentUser = userManager.currentUser.blockingGet() - val apiVersion = ApiUtils.getConversationApiVersion(currentUser, intArrayOf(ApiUtils.APIv4, 1)) + val apiVersion = ApiUtils.getConversationApiVersion(currentUser, intArrayOf(ApiUtils.API_V4, 1)) val credentials = ApiUtils.getCredentials(currentUser?.username, currentUser?.token) val retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom( apiVersion, - currentUser?.baseUrl, + currentUser?.baseUrl!!, roomType, null, userId, diff --git a/app/src/main/java/com/nextcloud/talk/adapters/items/ConversationItem.kt b/app/src/main/java/com/nextcloud/talk/adapters/items/ConversationItem.kt index 3dbf39f16..399d1871a 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/items/ConversationItem.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/items/ConversationItem.kt @@ -50,9 +50,10 @@ import com.nextcloud.talk.models.json.conversations.Conversation import com.nextcloud.talk.models.json.conversations.Conversation.ConversationType import com.nextcloud.talk.ui.StatusDrawable import com.nextcloud.talk.ui.theme.ViewThemeUtils +import com.nextcloud.talk.utils.SpreedFeatures import com.nextcloud.talk.utils.ConversationUtils import com.nextcloud.talk.utils.DisplayUtils -import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew.hasSpreedFeatureCapability +import com.nextcloud.talk.utils.CapabilitiesUtil.hasSpreedFeatureCapability import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.items.AbstractFlexibleItem import eu.davidea.flexibleadapter.items.IFilterable @@ -312,7 +313,7 @@ class ConversationItem( if (model.type === ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL) { viewThemeUtils.material.colorChipBackground(holder.binding.dialogUnreadBubble) } else if (model.unreadMention) { - if (hasSpreedFeatureCapability(user, "direct-mention-flag")) { + if (hasSpreedFeatureCapability(user.capabilities?.spreedCapability!!, SpreedFeatures.DIRECT_MENTION_FLAG)) { if (model.unreadMentionDirect!!) { viewThemeUtils.material.colorChipBackground(holder.binding.dialogUnreadBubble) } else { diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/CallStartedViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/CallStartedViewHolder.kt index 8aa0665cf..dfff2e714 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/CallStartedViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/CallStartedViewHolder.kt @@ -77,12 +77,12 @@ class CallStartedViewHolder(incomingView: View, payload: Any) : val user = userManager.currentUser.blockingGet() val url: String = if (message.actorType == "guests" || message.actorType == "guest") { ApiUtils.getUrlForGuestAvatar( - user!!.baseUrl, + user!!.baseUrl!!, message.actorDisplayName, true ) } else { - ApiUtils.getUrlForAvatar(user!!.baseUrl, message.actorDisplayName, false) + ApiUtils.getUrlForAvatar(user!!.baseUrl!!, message.actorDisplayName, false) } val imageRequest: ImageRequest = ImageRequest.Builder(context) diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingLinkPreviewMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingLinkPreviewMessageViewHolder.kt index 5af8760c5..ed2662ff9 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingLinkPreviewMessageViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingLinkPreviewMessageViewHolder.kt @@ -188,7 +188,7 @@ class IncomingLinkPreviewMessageViewHolder(incomingView: View, payload: Any) : binding.messageQuote.quotedMessageImage.load(it) { addHeader( "Authorization", - ApiUtils.getCredentials(message.activeUser!!.username, message.activeUser!!.token) + ApiUtils.getCredentials(message.activeUser!!.username, message.activeUser!!.token)!! ) } } ?: run { diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingLocationMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingLocationMessageViewHolder.kt index a88a1de93..aa9ab400c 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingLocationMessageViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingLocationMessageViewHolder.kt @@ -172,7 +172,7 @@ class IncomingLocationMessageViewHolder(incomingView: View, payload: Any) : binding.messageQuote.quotedMessageImage.load(it) { addHeader( "Authorization", - ApiUtils.getCredentials(message.activeUser!!.username, message.activeUser!!.token) + ApiUtils.getCredentials(message.activeUser!!.username, message.activeUser!!.token)!! ) } } ?: run { diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt index 148675646..a3ee9c61e 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt @@ -195,7 +195,7 @@ class IncomingPollMessageViewHolder(incomingView: View, payload: Any) : binding.messageQuote.quotedMessageImage.load(it) { addHeader( "Authorization", - ApiUtils.getCredentials(message.activeUser!!.username, message.activeUser!!.token) + ApiUtils.getCredentials(message.activeUser!!.username, message.activeUser!!.token)!! ) } } ?: run { diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingTextMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingTextMessageViewHolder.kt index 4533c8816..1bfb832df 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingTextMessageViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingTextMessageViewHolder.kt @@ -197,7 +197,7 @@ class IncomingTextMessageViewHolder(itemView: View, payload: Any) : binding.messageQuote.quotedMessageImage.load(it) { addHeader( "Authorization", - ApiUtils.getCredentials(message.activeUser!!.username, message.activeUser!!.token) + ApiUtils.getCredentials(message.activeUser!!.username, message.activeUser!!.token)!! ) } } ?: run { diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingVoiceMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingVoiceMessageViewHolder.kt index d13a69fba..e09bae1fb 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingVoiceMessageViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingVoiceMessageViewHolder.kt @@ -301,7 +301,7 @@ class IncomingVoiceMessageViewHolder(incomingView: View, payload: Any) : binding.messageQuote.quotedMessageImage.load(it) { addHeader( "Authorization", - ApiUtils.getCredentials(message.activeUser!!.username, message.activeUser!!.token) + ApiUtils.getCredentials(message.activeUser!!.username, message.activeUser!!.token)!! ) } } ?: run { diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/LinkPreview.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/LinkPreview.kt index 8ddd8a52e..03fd4f995 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/LinkPreview.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/LinkPreview.kt @@ -45,8 +45,8 @@ class LinkPreview { binding.referenceThumbImage.setImageDrawable(null) if (!message.extractedUrlToPreview.isNullOrEmpty()) { - val credentials: String = ApiUtils.getCredentials(message.activeUser?.username, message.activeUser?.token) - val openGraphLink = ApiUtils.getUrlForOpenGraph(message.activeUser?.baseUrl) + val credentials: String = ApiUtils.getCredentials(message.activeUser?.username, message.activeUser?.token)!! + val openGraphLink = ApiUtils.getUrlForOpenGraph(message.activeUser?.baseUrl!!) ncApi.getOpenGraph( credentials, openGraphLink, diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingLinkPreviewMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingLinkPreviewMessageViewHolder.kt index 7a1f9aa81..c92a4ecca 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingLinkPreviewMessageViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingLinkPreviewMessageViewHolder.kt @@ -161,7 +161,7 @@ class OutcomingLinkPreviewMessageViewHolder(outcomingView: View, payload: Any) : binding.messageQuote.quotedMessageImage.load(it) { addHeader( "Authorization", - ApiUtils.getCredentials(message.activeUser!!.username, message.activeUser!!.token) + ApiUtils.getCredentials(message.activeUser!!.username, message.activeUser!!.token)!! ) } } ?: run { diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingLocationMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingLocationMessageViewHolder.kt index bfea357d4..c1f3587d8 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingLocationMessageViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingLocationMessageViewHolder.kt @@ -213,7 +213,7 @@ class OutcomingLocationMessageViewHolder(incomingView: View) : binding.messageQuote.quotedMessageImage.load(it) { addHeader( "Authorization", - ApiUtils.getCredentials(message.activeUser!!.username, message.activeUser!!.token) + ApiUtils.getCredentials(message.activeUser!!.username, message.activeUser!!.token)!! ) } } ?: run { diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingPollMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingPollMessageViewHolder.kt index dcc0ca7c7..53a7bc683 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingPollMessageViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingPollMessageViewHolder.kt @@ -175,7 +175,7 @@ class OutcomingPollMessageViewHolder(outcomingView: View, payload: Any) : binding.messageQuote.quotedMessageImage.load(it) { addHeader( "Authorization", - ApiUtils.getCredentials(message.activeUser!!.username, message.activeUser!!.token) + ApiUtils.getCredentials(message.activeUser!!.username, message.activeUser!!.token)!! ) } } ?: run { diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingTextMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingTextMessageViewHolder.kt index 232b9e433..41e805077 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingTextMessageViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingTextMessageViewHolder.kt @@ -170,7 +170,7 @@ class OutcomingTextMessageViewHolder(itemView: View) : OutcomingTextMessageViewH binding.messageQuote.quotedMessageImage.load(it) { addHeader( "Authorization", - ApiUtils.getCredentials(message.activeUser!!.username, message.activeUser!!.token) + ApiUtils.getCredentials(message.activeUser!!.username, message.activeUser!!.token)!! ) } } ?: run { diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingVoiceMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingVoiceMessageViewHolder.kt index 1effa576a..6a61a7365 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingVoiceMessageViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingVoiceMessageViewHolder.kt @@ -285,7 +285,7 @@ class OutcomingVoiceMessageViewHolder(outcomingView: View) : binding.messageQuote.quotedMessageImage.load(it) { addHeader( "Authorization", - ApiUtils.getCredentials(message.activeUser!!.username, message.activeUser!!.token) + ApiUtils.getCredentials(message.activeUser!!.username, message.activeUser!!.token)!! ) } } ?: run { diff --git a/app/src/main/java/com/nextcloud/talk/api/NcApi.java b/app/src/main/java/com/nextcloud/talk/api/NcApi.java index cbc446eb1..c2c65e17c 100644 --- a/app/src/main/java/com/nextcloud/talk/api/NcApi.java +++ b/app/src/main/java/com/nextcloud/talk/api/NcApi.java @@ -24,6 +24,7 @@ package com.nextcloud.talk.api; import com.nextcloud.talk.models.json.capabilities.CapabilitiesOverall; +import com.nextcloud.talk.models.json.capabilities.RoomCapabilitiesOverall; import com.nextcloud.talk.models.json.chat.ChatOverall; import com.nextcloud.talk.models.json.chat.ChatOverallSingleMessage; import com.nextcloud.talk.models.json.chat.ChatShareOverall; @@ -367,6 +368,10 @@ public interface NcApi { @GET Observable getCapabilities(@Url String url); + @GET + Observable getRoomCapabilities(@Header("Authorization") String authorization, + @Url String url); + /* QueryMap items are as follows: - "lookIntoFuture": int (0 or 1), diff --git a/app/src/main/java/com/nextcloud/talk/callnotification/CallNotificationActivity.kt b/app/src/main/java/com/nextcloud/talk/callnotification/CallNotificationActivity.kt index 5d88fdc64..84d1c33e5 100644 --- a/app/src/main/java/com/nextcloud/talk/callnotification/CallNotificationActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/callnotification/CallNotificationActivity.kt @@ -50,6 +50,7 @@ import com.nextcloud.talk.models.domain.ConversationType import com.nextcloud.talk.models.json.participants.Participant import com.nextcloud.talk.users.UserManager import com.nextcloud.talk.utils.ApiUtils +import com.nextcloud.talk.utils.SpreedFeatures import com.nextcloud.talk.utils.ConversationUtils import com.nextcloud.talk.utils.NotificationUtils import com.nextcloud.talk.utils.ParticipantPermissions @@ -57,7 +58,7 @@ import com.nextcloud.talk.utils.bundle.BundleKeys import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_CALL_VOICE_ONLY import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_CONVERSATION_NAME import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_TOKEN -import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew.hasSpreedFeatureCapability +import com.nextcloud.talk.utils.CapabilitiesUtil.hasSpreedFeatureCapability import io.reactivex.disposables.Disposable import okhttp3.Cache import java.io.IOException @@ -148,10 +149,10 @@ class CallNotificationActivity : CallBaseActivity() { private fun initObservers() { val apiVersion = ApiUtils.getConversationApiVersion( - userBeingCalled, + userBeingCalled!!, intArrayOf( - ApiUtils.APIv4, - ApiUtils.APIv3, + ApiUtils.API_V4, + ApiUtils.API_V3, 1 ) ) @@ -186,10 +187,10 @@ class CallNotificationActivity : CallBaseActivity() { showAnswerControls() - if (apiVersion >= ApiUtils.APIv3) { + if (apiVersion >= ApiUtils.API_V3) { val hasCallFlags = hasSpreedFeatureCapability( - userBeingCalled, - "conversation-call-flags" + userBeingCalled?.capabilities?.spreedCapability!!, + SpreedFeatures.CONVERSATION_CALL_FLAGS ) if (hasCallFlags) { if (isInCallWithVideo(currentConversation!!.callFlag)) { @@ -243,7 +244,7 @@ class CallNotificationActivity : CallBaseActivity() { originalBundle!!.putString(KEY_CONVERSATION_NAME, currentConversation!!.displayName) val participantPermission = ParticipantPermissions( - userBeingCalled!!, + userBeingCalled!!.capabilities!!.spreedCapability!!, currentConversation!! ) originalBundle!!.putBoolean( diff --git a/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt b/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt index 9dc649dec..6233b144f 100644 --- a/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt @@ -167,6 +167,7 @@ import com.nextcloud.talk.models.domain.ConversationReadOnlyState import com.nextcloud.talk.models.domain.ConversationType import com.nextcloud.talk.models.domain.LobbyState import com.nextcloud.talk.models.domain.ObjectType +import com.nextcloud.talk.models.json.capabilities.SpreedCapability import com.nextcloud.talk.models.json.chat.ChatMessage import com.nextcloud.talk.models.json.chat.ChatOverall import com.nextcloud.talk.models.json.chat.ReadStatus @@ -192,6 +193,7 @@ import com.nextcloud.talk.ui.recyclerview.MessageSwipeActions import com.nextcloud.talk.ui.recyclerview.MessageSwipeCallback import com.nextcloud.talk.utils.ApiUtils import com.nextcloud.talk.utils.AudioUtils +import com.nextcloud.talk.utils.SpreedFeatures import com.nextcloud.talk.utils.ContactUtils import com.nextcloud.talk.utils.ConversationUtils import com.nextcloud.talk.utils.DateConstants @@ -218,7 +220,7 @@ 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.bundle.BundleKeys.KEY_START_CALL_AFTER_ROOM_SWITCH import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_SWITCH_TO_ROOM -import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew +import com.nextcloud.talk.utils.CapabilitiesUtil import com.nextcloud.talk.utils.database.user.CurrentUserProviderNew import com.nextcloud.talk.utils.permissions.PlatformPermissionUtil import com.nextcloud.talk.utils.rx.DisposableSet @@ -301,6 +303,8 @@ class ChatActivity : var sessionIdAfterRoomJoined: String? = null lateinit var roomToken: String var conversationUser: User? = null + lateinit var spreedCapabilities: SpreedCapability + var chatApiVersion: Int = 1 private var roomPassword: String = "" var credentials: String? = null var currentConversation: ConversationModel? = null @@ -351,6 +355,7 @@ class ChatActivity : RELEASED, ERROR } + private val editableBehaviorSubject = BehaviorSubject.createDefault(false) private val editedTextBehaviorSubject = BehaviorSubject.createDefault("") @@ -541,14 +546,6 @@ class ChatActivity : } this.lifecycle.addObserver(AudioUtils) this.lifecycle.addObserver(ChatViewModel.LifeCycleObserver) - - chatViewModel.refreshChatParams( - setupFieldsForPullChatMessages( - false, - 0, - false - ) - ) } override fun onStop() { @@ -587,6 +584,30 @@ class ChatActivity : is ChatViewModel.GetRoomSuccessState -> { currentConversation = state.conversationModel logConversationInfos("GetRoomSuccessState") + chatViewModel.getCapabilities(conversationUser!!, roomToken, currentConversation!!) + } + + is ChatViewModel.GetRoomErrorState -> { + Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show() + } + + else -> {} + } + } + + chatViewModel.getCapabilitiesViewState.observe(this) { state -> + when (state) { + is ChatViewModel.GetCapabilitiesSuccessState -> { + spreedCapabilities = state.spreedCapabilities + chatApiVersion = ApiUtils.getChatApiVersion(spreedCapabilities, intArrayOf(1)) + + initMessageInputView() + + if (conversationUser?.userId != "?" && + CapabilitiesUtil.hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.MENTION_FLAG) + ) { + binding.chatToolbar.setOnClickListener { v -> showConversationInfoScreen() } + } if (adapter == null) { initAdapter() @@ -597,7 +618,7 @@ class ChatActivity : loadAvatarForStatusBar() setActionBarTitle() - participantPermissions = ParticipantPermissions(conversationUser!!, currentConversation!!) + participantPermissions = ParticipantPermissions(spreedCapabilities, currentConversation!!) setupSwipeToReply() setupMentionAutocomplete() @@ -626,9 +647,17 @@ class ChatActivity : }, delayForRecursiveCall ) + + chatViewModel.refreshChatParams( + setupFieldsForPullChatMessages( + false, + 0, + false + ) + ) } - is ChatViewModel.GetRoomErrorState -> { + is ChatViewModel.GetCapabilitiesErrorState -> { Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show() } @@ -716,6 +745,7 @@ class ChatActivity : } binding.messagesListView.smoothScrollToPosition(0) } + is ChatViewModel.SendChatMessageErrorState -> { if (state.e is HttpException) { val code = state.e.code() @@ -730,6 +760,7 @@ class ChatActivity : } } } + else -> {} } } @@ -753,9 +784,11 @@ class ChatActivity : ) ) } + is ChatViewModel.DeleteChatMessageErrorState -> { Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show() } + else -> {} } } @@ -774,24 +807,22 @@ class ChatActivity : startActivity(chatIntent) } } + is ChatViewModel.CreateRoomErrorState -> { Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show() } + else -> {} } } - var apiVersion = 1 - // FIXME this is a best guess, guests would need to get the capabilities themselves - if (conversationUser != null) { - apiVersion = ApiUtils.getChatApiVersion(conversationUser, intArrayOf(1)) - } - - chatViewModel.getFieldMapForChat.observe(this) { _ -> - chatViewModel.pullChatMessages( - credentials!!, - ApiUtils.getUrlForChat(apiVersion, conversationUser?.baseUrl, roomToken) - ) + chatViewModel.getFieldMapForChat.observe(this) { fieldMap -> + if (fieldMap.isNotEmpty()) { + chatViewModel.pullChatMessages( + credentials!!, + ApiUtils.getUrlForChat(chatApiVersion, conversationUser?.baseUrl, roomToken) + ) + } } chatViewModel.pullChatMessageViewState.observe(this) { state -> @@ -865,6 +896,7 @@ class ChatActivity : ) ) } + HTTP_CODE_NOT_MODIFIED -> { processHeaderChatLastGiven(state.response, state.lookIntoFuture) chatViewModel.refreshChatParams( @@ -875,6 +907,7 @@ class ChatActivity : ) ) } + HTTP_CODE_PRECONDITION_FAILED -> { processHeaderChatLastGiven(state.response, state.lookIntoFuture) chatViewModel.refreshChatParams( @@ -885,6 +918,7 @@ class ChatActivity : ) ) } + else -> {} } @@ -898,12 +932,15 @@ class ChatActivity : collapseSystemMessages() } } + is ChatViewModel.PullChatMessageCompleteState -> { Log.d(TAG, "PullChatMessageCompleted") } + is ChatViewModel.PullChatMessageErrorState -> { Log.d(TAG, "PullChatMessageError") } + else -> {} } } @@ -916,6 +953,7 @@ class ChatActivity : state.reactionDeletedModel.emoji ) } + else -> {} } } @@ -928,6 +966,7 @@ class ChatActivity : state.reactionAddedModel.emoji ) } + else -> {} } } @@ -943,6 +982,7 @@ class ChatActivity : Snackbar.LENGTH_LONG ).show() } + HTTP_FORBIDDEN -> { Snackbar.make( binding.root, @@ -950,6 +990,7 @@ class ChatActivity : Snackbar.LENGTH_LONG ).show() } + HTTP_NOT_FOUND -> { Snackbar.make( binding.root, @@ -960,9 +1001,11 @@ class ChatActivity : } clearEditUI() } + is ChatViewModel.EditMessageErrorState -> { Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show() } + else -> {} } } @@ -980,12 +1023,6 @@ class ChatActivity : webSocketInstance?.getSignalingMessageReceiver()?.addListener(localParticipantMessageListener) webSocketInstance?.getSignalingMessageReceiver()?.addListener(conversationMessageListener) - if (conversationUser?.userId != "?" && - CapabilitiesUtilNew.hasSpreedFeatureCapability(conversationUser, "mention-flag") - ) { - binding.chatToolbar.setOnClickListener { v -> showConversationInfoScreen() } - } - initSmileyKeyboardToggler() themeMessageInputView() @@ -1053,7 +1090,6 @@ class ChatActivity : } }) - initMessageInputView() loadAvatarForStatusBar() setActionBarTitle() viewThemeUtils.material.colorToolbarOverflowIcon(binding.chatToolbar) @@ -1061,7 +1097,7 @@ class ChatActivity : private fun initMessageInputView() { val filters = arrayOfNulls(1) - val lengthFilter = CapabilitiesUtilNew.getMessageMaxLength(conversationUser) + val lengthFilter = CapabilitiesUtil.getMessageMaxLength(spreedCapabilities) binding.editView.editMessageView.visibility = View.GONE @@ -1160,7 +1196,7 @@ class ChatActivity : clearEditUI() } - if (CapabilitiesUtilNew.hasSpreedFeatureCapability(conversationUser, "silent-send")) { + if (CapabilitiesUtil.hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.SILENT_SEND)) { binding.messageInputView.button?.setOnLongClickListener { showSendButtonMenu() true @@ -1175,14 +1211,14 @@ class ChatActivity : var apiVersion = 1 // FIXME Fix API checking with guests? if (conversationUser != null) { - apiVersion = ApiUtils.getChatApiVersion(conversationUser, intArrayOf(1)) + apiVersion = ApiUtils.getChatApiVersion(spreedCapabilities, intArrayOf(1)) } chatViewModel.editChatMessage( credentials!!, ApiUtils.getUrlForChatMessage( apiVersion, - conversationUser?.baseUrl, + conversationUser?.baseUrl!!, roomToken, message.id ), @@ -2016,7 +2052,7 @@ class ChatActivity : private fun isTypingStatusEnabled(): Boolean { return webSocketInstance != null && - !CapabilitiesUtilNew.isTypingStatusPrivate(conversationUser!!) + !CapabilitiesUtil.isTypingStatusPrivate(conversationUser!!) } private fun setupSwipeToReply() { @@ -2048,7 +2084,7 @@ class ChatActivity : if (isOneToOneConversation()) { var url = ApiUtils.getUrlForAvatar( - conversationUser!!.baseUrl, + conversationUser!!.baseUrl!!, currentConversation!!.name, true ) @@ -2097,18 +2133,19 @@ class ChatActivity : } val credentials = ApiUtils.getCredentials(conversationUser!!.username, conversationUser!!.token) - - context.imageLoader.enqueue( - ImageRequest.Builder(context) - .data(url) - .addHeader("Authorization", credentials) - .transformations(CircleCropTransformation()) - .crossfade(true) - .target(target) - .memoryCachePolicy(CachePolicy.DISABLED) - .diskCachePolicy(CachePolicy.DISABLED) - .build() - ) + if (credentials != null) { + context.imageLoader.enqueue( + ImageRequest.Builder(context) + .data(url) + .addHeader("Authorization", credentials) + .transformations(CircleCropTransformation()) + .crossfade(true) + .target(target) + .memoryCachePolicy(CachePolicy.DISABLED) + .diskCachePolicy(CachePolicy.DISABLED) + .build() + ) + } } else { binding.chatToolbar.findViewById(R.id.chat_toolbar_avatar_container).visibility = View.GONE } @@ -2463,7 +2500,10 @@ class ChatActivity : val baseUrl = message.activeUser!!.baseUrl val userId = message.activeUser!!.userId - val attachmentFolder = CapabilitiesUtilNew.getAttachmentFolder(message.activeUser!!) + val attachmentFolder = CapabilitiesUtil.getAttachmentFolder( + message.activeUser!!.capabilities!! + .spreedCapability!! + ) val fileName = message.selectedIndividualHashMap!!["name"] var size = message.selectedIndividualHashMap!!["size"] if (size == null) { @@ -2801,16 +2841,16 @@ class ChatActivity : private fun shouldShowLobby(): Boolean { if (currentConversation != null) { - return CapabilitiesUtilNew.hasSpreedFeatureCapability(conversationUser, "webinary-lobby") && + return CapabilitiesUtil.hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.WEBINARY_LOBBY) && currentConversation?.lobbyState == LobbyState.LOBBY_STATE_MODERATORS_ONLY && - !ConversationUtils.canModerate(currentConversation!!, conversationUser!!) && + !ConversationUtils.canModerate(currentConversation!!, spreedCapabilities) && !participantPermissions.canIgnoreLobby() } return false } private fun disableCallButtons() { - if (CapabilitiesUtilNew.isAbleToCall(conversationUser)) { + if (CapabilitiesUtil.isAbleToCall(spreedCapabilities)) { if (conversationVoiceCallMenuItem != null && conversationVideoMenuItem != null) { conversationVoiceCallMenuItem?.icon?.alpha = SEMI_TRANSPARENT_INT conversationVideoMenuItem?.icon?.alpha = SEMI_TRANSPARENT_INT @@ -2823,7 +2863,7 @@ class ChatActivity : } private fun enableCallButtons() { - if (CapabilitiesUtilNew.isAbleToCall(conversationUser)) { + if (CapabilitiesUtil.isAbleToCall(spreedCapabilities)) { if (conversationVoiceCallMenuItem != null && conversationVideoMenuItem != null) { conversationVoiceCallMenuItem?.icon?.alpha = FULLY_OPAQUE_INT conversationVideoMenuItem?.icon?.alpha = FULLY_OPAQUE_INT @@ -2843,7 +2883,7 @@ class ChatActivity : private fun checkLobbyState() { if (currentConversation != null && - ConversationUtils.isLobbyViewApplicable(currentConversation!!, conversationUser!!) + ConversationUtils.isLobbyViewApplicable(currentConversation!!, spreedCapabilities) ) { if (shouldShowLobby()) { binding.lobby.lobbyView.visibility = View.VISIBLE @@ -3252,6 +3292,7 @@ class ChatActivity : val intent = Intent(this, LocationPickerActivity::class.java) intent.putExtra(KEY_ROOM_TOKEN, roomToken) + intent.putExtra(BundleKeys.KEY_CHAT_API_VERSION, chatApiVersion) startActivity(intent) } @@ -3270,7 +3311,7 @@ class ChatActivity : val elevation = MENTION_AUTO_COMPLETE_ELEVATION resources?.let { val backgroundDrawable = ColorDrawable(it.getColor(R.color.bg_default, null)) - val presenter = MentionAutocompletePresenter(this, roomToken) + val presenter = MentionAutocompletePresenter(this, roomToken, chatApiVersion) val callback = MentionAutocompleteCallback( this, conversationUser!!, @@ -3430,12 +3471,6 @@ class ChatActivity : if (!validSessionId()) { Log.d(TAG, "sessionID was not valid -> joinRoom") - var apiVersion = 1 - // FIXME Fix API checking with guests? - if (conversationUser != null) { - apiVersion = ApiUtils.getConversationApiVersion(conversationUser, intArrayOf(ApiUtils.APIv4, 1)) - } - val startNanoTime = System.nanoTime() Log.d(TAG, "joinRoomWithPassword - joinRoom - calling: $startNanoTime") @@ -3458,7 +3493,7 @@ class ChatActivity : var apiVersion = 1 // FIXME Fix API checking with guests? if (conversationUser != null) { - apiVersion = ApiUtils.getConversationApiVersion(conversationUser, intArrayOf(ApiUtils.APIv4, 1)) + apiVersion = ApiUtils.getConversationApiVersion(conversationUser!!, intArrayOf(ApiUtils.API_V4, 1)) } val startNanoTime = System.nanoTime() @@ -3467,7 +3502,7 @@ class ChatActivity : credentials!!, ApiUtils.getUrlForParticipantsActive( apiVersion, - conversationUser?.baseUrl, + conversationUser?.baseUrl!!, roomToken ), funToCallWhenLeaveSuccessful @@ -3513,11 +3548,9 @@ class ChatActivity : private fun sendMessage(message: CharSequence, replyTo: Int?, sendWithoutNotification: Boolean) { if (conversationUser != null) { - val apiVersion = ApiUtils.getChatApiVersion(conversationUser, intArrayOf(1)) - chatViewModel.sendChatMessage( credentials!!, - ApiUtils.getUrlForChat(apiVersion, conversationUser!!.baseUrl, roomToken), + ApiUtils.getUrlForChat(chatApiVersion, conversationUser!!.baseUrl!!, roomToken), message, conversationUser!!.displayName ?: "", replyTo ?: 0, @@ -3637,7 +3670,7 @@ class ChatActivity : } } - if (CapabilitiesUtilNew.hasSpreedFeatureCapability(conversationUser, "message-expiration")) { + if (CapabilitiesUtil.hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.MESSAGE_EXPIRATION)) { deleteExpiredMessages() } } @@ -3871,53 +3904,58 @@ class ChatActivity : if (conversationUser?.userId == "?") { menu.removeItem(R.id.conversation_info) } else { - conversationInfoMenuItem = menu.findItem(R.id.conversation_info) - - if (CapabilitiesUtilNew.hasSpreedFeatureCapability(conversationUser, "rich-object-list-media")) { - conversationSharedItemsItem = menu.findItem(R.id.shared_items) - } else { - menu.removeItem(R.id.shared_items) - } - loadAvatarForStatusBar() setActionBarTitle() } - - if (CapabilitiesUtilNew.isAbleToCall(conversationUser)) { - conversationVoiceCallMenuItem = menu.findItem(R.id.conversation_voice_call) - conversationVideoMenuItem = menu.findItem(R.id.conversation_video_call) - - if (CapabilitiesUtilNew.hasSpreedFeatureCapability(conversationUser, "silent-call")) { - Handler().post { - findViewById(R.id.conversation_voice_call)?.setOnLongClickListener { - showCallButtonMenu(true) - true - } - } - - Handler().post { - findViewById(R.id.conversation_video_call)?.setOnLongClickListener { - showCallButtonMenu(false) - true - } - } - } - } else { - menu.removeItem(R.id.conversation_video_call) - menu.removeItem(R.id.conversation_voice_call) - } return true } override fun onPrepareOptionsMenu(menu: Menu): Boolean { super.onPrepareOptionsMenu(menu) - conversationUser?.let { - if (CapabilitiesUtilNew.hasSpreedFeatureCapability(it, "read-only-rooms")) { + + if (this::spreedCapabilities.isInitialized) { + if (CapabilitiesUtil.hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.READ_ONLY_ROOMS)) { checkShowCallButtons() } + val searchItem = menu.findItem(R.id.conversation_search) - searchItem.isVisible = CapabilitiesUtilNew.isUnifiedSearchAvailable(it) + searchItem.isVisible = CapabilitiesUtil.isUnifiedSearchAvailable(spreedCapabilities) + + if (CapabilitiesUtil.hasSpreedFeatureCapability( + spreedCapabilities, + SpreedFeatures.RICH_OBJECT_LIST_MEDIA + ) + ) { + conversationSharedItemsItem = menu.findItem(R.id.shared_items) + } else { + menu.removeItem(R.id.shared_items) + } + + if (CapabilitiesUtil.isAbleToCall(spreedCapabilities)) { + conversationVoiceCallMenuItem = menu.findItem(R.id.conversation_voice_call) + conversationVideoMenuItem = menu.findItem(R.id.conversation_video_call) + + if (CapabilitiesUtil.hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.SILENT_CALL)) { + Handler().post { + findViewById(R.id.conversation_voice_call)?.setOnLongClickListener { + showCallButtonMenu(true) + true + } + } + + Handler().post { + findViewById(R.id.conversation_video_call)?.setOnLongClickListener { + showCallButtonMenu(false) + true + } + } + } + } else { + menu.removeItem(R.id.conversation_video_call) + menu.removeItem(R.id.conversation_voice_call) + } } + return true } @@ -4054,7 +4092,7 @@ class ChatActivity : private fun startACall(isVoiceOnlyCall: Boolean, callWithoutNotification: Boolean) { currentConversation?.let { if (conversationUser != null) { - val pp = ParticipantPermissions(conversationUser!!, it) + val pp = ParticipantPermissions(spreedCapabilities, it) if (!pp.canStartCall() && currentConversation?.hasCall == false) { Snackbar.make(binding.root, R.string.startCallForbidden, Snackbar.LENGTH_LONG).show() } else { @@ -4074,7 +4112,7 @@ class ChatActivity : bundle.putString(KEY_ROOM_TOKEN, roomToken) bundle.putString(KEY_ROOM_ID, roomId) bundle.putString(BundleKeys.KEY_CONVERSATION_PASSWORD, roomPassword) - bundle.putString(BundleKeys.KEY_MODIFIED_BASE_URL, conversationUser?.baseUrl) + bundle.putString(BundleKeys.KEY_MODIFIED_BASE_URL, conversationUser?.baseUrl!!) bundle.putString(KEY_CONVERSATION_NAME, it.displayName) bundle.putInt(KEY_RECORDING_STATE, it.callRecording) bundle.putBoolean(KEY_IS_MODERATOR, ConversationUtils.isParticipantOwnerOrModerator(it)) @@ -4147,7 +4185,8 @@ class ChatActivity : conversationUser, currentConversation, isShowMessageDeletionButton(message), - participantPermissions.hasChatPermission() + participantPermissions.hasChatPermission(), + spreedCapabilities ).show() } } @@ -4156,7 +4195,7 @@ class ChatActivity : return ChatMessage.MessageType.SYSTEM_MESSAGE == message.getCalculateMessageType() } - fun deleteMessage(message: IMessage?) { + fun deleteMessage(message: IMessage) { if (!participantPermissions.hasChatPermission()) { Log.w( TAG, @@ -4168,28 +4207,28 @@ class ChatActivity : var apiVersion = 1 // FIXME Fix API checking with guests? if (conversationUser != null) { - apiVersion = ApiUtils.getChatApiVersion(conversationUser, intArrayOf(1)) + apiVersion = ApiUtils.getChatApiVersion(spreedCapabilities, intArrayOf(1)) } chatViewModel.deleteChatMessages( credentials!!, ApiUtils.getUrlForChatMessage( apiVersion, - conversationUser?.baseUrl, + conversationUser?.baseUrl!!, roomToken, - message?.id + message.id!! ), - message?.id!! + message.id!! ) } } fun replyPrivately(message: IMessage?) { val apiVersion = - ApiUtils.getConversationApiVersion(conversationUser, intArrayOf(ApiUtils.APIv4, 1)) + ApiUtils.getConversationApiVersion(conversationUser!!, intArrayOf(ApiUtils.API_V4, 1)) val retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom( apiVersion, - conversationUser?.baseUrl, + conversationUser?.baseUrl!!, "1", null, message?.user?.id?.substring(INVITE_LENGTH), @@ -4215,10 +4254,14 @@ class ChatActivity : fun remindMeLater(message: ChatMessage?) { Log.d(TAG, "remindMeLater called") + + val chatApiVersion = ApiUtils.getChatApiVersion(spreedCapabilities, intArrayOf(ApiUtils.API_V1, 1)) + val newFragment: DialogFragment = DateTimePickerFragment.newInstance( roomToken, message!!.id, - chatViewModel + chatViewModel, + chatApiVersion ) newFragment.show(supportFragmentManager, DateTimePickerFragment.TAG) } @@ -4229,8 +4272,8 @@ class ChatActivity : chatViewModel.setChatReadMarker( credentials!!, ApiUtils.getUrlForChatReadMarker( - ApiUtils.getChatApiVersion(conversationUser, intArrayOf(ApiUtils.APIv1)), - conversationUser?.baseUrl, + ApiUtils.getChatApiVersion(spreedCapabilities, intArrayOf(ApiUtils.API_V1)), + conversationUser?.baseUrl!!, roomToken ), chatMessage.previousMessageId @@ -4312,10 +4355,10 @@ class ChatActivity : } fun shareToNotes(message: ChatMessage, roomToken: String) { - val apiVersion = ApiUtils.getChatApiVersion(conversationUser, intArrayOf(1)) + val apiVersion = ApiUtils.getChatApiVersion(spreedCapabilities, intArrayOf(1)) val type = message.getCalculateMessageType() var shareUri: Uri? = null - var data: HashMap? + val data: HashMap? var metaData: String = "" var objectId: String = "" if (message.hasFileAttachment()) { @@ -4335,9 +4378,9 @@ class ChatActivity : } else if (message.hasGeoLocation()) { data = message.messageParameters?.get("object") objectId = data?.get("id")!! - val name = data.get("name")!! - val lat = data.get("latitude")!! - val lon = data.get("longitude")!! + val name = data["name"]!! + val lat = data["latitude"]!! + val lon = data["longitude"]!! metaData = "{\"type\":\"geo-location\",\"id\":\"geo:$lat,$lon\",\"latitude\":\"$lat\"," + "\"longitude\":\"$lon\",\"name\":\"$name\"}" @@ -4348,6 +4391,7 @@ class ChatActivity : uploadFile(shareUri.toString(), true, token = roomToken) Snackbar.make(binding.root, R.string.nc_message_sent, Snackbar.LENGTH_SHORT).show() } + ChatMessage.MessageType.SINGLE_NC_ATTACHMENT_MESSAGE -> { val caption = if (message.message != "{file}") message.message else "" if (null != shareUri) { @@ -4364,25 +4408,28 @@ class ChatActivity : } } } + ChatMessage.MessageType.SINGLE_NC_GEOLOCATION_MESSAGE -> { chatViewModel.shareLocationToNotes( credentials!!, - ApiUtils.getUrlToSendLocation(apiVersion, conversationUser!!.baseUrl, roomToken), + ApiUtils.getUrlToSendLocation(apiVersion, conversationUser!!.baseUrl!!, roomToken), "geo-location", objectId, metaData ) Snackbar.make(binding.root, R.string.nc_message_sent, Snackbar.LENGTH_SHORT).show() } + ChatMessage.MessageType.REGULAR_TEXT_MESSAGE -> { chatViewModel.shareToNotes( credentials!!, - ApiUtils.getUrlForChat(apiVersion, conversationUser!!.baseUrl, roomToken), + ApiUtils.getUrlForChat(apiVersion, conversationUser!!.baseUrl!!, roomToken), message.message!!, conversationUser!!.displayName!! ) Snackbar.make(binding.root, R.string.nc_message_sent, Snackbar.LENGTH_SHORT).show() } + else -> {} } } @@ -4456,7 +4503,10 @@ class ChatActivity : } private fun showMicrophoneButton(show: Boolean) { - if (show && CapabilitiesUtilNew.hasSpreedFeatureCapability(conversationUser, "voice-message-sharing")) { + if (show && CapabilitiesUtil.hasSpreedFeatureCapability( + spreedCapabilities, SpreedFeatures.VOICE_MESSAGE_SHARING + ) + ) { Log.d(TAG, "Microphone shown") binding.messageInputView.messageSendButton.visibility = View.GONE binding.messageInputView.recordAudioButton.visibility = View.VISIBLE @@ -4542,7 +4592,7 @@ class ChatActivity : !isUserAllowedByPrivileges -> false message.systemMessageType != ChatMessage.SystemMessageType.DUMMY -> false message.isDeleted -> false - !CapabilitiesUtilNew.hasSpreedFeatureCapability(conversationUser, "delete-messages") -> false + !CapabilitiesUtil.hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.DELETE_MESSAGES) -> false !participantPermissions.hasChatPermission() -> false else -> true } @@ -4554,7 +4604,7 @@ class ChatActivity : val isUserAllowedByPrivileges = if (message.actorId == conversationUser!!.userId) { true } else { - ConversationUtils.canModerate(currentConversation!!, conversationUser!!) + ConversationUtils.canModerate(currentConversation!!, spreedCapabilities) } return isUserAllowedByPrivileges } @@ -4628,12 +4678,12 @@ class ChatActivity : var apiVersion = 1 // FIXME Fix API checking with guests? if (conversationUser != null) { - apiVersion = ApiUtils.getConversationApiVersion(conversationUser, intArrayOf(ApiUtils.APIv4, 1)) + apiVersion = ApiUtils.getConversationApiVersion(conversationUser!!, intArrayOf(ApiUtils.API_V4, 1)) } val retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom( apiVersion, - conversationUser?.baseUrl, + conversationUser?.baseUrl!!, "1", null, userMentionClickEvent.userId, diff --git a/app/src/main/java/com/nextcloud/talk/chat/data/ChatRepository.kt b/app/src/main/java/com/nextcloud/talk/chat/data/ChatRepository.kt index 88eae5d4e..db1fd1f4d 100644 --- a/app/src/main/java/com/nextcloud/talk/chat/data/ChatRepository.kt +++ b/app/src/main/java/com/nextcloud/talk/chat/data/ChatRepository.kt @@ -22,6 +22,7 @@ package com.nextcloud.talk.chat.data import com.nextcloud.talk.data.user.model.User import com.nextcloud.talk.models.domain.ConversationModel +import com.nextcloud.talk.models.json.capabilities.SpreedCapability import com.nextcloud.talk.models.json.chat.ChatOverallSingleMessage import com.nextcloud.talk.models.json.conversations.RoomOverall import com.nextcloud.talk.models.json.conversations.RoomsOverall @@ -33,10 +34,17 @@ import retrofit2.Response @Suppress("LongParameterList", "TooManyFunctions") interface ChatRepository { fun getRoom(user: User, roomToken: String): Observable + fun getCapabilities(user: User, roomToken: String): Observable fun joinRoom(user: User, roomToken: String, roomPassword: String): Observable - fun setReminder(user: User, roomToken: String, messageId: String, timeStamp: Int): Observable - fun getReminder(user: User, roomToken: String, messageId: String): Observable - fun deleteReminder(user: User, roomToken: String, messageId: String): Observable + fun setReminder( + user: User, + roomToken: String, + messageId: String, + timeStamp: Int, + chatApiVersion: Int + ): Observable + fun getReminder(user: User, roomToken: String, messageId: String, apiVersion: Int): Observable + fun deleteReminder(user: User, roomToken: String, messageId: String, apiVersion: Int): Observable fun shareToNotes( credentials: String, url: String, diff --git a/app/src/main/java/com/nextcloud/talk/chat/data/network/NetworkChatRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/chat/data/network/NetworkChatRepositoryImpl.kt index 95edcb21c..2fbb3a0c9 100644 --- a/app/src/main/java/com/nextcloud/talk/chat/data/network/NetworkChatRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/chat/data/network/NetworkChatRepositoryImpl.kt @@ -24,6 +24,7 @@ import com.nextcloud.talk.api.NcApi import com.nextcloud.talk.chat.data.ChatRepository import com.nextcloud.talk.data.user.model.User import com.nextcloud.talk.models.domain.ConversationModel +import com.nextcloud.talk.models.json.capabilities.SpreedCapability import com.nextcloud.talk.models.json.chat.ChatOverallSingleMessage import com.nextcloud.talk.models.json.conversations.RoomOverall import com.nextcloud.talk.models.json.conversations.RoomsOverall @@ -35,55 +36,78 @@ import retrofit2.Response class NetworkChatRepositoryImpl(private val ncApi: NcApi) : ChatRepository { override fun getRoom(user: User, roomToken: String): Observable { - val credentials: String = ApiUtils.getCredentials(user.username, user.token) - val apiVersion = ApiUtils.getConversationApiVersion(user, intArrayOf(ApiUtils.APIv4, ApiUtils.APIv3, 1)) + val credentials: String = ApiUtils.getCredentials(user.username, user.token)!! + val apiVersion = ApiUtils.getConversationApiVersion(user, intArrayOf(ApiUtils.API_V4, ApiUtils.API_V3, 1)) return ncApi.getRoom( credentials, - ApiUtils.getUrlForRoom(apiVersion, user.baseUrl, roomToken) + ApiUtils.getUrlForRoom(apiVersion, user.baseUrl!!, roomToken) ).map { ConversationModel.mapToConversationModel(it.ocs?.data!!) } } + override fun getCapabilities(user: User, roomToken: String): Observable { + val credentials: String = ApiUtils.getCredentials(user.username, user.token)!! + val apiVersion = ApiUtils.getConversationApiVersion(user, intArrayOf(ApiUtils.API_V4, ApiUtils.API_V3, 1)) + + return ncApi.getRoomCapabilities( + credentials, + ApiUtils.getUrlForRoomCapabilities(apiVersion, user.baseUrl!!, roomToken) + ).map { it.ocs?.data } + } + override fun joinRoom(user: User, roomToken: String, roomPassword: String): Observable { - val credentials: String = ApiUtils.getCredentials(user.username, user.token) - val apiVersion = ApiUtils.getConversationApiVersion(user, intArrayOf(ApiUtils.APIv4, 1)) + val credentials: String = ApiUtils.getCredentials(user.username, user.token)!! + val apiVersion = ApiUtils.getConversationApiVersion(user, intArrayOf(ApiUtils.API_V4, 1)) return ncApi.joinRoom( credentials, - ApiUtils.getUrlForParticipantsActive(apiVersion, user.baseUrl, roomToken), + ApiUtils.getUrlForParticipantsActive(apiVersion, user.baseUrl!!, roomToken), roomPassword ).map { ConversationModel.mapToConversationModel(it.ocs?.data!!) } } - override fun setReminder(user: User, roomToken: String, messageId: String, timeStamp: Int): Observable { - val credentials: String = ApiUtils.getCredentials(user.username, user.token) - val apiVersion = ApiUtils.getChatApiVersion(user, intArrayOf(ApiUtils.APIv1, 1)) + override fun setReminder( + user: User, + roomToken: String, + messageId: String, + timeStamp: Int, + chatApiVersion: Int + ): Observable { + val credentials: String = ApiUtils.getCredentials(user.username, user.token)!! return ncApi.setReminder( credentials, - ApiUtils.getUrlForReminder(user, roomToken, messageId, apiVersion), + ApiUtils.getUrlForReminder(user, roomToken, messageId, chatApiVersion), timeStamp ).map { it.ocs!!.data } } - override fun getReminder(user: User, roomToken: String, messageId: String): Observable { - val credentials: String = ApiUtils.getCredentials(user.username, user.token) - val apiVersion = ApiUtils.getChatApiVersion(user, intArrayOf(ApiUtils.APIv1, 1)) + override fun getReminder( + user: User, + roomToken: String, + messageId: String, + chatApiVersion: Int + ): Observable { + val credentials: String = ApiUtils.getCredentials(user.username, user.token)!! return ncApi.getReminder( credentials, - ApiUtils.getUrlForReminder(user, roomToken, messageId, apiVersion) + ApiUtils.getUrlForReminder(user, roomToken, messageId, chatApiVersion) ).map { it.ocs!!.data } } - override fun deleteReminder(user: User, roomToken: String, messageId: String): Observable { - val credentials: String = ApiUtils.getCredentials(user.username, user.token) - val apiVersion = ApiUtils.getChatApiVersion(user, intArrayOf(ApiUtils.APIv1, 1)) + override fun deleteReminder( + user: User, + roomToken: String, + messageId: String, + chatApiVersion: Int + ): Observable { + val credentials: String = ApiUtils.getCredentials(user.username, user.token)!! return ncApi.deleteReminder( credentials, - ApiUtils.getUrlForReminder(user, roomToken, messageId, apiVersion) + ApiUtils.getUrlForReminder(user, roomToken, messageId, chatApiVersion) ).map { it } diff --git a/app/src/main/java/com/nextcloud/talk/chat/viewmodels/ChatViewModel.kt b/app/src/main/java/com/nextcloud/talk/chat/viewmodels/ChatViewModel.kt index cbb392bb2..fadbe677b 100644 --- a/app/src/main/java/com/nextcloud/talk/chat/viewmodels/ChatViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/chat/viewmodels/ChatViewModel.kt @@ -31,6 +31,7 @@ import com.nextcloud.talk.data.user.model.User import com.nextcloud.talk.models.domain.ConversationModel import com.nextcloud.talk.models.domain.ReactionAddedModel import com.nextcloud.talk.models.domain.ReactionDeletedModel +import com.nextcloud.talk.models.json.capabilities.SpreedCapability import com.nextcloud.talk.models.json.chat.ChatMessage import com.nextcloud.talk.models.json.chat.ChatOverallSingleMessage import com.nextcloud.talk.models.json.conversations.RoomOverall @@ -105,6 +106,14 @@ class ChatViewModel @Inject constructor( val getRoomViewState: LiveData get() = _getRoomViewState + object GetCapabilitiesStartState : ViewState + object GetCapabilitiesErrorState : ViewState + open class GetCapabilitiesSuccessState(val spreedCapabilities: SpreedCapability) : ViewState + + private val _getCapabilitiesViewState: MutableLiveData = MutableLiveData(GetCapabilitiesStartState) + val getCapabilitiesViewState: LiveData + get() = _getCapabilitiesViewState + object JoinRoomStartState : ViewState object JoinRoomErrorState : ViewState open class JoinRoomSuccessState(val conversationModel: ConversationModel) : ViewState @@ -184,6 +193,36 @@ class ChatViewModel @Inject constructor( ?.subscribe(GetRoomObserver()) } + fun getCapabilities(user: User, token: String, conversationModel: ConversationModel) { + _getCapabilitiesViewState.value = GetCapabilitiesStartState + + if (conversationModel.remoteServer.isNullOrEmpty()) { + _getCapabilitiesViewState.value = GetCapabilitiesSuccessState(user.capabilities!!.spreedCapability!!) + } else { + chatRepository.getCapabilities(user, token) + .subscribeOn(Schedulers.io()) + ?.observeOn(AndroidSchedulers.mainThread()) + ?.subscribe(object : Observer { + override fun onSubscribe(d: Disposable) { + LifeCycleObserver.disposableSet.add(d) + } + + override fun onNext(spreedCapabilities: SpreedCapability) { + _getCapabilitiesViewState.value = GetCapabilitiesSuccessState(spreedCapabilities) + } + + override fun onError(e: Throwable) { + Log.e(TAG, "Error when fetching spreed capabilities", e) + _getCapabilitiesViewState.value = GetCapabilitiesErrorState + } + + override fun onComplete() { + // unused atm + } + }) + } + } + fun joinRoom(user: User, token: String, roomPassword: String) { _joinRoomViewState.value = JoinRoomStartState chatRepository.joinRoom(user, token, roomPassword) @@ -193,6 +232,43 @@ class ChatViewModel @Inject constructor( ?.subscribe(JoinRoomObserver()) } + fun setReminder(user: User, roomToken: String, messageId: String, timestamp: Int, chatApiVersion: Int) { + chatRepository.setReminder(user, roomToken, messageId, timestamp, chatApiVersion) + .subscribeOn(Schedulers.io()) + ?.observeOn(AndroidSchedulers.mainThread()) + ?.subscribe(SetReminderObserver()) + } + + fun getReminder(user: User, roomToken: String, messageId: String, chatApiVersion: Int) { + chatRepository.getReminder(user, roomToken, messageId, chatApiVersion) + .subscribeOn(Schedulers.io()) + ?.observeOn(AndroidSchedulers.mainThread()) + ?.subscribe(GetReminderObserver()) + } + + fun deleteReminder(user: User, roomToken: String, messageId: String, chatApiVersion: Int) { + chatRepository.deleteReminder(user, roomToken, messageId, chatApiVersion) + .subscribeOn(Schedulers.io()) + ?.observeOn(AndroidSchedulers.mainThread()) + ?.subscribe(object : Observer { + override fun onSubscribe(d: Disposable) { + LifeCycleObserver.disposableSet.add(d) + } + + override fun onNext(genericOverall: GenericOverall) { + _getReminderExistState.value = GetReminderStartState + } + + override fun onError(e: Throwable) { + Log.d(TAG, "Error when deleting reminder", e) + } + + override fun onComplete() { + // unused atm + } + }) + } + fun leaveRoom(credentials: String, url: String, funToCallWhenLeaveSuccessful: (() -> Unit)?) { val startNanoTime = System.nanoTime() chatRepository.leaveRoom(credentials, url) @@ -357,43 +433,6 @@ class ChatViewModel @Inject constructor( }) } - fun setReminder(user: User, roomToken: String, messageId: String, timestamp: Int) { - chatRepository.setReminder(user, roomToken, messageId, timestamp) - .subscribeOn(Schedulers.io()) - ?.observeOn(AndroidSchedulers.mainThread()) - ?.subscribe(SetReminderObserver()) - } - - fun getReminder(user: User, roomToken: String, messageId: String) { - chatRepository.getReminder(user, roomToken, messageId) - .subscribeOn(Schedulers.io()) - ?.observeOn(AndroidSchedulers.mainThread()) - ?.subscribe(GetReminderObserver()) - } - - fun deleteReminder(user: User, roomToken: String, messageId: String) { - chatRepository.deleteReminder(user, roomToken, messageId) - .subscribeOn(Schedulers.io()) - ?.observeOn(AndroidSchedulers.mainThread()) - ?.subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - LifeCycleObserver.disposableSet.add(d) - } - - override fun onNext(genericOverall: GenericOverall) { - _getReminderExistState.value = GetReminderStartState - } - - override fun onError(e: Throwable) { - Log.d(TAG, "Error when deleting reminder $e") - } - - override fun onComplete() { - // unused atm - } - }) - } - fun shareToNotes(credentials: String, url: String, message: String, displayName: String) { chatRepository.shareToNotes(credentials, url, message, displayName) .subscribeOn(Schedulers.io()) @@ -522,7 +561,7 @@ class ChatViewModel @Inject constructor( inner class GetRoomObserver : Observer { override fun onSubscribe(d: Disposable) { - LifeCycleObserver.disposableSet.add(d) + // unused atm } override fun onNext(conversationModel: ConversationModel) { diff --git a/app/src/main/java/com/nextcloud/talk/components/filebrowser/webdav/ReadFolderListingOperation.kt b/app/src/main/java/com/nextcloud/talk/components/filebrowser/webdav/ReadFolderListingOperation.kt index 293dfa925..18625a511 100644 --- a/app/src/main/java/com/nextcloud/talk/components/filebrowser/webdav/ReadFolderListingOperation.kt +++ b/app/src/main/java/com/nextcloud/talk/components/filebrowser/webdav/ReadFolderListingOperation.kt @@ -65,7 +65,7 @@ class ReadFolderListingOperation(okHttpClient: OkHttpClient, currentUser: User, ApiUtils.getCredentials( currentUser.username, currentUser.token - ), + )!!, "Authorization" ) ) diff --git a/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivity.kt b/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivity.kt index c32f748ff..16f955b54 100644 --- a/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivity.kt @@ -68,9 +68,10 @@ import com.nextcloud.talk.models.json.participants.Participant import com.nextcloud.talk.openconversations.ListOpenConversationsActivity import com.nextcloud.talk.users.UserManager import com.nextcloud.talk.utils.ApiUtils +import com.nextcloud.talk.utils.SpreedFeatures import com.nextcloud.talk.utils.UserIdUtils.getIdForUser import com.nextcloud.talk.utils.bundle.BundleKeys -import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew +import com.nextcloud.talk.utils.CapabilitiesUtil import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.SelectableAdapter import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager @@ -318,10 +319,10 @@ class ContactsActivity : } private fun createRoom(roomType: String, sourceType: String?, userId: String) { - val apiVersion: Int = ApiUtils.getConversationApiVersion(currentUser, intArrayOf(ApiUtils.APIv4, 1)) + val apiVersion: Int = ApiUtils.getConversationApiVersion(currentUser!!, intArrayOf(ApiUtils.API_V4, 1)) val retrofitBucket: RetrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom( apiVersion, - currentUser!!.baseUrl, + currentUser!!.baseUrl!!, roomType, sourceType, userId, @@ -438,7 +439,7 @@ class ContactsActivity : userHeaderItems = HashMap() val query = adapter!!.getFilter(String::class.java) val retrofitBucket: RetrofitBucket = - ApiUtils.getRetrofitBucketForContactsSearchFor14(currentUser!!.baseUrl, query) + ApiUtils.getRetrofitBucketForContactsSearchFor14(currentUser!!.baseUrl!!, query) val modifiedQueryMap: HashMap = HashMap(retrofitBucket.queryMap) modifiedQueryMap["limit"] = CONTACTS_BATCH_SIZE if (isAddingParticipantsView) { @@ -450,13 +451,21 @@ class ContactsActivity : if (!isAddingParticipantsView) { // groups shareTypesList.add("1") - } else if (CapabilitiesUtilNew.hasSpreedFeatureCapability(currentUser, "invite-groups-and-mails")) { + } else if (CapabilitiesUtil.hasSpreedFeatureCapability( + currentUser?.capabilities?.spreedCapability!!, + SpreedFeatures.INVITE_GROUPS_AND_MAILS + ) + ) { // groups shareTypesList.add("1") // emails shareTypesList.add("4") } - if (CapabilitiesUtilNew.hasSpreedFeatureCapability(currentUser, "circles-support")) { + if (CapabilitiesUtil.hasSpreedFeatureCapability( + currentUser?.capabilities?.spreedCapability!!, + SpreedFeatures.CIRCLES_SUPPORT + ) + ) { // circles shareTypesList.add("7") } @@ -745,8 +754,12 @@ class ContactsActivity : private fun updateSelection(contactItem: ContactItem) { contactItem.model.selected = !contactItem.model.selected updateSelectionLists(contactItem.model) - if (CapabilitiesUtilNew.hasSpreedFeatureCapability(currentUser, "last-room-activity") && - !CapabilitiesUtilNew.hasSpreedFeatureCapability(currentUser, "invite-groups-and-mails") && + if (CapabilitiesUtil.hasSpreedFeatureCapability( + currentUser?.capabilities?.spreedCapability!!, SpreedFeatures.LAST_ROOM_ACTIVITY + ) && + !CapabilitiesUtil.hasSpreedFeatureCapability( + currentUser?.capabilities?.spreedCapability!!, SpreedFeatures.INVITE_GROUPS_AND_MAILS + ) && isValidGroupSelection(contactItem, contactItem.model, adapter) ) { val currentItems: List = adapter?.currentItems as List @@ -771,10 +784,10 @@ class ContactsActivity : if ("groups" == contactItem.model.source) { roomType = "2" } - val apiVersion: Int = ApiUtils.getConversationApiVersion(currentUser, intArrayOf(ApiUtils.APIv4, 1)) + val apiVersion: Int = ApiUtils.getConversationApiVersion(currentUser!!, intArrayOf(ApiUtils.API_V4, 1)) val retrofitBucket: RetrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom( apiVersion, - currentUser!!.baseUrl, + currentUser!!.baseUrl!!, roomType, null, contactItem.model.calculatedActorId, diff --git a/app/src/main/java/com/nextcloud/talk/conversation/repository/ConversationRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/conversation/repository/ConversationRepositoryImpl.kt index adc6f88eb..d4b657b6c 100644 --- a/app/src/main/java/com/nextcloud/talk/conversation/repository/ConversationRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/conversation/repository/ConversationRepositoryImpl.kt @@ -36,16 +36,16 @@ class ConversationRepositoryImpl(private val ncApi: NcApi, currentUserProvider: ConversationRepository { val currentUser: User = currentUserProvider.currentUser.blockingGet() - val credentials: String = ApiUtils.getCredentials(currentUser.username, currentUser.token) + val credentials: String = ApiUtils.getCredentials(currentUser.username, currentUser.token)!! override fun renameConversation(roomToken: String, roomNameNew: String): Observable { - val apiVersion = ApiUtils.getConversationApiVersion(currentUser, intArrayOf(ApiUtils.APIv4, ApiUtils.APIv1)) + val apiVersion = ApiUtils.getConversationApiVersion(currentUser, intArrayOf(ApiUtils.API_V4, ApiUtils.API_V1)) return ncApi.renameRoom( credentials, ApiUtils.getUrlForRoom( apiVersion, - currentUser.baseUrl, + currentUser.baseUrl!!, roomToken ), roomNameNew @@ -59,12 +59,12 @@ class ConversationRepositoryImpl(private val ncApi: NcApi, currentUserProvider: roomName: String, conversationType: Conversation.ConversationType? ): Observable { - val apiVersion = ApiUtils.getConversationApiVersion(currentUser, intArrayOf(ApiUtils.APIv4, ApiUtils.APIv1)) + val apiVersion = ApiUtils.getConversationApiVersion(currentUser, intArrayOf(ApiUtils.API_V4, ApiUtils.API_V1)) val retrofitBucket: RetrofitBucket = if (conversationType == Conversation.ConversationType.ROOM_PUBLIC_CALL) { ApiUtils.getRetrofitBucketForCreateRoom( apiVersion, - currentUser.baseUrl, + currentUser.baseUrl!!, ROOM_TYPE_PUBLIC, null, null, @@ -73,7 +73,7 @@ class ConversationRepositoryImpl(private val ncApi: NcApi, currentUserProvider: } else { ApiUtils.getRetrofitBucketForCreateRoom( apiVersion, - currentUser.baseUrl, + currentUser.baseUrl!!, ROOM_TYPE_GROUP, null, null, diff --git a/app/src/main/java/com/nextcloud/talk/conversationinfo/ConversationInfoActivity.kt b/app/src/main/java/com/nextcloud/talk/conversationinfo/ConversationInfoActivity.kt index e04bd2ac6..70fa77133 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationinfo/ConversationInfoActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationinfo/ConversationInfoActivity.kt @@ -40,6 +40,7 @@ import android.view.View import android.view.View.GONE import android.view.View.VISIBLE import androidx.appcompat.app.AlertDialog +import androidx.lifecycle.ViewModelProvider import androidx.work.Data import androidx.work.OneTimeWorkRequest import androidx.work.WorkManager @@ -61,6 +62,7 @@ import com.nextcloud.talk.bottomsheet.items.BasicListItemWithImage import com.nextcloud.talk.bottomsheet.items.listItemsWithImage import com.nextcloud.talk.contacts.ContactsActivity import com.nextcloud.talk.conversationinfoedit.ConversationInfoEditActivity +import com.nextcloud.talk.conversationinfo.viewmodel.ConversationInfoViewModel import com.nextcloud.talk.data.user.model.User import com.nextcloud.talk.databinding.ActivityConversationInfoBinding import com.nextcloud.talk.events.EventStatus @@ -71,9 +73,11 @@ import com.nextcloud.talk.extensions.loadUserAvatar import com.nextcloud.talk.jobs.DeleteConversationWorker import com.nextcloud.talk.jobs.LeaveConversationWorker import com.nextcloud.talk.models.domain.ConversationModel -import com.nextcloud.talk.models.json.conversations.Conversation -import com.nextcloud.talk.models.json.conversations.RoomOverall -import com.nextcloud.talk.models.json.converters.EnumNotificationLevelConverter +import com.nextcloud.talk.models.domain.ConversationType +import com.nextcloud.talk.models.domain.LobbyState +import com.nextcloud.talk.models.domain.NotificationLevel +import com.nextcloud.talk.models.domain.converters.DomainEnumNotificationLevelConverter +import com.nextcloud.talk.models.json.capabilities.SpreedCapability import com.nextcloud.talk.models.json.generic.GenericOverall import com.nextcloud.talk.models.json.participants.Participant import com.nextcloud.talk.models.json.participants.Participant.ActorType.CIRCLES @@ -83,11 +87,12 @@ import com.nextcloud.talk.models.json.participants.ParticipantsOverall import com.nextcloud.talk.repositories.conversations.ConversationsRepository import com.nextcloud.talk.shareditems.activities.SharedItemsActivity import com.nextcloud.talk.utils.ApiUtils +import com.nextcloud.talk.utils.SpreedFeatures import com.nextcloud.talk.utils.ConversationUtils import com.nextcloud.talk.utils.DateConstants import com.nextcloud.talk.utils.DateUtils import com.nextcloud.talk.utils.bundle.BundleKeys -import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew +import com.nextcloud.talk.utils.CapabilitiesUtil import com.nextcloud.talk.utils.database.user.CurrentUserProviderNew import com.nextcloud.talk.utils.preferences.preferencestorage.DatabaseStorageModule import eu.davidea.flexibleadapter.FlexibleAdapter @@ -109,6 +114,9 @@ class ConversationInfoActivity : FlexibleAdapter.OnItemClickListener { private lateinit var binding: ActivityConversationInfoBinding + @Inject + lateinit var viewModelFactory: ViewModelProvider.Factory + @Inject lateinit var ncApi: NcApi @@ -121,6 +129,10 @@ class ConversationInfoActivity : @Inject lateinit var dateUtils: DateUtils + lateinit var viewModel: ConversationInfoViewModel + + private lateinit var spreedCapabilities: SpreedCapability + private lateinit var conversationToken: String private lateinit var conversationUser: User private var hasAvatarSpacing: Boolean = false @@ -129,7 +141,9 @@ class ConversationInfoActivity : private var participantsDisposable: Disposable? = null private var databaseStorageModule: DatabaseStorageModule? = null - private var conversation: Conversation? = null + + // private var conversation: Conversation? = null + private var conversation: ConversationModel? = null private var adapter: FlexibleAdapter? = null private var userItems: MutableList = ArrayList() @@ -157,11 +171,26 @@ class ConversationInfoActivity : setContentView(binding.root) setupSystemColors() + viewModel = + ViewModelProvider(this, viewModelFactory)[ConversationInfoViewModel::class.java] + conversationUser = currentUserProvider.currentUser.blockingGet() conversationToken = intent.getStringExtra(BundleKeys.KEY_ROOM_TOKEN)!! hasAvatarSpacing = intent.getBooleanExtra(BundleKeys.KEY_ROOM_ONE_TO_ONE, false) - credentials = ApiUtils.getCredentials(conversationUser.username, conversationUser.token) + credentials = ApiUtils.getCredentials(conversationUser.username, conversationUser.token)!! + + initObservers() + } + + override fun onStart() { + super.onStart() + this.lifecycle.addObserver(ConversationInfoViewModel.LifeCycleObserver) + } + + override fun onStop() { + super.onStop() + this.lifecycle.removeObserver(ConversationInfoViewModel.LifeCycleObserver) } override fun onResume() { @@ -176,13 +205,7 @@ class ConversationInfoActivity : binding.clearConversationHistory.setOnClickListener { showClearHistoryDialog() } binding.addParticipantsAction.setOnClickListener { addParticipants() } - if (CapabilitiesUtilNew.hasSpreedFeatureCapability(conversationUser, "rich-object-list-media")) { - binding.sharedItemsButton.setOnClickListener { showSharedItems() } - } else { - binding.sharedItems.visibility = GONE - } - - fetchRoomInfo() + viewModel.getRoom(conversationUser, conversationToken) themeTextViews() themeSwitchPreferences() @@ -192,6 +215,35 @@ class ConversationInfoActivity : binding.progressBar.let { viewThemeUtils.platform.colorCircularProgressBar(it, ColorRole.PRIMARY) } } + private fun initObservers() { + viewModel.viewState.observe(this) { state -> + when (state) { + is ConversationInfoViewModel.GetRoomSuccessState -> { + conversation = state.conversationModel + viewModel.getCapabilities(conversationUser, conversationToken, conversation!!) + } + + is ConversationInfoViewModel.GetRoomErrorState -> { + Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show() + } + + else -> {} + } + } + + viewModel.getCapabilitiesViewState.observe(this) { state -> + when (state) { + is ConversationInfoViewModel.GetCapabilitiesSuccessState -> { + spreedCapabilities = state.spreedCapabilities + + handleConversation() + } + + else -> {} + } + } + } + private fun setupActionBar() { setSupportActionBar(binding.conversationInfoToolbar) binding.conversationInfoToolbar.setNavigationOnClickListener { @@ -217,7 +269,7 @@ class ConversationInfoActivity : fun showOptionsMenu() { if (::optionsMenu.isInitialized) { optionsMenu.clear() - if (CapabilitiesUtilNew.isConversationAvatarEndpointAvailable(conversationUser)) { + if (CapabilitiesUtil.hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.AVATAR)) { menuInflater.inflate(R.menu.menu_conversation_info, optionsMenu) } } @@ -273,19 +325,22 @@ class ConversationInfoActivity : intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) intent.putExtra(BundleKeys.KEY_CONVERSATION_NAME, conversation?.displayName) intent.putExtra(BundleKeys.KEY_ROOM_TOKEN, conversationToken) - intent.putExtra(SharedItemsActivity.KEY_USER_IS_OWNER_OR_MODERATOR, conversation?.isParticipantOwnerOrModerator) + intent.putExtra( + SharedItemsActivity.KEY_USER_IS_OWNER_OR_MODERATOR, + ConversationUtils.isParticipantOwnerOrModerator(conversation!!) + ) startActivity(intent) } private fun setupWebinaryView() { - if (CapabilitiesUtilNew.hasSpreedFeatureCapability(conversationUser, "webinary-lobby") && + if (CapabilitiesUtil.hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.WEBINARY_LOBBY) && webinaryRoomType(conversation!!) && - conversation!!.canModerate(conversationUser) + ConversationUtils.canModerate(conversation!!, spreedCapabilities) ) { binding.webinarInfoView.webinarSettings.visibility = VISIBLE val isLobbyOpenToModeratorsOnly = - conversation!!.lobbyState == Conversation.LobbyState.LOBBY_STATE_MODERATORS_ONLY + conversation!!.lobbyState == LobbyState.LOBBY_STATE_MODERATORS_ONLY binding.webinarInfoView.lobbySwitch.isChecked = isLobbyOpenToModeratorsOnly reconfigureLobbyTimerView() @@ -320,9 +375,9 @@ class ConversationInfoActivity : } } - private fun webinaryRoomType(conversation: Conversation): Boolean { - return conversation.type == Conversation.ConversationType.ROOM_GROUP_CALL || - conversation.type == Conversation.ConversationType.ROOM_PUBLIC_CALL + private fun webinaryRoomType(conversation: ConversationModel): Boolean { + return conversation.type == ConversationType.ROOM_GROUP_CALL || + conversation.type == ConversationType.ROOM_PUBLIC_CALL } private fun reconfigureLobbyTimerView(dateTime: Calendar? = null) { @@ -337,9 +392,9 @@ class ConversationInfoActivity : } conversation!!.lobbyState = if (isChecked) { - Conversation.LobbyState.LOBBY_STATE_MODERATORS_ONLY + LobbyState.LOBBY_STATE_MODERATORS_ONLY } else { - Conversation.LobbyState.LOBBY_STATE_ALL_PARTICIPANTS + LobbyState.LOBBY_STATE_ALL_PARTICIPANTS } if ( @@ -370,11 +425,11 @@ class ConversationInfoActivity : 0 } - val apiVersion = ApiUtils.getConversationApiVersion(conversationUser, intArrayOf(ApiUtils.APIv4, 1)) + val apiVersion = ApiUtils.getConversationApiVersion(conversationUser, intArrayOf(ApiUtils.API_V4, 1)) ncApi.setLobbyForConversation( ApiUtils.getCredentials(conversationUser.username, conversationUser.token), - ApiUtils.getUrlForRoomWebinaryLobby(apiVersion, conversationUser.baseUrl, conversation!!.token), + ApiUtils.getUrlForRoomWebinaryLobby(apiVersion, conversationUser.baseUrl!!, conversation!!.token), state, conversation!!.lobbyTimer ) @@ -487,7 +542,7 @@ class ConversationInfoActivity : private fun getListOfParticipants() { // FIXME Fix API checking with guests? - val apiVersion: Int = ApiUtils.getConversationApiVersion(conversationUser, intArrayOf(ApiUtils.APIv4, 1)) + val apiVersion: Int = ApiUtils.getConversationApiVersion(conversationUser, intArrayOf(ApiUtils.API_V4, 1)) val fieldMap = HashMap() fieldMap["includeStatus"] = true @@ -496,7 +551,7 @@ class ConversationInfoActivity : credentials, ApiUtils.getUrlForParticipants( apiVersion, - conversationUser.baseUrl, + conversationUser.baseUrl!!, conversationToken ), fieldMap @@ -586,11 +641,11 @@ class ConversationInfoActivity : } private fun clearHistory() { - val apiVersion = ApiUtils.getChatApiVersion(conversationUser, intArrayOf(1)) + val apiVersion = ApiUtils.getChatApiVersion(spreedCapabilities, intArrayOf(1)) ncApi.clearChatHistory( credentials, - ApiUtils.getUrlForChat(apiVersion, conversationUser.baseUrl, conversationToken) + ApiUtils.getUrlForChat(apiVersion, conversationUser.baseUrl!!, conversationToken) ) ?.subscribeOn(Schedulers.io()) ?.observeOn(AndroidSchedulers.mainThread()) @@ -631,123 +686,99 @@ class ConversationInfoActivity : } } - private fun fetchRoomInfo() { - val apiVersion: Int - // FIXME Fix API checking with guests? - apiVersion = ApiUtils.getConversationApiVersion(conversationUser, intArrayOf(ApiUtils.APIv4, 1)) + @Suppress("LongMethod") + private fun handleConversation() { + val conversationCopy = conversation!! - ncApi.getRoom(credentials, ApiUtils.getUrlForRoom(apiVersion, conversationUser.baseUrl, conversationToken)) - ?.subscribeOn(Schedulers.io()) - ?.observeOn(AndroidSchedulers.mainThread()) - ?.subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - roomDisposable = d - } + if (CapabilitiesUtil.hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.RICH_OBJECT_LIST_MEDIA)) { + binding.sharedItemsButton.setOnClickListener { showSharedItems() } + } else { + binding.sharedItems.visibility = GONE + } - @Suppress("Detekt.TooGenericExceptionCaught") - override fun onNext(roomOverall: RoomOverall) { - conversation = roomOverall.ocs!!.data + if (ConversationUtils.canModerate(conversationCopy, spreedCapabilities)) { + binding.addParticipantsAction.visibility = VISIBLE + if (CapabilitiesUtil.hasSpreedFeatureCapability( + spreedCapabilities, + SpreedFeatures.CLEAR_HISTORY + ) + ) { + binding.clearConversationHistory.visibility = VISIBLE + } else { + binding.clearConversationHistory.visibility = GONE + } + showOptionsMenu() + } else { + binding.addParticipantsAction.visibility = GONE - val conversationCopy = conversation + if (ConversationUtils.isNoteToSelfConversation(conversation)) { + binding.notificationSettingsView.notificationSettings.visibility = VISIBLE + } else { + binding.clearConversationHistory.visibility = GONE + } + } - if (conversationCopy!!.canModerate(conversationUser)) { - binding.addParticipantsAction.visibility = VISIBLE - if (CapabilitiesUtilNew.hasSpreedFeatureCapability( - conversationUser, - "clear-history" - ) - ) { - binding.clearConversationHistory.visibility = VISIBLE - } else { - binding.clearConversationHistory.visibility = GONE - } - showOptionsMenu() - } else { - binding.addParticipantsAction.visibility = GONE + if (!isDestroyed) { + binding.dangerZoneOptions.visibility = VISIBLE - if (ConversationUtils.isNoteToSelfConversation( - ConversationModel.mapToConversationModel(conversation!!) - ) - ) { - binding.notificationSettingsView.notificationSettings.visibility = VISIBLE - } else { - binding.clearConversationHistory.visibility = GONE - } - } + setupWebinaryView() - if (!isDestroyed) { - binding.dangerZoneOptions.visibility = VISIBLE + if (ConversationUtils.canLeave(conversation!!)) { + binding.leaveConversationAction.visibility = GONE + } else { + binding.leaveConversationAction.visibility = VISIBLE + } - setupWebinaryView() + if (ConversationUtils.canDelete(conversation!!, spreedCapabilities)) { + binding.deleteConversationAction.visibility = GONE + } else { + binding.deleteConversationAction.visibility = VISIBLE + } - if (!conversation!!.canLeave()) { - binding.leaveConversationAction.visibility = GONE - } else { - binding.leaveConversationAction.visibility = VISIBLE - } + if (ConversationType.ROOM_SYSTEM == conversation!!.type) { + binding.notificationSettingsView.callNotificationsSwitch.visibility = GONE + } - if (!conversation!!.canDelete(conversationUser)) { - binding.deleteConversationAction.visibility = GONE - } else { - binding.deleteConversationAction.visibility = VISIBLE - } + if (conversation!!.notificationCalls === null) { + binding.notificationSettingsView.callNotificationsSwitch.visibility = GONE + } else { + binding.notificationSettingsView.callNotificationsSwitch.isChecked = + (conversationCopy.notificationCalls == 1) + } - if (Conversation.ConversationType.ROOM_SYSTEM == conversation!!.type) { - binding.notificationSettingsView.callNotificationsSwitch.visibility = GONE - } + getListOfParticipants() - if (conversation!!.notificationCalls === null) { - binding.notificationSettingsView.callNotificationsSwitch.visibility = GONE - } else { - binding.notificationSettingsView.callNotificationsSwitch.isChecked = - (conversationCopy.notificationCalls == 1) - } + binding.progressBar.visibility = GONE - getListOfParticipants() + binding.conversationInfoName.visibility = VISIBLE - binding.progressBar.visibility = GONE + binding.displayNameText.text = conversation!!.displayName - binding.conversationInfoName.visibility = VISIBLE + if (conversation!!.description != null && conversation!!.description!!.isNotEmpty()) { + binding.descriptionText.text = conversation!!.description + binding.conversationDescription.visibility = VISIBLE + } - binding.displayNameText.text = conversation!!.displayName + loadConversationAvatar() + adjustNotificationLevelUI() + initRecordingConsentOption() + initExpiringMessageOption() - if (conversation!!.description != null && conversation!!.description!!.isNotEmpty()) { - binding.descriptionText.text = conversation!!.description - binding.conversationDescription.visibility = VISIBLE - } - - loadConversationAvatar() - adjustNotificationLevelUI() - initRecordingConsentOption() - initExpiringMessageOption() - - binding.let { - GuestAccessHelper( - this@ConversationInfoActivity, - it, - conversation!!, - conversationUser - ).setupGuestAccess() - } - if (ConversationUtils.isNoteToSelfConversation( - ConversationModel.mapToConversationModel(conversation!!) - ) - ) { - binding.notificationSettingsView.notificationSettings.visibility = GONE - } else { - binding.notificationSettingsView.notificationSettings.visibility = VISIBLE - } - } - } - - override fun onError(e: Throwable) { - Log.e(TAG, "failed to fetch room info", e) - } - - override fun onComplete() { - roomDisposable!!.dispose() - } - }) + binding.let { + GuestAccessHelper( + this@ConversationInfoActivity, + it, + conversation!!, + spreedCapabilities, + conversationUser + ).setupGuestAccess() + } + if (ConversationUtils.isNoteToSelfConversation(conversation!!)) { + binding.notificationSettingsView.notificationSettings.visibility = GONE + } else { + binding.notificationSettingsView.notificationSettings.visibility = VISIBLE + } + } } private fun initRecordingConsentOption() { @@ -781,13 +812,13 @@ class ConversationInfoActivity : } } - if (conversation!!.isParticipantOwnerOrModerator && - !ConversationUtils.isNoteToSelfConversation(ConversationModel.mapToConversationModel(conversation!!)) + if (ConversationUtils.isParticipantOwnerOrModerator(conversation!!) && + !ConversationUtils.isNoteToSelfConversation(conversation!!) ) { - when (CapabilitiesUtilNew.getRecordingConsentType(conversationUser)) { - CapabilitiesUtilNew.RECORDING_CONSENT_NOT_REQUIRED -> hide() - CapabilitiesUtilNew.RECORDING_CONSENT_REQUIRED -> showAlwaysRequiredInfo() - CapabilitiesUtilNew.RECORDING_CONSENT_DEPEND_ON_CONVERSATION -> showSwitch() + when (CapabilitiesUtil.getRecordingConsentType(spreedCapabilities)) { + CapabilitiesUtil.RECORDING_CONSENT_NOT_REQUIRED -> hide() + CapabilitiesUtil.RECORDING_CONSENT_REQUIRED -> showAlwaysRequiredInfo() + CapabilitiesUtil.RECORDING_CONSENT_DEPEND_ON_CONVERSATION -> showSwitch() } } else { hide() @@ -801,11 +832,11 @@ class ConversationInfoActivity : RECORDING_CONSENT_NOT_REQUIRED_FOR_CONVERSATION } - val apiVersion = ApiUtils.getConversationApiVersion(conversationUser, intArrayOf(ApiUtils.APIv4, 1)) + val apiVersion = ApiUtils.getConversationApiVersion(conversationUser, intArrayOf(ApiUtils.API_V4, 1)) ncApi.setRecordingConsent( ApiUtils.getCredentials(conversationUser.username, conversationUser.token), - ApiUtils.getUrlForRecordingConsent(apiVersion, conversationUser.baseUrl, conversation!!.token), + ApiUtils.getUrlForRecordingConsent(apiVersion, conversationUser.baseUrl!!, conversation!!.token), state ) ?.subscribeOn(Schedulers.io()) @@ -831,8 +862,8 @@ class ConversationInfoActivity : } private fun initExpiringMessageOption() { - if (conversation!!.isParticipantOwnerOrModerator && - CapabilitiesUtilNew.hasSpreedFeatureCapability(conversationUser, "message-expiration") + if (ConversationUtils.isParticipantOwnerOrModerator(conversation!!) && + CapabilitiesUtil.hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.MESSAGE_EXPIRATION) ) { databaseStorageModule?.setMessageExpiration(conversation!!.messageExpiration) val value = databaseStorageModule!!.getString("conversation_settings_dropdown", "") @@ -855,13 +886,16 @@ class ConversationInfoActivity : private fun adjustNotificationLevelUI() { if (conversation != null) { - if (CapabilitiesUtilNew.hasSpreedFeatureCapability(conversationUser, "notification-levels")) { + if (CapabilitiesUtil.hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.NOTIFICATION_LEVELS)) { binding.notificationSettingsView.conversationInfoMessageNotificationsDropdown.isEnabled = true binding.notificationSettingsView.conversationInfoMessageNotificationsDropdown.alpha = 1.0f - if (conversation!!.notificationLevel != Conversation.NotificationLevel.DEFAULT) { + if (conversation!!.notificationLevel != NotificationLevel.DEFAULT) { val stringValue: String = - when (EnumNotificationLevelConverter().convertToInt(conversation!!.notificationLevel)) { + when ( + DomainEnumNotificationLevelConverter() + .convertToInt(conversation!!.notificationLevel!!) + ) { NOTIFICATION_LEVEL_ALWAYS -> resources.getString(R.string.nc_notify_me_always) NOTIFICATION_LEVEL_MENTION -> resources.getString(R.string.nc_notify_me_mention) NOTIFICATION_LEVEL_NEVER -> resources.getString(R.string.nc_notify_me_never) @@ -885,9 +919,9 @@ class ConversationInfoActivity : } } - private fun setProperNotificationValue(conversation: Conversation?) { - if (conversation!!.type == Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL) { - if (CapabilitiesUtilNew.hasSpreedFeatureCapability(conversationUser, "mention-flag")) { + private fun setProperNotificationValue(conversation: ConversationModel?) { + if (conversation!!.type == ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL) { + if (CapabilitiesUtil.hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.MENTION_FLAG)) { binding.notificationSettingsView.conversationInfoMessageNotificationsDropdown.setText( resources.getString(R.string.nc_notify_me_always) ) @@ -905,7 +939,7 @@ class ConversationInfoActivity : private fun loadConversationAvatar() { when (conversation!!.type) { - Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL -> if (!TextUtils.isEmpty(conversation!!.name)) { + ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL -> if (!TextUtils.isEmpty(conversation!!.name)) { conversation!!.name?.let { binding.avatarImage.loadUserAvatar( conversationUser, @@ -916,7 +950,7 @@ class ConversationInfoActivity : } } - Conversation.ConversationType.ROOM_GROUP_CALL, Conversation.ConversationType.ROOM_PUBLIC_CALL -> { + ConversationType.ROOM_GROUP_CALL, ConversationType.ROOM_PUBLIC_CALL -> { binding.avatarImage.loadConversationAvatar( conversationUser, conversation!!, @@ -925,15 +959,12 @@ class ConversationInfoActivity : ) } - Conversation.ConversationType.ROOM_SYSTEM -> { + ConversationType.ROOM_SYSTEM -> { binding.avatarImage.loadSystemAvatar() } - Conversation.ConversationType.DUMMY -> { - if (ConversationUtils.isNoteToSelfConversation( - ConversationModel.mapToConversationModel(conversation!!) - ) - ) { + ConversationType.DUMMY -> { + if (ConversationUtils.isNoteToSelfConversation(conversation!!)) { binding.avatarImage.loadNoteToSelfAvatar() } } @@ -971,7 +1002,7 @@ class ConversationInfoActivity : credentials, ApiUtils.getUrlForRoomModerators( apiVersion, - conversationUser.baseUrl, + conversationUser.baseUrl!!, conversation!!.token ), participant.attendeeId @@ -986,7 +1017,7 @@ class ConversationInfoActivity : credentials, ApiUtils.getUrlForRoomModerators( apiVersion, - conversationUser.baseUrl, + conversationUser.baseUrl!!, conversation!!.token ), participant.attendeeId @@ -1022,7 +1053,7 @@ class ConversationInfoActivity : credentials, ApiUtils.getUrlForRoomModerators( apiVersion, - conversationUser.baseUrl, + conversationUser.baseUrl!!, conversation!!.token ), participant.userId @@ -1035,7 +1066,7 @@ class ConversationInfoActivity : credentials, ApiUtils.getUrlForRoomModerators( apiVersion, - conversationUser.baseUrl, + conversationUser.baseUrl!!, conversation!!.token ), participant.userId @@ -1047,12 +1078,12 @@ class ConversationInfoActivity : } private fun removeAttendeeFromConversation(apiVersion: Int, participant: Participant) { - if (apiVersion >= ApiUtils.APIv4) { + if (apiVersion >= ApiUtils.API_V4) { ncApi.removeAttendeeFromConversation( credentials, ApiUtils.getUrlForAttendees( apiVersion, - conversationUser.baseUrl, + conversationUser.baseUrl!!, conversation!!.token ), participant.attendeeId @@ -1084,7 +1115,7 @@ class ConversationInfoActivity : ncApi.removeParticipantFromConversation( credentials, ApiUtils.getUrlForRemovingParticipantFromConversation( - conversationUser.baseUrl, + conversationUser.baseUrl!!, conversation!!.token, true ), @@ -1114,7 +1145,7 @@ class ConversationInfoActivity : ncApi.removeParticipantFromConversation( credentials, ApiUtils.getUrlForRemovingParticipantFromConversation( - conversationUser.baseUrl, + conversationUser.baseUrl!!, conversation!!.token, false ), @@ -1146,14 +1177,14 @@ class ConversationInfoActivity : @SuppressLint("CheckResult") override fun onItemClick(view: View?, position: Int): Boolean { - if (!conversation!!.canModerate(conversationUser)) { + if (ConversationUtils.canModerate(conversation!!, spreedCapabilities)) { return true } val userItem = adapter?.getItem(position) as ParticipantItem val participant = userItem.model - val apiVersion = ApiUtils.getConversationApiVersion(conversationUser, intArrayOf(ApiUtils.APIv4, 1)) + val apiVersion = ApiUtils.getConversationApiVersion(conversationUser, intArrayOf(ApiUtils.API_V4, 1)) if (participant.calculatedActorType == USERS && participant.calculatedActorId == conversationUser.userId) { if (participant.attendeePin?.isNotEmpty() == true) { @@ -1277,7 +1308,7 @@ class ConversationInfoActivity : // Pin, nothing to do } else if (actionToTrigger == 1) { // Promote/demote - if (apiVersion >= ApiUtils.APIv4) { + if (apiVersion >= ApiUtils.API_V4) { toggleModeratorStatus(apiVersion, participant) } else { toggleModeratorStatusLegacy(apiVersion, participant) diff --git a/app/src/main/java/com/nextcloud/talk/conversationinfo/GuestAccessHelper.kt b/app/src/main/java/com/nextcloud/talk/conversationinfo/GuestAccessHelper.kt index 4ec42f0ea..c5c5dcef0 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationinfo/GuestAccessHelper.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationinfo/GuestAccessHelper.kt @@ -11,8 +11,11 @@ import com.nextcloud.talk.R import com.nextcloud.talk.data.user.model.User import com.nextcloud.talk.databinding.ActivityConversationInfoBinding import com.nextcloud.talk.databinding.DialogPasswordBinding -import com.nextcloud.talk.models.json.conversations.Conversation +import com.nextcloud.talk.models.domain.ConversationModel +import com.nextcloud.talk.models.domain.ConversationType +import com.nextcloud.talk.models.json.capabilities.SpreedCapability import com.nextcloud.talk.repositories.conversations.ConversationsRepository +import com.nextcloud.talk.utils.ConversationUtils import com.nextcloud.talk.utils.Mimetype import com.nextcloud.talk.utils.ShareUtils import io.reactivex.Observer @@ -23,7 +26,8 @@ import io.reactivex.schedulers.Schedulers class GuestAccessHelper( private val activity: ConversationInfoActivity, private val binding: ActivityConversationInfoBinding, - private val conversation: Conversation, + private val conversation: ConversationModel, + private val spreedCapabilities: SpreedCapability, private val conversationUser: User ) { @@ -32,13 +36,13 @@ class GuestAccessHelper( private val context = activity.context fun setupGuestAccess() { - if (conversation.canModerate(conversationUser)) { + if (ConversationUtils.canModerate(conversation, spreedCapabilities)) { binding.guestAccessView.guestAccessSettings.visibility = View.VISIBLE } else { binding.guestAccessView.guestAccessSettings.visibility = View.GONE } - if (conversation.type == Conversation.ConversationType.ROOM_PUBLIC_CALL) { + if (conversation.type == ConversationType.ROOM_PUBLIC_CALL) { binding.guestAccessView.allowGuestsSwitch.isChecked = true showAllOptions() if (conversation.hasPassword) { diff --git a/app/src/main/java/com/nextcloud/talk/conversationinfo/viewmodel/ConversationInfoViewModel.kt b/app/src/main/java/com/nextcloud/talk/conversationinfo/viewmodel/ConversationInfoViewModel.kt new file mode 100644 index 000000000..c086f470f --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/conversationinfo/viewmodel/ConversationInfoViewModel.kt @@ -0,0 +1,142 @@ +/* + * Nextcloud Talk application + * + * @author Marcel Hibbe + * Copyright (C) 2023 Marcel Hibbe + * + * 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 . + */ + +package com.nextcloud.talk.conversationinfo.viewmodel + +import android.util.Log +import androidx.lifecycle.DefaultLifecycleObserver +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import com.nextcloud.talk.chat.data.ChatRepository +import com.nextcloud.talk.data.user.model.User +import com.nextcloud.talk.models.domain.ConversationModel +import com.nextcloud.talk.models.json.capabilities.SpreedCapability +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 ConversationInfoViewModel @Inject constructor( + private val chatRepository: ChatRepository +) : ViewModel() { + + object LifeCycleObserver : DefaultLifecycleObserver { + enum class LifeCycleFlag { + PAUSED, + RESUMED + } + lateinit var currentLifeCycleFlag: LifeCycleFlag + public val disposableSet = mutableSetOf() + + override fun onResume(owner: LifecycleOwner) { + super.onResume(owner) + currentLifeCycleFlag = LifeCycleFlag.RESUMED + } + + override fun onPause(owner: LifecycleOwner) { + super.onPause(owner) + currentLifeCycleFlag = LifeCycleFlag.PAUSED + disposableSet.forEach { disposable -> disposable.dispose() } + disposableSet.clear() + } + } + + sealed interface ViewState + + object GetRoomStartState : ViewState + object GetRoomErrorState : ViewState + open class GetRoomSuccessState(val conversationModel: ConversationModel) : ViewState + + private val _viewState: MutableLiveData = MutableLiveData(GetRoomStartState) + val viewState: LiveData + get() = _viewState + + object GetCapabilitiesStartState : ViewState + object GetCapabilitiesErrorState : ViewState + open class GetCapabilitiesSuccessState(val spreedCapabilities: SpreedCapability) : ViewState + + private val _getCapabilitiesViewState: MutableLiveData = MutableLiveData(GetCapabilitiesStartState) + val getCapabilitiesViewState: LiveData + get() = _getCapabilitiesViewState + + fun getRoom(user: User, token: String) { + _viewState.value = GetRoomStartState + chatRepository.getRoom(user, token) + .subscribeOn(Schedulers.io()) + ?.observeOn(AndroidSchedulers.mainThread()) + ?.subscribe(GetRoomObserver()) + } + + fun getCapabilities(user: User, token: String, conversationModel: ConversationModel) { + _getCapabilitiesViewState.value = GetCapabilitiesStartState + + if (conversationModel.remoteServer.isNullOrEmpty()) { + _getCapabilitiesViewState.value = GetCapabilitiesSuccessState(user.capabilities!!.spreedCapability!!) + } else { + chatRepository.getCapabilities(user, token) + .subscribeOn(Schedulers.io()) + ?.observeOn(AndroidSchedulers.mainThread()) + ?.subscribe(object : Observer { + override fun onSubscribe(d: Disposable) { + LifeCycleObserver.disposableSet.add(d) + } + + override fun onNext(spreedCapabilities: SpreedCapability) { + _getCapabilitiesViewState.value = GetCapabilitiesSuccessState(spreedCapabilities) + } + + override fun onError(e: Throwable) { + Log.e(TAG, "Error when fetching spreed capabilities", e) + _getCapabilitiesViewState.value = GetCapabilitiesErrorState + } + + override fun onComplete() { + // unused atm + } + }) + } + } + + inner class GetRoomObserver : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } + + override fun onNext(conversationModel: ConversationModel) { + _viewState.value = GetRoomSuccessState(conversationModel) + } + + override fun onError(e: Throwable) { + Log.e(TAG, "Error when fetching room") + _viewState.value = GetRoomErrorState + } + + override fun onComplete() { + // unused atm + } + } + + companion object { + private val TAG = ConversationInfoViewModel::class.simpleName + } +} diff --git a/app/src/main/java/com/nextcloud/talk/conversationinfoedit/ConversationInfoEditActivity.kt b/app/src/main/java/com/nextcloud/talk/conversationinfoedit/ConversationInfoEditActivity.kt index 6329b570d..e0cb22489 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationinfoedit/ConversationInfoEditActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationinfoedit/ConversationInfoEditActivity.kt @@ -49,11 +49,12 @@ import com.nextcloud.talk.extensions.loadSystemAvatar import com.nextcloud.talk.extensions.loadUserAvatar import com.nextcloud.talk.models.domain.ConversationModel import com.nextcloud.talk.models.domain.ConversationType +import com.nextcloud.talk.models.json.capabilities.SpreedCapability import com.nextcloud.talk.models.json.generic.GenericOverall import com.nextcloud.talk.utils.ApiUtils +import com.nextcloud.talk.utils.CapabilitiesUtil import com.nextcloud.talk.utils.PickImage import com.nextcloud.talk.utils.bundle.BundleKeys -import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew import com.nextcloud.talk.utils.database.user.CurrentUserProviderNew import io.reactivex.Observer import io.reactivex.android.schedulers.AndroidSchedulers @@ -87,6 +88,8 @@ class ConversationInfoEditActivity : private lateinit var pickImage: PickImage + private lateinit var spreedCapabilities: SpreedCapability + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this) @@ -110,7 +113,7 @@ class ConversationInfoEditActivity : viewThemeUtils.material.colorTextInputLayout(binding.conversationNameInputLayout) viewThemeUtils.material.colorTextInputLayout(binding.conversationDescriptionInputLayout) - credentials = ApiUtils.getCredentials(conversationUser.username, conversationUser.token) + credentials = ApiUtils.getCredentials(conversationUser.username, conversationUser.token)!! pickImage = PickImage(this, conversationUser) @@ -127,13 +130,15 @@ class ConversationInfoEditActivity : is ConversationInfoEditViewModel.GetRoomSuccessState -> { conversation = state.conversationModel + spreedCapabilities = conversationUser.capabilities!!.spreedCapability!! + binding.conversationName.setText(conversation!!.displayName) if (conversation!!.description != null && conversation!!.description!!.isNotEmpty()) { binding.conversationDescription.setText(conversation!!.description) } - if (!CapabilitiesUtilNew.isConversationDescriptionEndpointAvailable(conversationUser)) { + if (!CapabilitiesUtil.isConversationDescriptionEndpointAvailable(spreedCapabilities)) { binding.conversationDescription.isEnabled = false } @@ -221,13 +226,13 @@ class ConversationInfoEditActivity : private fun saveConversationNameAndDescription() { val apiVersion = - ApiUtils.getConversationApiVersion(conversationUser, intArrayOf(ApiUtils.APIv4, ApiUtils.APIv1)) + ApiUtils.getConversationApiVersion(conversationUser, intArrayOf(ApiUtils.API_V4, ApiUtils.API_V1)) ncApi.renameRoom( credentials, ApiUtils.getUrlForRoom( apiVersion, - conversationUser.baseUrl, + conversationUser.baseUrl!!, conversation!!.token ), binding.conversationName.text.toString() @@ -241,7 +246,7 @@ class ConversationInfoEditActivity : } override fun onNext(genericOverall: GenericOverall) { - if (CapabilitiesUtilNew.isConversationDescriptionEndpointAvailable(conversationUser)) { + if (CapabilitiesUtil.isConversationDescriptionEndpointAvailable(spreedCapabilities)) { saveConversationDescription() } else { finish() @@ -265,13 +270,13 @@ class ConversationInfoEditActivity : fun saveConversationDescription() { val apiVersion = - ApiUtils.getConversationApiVersion(conversationUser, intArrayOf(ApiUtils.APIv4, ApiUtils.APIv1)) + ApiUtils.getConversationApiVersion(conversationUser, intArrayOf(ApiUtils.API_V4, ApiUtils.API_V1)) ncApi.setConversationDescription( credentials, ApiUtils.getUrlForConversationDescription( apiVersion, - conversationUser.baseUrl, + conversationUser.baseUrl!!, conversation!!.token ), binding.conversationDescription.text.toString() diff --git a/app/src/main/java/com/nextcloud/talk/conversationinfoedit/data/ConversationInfoEditRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/conversationinfoedit/data/ConversationInfoEditRepositoryImpl.kt index 0a4f49e54..1e4530b31 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationinfoedit/data/ConversationInfoEditRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationinfoedit/data/ConversationInfoEditRepositoryImpl.kt @@ -36,9 +36,9 @@ class ConversationInfoEditRepositoryImpl(private val ncApi: NcApi, currentUserPr ConversationInfoEditRepository { val currentUser: User = currentUserProvider.currentUser.blockingGet() - val credentials: String = ApiUtils.getCredentials(currentUser.username, currentUser.token) + val credentials: String = ApiUtils.getCredentials(currentUser.username, currentUser.token)!! - val apiVersion = ApiUtils.getConversationApiVersion(currentUser, intArrayOf(ApiUtils.APIv4, ApiUtils.APIv3, 1)) + val apiVersion = ApiUtils.getConversationApiVersion(currentUser, intArrayOf(ApiUtils.API_V4, ApiUtils.API_V3, 1)) override fun uploadConversationAvatar(user: User, file: File, roomToken: String): Observable { val builder = MultipartBody.Builder() @@ -56,7 +56,7 @@ class ConversationInfoEditRepositoryImpl(private val ncApi: NcApi, currentUserPr return ncApi.uploadConversationAvatar( credentials, - ApiUtils.getUrlForConversationAvatar(1, user.baseUrl, roomToken), + ApiUtils.getUrlForConversationAvatar(1, user.baseUrl!!, roomToken), filePart ).map { ConversationModel.mapToConversationModel(it.ocs?.data!!) } } @@ -64,7 +64,7 @@ class ConversationInfoEditRepositoryImpl(private val ncApi: NcApi, currentUserPr override fun deleteConversationAvatar(user: User, roomToken: String): Observable { return ncApi.deleteConversationAvatar( credentials, - ApiUtils.getUrlForConversationAvatar(1, user.baseUrl, roomToken) + ApiUtils.getUrlForConversationAvatar(1, user.baseUrl!!, roomToken) ).map { ConversationModel.mapToConversationModel(it.ocs?.data!!) } } } diff --git a/app/src/main/java/com/nextcloud/talk/conversationlist/ConversationsListActivity.kt b/app/src/main/java/com/nextcloud/talk/conversationlist/ConversationsListActivity.kt index e2309f977..858f77fb7 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationlist/ConversationsListActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationlist/ConversationsListActivity.kt @@ -115,6 +115,7 @@ import com.nextcloud.talk.ui.dialog.ConversationsListBottomDialog import com.nextcloud.talk.ui.dialog.FilterConversationFragment import com.nextcloud.talk.users.UserManager import com.nextcloud.talk.utils.ApiUtils +import com.nextcloud.talk.utils.SpreedFeatures import com.nextcloud.talk.utils.ClosedInterfaceImpl import com.nextcloud.talk.utils.FileUtils import com.nextcloud.talk.utils.Mimetype @@ -130,10 +131,10 @@ import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_NEW_CONVERSATION 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.bundle.BundleKeys.KEY_SHARED_TEXT -import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew.hasSpreedFeatureCapability -import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew.isServerEOL -import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew.isUnifiedSearchAvailable -import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew.isUserStatusAvailable +import com.nextcloud.talk.utils.CapabilitiesUtil.hasSpreedFeatureCapability +import com.nextcloud.talk.utils.CapabilitiesUtil.isServerEOL +import com.nextcloud.talk.utils.CapabilitiesUtil.isUnifiedSearchAvailable +import com.nextcloud.talk.utils.CapabilitiesUtil.isUserStatusAvailable import com.nextcloud.talk.utils.permissions.PlatformPermissionUtil import com.nextcloud.talk.utils.power.PowerManagerUtils import com.nextcloud.talk.utils.rx.SearchViewObservable.Companion.observeSearchView @@ -283,11 +284,11 @@ class ConversationsListActivity : } currentUser = userManager.currentUser.blockingGet() if (currentUser != null) { - if (isServerEOL(currentUser!!.capabilities)) { + if (isServerEOL(currentUser!!.serverVersion!!.major)) { showServerEOLDialog() return } - if (isUnifiedSearchAvailable(currentUser!!)) { + if (isUnifiedSearchAvailable(currentUser!!.capabilities!!.spreedCapability!!)) { searchHelper = MessageSearchHelper(unifiedSearchRepository) } credentials = ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token) @@ -423,7 +424,7 @@ class ConversationsListActivity : private fun loadUserAvatar(target: Target) { if (currentUser != null) { val url = ApiUtils.getUrlForAvatar( - currentUser!!.baseUrl, + currentUser!!.baseUrl!!, currentUser!!.userId, true ) @@ -433,7 +434,7 @@ class ConversationsListActivity : context.imageLoader.enqueue( ImageRequest.Builder(context) .data(url) - .addHeader("Authorization", credentials) + .addHeader("Authorization", credentials!!) .placeholder(R.drawable.ic_user) .transformations(CircleCropTransformation()) .crossfade(true) @@ -698,7 +699,10 @@ class ConversationsListActivity : isRefreshing = true conversationItems = ArrayList() conversationItemsWithHeader = ArrayList() - val apiVersion = ApiUtils.getConversationApiVersion(currentUser, intArrayOf(ApiUtils.APIv4, ApiUtils.APIv3, 1)) + val apiVersion = ApiUtils.getConversationApiVersion( + currentUser!!, + intArrayOf(ApiUtils.API_V4, ApiUtils.API_V3, 1) + ) val startNanoTime = System.nanoTime() Log.d(TAG, "fetchData - getRooms - calling: $startNanoTime") roomsQueryDisposable = ncApi.getRooms( @@ -868,11 +872,15 @@ class ConversationsListActivity : private fun fetchOpenConversations(apiVersion: Int) { searchableConversationItems.clear() searchableConversationItems.addAll(conversationItemsWithHeader) - if (hasSpreedFeatureCapability(currentUser, "listable-rooms")) { + if (hasSpreedFeatureCapability( + currentUser!!.capabilities!!.spreedCapability!!, + SpreedFeatures.LISTABLE_ROOMS + ) + ) { val openConversationItems: MutableList> = ArrayList() openConversationsQueryDisposable = ncApi.getOpenConversations( credentials, - ApiUtils.getUrlForOpenConversations(apiVersion, currentUser!!.baseUrl) + ApiUtils.getUrlForOpenConversations(apiVersion, currentUser!!.baseUrl!!) ) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) @@ -1087,7 +1095,7 @@ class ConversationsListActivity : clearMessageSearchResults() adapter!!.setFilter(filter) adapter!!.filterItems() - if (isUnifiedSearchAvailable(currentUser!!)) { + if (isUnifiedSearchAvailable(currentUser!!.capabilities!!.spreedCapability!!)) { startMessageSearch(filter) } } else { @@ -1173,7 +1181,11 @@ class ConversationsListActivity : private fun handleConversation(conversation: Conversation?) { selectedConversation = conversation if (selectedConversation != null) { - val hasChatPermission = ParticipantPermissions(currentUser!!, selectedConversation!!).hasChatPermission() + val hasChatPermission = ParticipantPermissions( + currentUser!!.capabilities!!.spreedCapability!!, + selectedConversation!! + ) + .hasChatPermission() if (showShareToScreen) { if (hasChatPermission && !isReadOnlyConversation(selectedConversation!!) && @@ -1197,7 +1209,10 @@ class ConversationsListActivity : } private fun shouldShowLobby(conversation: Conversation): Boolean { - val participantPermissions = ParticipantPermissions(currentUser!!, conversation) + val participantPermissions = ParticipantPermissions( + currentUser!!.capabilities?.spreedCapability!!, + conversation + ) return conversation.lobbyState == Conversation.LobbyState.LOBBY_STATE_MODERATORS_ONLY && !conversation.canModerate(currentUser!!) && !participantPermissions.canIgnoreLobby() @@ -1511,7 +1526,7 @@ class ConversationsListActivity : .setNegativeButton(R.string.nc_settings_reauthorize) { _, _ -> val intent = Intent(context, WebViewLoginActivity::class.java) val bundle = Bundle() - bundle.putString(BundleKeys.KEY_BASE_URL, currentUser!!.baseUrl) + bundle.putString(BundleKeys.KEY_BASE_URL, currentUser!!.baseUrl!!) bundle.putBoolean(BundleKeys.KEY_REAUTHORIZE_ACCOUNT, true) intent.putExtras(bundle) startActivity(intent) diff --git a/app/src/main/java/com/nextcloud/talk/dagger/modules/ViewModelModule.kt b/app/src/main/java/com/nextcloud/talk/dagger/modules/ViewModelModule.kt index e9ddd400b..140500b23 100644 --- a/app/src/main/java/com/nextcloud/talk/dagger/modules/ViewModelModule.kt +++ b/app/src/main/java/com/nextcloud/talk/dagger/modules/ViewModelModule.kt @@ -27,6 +27,7 @@ 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.conversationinfo.viewmodel.ConversationInfoViewModel import com.nextcloud.talk.conversationinfoedit.viewmodel.ConversationInfoEditViewModel import com.nextcloud.talk.conversationlist.viewmodels.ConversationsListViewModel import com.nextcloud.talk.invitation.viewmodels.InvitationsViewModel @@ -137,6 +138,11 @@ abstract class ViewModelModule { @ViewModelKey(CallNotificationViewModel::class) abstract fun callNotificationViewModel(viewModel: CallNotificationViewModel): ViewModel + @Binds + @IntoMap + @ViewModelKey(ConversationInfoViewModel::class) + abstract fun conversationInfoViewModel(viewModel: ConversationInfoViewModel): ViewModel + @Binds @IntoMap @ViewModelKey(ConversationInfoEditViewModel::class) diff --git a/app/src/main/java/com/nextcloud/talk/data/user/UserMapper.kt b/app/src/main/java/com/nextcloud/talk/data/user/UserMapper.kt index 48ef06e98..d6f5a1e2b 100644 --- a/app/src/main/java/com/nextcloud/talk/data/user/UserMapper.kt +++ b/app/src/main/java/com/nextcloud/talk/data/user/UserMapper.kt @@ -36,7 +36,7 @@ object UserMapper { entity.id, entity.userId, entity.username, - entity.baseUrl, + entity.baseUrl!!, entity.token, entity.displayName, entity.pushConfigurationState, @@ -52,8 +52,8 @@ object UserMapper { fun toEntity(model: User): UserEntity { val userEntity = when (val id = model.id) { - null -> UserEntity(userId = model.userId, username = model.username, baseUrl = model.baseUrl) - else -> UserEntity(id, model.userId, model.username, model.baseUrl) + null -> UserEntity(userId = model.userId, username = model.username, baseUrl = model.baseUrl!!) + else -> UserEntity(id, model.userId, model.username, model.baseUrl!!) } userEntity.apply { token = model.token diff --git a/app/src/main/java/com/nextcloud/talk/data/user/model/User.kt b/app/src/main/java/com/nextcloud/talk/data/user/model/User.kt index a10b55705..4ce808e3e 100644 --- a/app/src/main/java/com/nextcloud/talk/data/user/model/User.kt +++ b/app/src/main/java/com/nextcloud/talk/data/user/model/User.kt @@ -45,7 +45,7 @@ data class User( var scheduledForDeletion: Boolean = FALSE ) : Parcelable { - fun getCredentials(): String = ApiUtils.getCredentials(username, token) + fun getCredentials(): String = ApiUtils.getCredentials(username, token)!! fun hasSpreedFeatureCapability(capabilityName: String): Boolean { return capabilities?.spreedCapability?.features?.contains(capabilityName) ?: false diff --git a/app/src/main/java/com/nextcloud/talk/extensions/ImageViewExtensions.kt b/app/src/main/java/com/nextcloud/talk/extensions/ImageViewExtensions.kt index b16e30419..377d51d1f 100644 --- a/app/src/main/java/com/nextcloud/talk/extensions/ImageViewExtensions.kt +++ b/app/src/main/java/com/nextcloud/talk/extensions/ImageViewExtensions.kt @@ -118,7 +118,7 @@ fun ImageView.loadUserAvatar( ignoreCache: Boolean ): io.reactivex.disposables.Disposable { val imageRequestUri = ApiUtils.getUrlForAvatar( - user.baseUrl, + user.baseUrl!!, avatarId, requestBigSize ) @@ -155,7 +155,7 @@ private fun ImageView.loadAvatarInternal( user?.let { addHeader( "Authorization", - ApiUtils.getCredentials(user.username, user.token) + ApiUtils.getCredentials(user.username, user.token)!! ) } transformations(CircleCropTransformation()) @@ -196,7 +196,7 @@ fun ImageView.loadThumbnail(url: String, user: User): io.reactivex.disposables.D ) { requestBuilder.addHeader( "Authorization", - ApiUtils.getCredentials(user.username, user.token) + ApiUtils.getCredentials(user.username, user.token)!! ) } @@ -222,7 +222,7 @@ fun ImageView.loadImage(url: String, user: User, placeholder: Drawable? = null): ) { requestBuilder.addHeader( "Authorization", - ApiUtils.getCredentials(user.username, user.token) + ApiUtils.getCredentials(user.username, user.token)!! ) } diff --git a/app/src/main/java/com/nextcloud/talk/invitation/data/InvitationsRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/invitation/data/InvitationsRepositoryImpl.kt index 6429db880..4ec9d9b59 100644 --- a/app/src/main/java/com/nextcloud/talk/invitation/data/InvitationsRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/invitation/data/InvitationsRepositoryImpl.kt @@ -29,29 +29,29 @@ class InvitationsRepositoryImpl(private val ncApi: NcApi) : InvitationsRepository { override fun fetchInvitations(user: User): Observable { - val credentials: String = ApiUtils.getCredentials(user.username, user.token) + val credentials: String = ApiUtils.getCredentials(user.username, user.token)!! return ncApi.getInvitations( credentials, - ApiUtils.getUrlForInvitation(user.baseUrl) + ApiUtils.getUrlForInvitation(user.baseUrl!!) ).map { mapToInvitationsModel(user, it.ocs?.data!!) } } override fun acceptInvitation(user: User, invitation: Invitation): Observable { - val credentials: String = ApiUtils.getCredentials(user.username, user.token) + val credentials: String = ApiUtils.getCredentials(user.username, user.token)!! return ncApi.acceptInvitation( credentials, - ApiUtils.getUrlForInvitationAccept(user.baseUrl, invitation.id) + ApiUtils.getUrlForInvitationAccept(user.baseUrl!!, invitation.id) ).map { InvitationActionModel(ActionEnum.ACCEPT, it.ocs?.meta?.statusCode!!, invitation) } } override fun rejectInvitation(user: User, invitation: Invitation): Observable { - val credentials: String = ApiUtils.getCredentials(user.username, user.token) + val credentials: String = ApiUtils.getCredentials(user.username, user.token)!! return ncApi.rejectInvitation( credentials, - ApiUtils.getUrlForInvitationReject(user.baseUrl, invitation.id) + ApiUtils.getUrlForInvitationReject(user.baseUrl!!, invitation.id) ).map { InvitationActionModel(ActionEnum.REJECT, it.ocs?.meta?.statusCode!!, invitation) } } diff --git a/app/src/main/java/com/nextcloud/talk/jobs/AddParticipantsToConversation.java b/app/src/main/java/com/nextcloud/talk/jobs/AddParticipantsToConversation.java index 407ce8bca..03e14c8c3 100644 --- a/app/src/main/java/com/nextcloud/talk/jobs/AddParticipantsToConversation.java +++ b/app/src/main/java/com/nextcloud/talk/jobs/AddParticipantsToConversation.java @@ -70,7 +70,7 @@ public class AddParticipantsToConversation extends Worker { data.getLong(BundleKeys.KEY_INTERNAL_USER_ID, -1)) .blockingGet(); - int apiVersion = ApiUtils.getConversationApiVersion(user, new int[] {ApiUtils.APIv4, 1}); + int apiVersion = ApiUtils.getConversationApiVersion(user, new int[] {ApiUtils.API_V4, 1}); String conversationToken = data.getString(BundleKeys.KEY_TOKEN); String credentials = ApiUtils.getCredentials(user.getUsername(), user.getToken()); diff --git a/app/src/main/java/com/nextcloud/talk/jobs/ContactAddressBookWorker.kt b/app/src/main/java/com/nextcloud/talk/jobs/ContactAddressBookWorker.kt index c55949eec..0e089bca2 100644 --- a/app/src/main/java/com/nextcloud/talk/jobs/ContactAddressBookWorker.kt +++ b/app/src/main/java/com/nextcloud/talk/jobs/ContactAddressBookWorker.kt @@ -129,7 +129,7 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar ncApi.searchContactsByPhoneNumber( ApiUtils.getCredentials(currentUser.username, currentUser.token), - ApiUtils.getUrlForSearchByNumber(currentUser.baseUrl), + ApiUtils.getUrlForSearchByNumber(currentUser.baseUrl!!), json.toRequestBody("application/json".toMediaTypeOrNull()) ) .subscribeOn(Schedulers.io()) diff --git a/app/src/main/java/com/nextcloud/talk/jobs/DeleteConversationWorker.java b/app/src/main/java/com/nextcloud/talk/jobs/DeleteConversationWorker.java index 16e5851b6..fd953c82f 100644 --- a/app/src/main/java/com/nextcloud/talk/jobs/DeleteConversationWorker.java +++ b/app/src/main/java/com/nextcloud/talk/jobs/DeleteConversationWorker.java @@ -80,7 +80,7 @@ public class DeleteConversationWorker extends Worker { User operationUser = userManager.getUserWithId(operationUserId).blockingGet(); if (operationUser != null) { - int apiVersion = ApiUtils.getConversationApiVersion(operationUser, new int[]{ApiUtils.APIv4, 1}); + int apiVersion = ApiUtils.getConversationApiVersion(operationUser, new int[]{ApiUtils.API_V4, 1}); String credentials = ApiUtils.getCredentials(operationUser.getUsername(), operationUser.getToken()); ncApi = retrofit diff --git a/app/src/main/java/com/nextcloud/talk/jobs/LeaveConversationWorker.java b/app/src/main/java/com/nextcloud/talk/jobs/LeaveConversationWorker.java index 87b15e690..946bdf52d 100644 --- a/app/src/main/java/com/nextcloud/talk/jobs/LeaveConversationWorker.java +++ b/app/src/main/java/com/nextcloud/talk/jobs/LeaveConversationWorker.java @@ -92,7 +92,7 @@ public class LeaveConversationWorker extends Worker { EventStatus.EventType.CONVERSATION_UPDATE, true); - int apiVersion = ApiUtils.getConversationApiVersion(operationUser, new int[] {ApiUtils.APIv4, 1}); + int apiVersion = ApiUtils.getConversationApiVersion(operationUser, new int[] {ApiUtils.API_V4, 1}); ncApi.removeSelfFromRoom(credentials, ApiUtils.getUrlForParticipantsSelf(apiVersion, operationUser.getBaseUrl(), diff --git a/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt b/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt index 7057eb375..07a92ac64 100644 --- a/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt +++ b/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt @@ -248,7 +248,7 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor val soundUri = getCallRingtoneUri(applicationContext, appPreferences) val notificationChannelId = NotificationUtils.NotificationChannels.NOTIFICATION_CHANNEL_CALLS_V4.name - val uri = Uri.parse(signatureVerification.user!!.baseUrl) + val uri = Uri.parse(signatureVerification.user!!.baseUrl!!) val baseUrl = uri.host val notification = @@ -279,7 +279,7 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor credentials = ApiUtils.getCredentials( signatureVerification.user!!.username, signatureVerification.user!!.token - ) + )!! ncApi = retrofit!!.newBuilder().client( okHttpClient!!.newBuilder().cookieJar( JavaNetCookieJar( @@ -338,7 +338,7 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor ncApi.getNcNotification( credentials, ApiUtils.getUrlForNcNotificationWithId( - user!!.baseUrl, + user!!.baseUrl!!, (pushMessage.notificationId!!).toString() ) ) @@ -451,7 +451,7 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor 0 } val pendingIntent = PendingIntent.getActivity(context, requestCode, intent, intentFlag) - val uri = Uri.parse(signatureVerification.user!!.baseUrl) + val uri = Uri.parse(signatureVerification.user!!.baseUrl!!) val baseUrl = uri.host var contentTitle: CharSequence? = "" @@ -601,12 +601,12 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor val baseUrl = signatureVerification.user!!.baseUrl val avatarUrl = if ("user" == userType) { ApiUtils.getUrlForAvatar( - baseUrl, + baseUrl!!, notificationUser.id, false ) } else { - ApiUtils.getUrlForGuestAvatar(baseUrl, notificationUser.name, false) + ApiUtils.getUrlForGuestAvatar(baseUrl!!, notificationUser.name, false) } person.setIcon(loadAvatarSync(avatarUrl, context!!)) } @@ -840,8 +840,8 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor var inCallOnDifferentDevice = false val apiVersion = ApiUtils.getConversationApiVersion( - signatureVerification.user, - intArrayOf(ApiUtils.APIv4, 1) + signatureVerification.user!!, + intArrayOf(ApiUtils.API_V4, 1) ) var isCallNotificationVisible = true @@ -850,8 +850,8 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor credentials, ApiUtils.getUrlForCall( apiVersion, - signatureVerification.user!!.baseUrl, - pushMessage.id + signatureVerification.user!!.baseUrl!!, + pushMessage.id!! ) ) .repeatWhen { completed -> @@ -920,10 +920,10 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor if (isOngoingCallNotificationVisible) { val apiVersion = ApiUtils.getConversationApiVersion( - signatureVerification.user, + signatureVerification.user!!, intArrayOf( - ApiUtils.APIv4, - ApiUtils.APIv3, + ApiUtils.API_V4, + ApiUtils.API_V3, 1 ) ) @@ -931,7 +931,7 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor credentials, ApiUtils.getUrlForRoom( apiVersion, - signatureVerification.user?.baseUrl, + signatureVerification.user?.baseUrl!!, pushMessage.id ) ) diff --git a/app/src/main/java/com/nextcloud/talk/jobs/ShareOperationWorker.kt b/app/src/main/java/com/nextcloud/talk/jobs/ShareOperationWorker.kt index 45b3cae47..c29d1824c 100644 --- a/app/src/main/java/com/nextcloud/talk/jobs/ShareOperationWorker.kt +++ b/app/src/main/java/com/nextcloud/talk/jobs/ShareOperationWorker.kt @@ -62,7 +62,7 @@ class ShareOperationWorker(context: Context, workerParams: WorkerParameters) : W for (filePath in filesArray) { ncApi.createRemoteShare( credentials, - ApiUtils.getSharingUrl(baseUrl), + ApiUtils.getSharingUrl(baseUrl!!), filePath, roomToken, "10", @@ -87,7 +87,7 @@ class ShareOperationWorker(context: Context, workerParams: WorkerParameters) : W val operationsUser = userManager.getUserWithId(userId).blockingGet() baseUrl = operationsUser.baseUrl - credentials = ApiUtils.getCredentials(operationsUser.username, operationsUser.token) + credentials = ApiUtils.getCredentials(operationsUser.username, operationsUser.token)!! } companion object { diff --git a/app/src/main/java/com/nextcloud/talk/jobs/SignalingSettingsWorker.java b/app/src/main/java/com/nextcloud/talk/jobs/SignalingSettingsWorker.java index a0e4e7d07..6b53c6114 100644 --- a/app/src/main/java/com/nextcloud/talk/jobs/SignalingSettingsWorker.java +++ b/app/src/main/java/com/nextcloud/talk/jobs/SignalingSettingsWorker.java @@ -85,7 +85,7 @@ public class SignalingSettingsWorker extends Worker { for (User user : userEntityObjectList) { - int apiVersion = ApiUtils.getSignalingApiVersion(user, new int[] {ApiUtils.APIv3, 2, 1}); + int apiVersion = ApiUtils.getSignalingApiVersion(user, new int[] {ApiUtils.API_V3, 2, 1}); ncApi.getSignalingSettings( ApiUtils.getCredentials(user.getUsername(), user.getToken()), diff --git a/app/src/main/java/com/nextcloud/talk/jobs/UploadAndShareFilesWorker.kt b/app/src/main/java/com/nextcloud/talk/jobs/UploadAndShareFilesWorker.kt index f171b8f62..5f20742fc 100644 --- a/app/src/main/java/com/nextcloud/talk/jobs/UploadAndShareFilesWorker.kt +++ b/app/src/main/java/com/nextcloud/talk/jobs/UploadAndShareFilesWorker.kt @@ -56,7 +56,7 @@ import com.nextcloud.talk.utils.RemoteFileUtils import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_FROM_NOTIFICATION_START_CALL import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_INTERNAL_USER_ID import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_TOKEN -import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew +import com.nextcloud.talk.utils.CapabilitiesUtil import com.nextcloud.talk.utils.permissions.PlatformPermissionUtil import com.nextcloud.talk.utils.preferences.AppPreferences import okhttp3.MediaType.Companion.toMediaTypeOrNull @@ -186,7 +186,9 @@ class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerPa } private fun getRemotePath(currentUser: User): String { - var remotePath = CapabilitiesUtilNew.getAttachmentFolder(currentUser)!! + "/" + fileName + var remotePath = CapabilitiesUtil.getAttachmentFolder( + currentUser.capabilities!!.spreedCapability!! + ) + "/" + fileName remotePath = RemoteFileUtils.getNewPathIfFileExists( ncApi, currentUser, diff --git a/app/src/main/java/com/nextcloud/talk/location/GeocodingActivity.kt b/app/src/main/java/com/nextcloud/talk/location/GeocodingActivity.kt index 1ecc4769f..71ae0e931 100644 --- a/app/src/main/java/com/nextcloud/talk/location/GeocodingActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/location/GeocodingActivity.kt @@ -64,6 +64,7 @@ class GeocodingActivity : lateinit var okHttpClient: OkHttpClient lateinit var roomToken: String + private var chatApiVersion: Int = 1 private var nominatimClient: TalkJsonNominatimClient? = null private var searchItem: MenuItem? = null @@ -86,6 +87,7 @@ class GeocodingActivity : Configuration.getInstance().load(context, PreferenceManager.getDefaultSharedPreferences(context)) roomToken = intent.getStringExtra(BundleKeys.KEY_ROOM_TOKEN)!! + chatApiVersion = intent.getIntExtra(BundleKeys.KEY_CHAT_API_VERSION, 1) recyclerView = findViewById(R.id.geocoding_results) recyclerView.layoutManager = LinearLayoutManager(this) @@ -130,6 +132,7 @@ class GeocodingActivity : val intent = Intent(this@GeocodingActivity, LocationPickerActivity::class.java) intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) intent.putExtra(BundleKeys.KEY_ROOM_TOKEN, roomToken) + intent.putExtra(BundleKeys.KEY_CHAT_API_VERSION, chatApiVersion) intent.putExtra(BundleKeys.KEY_GEOCODING_RESULT, geocodingResult) startActivity(intent) } @@ -158,6 +161,7 @@ class GeocodingActivity : val intent = Intent(this@GeocodingActivity, LocationPickerActivity::class.java) intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) intent.putExtra(BundleKeys.KEY_ROOM_TOKEN, roomToken) + intent.putExtra(BundleKeys.KEY_CHAT_API_VERSION, chatApiVersion) intent.putExtra(BundleKeys.KEY_GEOCODING_RESULT, geocodingResult) startActivity(intent) } @@ -217,6 +221,7 @@ class GeocodingActivity : val intent = Intent(context, LocationPickerActivity::class.java) intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) intent.putExtra(BundleKeys.KEY_ROOM_TOKEN, roomToken) + intent.putExtra(BundleKeys.KEY_CHAT_API_VERSION, chatApiVersion) startActivity(intent) return true } diff --git a/app/src/main/java/com/nextcloud/talk/location/LocationPickerActivity.kt b/app/src/main/java/com/nextcloud/talk/location/LocationPickerActivity.kt index c93de415a..d80c7492b 100644 --- a/app/src/main/java/com/nextcloud/talk/location/LocationPickerActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/location/LocationPickerActivity.kt @@ -58,6 +58,7 @@ import com.nextcloud.talk.users.UserManager import com.nextcloud.talk.utils.ApiUtils import com.nextcloud.talk.utils.DisplayUtils import com.nextcloud.talk.utils.bundle.BundleKeys +import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_CHAT_API_VERSION import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_GEOCODING_RESULT import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_TOKEN import fr.dudie.nominatim.client.TalkJsonNominatimClient @@ -103,6 +104,7 @@ class LocationPickerActivity : var nominatimClient: TalkJsonNominatimClient? = null lateinit var roomToken: String + private var chatApiVersion: Int = 1 var geocodingResult: GeocodingResult? = null var myLocation: GeoPoint = GeoPoint(COORDINATE_ZERO, COORDINATE_ZERO) @@ -130,6 +132,7 @@ class LocationPickerActivity : NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this) roomToken = intent.getStringExtra(KEY_ROOM_TOKEN)!! + chatApiVersion = intent.getIntExtra(KEY_CHAT_API_VERSION, 1) geocodingResult = intent.getParcelableExtra(KEY_GEOCODING_RESULT) if (savedInstanceState != null) { @@ -244,6 +247,7 @@ class LocationPickerActivity : val intent = Intent(this, GeocodingActivity::class.java) intent.putExtra(BundleKeys.KEY_GEOCODING_QUERY, query) intent.putExtra(KEY_ROOM_TOKEN, roomToken) + intent.putExtra(KEY_CHAT_API_VERSION, chatApiVersion) startActivity(intent) } return true @@ -465,11 +469,10 @@ class LocationPickerActivity : "\"longitude\":\"$selectedLon\",\"name\":\"$locationNameToShare\"}" val currentUser = userManager.currentUser.blockingGet() - val apiVersion = ApiUtils.getChatApiVersion(currentUser, intArrayOf(1)) ncApi.sendLocation( ApiUtils.getCredentials(currentUser.username, currentUser.token), - ApiUtils.getUrlToSendLocation(apiVersion, currentUser.baseUrl, roomToken), + ApiUtils.getUrlToSendLocation(chatApiVersion, currentUser.baseUrl!!, roomToken), "geo-location", objectId, metaData diff --git a/app/src/main/java/com/nextcloud/talk/models/RetrofitBucket.kt b/app/src/main/java/com/nextcloud/talk/models/RetrofitBucket.kt index 8b6460836..a8a50abe5 100644 --- a/app/src/main/java/com/nextcloud/talk/models/RetrofitBucket.kt +++ b/app/src/main/java/com/nextcloud/talk/models/RetrofitBucket.kt @@ -27,5 +27,5 @@ import kotlinx.parcelize.Parcelize @Parcelize data class RetrofitBucket( var url: String? = null, - var queryMap: Map? = null + var queryMap: MutableMap? = null ) : Parcelable diff --git a/app/src/main/java/com/nextcloud/talk/models/domain/ConversationModel.kt b/app/src/main/java/com/nextcloud/talk/models/domain/ConversationModel.kt index e9e2a164a..c239cd5af 100644 --- a/app/src/main/java/com/nextcloud/talk/models/domain/ConversationModel.kt +++ b/app/src/main/java/com/nextcloud/talk/models/domain/ConversationModel.kt @@ -3,7 +3,7 @@ package com.nextcloud.talk.models.domain import com.nextcloud.talk.models.json.conversations.Conversation class ConversationModel( - var roomId: String?, + var roomId: String? = null, var token: String? = null, var name: String? = null, var displayName: String? = null, @@ -42,7 +42,11 @@ class ConversationModel( var statusClearAt: Long? = 0, var callRecording: Int = 0, var avatarVersion: String? = null, - var hasCustomAvatar: Boolean? = null + var hasCustomAvatar: Boolean? = null, + var callStartTime: Long? = null, + var recordingConsentRequired: Int = 0, + var remoteServer: String? = null, + var remoteToken: String? = null ) { companion object { @@ -95,7 +99,11 @@ class ConversationModel( statusClearAt = conversation.statusClearAt, callRecording = conversation.callRecording, avatarVersion = conversation.avatarVersion, - hasCustomAvatar = conversation.hasCustomAvatar + hasCustomAvatar = conversation.hasCustomAvatar, + callStartTime = conversation.callStartTime, + recordingConsentRequired = conversation.recordingConsentRequired, + remoteServer = conversation.remoteServer, + remoteToken = conversation.remoteToken ) } } diff --git a/app/src/main/java/com/nextcloud/talk/models/domain/converters/DomainEnumNotificationLevelConverter.kt b/app/src/main/java/com/nextcloud/talk/models/domain/converters/DomainEnumNotificationLevelConverter.kt new file mode 100644 index 000000000..037add02b --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/models/domain/converters/DomainEnumNotificationLevelConverter.kt @@ -0,0 +1,45 @@ +/* + * Nextcloud Talk application + * + * @author Mario Danic + * Copyright (C) 2017-2018 Mario Danic + * + * 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 . + */ +package com.nextcloud.talk.models.domain.converters + +import com.bluelinelabs.logansquare.typeconverters.IntBasedTypeConverter +import com.nextcloud.talk.models.domain.NotificationLevel + +class DomainEnumNotificationLevelConverter : IntBasedTypeConverter() { + override fun getFromInt(i: Int): NotificationLevel { + return when (i) { + 0 -> NotificationLevel.DEFAULT + 1 -> NotificationLevel.ALWAYS + 2 -> NotificationLevel.MENTION + 3 -> NotificationLevel.NEVER + else -> NotificationLevel.DEFAULT + } + } + + override fun convertToInt(`object`: NotificationLevel): Int { + return when (`object`) { + NotificationLevel.DEFAULT -> 0 + NotificationLevel.ALWAYS -> 1 + NotificationLevel.MENTION -> 2 + NotificationLevel.NEVER -> 3 + else -> 0 + } + } +} diff --git a/app/src/main/java/com/nextcloud/talk/models/json/capabilities/RoomCapabilitiesOCS.kt b/app/src/main/java/com/nextcloud/talk/models/json/capabilities/RoomCapabilitiesOCS.kt new file mode 100644 index 000000000..193dff529 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/models/json/capabilities/RoomCapabilitiesOCS.kt @@ -0,0 +1,42 @@ +/* + * Nextcloud Talk application + * + * @author Mario Danic + * @author Tim Krüger + * @author Andy Scherzinger + * Copyright (C) 2022 Andy Scherzinger + * Copyright (C) 2022 Tim Krüger + * Copyright (C) 2017-2018 Mario Danic + * + * 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 . + */ +package com.nextcloud.talk.models.json.capabilities + +import android.os.Parcelable +import com.bluelinelabs.logansquare.annotation.JsonField +import com.bluelinelabs.logansquare.annotation.JsonObject +import com.nextcloud.talk.models.json.generic.GenericMeta +import kotlinx.parcelize.Parcelize + +@Parcelize +@JsonObject +data class RoomCapabilitiesOCS( + @JsonField(name = ["meta"]) + var meta: GenericMeta?, + @JsonField(name = ["data"]) + var data: SpreedCapability? +) : Parcelable { + // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject' + constructor() : this(null, null) +} diff --git a/app/src/main/java/com/nextcloud/talk/models/json/capabilities/RoomCapabilitiesOverall.kt b/app/src/main/java/com/nextcloud/talk/models/json/capabilities/RoomCapabilitiesOverall.kt new file mode 100644 index 000000000..aa7234929 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/models/json/capabilities/RoomCapabilitiesOverall.kt @@ -0,0 +1,37 @@ +/* + * Nextcloud Talk application + * + * @author Mario Danic + * @author Tim Krüger + * Copyright (C) 2022 Tim Krüger + * Copyright (C) 2017-2018 Mario Danic + * + * 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 . + */ +package com.nextcloud.talk.models.json.capabilities + +import android.os.Parcelable +import com.bluelinelabs.logansquare.annotation.JsonField +import com.bluelinelabs.logansquare.annotation.JsonObject +import kotlinx.parcelize.Parcelize + +@Parcelize +@JsonObject +data class RoomCapabilitiesOverall( + @JsonField(name = ["ocs"]) + var ocs: RoomCapabilitiesOCS? = null +) : Parcelable { + // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject' + constructor() : this(null) +} diff --git a/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatMessage.kt b/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatMessage.kt index 467549314..dc285504c 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatMessage.kt +++ b/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatMessage.kt @@ -37,7 +37,7 @@ import com.nextcloud.talk.data.user.model.User import com.nextcloud.talk.models.json.chat.ChatUtils.Companion.getParsedMessage import com.nextcloud.talk.models.json.converters.EnumSystemMessageTypeConverter import com.nextcloud.talk.utils.ApiUtils -import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew +import com.nextcloud.talk.utils.CapabilitiesUtil import com.stfalcon.chatkit.commons.models.IUser import com.stfalcon.chatkit.commons.models.MessageContentType import kotlinx.parcelize.Parcelize @@ -213,7 +213,7 @@ data class ChatMessage( @Suppress("ReturnCount") fun isLinkPreview(): Boolean { - if (CapabilitiesUtilNew.isLinkPreviewAvailable(activeUser!!)) { + if (CapabilitiesUtil.isLinkPreviewAvailable(activeUser!!)) { val regexStringFromServer = activeUser?.capabilities?.coreCapability?.referenceRegex val regexFromServer = regexStringFromServer?.toRegex(setOf(RegexOption.MULTILINE, RegexOption.IGNORE_CASE)) @@ -249,8 +249,8 @@ data class ChatMessage( if (!isVoiceMessage) { if (activeUser != null && activeUser!!.baseUrl != null) { return ApiUtils.getUrlForFilePreviewWithFileId( - activeUser!!.baseUrl, - individualHashMap["id"], + activeUser!!.baseUrl!!, + individualHashMap["id"]!!, sharedApplication!!.resources.getDimensionPixelSize(R.dimen.maximum_file_preview_size) ) } else { @@ -413,11 +413,11 @@ data class ChatMessage( null } actorType == "users" -> { - ApiUtils.getUrlForAvatar(activeUser!!.baseUrl, actorId, true) + ApiUtils.getUrlForAvatar(activeUser!!.baseUrl!!, actorId, true) } actorType == "bridged" -> { ApiUtils.getUrlForAvatar( - activeUser!!.baseUrl, + activeUser!!.baseUrl!!, "bridge-bot", true ) @@ -427,7 +427,7 @@ data class ChatMessage( if (!TextUtils.isEmpty(actorDisplayName)) { apiId = actorDisplayName } - ApiUtils.getUrlForGuestAvatar(activeUser!!.baseUrl, apiId, true) + ApiUtils.getUrlForGuestAvatar(activeUser!!.baseUrl!!, apiId, true) } } } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/conversations/Conversation.kt b/app/src/main/java/com/nextcloud/talk/models/json/conversations/Conversation.kt index 57dc17779..97ec35660 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/conversations/Conversation.kt +++ b/app/src/main/java/com/nextcloud/talk/models/json/conversations/Conversation.kt @@ -38,8 +38,9 @@ import com.nextcloud.talk.models.json.converters.EnumParticipantTypeConverter import com.nextcloud.talk.models.json.converters.EnumReadOnlyConversationConverter import com.nextcloud.talk.models.json.converters.EnumRoomTypeConverter import com.nextcloud.talk.models.json.participants.Participant.ParticipantType +import com.nextcloud.talk.utils.SpreedFeatures import com.nextcloud.talk.utils.ConversationUtils -import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew +import com.nextcloud.talk.utils.CapabilitiesUtil import kotlinx.parcelize.Parcelize @Parcelize @@ -160,7 +161,13 @@ data class Conversation( var callStartTime: Long? = null, @JsonField(name = ["recordingConsent"]) - var recordingConsentRequired: Int = 0 + var recordingConsentRequired: Int = 0, + + @JsonField(name = ["remoteServer"]) + var remoteServer: String? = null, + + @JsonField(name = ["remoteToken"]) + var remoteToken: String? = null ) : Parcelable { // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject' @@ -185,13 +192,20 @@ data class Conversation( @Deprecated("Use ConversationUtil") private fun isLockedOneToOne(conversationUser: User): Boolean { return type == ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL && - CapabilitiesUtilNew.hasSpreedFeatureCapability(conversationUser, "locked-one-to-one-rooms") + CapabilitiesUtil.hasSpreedFeatureCapability( + conversationUser.capabilities?.spreedCapability!!, + SpreedFeatures.LOCKED_ONE_TO_ONE_ROOMS + ) } @Deprecated("Use ConversationUtil") fun canModerate(conversationUser: User): Boolean { return isParticipantOwnerOrModerator && - !isLockedOneToOne(conversationUser) && + ConversationUtils.isLockedOneToOne( + ConversationModel.mapToConversationModel(this), + conversationUser + .capabilities?.spreedCapability!! + ) && type != ConversationType.FORMER_ONE_TO_ONE && !ConversationUtils.isNoteToSelfConversation(ConversationModel.mapToConversationModel(this)) } diff --git a/app/src/main/java/com/nextcloud/talk/openconversations/data/OpenConversationsRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/openconversations/data/OpenConversationsRepositoryImpl.kt index cde534757..6da447142 100644 --- a/app/src/main/java/com/nextcloud/talk/openconversations/data/OpenConversationsRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/openconversations/data/OpenConversationsRepositoryImpl.kt @@ -31,14 +31,14 @@ class OpenConversationsRepositoryImpl(private val ncApi: NcApi, currentUserProvi OpenConversationsRepository { val currentUser: User = currentUserProvider.currentUser.blockingGet() - val credentials: String = ApiUtils.getCredentials(currentUser.username, currentUser.token) + val credentials: String = ApiUtils.getCredentials(currentUser.username, currentUser.token)!! - val apiVersion = ApiUtils.getConversationApiVersion(currentUser, intArrayOf(ApiUtils.APIv4, ApiUtils.APIv3, 1)) + val apiVersion = ApiUtils.getConversationApiVersion(currentUser, intArrayOf(ApiUtils.API_V4, ApiUtils.API_V3, 1)) override fun fetchConversations(): Observable { return ncApi.getOpenConversations( credentials, - ApiUtils.getUrlForOpenConversations(apiVersion, currentUser.baseUrl) + ApiUtils.getUrlForOpenConversations(apiVersion, currentUser.baseUrl!!) ).map { mapToOpenConversationsModel(it.ocs?.data!!) } } diff --git a/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt index d74c0a158..abd887b1b 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt @@ -37,7 +37,7 @@ class PollRepositoryImpl(private val ncApi: NcApi, private val currentUserProvid PollRepository { val currentUser: User = currentUserProvider.currentUser.blockingGet() - val credentials: String = ApiUtils.getCredentials(currentUser.username, currentUser.token) + val credentials: String = ApiUtils.getCredentials(currentUser.username, currentUser.token)!! override fun createPoll( roomToken: String, @@ -49,7 +49,7 @@ class PollRepositoryImpl(private val ncApi: NcApi, private val currentUserProvid return ncApi.createPoll( credentials, ApiUtils.getUrlForPoll( - currentUser.baseUrl, + currentUser.baseUrl!!, roomToken ), question, @@ -63,7 +63,7 @@ class PollRepositoryImpl(private val ncApi: NcApi, private val currentUserProvid return ncApi.getPoll( credentials, ApiUtils.getUrlForPoll( - currentUser.baseUrl, + currentUser.baseUrl!!, roomToken, pollId ) @@ -74,7 +74,7 @@ class PollRepositoryImpl(private val ncApi: NcApi, private val currentUserProvid return ncApi.votePoll( credentials, ApiUtils.getUrlForPoll( - currentUser.baseUrl, + currentUser.baseUrl!!, roomToken, pollId ), @@ -86,7 +86,7 @@ class PollRepositoryImpl(private val ncApi: NcApi, private val currentUserProvid return ncApi.closePoll( credentials, ApiUtils.getUrlForPoll( - currentUser.baseUrl, + currentUser.baseUrl!!, roomToken, pollId ) diff --git a/app/src/main/java/com/nextcloud/talk/presenters/MentionAutocompletePresenter.java b/app/src/main/java/com/nextcloud/talk/presenters/MentionAutocompletePresenter.java index 8aa553714..c11eb27b4 100644 --- a/app/src/main/java/com/nextcloud/talk/presenters/MentionAutocompletePresenter.java +++ b/app/src/main/java/com/nextcloud/talk/presenters/MentionAutocompletePresenter.java @@ -77,6 +77,7 @@ public class MentionAutocompletePresenter extends RecyclerViewPresenter private Context context; private String roomToken; + private int chatApiVersion; private List abstractFlexibleItemList = new ArrayList<>(); @@ -87,10 +88,11 @@ public class MentionAutocompletePresenter extends RecyclerViewPresenter currentUser = userManager.getCurrentUser().blockingGet(); } - public MentionAutocompletePresenter(Context context, String roomToken) { + public MentionAutocompletePresenter(Context context, String roomToken, int chatApiVersion) { super(context); this.roomToken = roomToken; this.context = context; + this.chatApiVersion = chatApiVersion; NextcloudTalkApplication.Companion.getSharedApplication().getComponentApplication().inject(this); currentUser = userManager.getCurrentUser().blockingGet(); } @@ -120,8 +122,6 @@ public class MentionAutocompletePresenter extends RecyclerViewPresenter queryString = ""; } - int apiVersion = ApiUtils.getChatApiVersion(currentUser, new int[] {1}); - adapter.setFilter(queryString); Map queryMap = new HashMap<>(); @@ -129,7 +129,7 @@ public class MentionAutocompletePresenter extends RecyclerViewPresenter ncApi.getMentionAutocompleteSuggestions( ApiUtils.getCredentials(currentUser.getUsername(), currentUser.getToken()), - ApiUtils.getUrlForMentionSuggestions(apiVersion, currentUser.getBaseUrl(), roomToken), + ApiUtils.getUrlForMentionSuggestions(chatApiVersion, currentUser.getBaseUrl(), roomToken), queryString, 5, queryMap) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) diff --git a/app/src/main/java/com/nextcloud/talk/profile/ProfileActivity.kt b/app/src/main/java/com/nextcloud/talk/profile/ProfileActivity.kt index 48020e8c9..6ace02f26 100644 --- a/app/src/main/java/com/nextcloud/talk/profile/ProfileActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/profile/ProfileActivity.kt @@ -66,12 +66,13 @@ import com.nextcloud.talk.ui.dialog.ScopeDialog import com.nextcloud.talk.ui.theme.ViewThemeUtils import com.nextcloud.talk.users.UserManager import com.nextcloud.talk.utils.ApiUtils +import com.nextcloud.talk.utils.SpreedFeatures import com.nextcloud.talk.utils.DisplayUtils import com.nextcloud.talk.utils.Mimetype.IMAGE_JPG import com.nextcloud.talk.utils.Mimetype.IMAGE_PREFIX_GENERIC import com.nextcloud.talk.utils.PickImage import com.nextcloud.talk.utils.PickImage.Companion.REQUEST_PERMISSION_CAMERA -import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew +import com.nextcloud.talk.utils.CapabilitiesUtil import io.reactivex.Observer import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.Disposable @@ -127,7 +128,7 @@ class ProfileActivity : BaseActivity() { binding.avatarDelete.setOnClickListener { ncApi.deleteAvatar( credentials, - ApiUtils.getUrlForTempAvatar(currentUser!!.baseUrl) + ApiUtils.getUrlForTempAvatar(currentUser!!.baseUrl!!) ) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) @@ -154,7 +155,7 @@ class ProfileActivity : BaseActivity() { }) } binding.avatarImage.let { ViewCompat.setTransitionName(it, "userAvatar.transitionTag") } - ncApi.getUserProfile(credentials, ApiUtils.getUrlForUserProfile(currentUser!!.baseUrl)) + ncApi.getUserProfile(credentials, ApiUtils.getUrlForUserProfile(currentUser!!.baseUrl!!)) .retry(DEFAULT_RETRIES) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) @@ -226,13 +227,17 @@ class ProfileActivity : BaseActivity() { item.icon = ContextCompat.getDrawable(this, R.drawable.ic_check) binding.emptyList.root.visibility = View.GONE binding.userinfoList.visibility = View.VISIBLE - if (CapabilitiesUtilNew.isAvatarEndpointAvailable(currentUser!!)) { + if (CapabilitiesUtil.hasSpreedFeatureCapability( + currentUser!!.capabilities!!.spreedCapability!!, + SpreedFeatures.TEMP_USER_AVATAR_API + ) + ) { // TODO later avatar can also be checked via user fields, for now it is in Talk capability binding.avatarButtons.visibility = View.VISIBLE } ncApi.getEditableUserProfileFields( ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token), - ApiUtils.getUrlForUserFields(currentUser!!.baseUrl) + ApiUtils.getUrlForUserFields(currentUser!!.baseUrl!!) ) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) @@ -292,7 +297,7 @@ class ProfileActivity : BaseActivity() { private fun showUserProfile() { if (currentUser!!.baseUrl != null) { - binding.userinfoBaseurl.text = Uri.parse(currentUser!!.baseUrl).host + binding.userinfoBaseurl.text = Uri.parse(currentUser!!.baseUrl!!).host } DisplayUtils.loadAvatarImage(currentUser, binding.avatarImage, false) if (!TextUtils.isEmpty(userInfo?.displayName)) { @@ -327,10 +332,10 @@ class ProfileActivity : BaseActivity() { } // show edit button - if (CapabilitiesUtilNew.canEditScopes(currentUser!!)) { + if (CapabilitiesUtil.canEditScopes(currentUser!!)) { ncApi.getEditableUserProfileFields( ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token), - ApiUtils.getUrlForUserFields(currentUser!!.baseUrl) + ApiUtils.getUrlForUserFields(currentUser!!.baseUrl!!) ) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) @@ -438,7 +443,7 @@ class ProfileActivity : BaseActivity() { val credentials = ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token) ncApi.setUserData( credentials, - ApiUtils.getUrlForUserData(currentUser!!.baseUrl, currentUser!!.userId), + ApiUtils.getUrlForUserData(currentUser!!.baseUrl!!, currentUser!!.userId!!), item.field.fieldName, item.text ) @@ -535,7 +540,7 @@ class ProfileActivity : BaseActivity() { // upload file ncApi.uploadAvatar( ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token), - ApiUtils.getUrlForTempAvatar(currentUser!!.baseUrl), + ApiUtils.getUrlForTempAvatar(currentUser!!.baseUrl!!), filePart ) .subscribeOn(Schedulers.io()) @@ -569,7 +574,7 @@ class ProfileActivity : BaseActivity() { val credentials = ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token) ncApi.setUserData( credentials, - ApiUtils.getUrlForUserData(currentUser!!.baseUrl, currentUser!!.userId), + ApiUtils.getUrlForUserData(currentUser!!.baseUrl!!, currentUser!!.userId!!), item.field.scopeName, item.scope!!.name ) diff --git a/app/src/main/java/com/nextcloud/talk/raisehand/RequestAssistanceRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/raisehand/RequestAssistanceRepositoryImpl.kt index 757602762..e15b42c2f 100644 --- a/app/src/main/java/com/nextcloud/talk/raisehand/RequestAssistanceRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/raisehand/RequestAssistanceRepositoryImpl.kt @@ -31,7 +31,7 @@ class RequestAssistanceRepositoryImpl(private val ncApi: NcApi, currentUserProvi RequestAssistanceRepository { val currentUser: User = currentUserProvider.currentUser.blockingGet() - val credentials: String = ApiUtils.getCredentials(currentUser.username, currentUser.token) + val credentials: String = ApiUtils.getCredentials(currentUser.username, currentUser.token)!! var apiVersion = 1 diff --git a/app/src/main/java/com/nextcloud/talk/receivers/DirectReplyReceiver.kt b/app/src/main/java/com/nextcloud/talk/receivers/DirectReplyReceiver.kt index b66b013d2..fdb8d6891 100644 --- a/app/src/main/java/com/nextcloud/talk/receivers/DirectReplyReceiver.kt +++ b/app/src/main/java/com/nextcloud/talk/receivers/DirectReplyReceiver.kt @@ -91,8 +91,8 @@ class DirectReplyReceiver : BroadcastReceiver() { private fun sendDirectReply() { val credentials = ApiUtils.getCredentials(currentUser.username, currentUser.token) - val apiVersion = ApiUtils.getChatApiVersion(currentUser, intArrayOf(1)) - val url = ApiUtils.getUrlForChat(apiVersion, currentUser.baseUrl, roomToken) + val apiVersion = ApiUtils.getChatApiVersion(currentUser.capabilities!!.spreedCapability!!, intArrayOf(1)) + val url = ApiUtils.getUrlForChat(apiVersion, currentUser.baseUrl!!, roomToken!!) ncApi.sendChatMessage(credentials, url, replyMessage, currentUser.displayName, null, false) ?.subscribeOn(Schedulers.io()) @@ -153,7 +153,7 @@ class DirectReplyReceiver : BroadcastReceiver() { // Add reply Single.fromCallable { - val avatarUrl = ApiUtils.getUrlForAvatar(currentUser.baseUrl, currentUser.userId, false) + val avatarUrl = ApiUtils.getUrlForAvatar(currentUser.baseUrl!!, currentUser.userId, false) val me = Person.Builder() .setName(currentUser.displayName) .setIcon(NotificationUtils.loadAvatarSync(avatarUrl, context)) diff --git a/app/src/main/java/com/nextcloud/talk/receivers/MarkAsReadReceiver.kt b/app/src/main/java/com/nextcloud/talk/receivers/MarkAsReadReceiver.kt index d0c892fe2..c245dc776 100644 --- a/app/src/main/java/com/nextcloud/talk/receivers/MarkAsReadReceiver.kt +++ b/app/src/main/java/com/nextcloud/talk/receivers/MarkAsReadReceiver.kt @@ -80,11 +80,11 @@ class MarkAsReadReceiver : BroadcastReceiver() { private fun markAsRead() { val credentials = ApiUtils.getCredentials(currentUser.username, currentUser.token) - val apiVersion = ApiUtils.getChatApiVersion(currentUser, intArrayOf(1)) + val apiVersion = ApiUtils.getChatApiVersion(currentUser.capabilities!!.spreedCapability!!, intArrayOf(1)) val url = ApiUtils.getUrlForChatReadMarker( apiVersion, - currentUser.baseUrl, - roomToken + currentUser.baseUrl!!, + roomToken!! ) ncApi.setChatReadMarker(credentials, url, messageId) diff --git a/app/src/main/java/com/nextcloud/talk/remotefilebrowser/adapters/RemoteFileBrowserItemsListViewHolder.kt b/app/src/main/java/com/nextcloud/talk/remotefilebrowser/adapters/RemoteFileBrowserItemsListViewHolder.kt index b28d94b29..c99d21d43 100644 --- a/app/src/main/java/com/nextcloud/talk/remotefilebrowser/adapters/RemoteFileBrowserItemsListViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/remotefilebrowser/adapters/RemoteFileBrowserItemsListViewHolder.kt @@ -94,7 +94,7 @@ class RemoteFileBrowserItemsListViewHolder( if (item.hasPreview) { val path = ApiUtils.getUrlForFilePreviewWithRemotePath( - currentUser.baseUrl, + currentUser.baseUrl!!, item.path, fileIcon.context.resources.getDimensionPixelSize(R.dimen.small_item_height) ) diff --git a/app/src/main/java/com/nextcloud/talk/repositories/callrecording/CallRecordingRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/repositories/callrecording/CallRecordingRepositoryImpl.kt index 71c6d64d2..369bca342 100644 --- a/app/src/main/java/com/nextcloud/talk/repositories/callrecording/CallRecordingRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/repositories/callrecording/CallRecordingRepositoryImpl.kt @@ -33,7 +33,7 @@ class CallRecordingRepositoryImpl(private val ncApi: NcApi, currentUserProvider: CallRecordingRepository { val currentUser: User = currentUserProvider.currentUser.blockingGet() - val credentials: String = ApiUtils.getCredentials(currentUser.username, currentUser.token) + val credentials: String = ApiUtils.getCredentials(currentUser.username, currentUser.token)!! var apiVersion = 1 @@ -42,7 +42,7 @@ class CallRecordingRepositoryImpl(private val ncApi: NcApi, currentUserProvider: credentials, ApiUtils.getUrlForRecording( apiVersion, - currentUser.baseUrl, + currentUser.baseUrl!!, roomToken ), 1 @@ -54,7 +54,7 @@ class CallRecordingRepositoryImpl(private val ncApi: NcApi, currentUserProvider: credentials, ApiUtils.getUrlForRecording( apiVersion, - currentUser.baseUrl, + currentUser.baseUrl!!, roomToken ) ).map { mapToStopCallRecordingModel(it.ocs?.meta!!) } diff --git a/app/src/main/java/com/nextcloud/talk/repositories/conversations/ConversationsRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/repositories/conversations/ConversationsRepositoryImpl.kt index c0706a7d4..0a969998e 100644 --- a/app/src/main/java/com/nextcloud/talk/repositories/conversations/ConversationsRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/repositories/conversations/ConversationsRepositoryImpl.kt @@ -38,12 +38,12 @@ class ConversationsRepositoryImpl(private val api: NcApi, private val userProvid get() = userProvider.currentUser.blockingGet() private val credentials: String - get() = ApiUtils.getCredentials(user.username, user.token) + get() = ApiUtils.getCredentials(user.username, user.token)!! override fun allowGuests(token: String, allow: Boolean): Observable { val url = ApiUtils.getUrlForRoomPublic( apiVersion(), - user.baseUrl, + user.baseUrl!!, token ) @@ -100,7 +100,7 @@ class ConversationsRepositoryImpl(private val api: NcApi, private val userProvid } private fun apiVersion(): Int { - return ApiUtils.getConversationApiVersion(user, intArrayOf(ApiUtils.APIv4)) + return ApiUtils.getConversationApiVersion(user, intArrayOf(ApiUtils.API_V4)) } companion object { diff --git a/app/src/main/java/com/nextcloud/talk/repositories/reactions/ReactionsRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/repositories/reactions/ReactionsRepositoryImpl.kt index 55de85b81..5a7837e7e 100644 --- a/app/src/main/java/com/nextcloud/talk/repositories/reactions/ReactionsRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/repositories/reactions/ReactionsRepositoryImpl.kt @@ -34,13 +34,13 @@ class ReactionsRepositoryImpl(private val ncApi: NcApi, currentUserProvider: Cur ReactionsRepository { val currentUser: User = currentUserProvider.currentUser.blockingGet() - val credentials: String = ApiUtils.getCredentials(currentUser.username, currentUser.token) + val credentials: String = ApiUtils.getCredentials(currentUser.username, currentUser.token)!! override fun addReaction(roomToken: String, message: ChatMessage, emoji: String): Observable { return ncApi.sendReaction( credentials, ApiUtils.getUrlForMessageReaction( - currentUser.baseUrl, + currentUser.baseUrl!!, roomToken, message.id ), @@ -56,7 +56,7 @@ class ReactionsRepositoryImpl(private val ncApi: NcApi, currentUserProvider: Cur return ncApi.deleteReaction( credentials, ApiUtils.getUrlForMessageReaction( - currentUser.baseUrl, + currentUser.baseUrl!!, roomToken, message.id ), diff --git a/app/src/main/java/com/nextcloud/talk/repositories/unifiedsearch/UnifiedSearchRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/repositories/unifiedsearch/UnifiedSearchRepositoryImpl.kt index df475b455..d636ced45 100644 --- a/app/src/main/java/com/nextcloud/talk/repositories/unifiedsearch/UnifiedSearchRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/repositories/unifiedsearch/UnifiedSearchRepositoryImpl.kt @@ -37,7 +37,7 @@ class UnifiedSearchRepositoryImpl(private val api: NcApi, private val userProvid get() = userProvider.currentUser.blockingGet() private val credentials: String - get() = ApiUtils.getCredentials(user.username, user.token) + get() = ApiUtils.getCredentials(user.username, user.token)!! override fun searchMessages( searchTerm: String, diff --git a/app/src/main/java/com/nextcloud/talk/settings/SettingsActivity.kt b/app/src/main/java/com/nextcloud/talk/settings/SettingsActivity.kt index f70b6e7d0..5b08303cf 100644 --- a/app/src/main/java/com/nextcloud/talk/settings/SettingsActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/settings/SettingsActivity.kt @@ -90,6 +90,7 @@ import com.nextcloud.talk.models.json.userprofile.UserProfileOverall import com.nextcloud.talk.profile.ProfileActivity import com.nextcloud.talk.users.UserManager import com.nextcloud.talk.utils.ApiUtils +import com.nextcloud.talk.utils.SpreedFeatures import com.nextcloud.talk.utils.ClosedInterfaceImpl import com.nextcloud.talk.utils.DisplayUtils import com.nextcloud.talk.utils.LoggingUtils.sendMailWithAttachment @@ -97,7 +98,7 @@ import com.nextcloud.talk.utils.NotificationUtils import com.nextcloud.talk.utils.NotificationUtils.getCallRingtoneUri import com.nextcloud.talk.utils.NotificationUtils.getMessageRingtoneUri import com.nextcloud.talk.utils.SecurityUtils -import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew +import com.nextcloud.talk.utils.CapabilitiesUtil import com.nextcloud.talk.utils.database.user.CurrentUserProviderNew import com.nextcloud.talk.utils.permissions.PlatformPermissionUtil import com.nextcloud.talk.utils.power.PowerManagerUtils @@ -266,7 +267,11 @@ class SettingsActivity : BaseActivity() { } private fun setupPhoneBookIntegration() { - if (CapabilitiesUtilNew.isPhoneBookIntegrationAvailable(currentUser!!)) { + if (CapabilitiesUtil.hasSpreedFeatureCapability( + currentUser?.capabilities?.spreedCapability!!, + SpreedFeatures.PHONEBOOK_SEARCH + ) + ) { binding.settingsPhoneBookIntegration.visibility = View.VISIBLE } else { binding.settingsPhoneBookIntegration.visibility = View.GONE @@ -507,7 +512,7 @@ class SettingsActivity : BaseActivity() { var port = -1 val uri: URI try { - uri = URI(currentUser!!.baseUrl) + uri = URI(currentUser!!.baseUrl!!) host = uri.host port = uri.port Log.d(TAG, "uri is $uri") @@ -823,7 +828,7 @@ class SettingsActivity : BaseActivity() { private fun setupProfileQueryDisposable() { profileQueryDisposable = ncApi.getUserProfile( credentials, - ApiUtils.getUrlForUserProfile(currentUser!!.baseUrl) + ApiUtils.getUrlForUserProfile(currentUser!!.baseUrl!!) ) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) @@ -854,7 +859,7 @@ class SettingsActivity : BaseActivity() { private fun setupServerAgeWarning() { when { - CapabilitiesUtilNew.isServerEOL(currentUser!!.capabilities) -> { + CapabilitiesUtil.isServerEOL(currentUser!!.serverVersion!!.major) -> { binding.serverAgeWarningText.setTextColor(ContextCompat.getColor((context), R.color.nc_darkRed)) binding.serverAgeWarningText.setText(R.string.nc_settings_server_eol) binding.serverAgeWarningIcon.setColorFilter( @@ -863,7 +868,7 @@ class SettingsActivity : BaseActivity() { ) } - CapabilitiesUtilNew.isServerAlmostEOL(currentUser!!) -> { + CapabilitiesUtil.isServerAlmostEOL(currentUser!!.serverVersion!!.major) -> { binding.serverAgeWarningText.setTextColor( ContextCompat.getColor((context), R.color.nc_darkYellow) ) @@ -889,8 +894,8 @@ class SettingsActivity : BaseActivity() { binding.settingsIncognitoKeyboardSwitch.visibility = View.GONE } - if (CapabilitiesUtilNew.isReadStatusAvailable(currentUser!!)) { - binding.settingsReadPrivacySwitch.isChecked = !CapabilitiesUtilNew.isReadStatusPrivate(currentUser!!) + if (CapabilitiesUtil.isReadStatusAvailable(currentUser!!.capabilities!!.spreedCapability!!)) { + binding.settingsReadPrivacySwitch.isChecked = !CapabilitiesUtil.isReadStatusPrivate(currentUser!!) } else { binding.settingsReadPrivacy.visibility = View.GONE } @@ -954,10 +959,10 @@ class SettingsActivity : BaseActivity() { private fun setupTypingStatusSetting() { if (currentUser!!.externalSignalingServer?.externalSignalingServer?.isNotEmpty() == true) { binding.settingsTypingStatusOnlyWithHpb.visibility = View.GONE - Log.i(TAG, "Typing Status Available: ${CapabilitiesUtilNew.isTypingStatusAvailable(currentUser!!)}") + Log.i(TAG, "Typing Status Available: ${CapabilitiesUtil.isTypingStatusAvailable(currentUser!!)}") - if (CapabilitiesUtilNew.isTypingStatusAvailable(currentUser!!)) { - binding.settingsTypingStatusSwitch.isChecked = !CapabilitiesUtilNew.isTypingStatusPrivate(currentUser!!) + if (CapabilitiesUtil.isTypingStatusAvailable(currentUser!!)) { + binding.settingsTypingStatusSwitch.isChecked = !CapabilitiesUtil.isTypingStatusPrivate(currentUser!!) } else { binding.settingsTypingStatus.visibility = View.GONE } @@ -1209,7 +1214,7 @@ class SettingsActivity : BaseActivity() { private fun checkForPhoneNumber() { ncApi.getUserData( ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token), - ApiUtils.getUrlForUserProfile(currentUser!!.baseUrl) + ApiUtils.getUrlForUserProfile(currentUser!!.baseUrl!!) ).subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(object : Observer { @@ -1294,7 +1299,7 @@ class SettingsActivity : BaseActivity() { val phoneNumber = textInputLayout.editText!!.text.toString() ncApi.setUserData( ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token), - ApiUtils.getUrlForUserData(currentUser!!.baseUrl, currentUser!!.userId), + ApiUtils.getUrlForUserData(currentUser!!.baseUrl!!, currentUser!!.userId!!), "phone", phoneNumber ).subscribeOn(Schedulers.io()) @@ -1349,7 +1354,7 @@ class SettingsActivity : BaseActivity() { val json = "{\"key\": \"read_status_privacy\", \"value\" : $booleanValue}" ncApi.setReadStatusPrivacy( ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token), - ApiUtils.getUrlForUserSettings(currentUser!!.baseUrl), + ApiUtils.getUrlForUserSettings(currentUser!!.baseUrl!!), json.toRequestBody("application/json".toMediaTypeOrNull()) ) .subscribeOn(Schedulers.io()) @@ -1387,7 +1392,7 @@ class SettingsActivity : BaseActivity() { val json = "{\"key\": \"typing_privacy\", \"value\" : $booleanValue}" ncApi.setTypingStatusPrivacy( ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token), - ApiUtils.getUrlForUserSettings(currentUser!!.baseUrl), + ApiUtils.getUrlForUserSettings(currentUser!!.baseUrl!!), json.toRequestBody("application/json".toMediaTypeOrNull()) ) .subscribeOn(Schedulers.io()) diff --git a/app/src/main/java/com/nextcloud/talk/shareditems/repositories/SharedItemsRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/shareditems/repositories/SharedItemsRepositoryImpl.kt index 21d7419ca..ea3d5f6df 100644 --- a/app/src/main/java/com/nextcloud/talk/shareditems/repositories/SharedItemsRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/shareditems/repositories/SharedItemsRepositoryImpl.kt @@ -105,7 +105,7 @@ class SharedItemsRepositoryImpl @Inject constructor(private val ncApi: NcApi, pr fileParameters["link"]!!, fileParameters["mimetype"]!!, previewAvailable, - previewLink(fileParameters["id"], parameters.baseUrl) + previewLink(fileParameters["id"], parameters.baseUrl!!) ) } else if (it.value.messageParameters?.containsKey("object") == true) { val objectParameters = it.value.messageParameters!!["object"]!! @@ -184,7 +184,7 @@ class SharedItemsRepositoryImpl @Inject constructor(private val ncApi: NcApi, pr return ncApi.getSharedItemsOverview( credentials, - ApiUtils.getUrlForChatSharedItemsOverview(1, parameters.baseUrl, parameters.roomToken), + ApiUtils.getUrlForChatSharedItemsOverview(1, parameters.baseUrl!!, parameters.roomToken), 1 ).map { val types = mutableSetOf() @@ -206,7 +206,7 @@ class SharedItemsRepositoryImpl @Inject constructor(private val ncApi: NcApi, pr private fun previewLink(fileId: String?, baseUrl: String): String { return ApiUtils.getUrlForFilePreviewWithFileId( baseUrl, - fileId, + fileId!!, sharedApplication!!.resources.getDimensionPixelSize(R.dimen.maximum_file_preview_size) ) } diff --git a/app/src/main/java/com/nextcloud/talk/translate/viewmodels/TranslateViewModel.kt b/app/src/main/java/com/nextcloud/talk/translate/viewmodels/TranslateViewModel.kt index 99a40e3bc..323a9aad1 100644 --- a/app/src/main/java/com/nextcloud/talk/translate/viewmodels/TranslateViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/translate/viewmodels/TranslateViewModel.kt @@ -37,8 +37,8 @@ class TranslateViewModel @Inject constructor( fun translateMessage(toLanguage: String, fromLanguage: String?, text: String) { val currentUser: User = userManager.currentUser.blockingGet() - val authorization: String = ApiUtils.getCredentials(currentUser.username, currentUser.token) - val url: String = ApiUtils.getUrlForTranslation(currentUser.baseUrl) + val authorization: String = ApiUtils.getCredentials(currentUser.username, currentUser.token)!! + val url: String = ApiUtils.getUrlForTranslation(currentUser.baseUrl!!) val calculatedFromLanguage = if (fromLanguage == null || fromLanguage == "") { null @@ -60,8 +60,8 @@ class TranslateViewModel @Inject constructor( fun getLanguages() { val currentUser: User = userManager.currentUser.blockingGet() - val authorization: String = ApiUtils.getCredentials(currentUser.username, currentUser.token) - val url: String = ApiUtils.getUrlForLanguages(currentUser.baseUrl) + val authorization: String = ApiUtils.getCredentials(currentUser.username, currentUser.token)!! + val url: String = ApiUtils.getUrlForLanguages(currentUser.baseUrl!!) Log.d(TAG, "URL is: $url") repository.getLanguages(authorization, url) .subscribeOn(Schedulers.io()) diff --git a/app/src/main/java/com/nextcloud/talk/ui/bottom/sheet/ProfileBottomSheet.kt b/app/src/main/java/com/nextcloud/talk/ui/bottom/sheet/ProfileBottomSheet.kt index 605e65b11..70e655c1f 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/bottom/sheet/ProfileBottomSheet.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/bottom/sheet/ProfileBottomSheet.kt @@ -57,7 +57,7 @@ class ProfileBottomSheet(val ncApi: NcApi, val userModel: User, val viewThemeUti fun showFor(user: String, context: Context) { ncApi.hoverCard( ApiUtils.getCredentials(userModel.username, userModel.token), - ApiUtils.getUrlForHoverCard(userModel.baseUrl, user) + ApiUtils.getUrlForHoverCard(userModel.baseUrl!!, user) ).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()) .subscribe(object : Observer { override fun onSubscribe(d: Disposable) { @@ -121,10 +121,10 @@ class ProfileBottomSheet(val ncApi: NcApi, val userModel: User, val viewThemeUti private fun talkTo(userId: String, context: Context) { val apiVersion = - ApiUtils.getConversationApiVersion(userModel, intArrayOf(ApiUtils.APIv4, 1)) + ApiUtils.getConversationApiVersion(userModel, intArrayOf(ApiUtils.API_V4, 1)) val retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom( apiVersion, - userModel.baseUrl, + userModel.baseUrl!!, "1", null, userId, diff --git a/app/src/main/java/com/nextcloud/talk/ui/dialog/AttachmentDialog.kt b/app/src/main/java/com/nextcloud/talk/ui/dialog/AttachmentDialog.kt index 0171c3f0e..0db62cacf 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/dialog/AttachmentDialog.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/dialog/AttachmentDialog.kt @@ -35,7 +35,8 @@ import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.chat.ChatActivity import com.nextcloud.talk.databinding.DialogAttachmentBinding import com.nextcloud.talk.ui.theme.ViewThemeUtils -import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew +import com.nextcloud.talk.utils.SpreedFeatures +import com.nextcloud.talk.utils.CapabilitiesUtil import javax.inject.Inject @AutoInjector(NextcloudTalkApplication::class) @@ -61,7 +62,7 @@ class AttachmentDialog(val activity: Activity, var chatActivity: ChatActivity) : } private fun initItemsStrings() { - var serverName = CapabilitiesUtilNew.getServerName(chatActivity.conversationUser) + var serverName = CapabilitiesUtil.getServerName(chatActivity.conversationUser) dialogAttachmentBinding.txtAttachFileFromCloud.text = chatActivity.resources?.let { if (serverName.isNullOrEmpty()) { serverName = it.getString(R.string.nc_server_product_name) @@ -71,15 +72,15 @@ class AttachmentDialog(val activity: Activity, var chatActivity: ChatActivity) : } private fun initItemsVisibility() { - if (!CapabilitiesUtilNew.hasSpreedFeatureCapability( - chatActivity.conversationUser, - "geo-location-sharing" + if (!CapabilitiesUtil.hasSpreedFeatureCapability( + chatActivity.spreedCapabilities, + SpreedFeatures.GEO_LOCATION_SHARING ) ) { dialogAttachmentBinding.menuShareLocation.visibility = View.GONE } - if (!CapabilitiesUtilNew.hasSpreedFeatureCapability(chatActivity.conversationUser, "talk-polls") || + if (!CapabilitiesUtil.hasSpreedFeatureCapability(chatActivity.spreedCapabilities, SpreedFeatures.TALK_POLLS) || chatActivity.isOneToOneConversation() ) { dialogAttachmentBinding.menuAttachPoll.visibility = View.GONE diff --git a/app/src/main/java/com/nextcloud/talk/ui/dialog/ChooseAccountDialogFragment.java b/app/src/main/java/com/nextcloud/talk/ui/dialog/ChooseAccountDialogFragment.java index 261df9edc..4ab8605c1 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/dialog/ChooseAccountDialogFragment.java +++ b/app/src/main/java/com/nextcloud/talk/ui/dialog/ChooseAccountDialogFragment.java @@ -54,7 +54,7 @@ import com.nextcloud.talk.ui.theme.ViewThemeUtils; import com.nextcloud.talk.users.UserManager; import com.nextcloud.talk.utils.ApiUtils; import com.nextcloud.talk.utils.DisplayUtils; -import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew; +import com.nextcloud.talk.utils.CapabilitiesUtil; import java.net.CookieManager; import java.util.ArrayList; @@ -262,7 +262,7 @@ public class ChooseAccountDialogFragment extends DialogFragment { private void loadCurrentStatus(User user) { String credentials = ApiUtils.getCredentials(user.getUsername(), user.getToken()); - if (CapabilitiesUtilNew.isUserStatusAvailable(userManager.getCurrentUser().blockingGet())) { + if (CapabilitiesUtil.isUserStatusAvailable(userManager.getCurrentUser().blockingGet())) { binding.statusView.setVisibility(View.VISIBLE); ncApi.status(credentials, ApiUtils.getUrlForStatus(user.getBaseUrl())). diff --git a/app/src/main/java/com/nextcloud/talk/ui/dialog/ChooseAccountShareToDialogFragment.kt b/app/src/main/java/com/nextcloud/talk/ui/dialog/ChooseAccountShareToDialogFragment.kt index 7e024a1a9..35d5c8d0d 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/dialog/ChooseAccountShareToDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/dialog/ChooseAccountShareToDialogFragment.kt @@ -89,7 +89,7 @@ class ChooseAccountShareToDialogFragment : DialogFragment() { if (user != null) { binding!!.currentAccount.userName.text = user.displayName binding!!.currentAccount.ticker.visibility = View.GONE - binding!!.currentAccount.account.text = Uri.parse(user.baseUrl).host + binding!!.currentAccount.account.text = Uri.parse(user.baseUrl!!).host viewThemeUtils!!.platform.colorImageView(binding!!.currentAccount.accountMenu, ColorRole.PRIMARY) if (user.baseUrl != null && (user.baseUrl!!.startsWith("http://") || user.baseUrl!!.startsWith("https://")) diff --git a/app/src/main/java/com/nextcloud/talk/ui/dialog/ConversationsListBottomDialog.kt b/app/src/main/java/com/nextcloud/talk/ui/dialog/ConversationsListBottomDialog.kt index b64e7d619..0006a9bc1 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/dialog/ConversationsListBottomDialog.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/dialog/ConversationsListBottomDialog.kt @@ -46,7 +46,7 @@ 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_ROOM_TOKEN -import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew +import com.nextcloud.talk.utils.CapabilitiesUtil import io.reactivex.Observer import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.Disposable @@ -86,7 +86,7 @@ class ConversationsListBottomDialog( initItemsVisibility() initClickListeners() - credentials = ApiUtils.getCredentials(currentUser.username, currentUser.token) + credentials = ApiUtils.getCredentials(currentUser.username, currentUser.token)!! } override fun onStart() { @@ -105,7 +105,10 @@ class ConversationsListBottomDialog( } private fun initItemsVisibility() { - val hasFavoritesCapability = CapabilitiesUtilNew.hasSpreedFeatureCapability(currentUser, "favorites") + val hasFavoritesCapability = CapabilitiesUtil.hasSpreedFeatureCapability( + currentUser.capabilities?.spreedCapability!!, + "favorites" + ) val canModerate = conversation.canModerate(currentUser) binding.conversationRemoveFromFavorites.visibility = setVisibleIf( @@ -116,11 +119,19 @@ class ConversationsListBottomDialog( ) binding.conversationMarkAsRead.visibility = setVisibleIf( - conversation.unreadMessages > 0 && CapabilitiesUtilNew.canSetChatReadMarker(currentUser) + conversation.unreadMessages > 0 && CapabilitiesUtil.hasSpreedFeatureCapability( + currentUser + .capabilities?.spreedCapability!!, + "chat-read-marker" + ) ) binding.conversationMarkAsUnread.visibility = setVisibleIf( - conversation.unreadMessages <= 0 && CapabilitiesUtilNew.canMarkRoomAsUnread(currentUser) + conversation.unreadMessages <= 0 && CapabilitiesUtil.hasSpreedFeatureCapability( + currentUser + .capabilities?.spreedCapability!!, + "chat-unread" + ) ) binding.conversationOperationRename.visibility = setVisibleIf( @@ -178,12 +189,12 @@ class ConversationsListBottomDialog( } private fun addConversationToFavorites() { - val apiVersion = ApiUtils.getConversationApiVersion(currentUser, intArrayOf(ApiUtils.APIv4, ApiUtils.APIv1)) + val apiVersion = ApiUtils.getConversationApiVersion(currentUser, intArrayOf(ApiUtils.API_V4, ApiUtils.API_V1)) ncApi.addConversationToFavorites( credentials, ApiUtils.getUrlForRoomFavorite( apiVersion, - currentUser.baseUrl, + currentUser.baseUrl!!, conversation.token ) ) @@ -218,12 +229,12 @@ class ConversationsListBottomDialog( } private fun removeConversationFromFavorites() { - val apiVersion = ApiUtils.getConversationApiVersion(currentUser, intArrayOf(ApiUtils.APIv4, ApiUtils.APIv1)) + val apiVersion = ApiUtils.getConversationApiVersion(currentUser, intArrayOf(ApiUtils.API_V4, ApiUtils.API_V1)) ncApi.removeConversationFromFavorites( credentials, ApiUtils.getUrlForRoomFavorite( apiVersion, - currentUser.baseUrl, + currentUser.baseUrl!!, conversation.token ) ) @@ -262,8 +273,8 @@ class ConversationsListBottomDialog( credentials, ApiUtils.getUrlForChatReadMarker( chatApiVersion(), - currentUser.baseUrl, - conversation.token + currentUser.baseUrl!!, + conversation.token!! ) ) .subscribeOn(Schedulers.io()) @@ -301,8 +312,8 @@ class ConversationsListBottomDialog( credentials, ApiUtils.getUrlForChatReadMarker( chatApiVersion(), - currentUser.baseUrl, - conversation.token + currentUser.baseUrl!!, + conversation.token!! ), conversation.lastMessage!!.jsonMessageId ) @@ -396,7 +407,7 @@ class ConversationsListBottomDialog( } private fun chatApiVersion(): Int { - return ApiUtils.getChatApiVersion(currentUser, intArrayOf(ApiUtils.APIv1)) + return ApiUtils.getChatApiVersion(currentUser.capabilities!!.spreedCapability!!, intArrayOf(ApiUtils.API_V1)) } companion object { diff --git a/app/src/main/java/com/nextcloud/talk/ui/dialog/DateTimePickerFragment.kt b/app/src/main/java/com/nextcloud/talk/ui/dialog/DateTimePickerFragment.kt index 93a8d42db..65afc1888 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/dialog/DateTimePickerFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/dialog/DateTimePickerFragment.kt @@ -50,7 +50,8 @@ import javax.inject.Inject class DateTimePickerFragment( token: String, id: String, - chatViewModel: ChatViewModel + chatViewModel: ChatViewModel, + private val chatApiVersion: Int ) : DialogFragment() { lateinit var binding: DialogDateTimePickerBinding private var dialogView: View? = null @@ -144,7 +145,7 @@ class DateTimePickerFragment( } private fun getReminder() { - viewModel.getReminder(userManager.currentUser.blockingGet(), roomToken, messageId) + viewModel.getReminder(userManager.currentUser.blockingGet(), roomToken, messageId, chatApiVersion) } private fun showDelete(value: Boolean) { @@ -221,12 +222,18 @@ class DateTimePickerFragment( binding.buttonClose.setOnClickListener { dismiss() } binding.buttonSet.setOnClickListener { currentTimeStamp?.let { time -> - viewModel.setReminder(userManager.currentUser.blockingGet(), roomToken, messageId, time.toInt()) + viewModel.setReminder( + userManager.currentUser.blockingGet(), + roomToken, + messageId, + time.toInt(), + chatApiVersion + ) } dismiss() } binding.buttonDelete.setOnClickListener { - viewModel.deleteReminder(userManager.currentUser.blockingGet(), roomToken, messageId) + viewModel.deleteReminder(userManager.currentUser.blockingGet(), roomToken, messageId, chatApiVersion) } } @@ -299,11 +306,12 @@ class DateTimePickerFragment( private const val HOUR_SIX_PM = 18 @JvmStatic - fun newInstance(token: String, id: String, chatViewModel: ChatViewModel) = + fun newInstance(token: String, id: String, chatViewModel: ChatViewModel, chatApiVersion: Int) = DateTimePickerFragment( token, id, - chatViewModel + chatViewModel, + chatApiVersion ) } } diff --git a/app/src/main/java/com/nextcloud/talk/ui/dialog/MessageActionsDialog.kt b/app/src/main/java/com/nextcloud/talk/ui/dialog/MessageActionsDialog.kt index ea6a9e936..b30099e81 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/dialog/MessageActionsDialog.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/dialog/MessageActionsDialog.kt @@ -46,14 +46,17 @@ import com.nextcloud.talk.models.domain.ConversationReadOnlyState import com.nextcloud.talk.models.domain.ConversationType import com.nextcloud.talk.models.domain.ReactionAddedModel import com.nextcloud.talk.models.domain.ReactionDeletedModel +import com.nextcloud.talk.models.json.capabilities.SpreedCapability import com.nextcloud.talk.models.json.chat.ChatMessage import com.nextcloud.talk.repositories.reactions.ReactionsRepository import com.nextcloud.talk.ui.theme.ViewThemeUtils import com.nextcloud.talk.utils.ApiUtils +import com.nextcloud.talk.utils.SpreedFeatures import com.nextcloud.talk.utils.ConversationUtils import com.nextcloud.talk.utils.DateConstants import com.nextcloud.talk.utils.DateUtils -import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew +import com.nextcloud.talk.utils.CapabilitiesUtil +import com.nextcloud.talk.utils.CapabilitiesUtil.hasSpreedFeatureCapability import com.vanniktech.emoji.EmojiPopup import com.vanniktech.emoji.EmojiTextView import com.vanniktech.emoji.installDisableKeyboardInput @@ -72,7 +75,8 @@ class MessageActionsDialog( private val user: User?, private val currentConversation: ConversationModel?, private val showMessageDeletionButton: Boolean, - private val hasChatPermission: Boolean + private val hasChatPermission: Boolean, + private val spreedCapabilities: SpreedCapability ) : BottomSheetDialog(chatActivity) { @Inject @@ -100,8 +104,8 @@ class MessageActionsDialog( private val isUserAllowedToEdit = chatActivity.userAllowedByPrivilages(message) - private val isMessageEditable = CapabilitiesUtilNew.hasSpreedFeatureCapability( - user, + private val isMessageEditable = CapabilitiesUtil.hasSpreedFeatureCapability( + spreedCapabilities, "edit-messages" ) && messageHasRegularText && !isOlderThanTwentyFourHours && isUserAllowedToEdit @@ -116,9 +120,9 @@ class MessageActionsDialog( viewThemeUtils.platform.themeDialog(dialogMessageActionsBinding.root) initEmojiBar(hasChatPermission) initMenuItemCopy(!message.isDeleted) - val apiVersion = ApiUtils.getConversationApiVersion(user, intArrayOf(ApiUtils.APIv4, ApiUtils.APIv3, 1)) + val apiVersion = ApiUtils.getConversationApiVersion(user!!, intArrayOf(ApiUtils.API_V4, ApiUtils.API_V3, 1)) chatActivity.chatViewModel.checkForNoteToSelf( - ApiUtils.getCredentials(user!!.username, user.token), + ApiUtils.getCredentials(user!!.username, user.token)!!, ApiUtils.getUrlForRooms( apiVersion, user.baseUrl @@ -144,7 +148,7 @@ class MessageActionsDialog( initMenuItemTranslate( !message.isDeleted && ChatMessage.MessageType.REGULAR_TEXT_MESSAGE == message.getCalculateMessageType() && - CapabilitiesUtilNew.isTranslationsSupported(user) + CapabilitiesUtil.isTranslationsSupported(spreedCapabilities) ) initMenuEditorDetails(message.lastEditTimestamp != 0L && !message.isDeleted) initMenuReplyToMessage(message.replyable && hasChatPermission) @@ -160,7 +164,10 @@ class MessageActionsDialog( ChatMessage.MessageType.REGULAR_TEXT_MESSAGE == message.getCalculateMessageType() && !(message.isDeletedCommentMessage || message.isDeleted) ) - initMenuRemindMessage(!message.isDeleted && CapabilitiesUtilNew.isRemindSupported(user)) + initMenuRemindMessage( + !message.isDeleted && CapabilitiesUtil.hasSpreedFeatureCapability + (spreedCapabilities, "remind-me-later") + ) initMenuMarkAsUnread( message.previousMessageId > NO_PREVIOUS_MESSAGE_ID && ChatMessage.MessageType.SYSTEM_MESSAGE != message.getCalculateMessageType() @@ -242,7 +249,7 @@ class MessageActionsDialog( } private fun initEmojiBar(hasChatPermission: Boolean) { - if (CapabilitiesUtilNew.hasSpreedFeatureCapability(user, "reactions") && + if (hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.REACTIONS) && isPermitted(hasChatPermission) && isReactableMessageType(message) ) { diff --git a/app/src/main/java/com/nextcloud/talk/ui/dialog/MoreCallActionsDialog.kt b/app/src/main/java/com/nextcloud/talk/ui/dialog/MoreCallActionsDialog.kt index 73f4a6cbb..68a1ff324 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/dialog/MoreCallActionsDialog.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/dialog/MoreCallActionsDialog.kt @@ -35,7 +35,7 @@ import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.databinding.DialogMoreCallActionsBinding import com.nextcloud.talk.raisehand.viewmodel.RaiseHandViewModel import com.nextcloud.talk.ui.theme.ViewThemeUtils -import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew +import com.nextcloud.talk.utils.CapabilitiesUtil import com.nextcloud.talk.viewmodels.CallRecordingViewModel import com.vanniktech.emoji.EmojiTextView import javax.inject.Inject @@ -72,7 +72,7 @@ class MoreCallActionsDialog(private val callActivity: CallActivity) : BottomShee } private fun initItemsVisibility() { - if (CapabilitiesUtilNew.isCallReactionsSupported(callActivity.conversationUser)) { + if (CapabilitiesUtil.isCallReactionsSupported(callActivity.conversationUser)) { binding.callEmojiBar.visibility = View.VISIBLE } else { binding.callEmojiBar.visibility = View.GONE @@ -102,7 +102,7 @@ class MoreCallActionsDialog(private val callActivity: CallActivity) : BottomShee } private fun initEmojiBar() { - if (CapabilitiesUtilNew.isCallReactionsSupported(callActivity.conversationUser)) { + if (CapabilitiesUtil.isCallReactionsSupported(callActivity.conversationUser)) { binding.advancedCallOptionsTitle.visibility = View.GONE val capabilities = callActivity.conversationUser?.capabilities diff --git a/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt b/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt index eab617bc5..90ec08774 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt @@ -128,8 +128,8 @@ class SetStatusDialogFragment : currentUser = currentUserProvider?.currentUser?.blockingGet() currentStatus = it.getParcelable(ARG_CURRENT_STATUS_PARAM) - credentials = ApiUtils.getCredentials(currentUser?.username, currentUser?.token) - ncApi.getPredefinedStatuses(credentials, ApiUtils.getUrlForPredefinedStatuses(currentUser?.baseUrl)) + credentials = ApiUtils.getCredentials(currentUser?.username, currentUser?.token)!! + ncApi.getPredefinedStatuses(credentials, ApiUtils.getUrlForPredefinedStatuses(currentUser?.baseUrl!!)) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(object : Observer { @@ -369,7 +369,7 @@ class SetStatusDialogFragment : private fun clearStatus() { val credentials = ApiUtils.getCredentials(currentUser?.username, currentUser?.token) - ncApi.statusDeleteMessage(credentials, ApiUtils.getUrlForStatusMessage(currentUser?.baseUrl)) + ncApi.statusDeleteMessage(credentials, ApiUtils.getUrlForStatusMessage(currentUser?.baseUrl!!)) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()).subscribe(object : Observer { override fun onSubscribe(d: Disposable) { @@ -393,7 +393,7 @@ class SetStatusDialogFragment : private fun setStatus(statusType: StatusType) { visualizeStatus(statusType) - ncApi.setStatusType(credentials, ApiUtils.getUrlForSetStatusType(currentUser?.baseUrl), statusType.string) + ncApi.setStatusType(credentials, ApiUtils.getUrlForSetStatusType(currentUser?.baseUrl!!), statusType.string) .subscribeOn( Schedulers .io() @@ -468,7 +468,7 @@ class SetStatusDialogFragment : ) { ncApi.setCustomStatusMessage( credentials, - ApiUtils.getUrlForSetCustomStatus(currentUser?.baseUrl), + ApiUtils.getUrlForSetCustomStatus(currentUser?.baseUrl!!), statusIcon, inputText, clearAt @@ -499,7 +499,7 @@ class SetStatusDialogFragment : ncApi.setPredefinedStatusMessage( credentials, - ApiUtils.getUrlForSetPredefinedStatus(currentUser?.baseUrl), + ApiUtils.getUrlForSetPredefinedStatus(currentUser?.baseUrl!!), selectedPredefinedStatus!!.id, if (clearAt == -1L) null else clearAt ) diff --git a/app/src/main/java/com/nextcloud/talk/ui/dialog/ShowReactionsDialog.kt b/app/src/main/java/com/nextcloud/talk/ui/dialog/ShowReactionsDialog.kt index 196a21b2c..72d691cda 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/dialog/ShowReactionsDialog.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/dialog/ShowReactionsDialog.kt @@ -154,7 +154,7 @@ class ShowReactionsDialog( ncApi.getReactions( credentials, ApiUtils.getUrlForMessageReaction( - user?.baseUrl, + user?.baseUrl!!, roomToken, chatMessage.id ), @@ -209,7 +209,7 @@ class ShowReactionsDialog( ncApi.deleteReaction( credentials, ApiUtils.getUrlForMessageReaction( - user?.baseUrl, + user?.baseUrl!!, roomToken, message.id ), diff --git a/app/src/main/java/com/nextcloud/talk/upload/chunked/ChunkedFileUploader.kt b/app/src/main/java/com/nextcloud/talk/upload/chunked/ChunkedFileUploader.kt index 23204c263..48f392523 100644 --- a/app/src/main/java/com/nextcloud/talk/upload/chunked/ChunkedFileUploader.kt +++ b/app/src/main/java/com/nextcloud/talk/upload/chunked/ChunkedFileUploader.kt @@ -81,7 +81,7 @@ class ChunkedFileUploader( init { initHttpClient(okHttpClient, currentUser) - remoteChunkUrl = ApiUtils.getUrlForChunkedUpload(currentUser.baseUrl, currentUser.userId) + remoteChunkUrl = ApiUtils.getUrlForChunkedUpload(currentUser.baseUrl!!, currentUser.userId!!) } @Suppress("Detekt.TooGenericExceptionCaught") @@ -295,7 +295,7 @@ class ChunkedFileUploader( ApiUtils.getCredentials( currentUser.username, currentUser.token - ), + )!!, "Authorization" ) ) @@ -304,8 +304,8 @@ class ChunkedFileUploader( private fun assembleChunks(uploadFolderUri: String, targetPath: String) { val destinationUri: String = ApiUtils.getUrlForFileUpload( - currentUser.baseUrl, - currentUser.userId, + currentUser.baseUrl!!, + currentUser.userId!!, targetPath ) val originUri = "$uploadFolderUri/.file" diff --git a/app/src/main/java/com/nextcloud/talk/upload/normal/FileUploader.kt b/app/src/main/java/com/nextcloud/talk/upload/normal/FileUploader.kt index d21f90605..066d7eb9f 100644 --- a/app/src/main/java/com/nextcloud/talk/upload/normal/FileUploader.kt +++ b/app/src/main/java/com/nextcloud/talk/upload/normal/FileUploader.kt @@ -24,7 +24,7 @@ class FileUploader( fun upload(sourceFileUri: Uri, fileName: String, remotePath: String, metaData: String?): Observable { return ncApi.uploadFile( ApiUtils.getCredentials(currentUser.username, currentUser.token), - ApiUtils.getUrlForFileUpload(currentUser.baseUrl, currentUser.userId, remotePath), + ApiUtils.getUrlForFileUpload(currentUser.baseUrl!!, currentUser.userId!!, remotePath), createRequestBody(sourceFileUri) ) .subscribeOn(Schedulers.io()) diff --git a/app/src/main/java/com/nextcloud/talk/utils/AccountUtils.kt b/app/src/main/java/com/nextcloud/talk/utils/AccountUtils.kt index 80cc199dc..5fd99b3fc 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/AccountUtils.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/AccountUtils.kt @@ -69,7 +69,7 @@ object AccountUtils { private fun matchAccounts(importAccount: ImportAccount, user: User): Boolean { var accountFound = false if (importAccount.token != null) { - if (UriUtils.hasHttpProtocolPrefixed(importAccount.baseUrl)) { + if (UriUtils.hasHttpProtocolPrefixed(importAccount.baseUrl!!)) { if ( user.username == importAccount.username && user.baseUrl == importAccount.baseUrl diff --git a/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java b/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java deleted file mode 100644 index c36d882f1..000000000 --- a/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java +++ /dev/null @@ -1,559 +0,0 @@ -/* - * Nextcloud Talk application - * - * @author Mario Danic - * @author Marcel Hibbe - * @author Tim Krüger - * Copyright (C) 2021 Tim Krüger - * Copyright (C) 2021-2022 Marcel Hibbe - * Copyright (C) 2017-2018 Mario Danic - * - * 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 . - */ -package com.nextcloud.talk.utils; - -import android.net.Uri; -import android.text.TextUtils; -import android.util.Log; - -import com.nextcloud.talk.BuildConfig; -import com.nextcloud.talk.R; -import com.nextcloud.talk.application.NextcloudTalkApplication; -import com.nextcloud.talk.data.user.model.User; -import com.nextcloud.talk.models.RetrofitBucket; -import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew; - -import java.nio.charset.StandardCharsets; -import java.util.HashMap; -import java.util.Map; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import okhttp3.Credentials; - -public class ApiUtils { - public static final int APIv1 = 1; - public static final int APIv2 = 2; - public static final int APIv3 = 3; - public static final int APIv4 = 4; - public static final int AVATAR_SIZE_BIG = 512; - public static final int AVATAR_SIZE_SMALL = 64; - private static final String TAG = "ApiUtils"; - private static final String ocsApiVersion = "/ocs/v2.php"; - private static final String spreedApiVersion = "/apps/spreed/api/v1"; - private static final String spreedApiBase = ocsApiVersion + "/apps/spreed/api/v"; - - private static final String userAgent = "Mozilla/5.0 (Android) Nextcloud-Talk v"; - - public static String getUserAgent() { - return userAgent + BuildConfig.VERSION_NAME; - } - - /** - * @deprecated This is only supported on API v1-3, in API v4+ please use - * {@link ApiUtils#getUrlForAttendees(int, String, String)} instead. - */ - @Deprecated - public static String getUrlForRemovingParticipantFromConversation(String baseUrl, String roomToken, boolean isGuest) { - String url = getUrlForParticipants(APIv1, baseUrl, roomToken); - - if (isGuest) { - url += "/guests"; - } - - return url; - } - - public static RetrofitBucket getRetrofitBucketForContactsSearch(String baseUrl, @Nullable String searchQuery) { - RetrofitBucket retrofitBucket = new RetrofitBucket(); - retrofitBucket.setUrl(baseUrl + ocsApiVersion + "/apps/files_sharing/api/v1/sharees"); - - Map queryMap = new HashMap<>(); - - if (searchQuery == null) { - searchQuery = ""; - } - queryMap.put("format", "json"); - queryMap.put("search", searchQuery); - queryMap.put("itemType", "call"); - - retrofitBucket.setQueryMap(queryMap); - - return retrofitBucket; - } - - public static String getUrlForFilePreviewWithRemotePath(String baseUrl, String remotePath, int px) { - return baseUrl + "/index.php/core/preview.png?file=" - + Uri.encode(remotePath, "UTF-8") - + "&x=" + px + "&y=" + px + "&a=1&mode=cover&forceIcon=1"; - } - - public static String getUrlForFilePreviewWithFileId(String baseUrl, String fileId, int px) { - return baseUrl + "/index.php/core/preview?fileId=" - + fileId + "&x=" + px + "&y=" + px + "&a=1&mode=cover&forceIcon=1"; - } - - public static String getSharingUrl(String baseUrl) { - return baseUrl + ocsApiVersion + "/apps/files_sharing/api/v1/shares"; - } - - public static RetrofitBucket getRetrofitBucketForContactsSearchFor14(String baseUrl, @Nullable String searchQuery) { - RetrofitBucket retrofitBucket = getRetrofitBucketForContactsSearch(baseUrl, searchQuery); - retrofitBucket.setUrl(baseUrl + ocsApiVersion + "/core/autocomplete/get"); - - retrofitBucket.getQueryMap().put("itemId", "new"); - - return retrofitBucket; - } - - public static String getUrlForCapabilities(String baseUrl) { - return baseUrl + ocsApiVersion + "/cloud/capabilities"; - } - - public static int getCallApiVersion(User capabilities, int[] versions) throws NoSupportedApiException { - return getConversationApiVersion(capabilities, versions); - } - - public static int getConversationApiVersion(User user, int[] versions) throws NoSupportedApiException { - boolean hasApiV4 = false; - for (int version : versions) { - hasApiV4 |= version == APIv4; - } - - if (!hasApiV4) { - Exception e = new Exception("Api call did not try conversation-v4 api"); - Log.d(TAG, e.getMessage(), e); - } - - for (int version : versions) { - if (user.hasSpreedFeatureCapability("conversation-v" + version)) { - return version; - } - - // Fallback for old API versions - if ((version == APIv1 || version == APIv2)) { - if (user.hasSpreedFeatureCapability("conversation-v2")) { - return version; - } - if (version == APIv1 && - user.hasSpreedFeatureCapability("mention-flag") && - !user.hasSpreedFeatureCapability("conversation-v4")) { - return version; - } - } - } - throw new NoSupportedApiException(); - } - - public static int getSignalingApiVersion(User user, int[] versions) throws NoSupportedApiException { - for (int version : versions) { - if (CapabilitiesUtilNew.hasSpreedFeatureCapability(user, "signaling-v" + version)) { - return version; - } - - if (version == APIv2 && - CapabilitiesUtilNew.hasSpreedFeatureCapability(user, "sip-support") && - !CapabilitiesUtilNew.hasSpreedFeatureCapability(user, "signaling-v3")) { - return version; - } - - if (version == APIv1 && - !CapabilitiesUtilNew.hasSpreedFeatureCapability(user, "signaling-v3")) { - // Has no capability, we just assume it is always there when there is no v3 or later - return version; - } - } - throw new NoSupportedApiException(); - } - - public static int getChatApiVersion(User user, int[] versions) throws NoSupportedApiException { - for (int version : versions) { - if (version == APIv1 && CapabilitiesUtilNew.hasSpreedFeatureCapability(user, "chat-v2")) { - // Do not question that chat-v2 capability shows the availability of api/v1/ endpoint *see no evil* - return version; - } - } - throw new NoSupportedApiException(); - } - - protected static String getUrlForApi(int version, String baseUrl) { - return baseUrl + spreedApiBase + version; - } - - public static String getUrlForRooms(int version, String baseUrl) { - return getUrlForApi(version, baseUrl) + "/room"; - } - - public static String getUrlForRoom(int version, String baseUrl, String token) { - return getUrlForRooms(version, baseUrl) + "/" + token; - } - - public static String getUrlForAttendees(int version, String baseUrl, String token) { - return getUrlForRoom(version, baseUrl, token) + "/attendees"; - } - - public static String getUrlForParticipants(int version, String baseUrl, String token) { - if (token == null || token.isEmpty()) { - Log.e(TAG, "token was null or empty"); - } - return getUrlForRoom(version, baseUrl, token) + "/participants"; - } - - public static String getUrlForParticipantsActive(int version, String baseUrl, String token) { - return getUrlForParticipants(version, baseUrl, token) + "/active"; - } - - public static String getUrlForParticipantsSelf(int version, String baseUrl, String token) { - return getUrlForParticipants(version, baseUrl, token) + "/self"; - } - - public static String getUrlForParticipantsResendInvitations(int version, String baseUrl, String token) { - return getUrlForParticipants(version, baseUrl, token) + "/resend-invitations"; - } - - public static String getUrlForRoomFavorite(int version, String baseUrl, String token) { - return getUrlForRoom(version, baseUrl, token) + "/favorite"; - } - - public static String getUrlForRoomModerators(int version, String baseUrl, String token) { - return getUrlForRoom(version, baseUrl, token) + "/moderators"; - } - - public static String getUrlForRoomNotificationLevel(int version, String baseUrl, String token) { - return getUrlForRoom(version, baseUrl, token) + "/notify"; - } - - public static String getUrlForRoomPublic(int version, String baseUrl, String token) { - return getUrlForRoom(version, baseUrl, token) + "/public"; - } - - public static String getUrlForRoomPassword(int version, String baseUrl, String token) { - return getUrlForRoom(version, baseUrl, token) + "/password"; - } - - public static String getUrlForRoomReadOnlyState(int version, String baseUrl, String token) { - return getUrlForRoom(version, baseUrl, token) + "/read-only"; - } - - public static String getUrlForRoomWebinaryLobby(int version, String baseUrl, String token) { - return getUrlForRoom(version, baseUrl, token) + "/webinar/lobby"; - } - - public static String getUrlForRoomNotificationCalls(int version, String baseUrl, String token) { - return getUrlForRoom(version, baseUrl, token) + "/notify-calls"; - } - - public static String getUrlForCall(int version, String baseUrl, String token) { - return getUrlForApi(version, baseUrl) + "/call/" + token; - } - - public static String getUrlForChat(int version, String baseUrl, String token) { - return getUrlForApi(version, baseUrl) + "/chat/" + token; - } - - public static String getUrlForMentionSuggestions(int version, String baseUrl, String token) { - return getUrlForChat(version, baseUrl, token) + "/mentions"; - } - - public static String getUrlForChatMessage(int version, String baseUrl, String token, String messageId) { - return getUrlForChat(version, baseUrl, token) + "/" + messageId; - } - - public static String getUrlForChatSharedItems(int version, String baseUrl, String token) { - return getUrlForChat(version, baseUrl, token) + "/share"; - } - - public static String getUrlForChatSharedItemsOverview(int version, String baseUrl, String token) { - return getUrlForChatSharedItems(version, baseUrl, token) + "/overview"; - } - - public static String getUrlForSignaling(int version, String baseUrl) { - return getUrlForApi(version, baseUrl) + "/signaling"; - } - - public static String getUrlForSignalingBackend(int version, String baseUrl) { - return getUrlForSignaling(version, baseUrl) + "/backend"; - } - - public static String getUrlForSignalingSettings(int version, String baseUrl) { - return getUrlForSignaling(version, baseUrl) + "/settings"; - } - - public static String getUrlForSignaling(int version, String baseUrl, String token) { - return getUrlForSignaling(version, baseUrl) + "/" + token; - } - - public static String getUrlForOpenConversations(int version, String baseUrl) { - return getUrlForApi(version, baseUrl) + "/listed-room"; - } - - public static RetrofitBucket getRetrofitBucketForCreateRoom(int version, String baseUrl, String roomType, - @Nullable String source, - @Nullable String invite, - @Nullable String conversationName) { - RetrofitBucket retrofitBucket = new RetrofitBucket(); - retrofitBucket.setUrl(getUrlForRooms(version, baseUrl)); - Map queryMap = new HashMap<>(); - - queryMap.put("roomType", roomType); - if (invite != null) { - queryMap.put("invite", invite); - } - if (source != null) { - queryMap.put("source", source); - } - - if (conversationName != null) { - queryMap.put("roomName", conversationName); - } - - retrofitBucket.setQueryMap(queryMap); - - return retrofitBucket; - } - - public static RetrofitBucket getRetrofitBucketForAddParticipant(int version, String baseUrl, String token, String user) { - RetrofitBucket retrofitBucket = new RetrofitBucket(); - retrofitBucket.setUrl(getUrlForParticipants(version, baseUrl, token)); - - Map queryMap = new HashMap<>(); - - queryMap.put("newParticipant", user); - - retrofitBucket.setQueryMap(queryMap); - - return retrofitBucket; - - } - - public static RetrofitBucket getRetrofitBucketForAddParticipantWithSource( - int version, - String baseUrl, - String token, - String source, - String id - ) { - RetrofitBucket retrofitBucket = getRetrofitBucketForAddParticipant(version, baseUrl, token, id); - retrofitBucket.getQueryMap().put("source", source); - return retrofitBucket; - } - - public static String getUrlForUserProfile(String baseUrl) { - return baseUrl + ocsApiVersion + "/cloud/user"; - } - - public static String getUrlForUserData(String baseUrl, String userId) { - return baseUrl + ocsApiVersion + "/cloud/users/" + userId; - } - - public static String getUrlForUserSettings(String baseUrl) { - // FIXME Introduce API version - return baseUrl + ocsApiVersion + spreedApiVersion + "/settings/user"; - } - - public static String getUrlPostfixForStatus() { - return "/status.php"; - } - - public static String getUrlForAvatar(String baseUrl, String name, boolean requestBigSize) { - int avatarSize = requestBigSize ? AVATAR_SIZE_BIG : AVATAR_SIZE_SMALL; - return baseUrl + "/index.php/avatar/" + Uri.encode(name) + "/" + avatarSize; - } - - public static String getUrlForGuestAvatar(String baseUrl, String name, boolean requestBigSize) { - int avatarSize = requestBigSize ? AVATAR_SIZE_BIG : AVATAR_SIZE_SMALL; - return baseUrl + "/index.php/avatar/guest/" + Uri.encode(name) + "/" + avatarSize; - } - - public static String getUrlForConversationAvatar(int version, String baseUrl, String token) { - return getUrlForRoom(version, baseUrl, token) + "/avatar"; - } - - public static String getUrlForConversationAvatarWithVersion(int version, String baseUrl, String token, - boolean isDark, - String avatarVersion) { - String isDarkString = ""; - if (isDark) { - isDarkString = "/dark"; - } - - String avatarVersionString = ""; - if (avatarVersion != null) { - avatarVersionString = "?avatarVersion=" + avatarVersion; - } - - return getUrlForRoom(version, baseUrl, token) + "/avatar" + isDarkString + avatarVersionString; - } - - public static String getCredentials(String username, String token) { - if (TextUtils.isEmpty(username) && TextUtils.isEmpty(token)) { - return null; - } - return Credentials.basic(username, token, StandardCharsets.UTF_8); - } - - public static String getUrlNextcloudPush(String baseUrl) { - return baseUrl + ocsApiVersion + "/apps/notifications/api/v2/push"; - } - - public static String getUrlPushProxy() { - return NextcloudTalkApplication.Companion.getSharedApplication(). - getApplicationContext().getResources().getString(R.string.nc_push_server_url) + "/devices"; - } - - // see https://github.com/nextcloud/notifications/blob/master/docs/ocs-endpoint-v2.md - public static String getUrlForNcNotificationWithId(String baseUrl, String notificationId) { - return baseUrl + ocsApiVersion + "/apps/notifications/api/v2/notifications/" + notificationId; - } - - public static String getUrlForSearchByNumber(String baseUrl) { - return baseUrl + ocsApiVersion + "/cloud/users/search/by-phone"; - } - - public static String getUrlForFileUpload(String baseUrl, String user, String remotePath) { - return baseUrl + "/remote.php/dav/files/" + user + remotePath; - } - - public static String getUrlForChunkedUpload(String baseUrl, String user) { - return baseUrl + "/remote.php/dav/uploads/" + user; - } - - public static String getUrlForFileDownload(String baseUrl, String user, String remotePath) { - return baseUrl + "/remote.php/dav/files/" + user + "/" + remotePath; - } - - public static String getUrlForTempAvatar(String baseUrl) { - return baseUrl + ocsApiVersion + "/apps/spreed/temp-user-avatar"; - } - - public static String getUrlForUserFields(String baseUrl) { - return baseUrl + ocsApiVersion + "/cloud/user/fields"; - } - - public static String getUrlToSendLocation(int version, String baseUrl, String roomToken) { - return getUrlForChat(version, baseUrl, roomToken) + "/share"; - } - - public static String getUrlForHoverCard(String baseUrl, String userId) { - return baseUrl + ocsApiVersion + - "/hovercard/v1/" + userId; - } - - public static String getUrlForChatReadMarker(int version, String baseUrl, String roomToken) { - return getUrlForChat(version, baseUrl, roomToken) + "/read"; - } - - /* - * OCS Status API - */ - - public static String getUrlForStatus(String baseUrl) { - return baseUrl + ocsApiVersion + "/apps/user_status/api/v1/user_status"; - } - - public static String getUrlForSetStatusType(String baseUrl) { - return getUrlForStatus(baseUrl) + "/status"; - } - - public static String getUrlForPredefinedStatuses(String baseUrl) { - return baseUrl + ocsApiVersion + "/apps/user_status/api/v1/predefined_statuses"; - } - - public static String getUrlForStatusMessage(String baseUrl) { - return getUrlForStatus(baseUrl) + "/message"; - } - - public static String getUrlForSetCustomStatus(String baseUrl) { - return baseUrl + ocsApiVersion + "/apps/user_status/api/v1/user_status/message/custom"; - } - - public static String getUrlForSetPredefinedStatus(String baseUrl) { - return baseUrl + ocsApiVersion + "/apps/user_status/api/v1/user_status/message/predefined"; - } - - public static String getUrlForUserStatuses(String baseUrl) { - return baseUrl + ocsApiVersion + "/apps/user_status/api/v1/statuses"; - } - - public static String getUrlForMessageReaction(String baseUrl, - String roomToken, - String messageId) { - return baseUrl + ocsApiVersion + spreedApiVersion + "/reaction/" + roomToken + "/" + messageId; - } - - @NonNull - public static String getUrlForUnifiedSearch(@NonNull String baseUrl, @NonNull String providerId) { - return baseUrl + ocsApiVersion + "/search/providers/" + providerId + "/search"; - } - - public static String getUrlForPoll(String baseUrl, - String roomToken, - String pollId) { - return getUrlForPoll(baseUrl, roomToken) + "/" + pollId; - } - - public static String getUrlForPoll(String baseUrl, - String roomToken) { - return baseUrl + ocsApiVersion + spreedApiVersion + "/poll/" + roomToken; - } - - public static String getUrlForMessageExpiration(int version, String baseUrl, String token) { - return getUrlForRoom(version, baseUrl, token) + "/message-expiration"; - } - - public static String getUrlForOpenGraph(String baseUrl) { - return baseUrl + ocsApiVersion + "/references/resolve"; - } - - public static String getUrlForRecording(int version, String baseUrl, String token) { - return getUrlForApi(version, baseUrl) + "/recording/" + token; - } - - public static String getUrlForRequestAssistance(int version, String baseUrl, String token) { - return getUrlForApi(version, baseUrl) + "/breakout-rooms/" + token + "/request-assistance"; - } - - public static String getUrlForConversationDescription(int version, String baseUrl, String token) { - return getUrlForRoom(version, baseUrl, token) + "/description"; - } - - public static String getUrlForTranslation(String baseUrl) { - return baseUrl + ocsApiVersion + "/translation/translate"; - } - - public static String getUrlForLanguages(String baseUrl) { - return baseUrl + ocsApiVersion + "/translation/languages"; - } - - public static String getUrlForReminder(User user, String roomToken, String messageId, int version) { - String url = ApiUtils.getUrlForChatMessage(version, user.getBaseUrl(), roomToken, messageId); - return url + "/reminder"; - } - - public static String getUrlForRecordingConsent(int version, String baseUrl, String token) { - return getUrlForRoom(version, baseUrl, token) + "/recording-consent"; - } - - public static String getUrlForInvitation(String baseUrl) { - return baseUrl + ocsApiVersion + spreedApiVersion + "/federation/invitation"; - } - - public static String getUrlForInvitationAccept(String baseUrl, int id) { - return getUrlForInvitation(baseUrl) + "/" + id; - } - - public static String getUrlForInvitationReject(String baseUrl, int id) { - return getUrlForInvitation(baseUrl) + "/" + id; - } -} diff --git a/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.kt b/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.kt new file mode 100644 index 000000000..dc496b21d --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.kt @@ -0,0 +1,577 @@ +/* + * Nextcloud Talk application + * + * @author Mario Danic + * @author Marcel Hibbe + * @author Tim Krüger + * Copyright (C) 2021 Tim Krüger + * Copyright (C) 2021-2022 Marcel Hibbe + * Copyright (C) 2017-2018 Mario Danic + * + * 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 . + */ +package com.nextcloud.talk.utils + +import android.net.Uri +import android.text.TextUtils +import android.util.Log +import com.nextcloud.talk.BuildConfig +import com.nextcloud.talk.R +import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication +import com.nextcloud.talk.data.user.model.User +import com.nextcloud.talk.models.RetrofitBucket +import com.nextcloud.talk.models.json.capabilities.SpreedCapability +import com.nextcloud.talk.utils.CapabilitiesUtil.hasSpreedFeatureCapability +import okhttp3.Credentials.basic +import java.nio.charset.StandardCharsets + +@Suppress("TooManyFunctions") +object ApiUtils { + private val TAG = ApiUtils::class.java.simpleName + const val API_V1 = 1 + private const val API_V2 = 2 + const val API_V3 = 3 + const val API_V4 = 4 + private const val AVATAR_SIZE_BIG = 512 + private const val AVATAR_SIZE_SMALL = 64 + private const val OCS_API_VERSION = "/ocs/v2.php" + private const val SPREED_API_VERSION = "/apps/spreed/api/v1" + private const val SPREED_API_BASE = "$OCS_API_VERSION/apps/spreed/api/v" + + @JvmStatic + val userAgent = "Mozilla/5.0 (Android) Nextcloud-Talk v" + get() = field + BuildConfig.VERSION_NAME + + @Deprecated( + "This is only supported on API v1-3, in API v4+ please use " + + "{@link ApiUtils#getUrlForAttendees(int, String, String)} instead." + ) + fun getUrlForRemovingParticipantFromConversation(baseUrl: String?, roomToken: String?, isGuest: Boolean): String { + var url = getUrlForParticipants(API_V1, baseUrl, roomToken) + if (isGuest) { + url += "/guests" + } + return url + } + + private fun getRetrofitBucketForContactsSearch(baseUrl: String, searchQuery: String?): RetrofitBucket { + var query = searchQuery + val retrofitBucket = RetrofitBucket() + retrofitBucket.url = "$baseUrl$OCS_API_VERSION/apps/files_sharing/api/v1/sharees" + val queryMap: MutableMap = HashMap() + if (query == null) { + query = "" + } + queryMap["format"] = "json" + queryMap["search"] = query + queryMap["itemType"] = "call" + retrofitBucket.queryMap = queryMap + return retrofitBucket + } + + fun getUrlForFilePreviewWithRemotePath(baseUrl: String, remotePath: String?, px: Int): String { + return ( + baseUrl + "/index.php/core/preview.png?file=" + + Uri.encode(remotePath, "UTF-8") + + "&x=" + px + "&y=" + px + "&a=1&mode=cover&forceIcon=1" + ) + } + + fun getUrlForFilePreviewWithFileId(baseUrl: String, fileId: String, px: Int): String { + return ( + baseUrl + "/index.php/core/preview?fileId=" + + fileId + "&x=" + px + "&y=" + px + "&a=1&mode=cover&forceIcon=1" + ) + } + + fun getSharingUrl(baseUrl: String): String { + return "$baseUrl$OCS_API_VERSION/apps/files_sharing/api/v1/shares" + } + + fun getRetrofitBucketForContactsSearchFor14(baseUrl: String, searchQuery: String?): RetrofitBucket { + val retrofitBucket = getRetrofitBucketForContactsSearch(baseUrl, searchQuery) + retrofitBucket.url = "$baseUrl$OCS_API_VERSION/core/autocomplete/get" + retrofitBucket.queryMap?.put("itemId", "new") + return retrofitBucket + } + + @JvmStatic + fun getUrlForCapabilities(baseUrl: String): String { + return "$baseUrl$OCS_API_VERSION/cloud/capabilities" + } + + @Throws(NoSupportedApiException::class) + fun getCallApiVersion(capabilities: User, versions: IntArray): Int { + return getConversationApiVersion(capabilities, versions) + } + + @JvmStatic + @Throws(NoSupportedApiException::class) + @Suppress("ReturnCount") + fun getConversationApiVersion(user: User, versions: IntArray): Int { + var hasApiV4 = false + for (version in versions) { + hasApiV4 = hasApiV4 or (version == API_V4) + } + if (!hasApiV4) { + val e = Exception("Api call did not try conversation-v4 api") + Log.d(TAG, e.message, e) + } + for (version in versions) { + if (user.hasSpreedFeatureCapability("conversation-v$version")) { + return version + } + + // Fallback for old API versions + if (version == API_V1 || version == API_V2) { + if (user.hasSpreedFeatureCapability("conversation-v2")) { + return version + } + if (version == API_V1 && + user.hasSpreedFeatureCapability("mention-flag") && + !user.hasSpreedFeatureCapability("conversation-v4") + ) { + return version + } + } + } + throw NoSupportedApiException() + } + + @JvmStatic + @Throws(NoSupportedApiException::class) + @Suppress("ReturnCount") + fun getSignalingApiVersion(user: User, versions: IntArray): Int { + val spreedCapabilities = user.capabilities!!.spreedCapability + for (version in versions) { + if (spreedCapabilities != null) { + if (hasSpreedFeatureCapability(spreedCapabilities, "signaling-v$version")) { + return version + } + if (version == API_V2 && + hasSpreedFeatureCapability(spreedCapabilities, "sip-support") && + !hasSpreedFeatureCapability(spreedCapabilities, "signaling-v3") + ) { + return version + } + if (version == API_V1 && + !hasSpreedFeatureCapability(spreedCapabilities, "signaling-v3") + ) { + // Has no capability, we just assume it is always there when there is no v3 or later + return version + } + } + } + throw NoSupportedApiException() + } + + @JvmStatic + @Throws(NoSupportedApiException::class) + fun getChatApiVersion(spreedCapabilities: SpreedCapability, versions: IntArray): Int { + for (version in versions) { + if (version == API_V1 && hasSpreedFeatureCapability(spreedCapabilities, "chat-v2")) { + // Do not question that chat-v2 capability shows the availability of api/v1/ endpoint *see no evil* + return version + } + } + throw NoSupportedApiException() + } + + private fun getUrlForApi(version: Int, baseUrl: String?): String { + return baseUrl + SPREED_API_BASE + version + } + + fun getUrlForRooms(version: Int, baseUrl: String?): String { + return getUrlForApi(version, baseUrl) + "/room" + } + + @JvmStatic + fun getUrlForRoom(version: Int, baseUrl: String?, token: String?): String { + return getUrlForRooms(version, baseUrl) + "/" + token + } + + fun getUrlForAttendees(version: Int, baseUrl: String?, token: String?): String { + return getUrlForRoom(version, baseUrl, token) + "/attendees" + } + + fun getUrlForParticipants(version: Int, baseUrl: String?, token: String?): String { + if (token.isNullOrEmpty()) { + Log.e(TAG, "token was null or empty") + } + return getUrlForRoom(version, baseUrl, token) + "/participants" + } + + fun getUrlForParticipantsActive(version: Int, baseUrl: String?, token: String?): String { + return getUrlForParticipants(version, baseUrl, token) + "/active" + } + + @JvmStatic + fun getUrlForParticipantsSelf(version: Int, baseUrl: String?, token: String?): String { + return getUrlForParticipants(version, baseUrl, token) + "/self" + } + + fun getUrlForParticipantsResendInvitations(version: Int, baseUrl: String?, token: String?): String { + return getUrlForParticipants(version, baseUrl, token) + "/resend-invitations" + } + + fun getUrlForRoomFavorite(version: Int, baseUrl: String?, token: String?): String { + return getUrlForRoom(version, baseUrl, token) + "/favorite" + } + + fun getUrlForRoomModerators(version: Int, baseUrl: String?, token: String?): String { + return getUrlForRoom(version, baseUrl, token) + "/moderators" + } + + @JvmStatic + fun getUrlForRoomNotificationLevel(version: Int, baseUrl: String?, token: String?): String { + return getUrlForRoom(version, baseUrl, token) + "/notify" + } + + fun getUrlForRoomPublic(version: Int, baseUrl: String?, token: String?): String { + return getUrlForRoom(version, baseUrl, token) + "/public" + } + + fun getUrlForRoomPassword(version: Int, baseUrl: String?, token: String?): String { + return getUrlForRoom(version, baseUrl, token) + "/password" + } + + fun getUrlForRoomReadOnlyState(version: Int, baseUrl: String?, token: String?): String { + return getUrlForRoom(version, baseUrl, token) + "/read-only" + } + + fun getUrlForRoomWebinaryLobby(version: Int, baseUrl: String?, token: String?): String { + return getUrlForRoom(version, baseUrl, token) + "/webinar/lobby" + } + + @JvmStatic + fun getUrlForRoomNotificationCalls(version: Int, baseUrl: String?, token: String?): String { + return getUrlForRoom(version, baseUrl, token) + "/notify-calls" + } + + fun getUrlForCall(version: Int, baseUrl: String?, token: String): String { + return getUrlForApi(version, baseUrl) + "/call/" + token + } + + fun getUrlForChat(version: Int, baseUrl: String?, token: String): String { + return getUrlForApi(version, baseUrl) + "/chat/" + token + } + + @JvmStatic + fun getUrlForMentionSuggestions(version: Int, baseUrl: String?, token: String): String { + return getUrlForChat(version, baseUrl, token) + "/mentions" + } + + fun getUrlForChatMessage(version: Int, baseUrl: String?, token: String, messageId: String): String { + return getUrlForChat(version, baseUrl, token) + "/" + messageId + } + + fun getUrlForChatSharedItems(version: Int, baseUrl: String?, token: String): String { + return getUrlForChat(version, baseUrl, token) + "/share" + } + + fun getUrlForChatSharedItemsOverview(version: Int, baseUrl: String?, token: String): String { + return getUrlForChatSharedItems(version, baseUrl, token) + "/overview" + } + + fun getUrlForSignaling(version: Int, baseUrl: String?): String { + return getUrlForApi(version, baseUrl) + "/signaling" + } + + @JvmStatic + fun getUrlForSignalingBackend(version: Int, baseUrl: String?): String { + return getUrlForSignaling(version, baseUrl) + "/backend" + } + + @JvmStatic + fun getUrlForSignalingSettings(version: Int, baseUrl: String?): String { + return getUrlForSignaling(version, baseUrl) + "/settings" + } + + fun getUrlForSignaling(version: Int, baseUrl: String?, token: String): String { + return getUrlForSignaling(version, baseUrl) + "/" + token + } + + fun getUrlForOpenConversations(version: Int, baseUrl: String?): String { + return getUrlForApi(version, baseUrl) + "/listed-room" + } + + @Suppress("LongParameterList") + fun getRetrofitBucketForCreateRoom( + version: Int, + baseUrl: String?, + roomType: String, + source: String?, + invite: String?, + conversationName: String? + ): RetrofitBucket { + val retrofitBucket = RetrofitBucket() + retrofitBucket.url = getUrlForRooms(version, baseUrl) + val queryMap: MutableMap = HashMap() + queryMap["roomType"] = roomType + if (invite != null) { + queryMap["invite"] = invite + } + if (source != null) { + queryMap["source"] = source + } + if (conversationName != null) { + queryMap["roomName"] = conversationName + } + retrofitBucket.queryMap = queryMap + return retrofitBucket + } + + @JvmStatic + fun getRetrofitBucketForAddParticipant( + version: Int, + baseUrl: String?, + token: String?, + user: String + ): RetrofitBucket { + val retrofitBucket = RetrofitBucket() + retrofitBucket.url = getUrlForParticipants(version, baseUrl, token) + val queryMap: MutableMap = HashMap() + queryMap["newParticipant"] = user + retrofitBucket.queryMap = queryMap + return retrofitBucket + } + + @JvmStatic + fun getRetrofitBucketForAddParticipantWithSource( + version: Int, + baseUrl: String?, + token: String?, + source: String, + id: String + ): RetrofitBucket { + val retrofitBucket = getRetrofitBucketForAddParticipant(version, baseUrl, token, id) + retrofitBucket.queryMap?.put("source", source) + return retrofitBucket + } + + fun getUrlForUserProfile(baseUrl: String): String { + return "$baseUrl$OCS_API_VERSION/cloud/user" + } + + fun getUrlForUserData(baseUrl: String, userId: String): String { + return "$baseUrl$OCS_API_VERSION/cloud/users/$userId" + } + + fun getUrlForUserSettings(baseUrl: String): String { + // FIXME Introduce API version + return "$baseUrl$OCS_API_VERSION$SPREED_API_VERSION/settings/user" + } + + fun getUrlPostfixForStatus(): String { + return "/status.php" + } + + @JvmStatic + fun getUrlForAvatar(baseUrl: String?, name: String?, requestBigSize: Boolean): String { + val avatarSize = if (requestBigSize) AVATAR_SIZE_BIG else AVATAR_SIZE_SMALL + return baseUrl + "/index.php/avatar/" + Uri.encode(name) + "/" + avatarSize + } + + @JvmStatic + fun getUrlForGuestAvatar(baseUrl: String?, name: String?, requestBigSize: Boolean): String { + val avatarSize = if (requestBigSize) AVATAR_SIZE_BIG else AVATAR_SIZE_SMALL + return baseUrl + "/index.php/avatar/guest/" + Uri.encode(name) + "/" + avatarSize + } + + fun getUrlForConversationAvatar(version: Int, baseUrl: String?, token: String?): String { + return getUrlForRoom(version, baseUrl, token) + "/avatar" + } + + fun getUrlForConversationAvatarWithVersion( + version: Int, + baseUrl: String?, + token: String?, + isDark: Boolean, + avatarVersion: String? + ): String { + var isDarkString = "" + if (isDark) { + isDarkString = "/dark" + } + var avatarVersionString = "" + if (avatarVersion != null) { + avatarVersionString = "?avatarVersion=$avatarVersion" + } + return getUrlForRoom(version, baseUrl, token) + "/avatar" + isDarkString + avatarVersionString + } + + @JvmStatic + fun getCredentials(username: String?, token: String?): String? { + return if (TextUtils.isEmpty(username) && TextUtils.isEmpty(token)) { + null + } else { + basic(username!!, token!!, StandardCharsets.UTF_8) + } + } + + @JvmStatic + fun getUrlNextcloudPush(baseUrl: String): String { + return "$baseUrl$OCS_API_VERSION/apps/notifications/api/v2/push" + } + + @JvmStatic + fun getUrlPushProxy(): String { + return sharedApplication!!.applicationContext.resources.getString(R.string.nc_push_server_url) + "/devices" + } + + // see https://github.com/nextcloud/notifications/blob/master/docs/ocs-endpoint-v2.md + fun getUrlForNcNotificationWithId(baseUrl: String, notificationId: String): String { + return "$baseUrl$OCS_API_VERSION/apps/notifications/api/v2/notifications/$notificationId" + } + + fun getUrlForSearchByNumber(baseUrl: String): String { + return "$baseUrl$OCS_API_VERSION/cloud/users/search/by-phone" + } + + fun getUrlForFileUpload(baseUrl: String, user: String, remotePath: String): String { + return "$baseUrl/remote.php/dav/files/$user$remotePath" + } + + fun getUrlForChunkedUpload(baseUrl: String, user: String): String { + return "$baseUrl/remote.php/dav/uploads/$user" + } + + fun getUrlForFileDownload(baseUrl: String, user: String, remotePath: String): String { + return "$baseUrl/remote.php/dav/files/$user/$remotePath" + } + + fun getUrlForTempAvatar(baseUrl: String): String { + return "$baseUrl$OCS_API_VERSION/apps/spreed/temp-user-avatar" + } + + fun getUrlForUserFields(baseUrl: String): String { + return "$baseUrl$OCS_API_VERSION/cloud/user/fields" + } + + fun getUrlToSendLocation(version: Int, baseUrl: String?, roomToken: String): String { + return getUrlForChat(version, baseUrl, roomToken) + "/share" + } + + fun getUrlForHoverCard(baseUrl: String, userId: String): String { + return baseUrl + OCS_API_VERSION + + "/hovercard/v1/" + userId + } + + fun getUrlForChatReadMarker(version: Int, baseUrl: String?, roomToken: String): String { + return getUrlForChat(version, baseUrl, roomToken) + "/read" + } + + /* + * OCS Status API + */ + @JvmStatic + fun getUrlForStatus(baseUrl: String): String { + return "$baseUrl$OCS_API_VERSION/apps/user_status/api/v1/user_status" + } + + fun getUrlForSetStatusType(baseUrl: String): String { + return getUrlForStatus(baseUrl) + "/status" + } + + fun getUrlForPredefinedStatuses(baseUrl: String): String { + return "$baseUrl$OCS_API_VERSION/apps/user_status/api/v1/predefined_statuses" + } + + fun getUrlForStatusMessage(baseUrl: String): String { + return getUrlForStatus(baseUrl) + "/message" + } + + fun getUrlForSetCustomStatus(baseUrl: String): String { + return "$baseUrl$OCS_API_VERSION/apps/user_status/api/v1/user_status/message/custom" + } + + fun getUrlForSetPredefinedStatus(baseUrl: String): String { + return "$baseUrl$OCS_API_VERSION/apps/user_status/api/v1/user_status/message/predefined" + } + + fun getUrlForUserStatuses(baseUrl: String): String { + return "$baseUrl$OCS_API_VERSION/apps/user_status/api/v1/statuses" + } + + fun getUrlForMessageReaction(baseUrl: String, roomToken: String, messageId: String): String { + return "$baseUrl$OCS_API_VERSION$SPREED_API_VERSION/reaction/$roomToken/$messageId" + } + + fun getUrlForUnifiedSearch(baseUrl: String, providerId: String): String { + return "$baseUrl$OCS_API_VERSION/search/providers/$providerId/search" + } + + fun getUrlForPoll(baseUrl: String, roomToken: String, pollId: String): String { + return getUrlForPoll(baseUrl, roomToken) + "/" + pollId + } + + fun getUrlForPoll(baseUrl: String, roomToken: String): String { + return "$baseUrl$OCS_API_VERSION$SPREED_API_VERSION/poll/$roomToken" + } + + @JvmStatic + fun getUrlForMessageExpiration(version: Int, baseUrl: String?, token: String?): String { + return getUrlForRoom(version, baseUrl, token) + "/message-expiration" + } + + fun getUrlForOpenGraph(baseUrl: String): String { + return "$baseUrl$OCS_API_VERSION/references/resolve" + } + + fun getUrlForRecording(version: Int, baseUrl: String?, token: String): String { + return getUrlForApi(version, baseUrl) + "/recording/" + token + } + + fun getUrlForRequestAssistance(version: Int, baseUrl: String?, token: String): String { + return getUrlForApi(version, baseUrl) + "/breakout-rooms/" + token + "/request-assistance" + } + + fun getUrlForConversationDescription(version: Int, baseUrl: String?, token: String?): String { + return getUrlForRoom(version, baseUrl, token) + "/description" + } + + fun getUrlForTranslation(baseUrl: String): String { + return "$baseUrl$OCS_API_VERSION/translation/translate" + } + + fun getUrlForLanguages(baseUrl: String): String { + return "$baseUrl$OCS_API_VERSION/translation/languages" + } + + fun getUrlForReminder(user: User, roomToken: String, messageId: String, version: Int): String { + val url = getUrlForChatMessage(version, user.baseUrl!!, roomToken, messageId) + return "$url/reminder" + } + + fun getUrlForRecordingConsent(version: Int, baseUrl: String?, token: String?): String { + return getUrlForRoom(version, baseUrl, token) + "/recording-consent" + } + + fun getUrlForInvitation(baseUrl: String): String { + return baseUrl + OCS_API_VERSION + SPREED_API_VERSION + "/federation/invitation" + } + + fun getUrlForInvitationAccept(baseUrl: String, id: Int): String { + return getUrlForInvitation(baseUrl) + "/" + id + } + + fun getUrlForInvitationReject(baseUrl: String, id: Int): String { + return getUrlForInvitation(baseUrl) + "/" + id + } + + @JvmStatic + fun getUrlForRoomCapabilities(version: Int, baseUrl: String?, token: String?): String { + return getUrlForRooms(version, baseUrl) + "/" + token + "/capabilities" + } +} diff --git a/app/src/main/java/com/nextcloud/talk/utils/CapabilitiesUtil.kt b/app/src/main/java/com/nextcloud/talk/utils/CapabilitiesUtil.kt new file mode 100644 index 000000000..d58cf1bf5 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/utils/CapabilitiesUtil.kt @@ -0,0 +1,275 @@ +/* + * Nextcloud Talk application + * + * @author Andy Scherzinger + * @author Mario Danic + * @author Marcel Hibbe + * Copyright (C) 2023-2024 Marcel Hibbe + * Copyright (C) 2021 Andy Scherzinger (info@andy-scherzinger.de) + * Copyright (C) 2017-2018 Mario Danic + * + * 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 . + */ +package com.nextcloud.talk.utils + +import com.nextcloud.talk.data.user.model.User +import com.nextcloud.talk.models.json.capabilities.SpreedCapability + +enum class SpreedFeatures(val value: String) { + RECORDING_V1("recording-v1"), + REACTIONS("reactions"), + RAISE_HAND("raise-hand"), + DIRECT_MENTION_FLAG("direct-mention-flag"), + CONVERSATION_CALL_FLAGS("conversation-call-flags"), + SILENT_SEND("silent-send"), + MENTION_FLAG("mention-flag"), + DELETE_MESSAGES("delete-messages"), + READ_ONLY_ROOMS("read-only-rooms"), + RICH_OBJECT_LIST_MEDIA("rich-object-list-media"), + SILENT_CALL("silent-call"), + MESSAGE_EXPIRATION("message-expiration"), + WEBINARY_LOBBY("webinary-lobby"), + VOICE_MESSAGE_SHARING("voice-message-sharing"), + INVITE_GROUPS_AND_MAILS("invite-groups-and-mails"), + CIRCLES_SUPPORT("circles-support"), + LAST_ROOM_ACTIVITY("last-room-activity"), + NOTIFICATION_LEVELS("notification-levels"), + CLEAR_HISTORY("clear-history"), + AVATAR("avatar"), + LISTABLE_ROOMS("listable-rooms"), + LOCKED_ONE_TO_ONE_ROOMS("locked-one-to-one-rooms"), + TEMP_USER_AVATAR_API("temp-user-avatar-api"), + PHONEBOOK_SEARCH("phonebook-search"), + GEO_LOCATION_SHARING("geo-location-sharing"), + TALK_POLLS("talk-polls") +} + +@Suppress("TooManyFunctions") +object CapabilitiesUtil { + + //region Version checks + fun isServerEOL(serverVersion: Int): Boolean { + return (serverVersion < SERVER_VERSION_MIN_SUPPORTED) + } + + fun isServerAlmostEOL(serverVersion: Int): Boolean { + return (serverVersion < SERVER_VERSION_SUPPORT_WARNING) + } + + // endregion + + //region CoreCapabilities + + @JvmStatic + fun isLinkPreviewAvailable(user: User): Boolean { + return user.capabilities?.coreCapability?.referenceApi != null && + user.capabilities?.coreCapability?.referenceApi == "true" + } + + // endregion + + //region SpreedCapabilities + + @JvmStatic + fun hasSpreedFeatureCapability(spreedCapabilities: SpreedCapability, spreedFeatures: SpreedFeatures): Boolean { + if (spreedCapabilities.features != null) { + return spreedCapabilities.features!!.contains(spreedFeatures.value) + } + return false + } + + @JvmStatic + @Deprecated("Add your capability to Capability enums and use hasSpreedFeatureCapability with enum.") + fun hasSpreedFeatureCapability(spreedCapabilities: SpreedCapability, capabilityName: String): Boolean { + if (spreedCapabilities.features != null) { + return spreedCapabilities.features!!.contains(capabilityName) + } + return false + } + + fun getMessageMaxLength(spreedCapabilities: SpreedCapability): Int { + if (spreedCapabilities.config?.containsKey("chat") == true) { + val chatConfigHashMap = spreedCapabilities.config!!["chat"] + if (chatConfigHashMap?.containsKey("max-length") == true) { + val chatSize = (chatConfigHashMap["max-length"]!!.toString()).toInt() + return if (chatSize > 0) { + chatSize + } else { + DEFAULT_CHAT_SIZE + } + } + } + + return DEFAULT_CHAT_SIZE + } + + fun isReadStatusAvailable(spreedCapabilities: SpreedCapability): Boolean { + if (spreedCapabilities.config?.containsKey("chat") == true) { + val map: Map? = spreedCapabilities.config!!["chat"] + return map != null && map.containsKey("read-privacy") + } + return false + } + + @JvmStatic + fun isCallRecordingAvailable(spreedCapabilities: SpreedCapability): Boolean { + if (hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.RECORDING_V1) && + spreedCapabilities.config?.containsKey("call") == true + ) { + val map: Map? = spreedCapabilities.config!!["call"] + if (map != null && map.containsKey("recording")) { + return (map["recording"].toString()).toBoolean() + } + } + return false + } + + @JvmStatic + fun getAttachmentFolder(spreedCapabilities: SpreedCapability): String { + if (spreedCapabilities.config?.containsKey("attachments") == true) { + val map = spreedCapabilities.config!!["attachments"] + if (map?.containsKey("folder") == true) { + return map["folder"].toString() + } + } + return "/Talk" + } + + fun isConversationDescriptionEndpointAvailable(spreedCapabilities: SpreedCapability): Boolean { + return hasSpreedFeatureCapability(spreedCapabilities, "room-description") + } + + fun isUnifiedSearchAvailable(spreedCapabilities: SpreedCapability): Boolean { + return hasSpreedFeatureCapability(spreedCapabilities, "unified-search") + } + + fun isAbleToCall(spreedCapabilities: SpreedCapability): Boolean { + return if ( + spreedCapabilities.config?.containsKey("call") == true && + spreedCapabilities.config!!["call"] != null && + spreedCapabilities.config!!["call"]!!.containsKey("enabled") + ) { + java.lang.Boolean.parseBoolean(spreedCapabilities.config!!["call"]!!["enabled"].toString()) + } else { + // older nextcloud versions without the capability can't disable the calls + true + } + } + + fun isCallReactionsSupported(user: User?): Boolean { + if (user?.capabilities != null) { + val capabilities = user.capabilities + return capabilities?.spreedCapability?.config?.containsKey("call") == true && + capabilities.spreedCapability!!.config!!["call"] != null && + capabilities.spreedCapability!!.config!!["call"]!!.containsKey("supported-reactions") + } + return false + } + + fun isTranslationsSupported(spreedCapabilities: SpreedCapability): Boolean { + return spreedCapabilities.config?.containsKey("chat") == true && + spreedCapabilities.config!!["chat"] != null && + spreedCapabilities.config!!["chat"]!!.containsKey("has-translation-providers") && + spreedCapabilities.config!!["chat"]!!["has-translation-providers"] == true + } + + fun getRecordingConsentType(spreedCapabilities: SpreedCapability): Int { + if ( + spreedCapabilities.config?.containsKey("call") == true && + spreedCapabilities.config!!["call"] != null && + spreedCapabilities.config!!["call"]!!.containsKey("recording-consent") + ) { + return when ( + spreedCapabilities.config!!["call"]!!["recording-consent"].toString() + .toInt() + ) { + 1 -> RECORDING_CONSENT_REQUIRED + 2 -> RECORDING_CONSENT_DEPEND_ON_CONVERSATION + else -> RECORDING_CONSENT_NOT_REQUIRED + } + } + return RECORDING_CONSENT_NOT_REQUIRED + } + + // endregion + + //region SpreedCapabilities that can't be used with federation as the settings for them are global + + fun isReadStatusPrivate(user: User): Boolean { + if (user.capabilities?.spreedCapability?.config?.containsKey("chat") == true) { + val map = user.capabilities!!.spreedCapability!!.config!!["chat"] + if (map?.containsKey("read-privacy") == true) { + return (map["read-privacy"]!!.toString()).toInt() == 1 + } + } + return false + } + + fun isTypingStatusAvailable(user: User): Boolean { + if (user.capabilities?.spreedCapability?.config?.containsKey("chat") == true) { + val map = user.capabilities!!.spreedCapability!!.config!!["chat"] + return map != null && map.containsKey("typing-privacy") + } + return false + } + + fun isTypingStatusPrivate(user: User): Boolean { + if (user.capabilities?.spreedCapability?.config?.containsKey("chat") == true) { + val map = user.capabilities!!.spreedCapability!!.config!!["chat"] + if (map?.containsKey("typing-privacy") == true) { + return (map["typing-privacy"]!!.toString()).toInt() == 1 + } + } + return false + } + + // endregion + + //region ThemingCapabilities + + fun getServerName(user: User?): String? { + if (user?.capabilities?.themingCapability != null) { + return user.capabilities!!.themingCapability!!.name + } + return "" + } + + // endregion + + //region ProvisioningCapabilities + + fun canEditScopes(user: User): Boolean { + return user.capabilities?.provisioningCapability?.accountPropertyScopesVersion != null && + user.capabilities!!.provisioningCapability!!.accountPropertyScopesVersion!! > 1 + } + + // endregion + + //region UserStatusCapabilities + + @JvmStatic + fun isUserStatusAvailable(user: User): Boolean { + return user.capabilities?.userStatusCapability?.enabled == true && + user.capabilities?.userStatusCapability?.supportsEmoji == true + } + + // endregion + + const val DEFAULT_CHAT_SIZE = 1000 + const val RECORDING_CONSENT_NOT_REQUIRED = 0 + const val RECORDING_CONSENT_REQUIRED = 1 + const val RECORDING_CONSENT_DEPEND_ON_CONVERSATION = 2 + private const val SERVER_VERSION_MIN_SUPPORTED = 14 + private const val SERVER_VERSION_SUPPORT_WARNING = 18 +} diff --git a/app/src/main/java/com/nextcloud/talk/utils/ConversationUtils.kt b/app/src/main/java/com/nextcloud/talk/utils/ConversationUtils.kt index a4c2414a8..92dea95de 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/ConversationUtils.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/ConversationUtils.kt @@ -1,10 +1,9 @@ package com.nextcloud.talk.utils -import com.nextcloud.talk.data.user.model.User import com.nextcloud.talk.models.domain.ConversationModel import com.nextcloud.talk.models.domain.ConversationType import com.nextcloud.talk.models.domain.ParticipantType -import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew +import com.nextcloud.talk.models.json.capabilities.SpreedCapability /* * Nextcloud Talk application @@ -45,28 +44,28 @@ object ConversationUtils { ParticipantType.MODERATOR == conversation.participantType } - private fun isLockedOneToOne(conversation: ConversationModel, conversationUser: User): Boolean { + fun isLockedOneToOne(conversation: ConversationModel, spreedCapabilities: SpreedCapability): Boolean { return conversation.type == ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL && - CapabilitiesUtilNew.hasSpreedFeatureCapability(conversationUser, "locked-one-to-one-rooms") + CapabilitiesUtil.hasSpreedFeatureCapability(spreedCapabilities, "locked-one-to-one-rooms") } - fun canModerate(conversation: ConversationModel, conversationUser: User): Boolean { + fun canModerate(conversation: ConversationModel, spreedCapabilities: SpreedCapability): Boolean { return isParticipantOwnerOrModerator(conversation) && - !isLockedOneToOne(conversation, conversationUser) && + !isLockedOneToOne(conversation, spreedCapabilities) && conversation.type != ConversationType.FORMER_ONE_TO_ONE && !isNoteToSelfConversation(conversation) } - fun isLobbyViewApplicable(conversation: ConversationModel, conversationUser: User): Boolean { - return !canModerate(conversation, conversationUser) && + fun isLobbyViewApplicable(conversation: ConversationModel, spreedCapabilities: SpreedCapability): Boolean { + return !canModerate(conversation, spreedCapabilities) && ( conversation.type == ConversationType.ROOM_GROUP_CALL || conversation.type == ConversationType.ROOM_PUBLIC_CALL ) } - fun isNameEditable(conversation: ConversationModel, conversationUser: User): Boolean { - return canModerate(conversation, conversationUser) && + fun isNameEditable(conversation: ConversationModel, spreedCapabilities: SpreedCapability): Boolean { + return canModerate(conversation, spreedCapabilities) && ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL != conversation.type } @@ -79,12 +78,12 @@ object ConversationUtils { } } - fun canDelete(conversation: ConversationModel, conversationUser: User): Boolean { + fun canDelete(conversation: ConversationModel, spreedCapability: SpreedCapability): Boolean { return if (conversation.canDeleteConversation != null) { // Available since APIv2 conversation.canDeleteConversation!! } else { - canModerate(conversation, conversationUser) + canModerate(conversation, spreedCapability) // Fallback for APIv1 } } diff --git a/app/src/main/java/com/nextcloud/talk/utils/FileViewerUtils.kt b/app/src/main/java/com/nextcloud/talk/utils/FileViewerUtils.kt index 92cc4183f..d2aeb4783 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/FileViewerUtils.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/FileViewerUtils.kt @@ -61,7 +61,6 @@ import com.nextcloud.talk.utils.MimetypeUtils.isGif import com.nextcloud.talk.utils.MimetypeUtils.isMarkdown import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ACCOUNT import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_FILE_ID -import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew import java.io.File import java.util.concurrent.ExecutionException @@ -308,7 +307,7 @@ class FileViewerUtils(private val context: Context, private val user: User) { .putString(DownloadFileToCacheWorker.KEY_USER_ID, user.userId) .putString( DownloadFileToCacheWorker.KEY_ATTACHMENT_FOLDER, - CapabilitiesUtilNew.getAttachmentFolder(user) + CapabilitiesUtil.getAttachmentFolder(user.capabilities!!.spreedCapability!!) ) .putString(DownloadFileToCacheWorker.KEY_FILE_NAME, fileInfo.fileName) .putString(DownloadFileToCacheWorker.KEY_FILE_PATH, path) diff --git a/app/src/main/java/com/nextcloud/talk/utils/ParticipantPermissions.kt b/app/src/main/java/com/nextcloud/talk/utils/ParticipantPermissions.kt index dddb8753e..829991b5a 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/ParticipantPermissions.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/ParticipantPermissions.kt @@ -22,22 +22,21 @@ package com.nextcloud.talk.utils -import com.nextcloud.talk.data.user.model.User import com.nextcloud.talk.models.domain.ConversationModel +import com.nextcloud.talk.models.json.capabilities.SpreedCapability import com.nextcloud.talk.models.json.conversations.Conversation -import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew /** * see https://nextcloud-talk.readthedocs.io/en/latest/constants/#attendee-permissions */ class ParticipantPermissions( - private val user: User, + private val spreedCapabilities: SpreedCapability, private val conversation: ConversationModel ) { @Deprecated("Use ChatRepository.ConversationModel") - constructor(user: User, conversation: Conversation) : this( - user, + constructor(spreedCapabilities: SpreedCapability, conversation: Conversation) : this( + spreedCapabilities, ConversationModel.mapToConversationModel(conversation) ) @@ -52,8 +51,8 @@ class ParticipantPermissions( private val hasChatPermission = (conversation.permissions and CHAT) == CHAT private fun hasConversationPermissions(): Boolean { - return CapabilitiesUtilNew.hasSpreedFeatureCapability( - user, + return CapabilitiesUtil.hasSpreedFeatureCapability( + spreedCapabilities, "conversation-permissions" ) } @@ -91,7 +90,7 @@ class ParticipantPermissions( } fun hasChatPermission(): Boolean { - if (CapabilitiesUtilNew.hasSpreedFeatureCapability(user, "chat-permission")) { + if (CapabilitiesUtil.hasSpreedFeatureCapability(spreedCapabilities, "chat-permission")) { return hasChatPermission } // if capability is not available then the spreed version doesn't support to restrict this diff --git a/app/src/main/java/com/nextcloud/talk/utils/PushUtils.kt b/app/src/main/java/com/nextcloud/talk/utils/PushUtils.kt index 0cfac75b7..69c85fef9 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/PushUtils.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/PushUtils.kt @@ -238,7 +238,7 @@ class PushUtils { val credentials = ApiUtils.getCredentials(user.username, user.token) ncApi.registerDeviceForNotificationsWithNextcloud( credentials, - ApiUtils.getUrlNextcloudPush(user.baseUrl), + ApiUtils.getUrlNextcloudPush(user.baseUrl!!), nextcloudRegisterPushMap ) .subscribe(object : Observer { diff --git a/app/src/main/java/com/nextcloud/talk/utils/RemoteFileUtils.kt b/app/src/main/java/com/nextcloud/talk/utils/RemoteFileUtils.kt index 2e3681f88..d54cdcbc5 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/RemoteFileUtils.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/RemoteFileUtils.kt @@ -53,8 +53,8 @@ object RemoteFileUtils { return ncApi.checkIfFileExists( ApiUtils.getCredentials(currentUser.username, currentUser.token), ApiUtils.getUrlForFileUpload( - currentUser.baseUrl, - currentUser.userId, + currentUser.baseUrl!!, + currentUser.userId!!, remotePath ) ) diff --git a/app/src/main/java/com/nextcloud/talk/utils/ShareUtils.kt b/app/src/main/java/com/nextcloud/talk/utils/ShareUtils.kt index 41c644878..2e6e10244 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/ShareUtils.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/ShareUtils.kt @@ -22,10 +22,10 @@ package com.nextcloud.talk.utils import android.content.Context import com.nextcloud.talk.R import com.nextcloud.talk.data.user.model.User -import com.nextcloud.talk.models.json.conversations.Conversation +import com.nextcloud.talk.models.domain.ConversationModel object ShareUtils { - fun getStringForIntent(context: Context, user: User, conversation: Conversation?): String { + fun getStringForIntent(context: Context, user: User, conversation: ConversationModel?): String { return String.format( context.resources.getString(R.string.nc_share_text), user.baseUrl, diff --git a/app/src/main/java/com/nextcloud/talk/utils/bundle/BundleKeys.kt b/app/src/main/java/com/nextcloud/talk/utils/bundle/BundleKeys.kt index a8cb1e405..e899a040a 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/bundle/BundleKeys.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/bundle/BundleKeys.kt @@ -89,4 +89,5 @@ object BundleKeys { const val KEY_REAUTHORIZE_ACCOUNT = "KEY_REAUTHORIZE_ACCOUNT" const val KEY_PASSWORD = "KEY_PASSWORD" const val KEY_REMOTE_TALK_SHARE = "KEY_REMOTE_TALK_SHARE" + const val KEY_CHAT_API_VERSION = "KEY_CHAT_API_VERSION" } diff --git a/app/src/main/java/com/nextcloud/talk/utils/database/user/CapabilitiesUtilNew.kt b/app/src/main/java/com/nextcloud/talk/utils/database/user/CapabilitiesUtilNew.kt deleted file mode 100644 index 1969c6912..000000000 --- a/app/src/main/java/com/nextcloud/talk/utils/database/user/CapabilitiesUtilNew.kt +++ /dev/null @@ -1,267 +0,0 @@ -/* - * Nextcloud Talk application - * - * @author Andy Scherzinger - * @author Mario Danic - * Copyright (C) 2021 Andy Scherzinger (info@andy-scherzinger.de) - * Copyright (C) 2017-2018 Mario Danic - * - * 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 . - */ -package com.nextcloud.talk.utils.database.user - -import com.nextcloud.talk.data.user.model.User -import com.nextcloud.talk.models.json.capabilities.Capabilities - -@Suppress("TooManyFunctions") -object CapabilitiesUtilNew { - fun hasNotificationsCapability(user: User, capabilityName: String): Boolean { - return user.capabilities?.spreedCapability?.features?.contains(capabilityName) == true - } - - fun hasExternalCapability(user: User, capabilityName: String?): Boolean { - if (user.capabilities?.externalCapability?.containsKey("v1") == true) { - return user.capabilities!!.externalCapability!!["v1"]?.contains(capabilityName!!) == true - } - return false - } - - @JvmStatic - fun isServerEOL(capabilities: Capabilities?): Boolean { - // Capability is available since Talk 4 => Nextcloud 14 => Autmn 2018 - return !hasSpreedFeatureCapability(capabilities, "no-ping") - } - - fun isServerAlmostEOL(user: User): Boolean { - // Capability is available since Talk 8 => Nextcloud 18 => January 2020 - return !hasSpreedFeatureCapability(user, "chat-replies") - } - - fun canSetChatReadMarker(user: User): Boolean { - return hasSpreedFeatureCapability(user, "chat-read-marker") - } - - fun canMarkRoomAsUnread(user: User): Boolean { - return hasSpreedFeatureCapability(user, "chat-unread") - } - - @JvmStatic - fun hasSpreedFeatureCapability(user: User?, capabilityName: String): Boolean { - return hasSpreedFeatureCapability(user?.capabilities, capabilityName) - } - - @JvmStatic - fun hasSpreedFeatureCapability(capabilities: Capabilities?, capabilityName: String): Boolean { - if (capabilities?.spreedCapability?.features != null) { - return capabilities.spreedCapability!!.features!!.contains(capabilityName) - } - return false - } - - fun getMessageMaxLength(user: User?): Int { - if (user?.capabilities?.spreedCapability?.config?.containsKey("chat") == true) { - val chatConfigHashMap = user.capabilities!!.spreedCapability!!.config!!["chat"] - if (chatConfigHashMap?.containsKey("max-length") == true) { - val chatSize = (chatConfigHashMap["max-length"]!!.toString()).toInt() - return if (chatSize > 0) { - chatSize - } else { - DEFAULT_CHAT_SIZE - } - } - } - - return DEFAULT_CHAT_SIZE - } - - fun isPhoneBookIntegrationAvailable(user: User): Boolean { - return user.capabilities?.spreedCapability?.features?.contains("phonebook-search") == true - } - - fun isReadStatusAvailable(user: User): Boolean { - if (user.capabilities?.spreedCapability?.config?.containsKey("chat") == true) { - val map: Map? = user.capabilities!!.spreedCapability!!.config!!["chat"] - return map != null && map.containsKey("read-privacy") - } - return false - } - - fun isReadStatusPrivate(user: User): Boolean { - if (user.capabilities?.spreedCapability?.config?.containsKey("chat") == true) { - val map = user.capabilities!!.spreedCapability!!.config!!["chat"] - if (map?.containsKey("read-privacy") == true) { - return (map["read-privacy"]!!.toString()).toInt() == 1 - } - } - return false - } - - fun isTypingStatusAvailable(user: User): Boolean { - if (user.capabilities?.spreedCapability?.config?.containsKey("chat") == true) { - val map = user.capabilities!!.spreedCapability!!.config!!["chat"] - return map != null && map.containsKey("typing-privacy") - } - return false - } - - fun isTypingStatusPrivate(user: User): Boolean { - if (user.capabilities?.spreedCapability?.config?.containsKey("chat") == true) { - val map = user.capabilities!!.spreedCapability!!.config!!["chat"] - if (map?.containsKey("typing-privacy") == true) { - return (map["typing-privacy"]!!.toString()).toInt() == 1 - } - } - return false - } - - @JvmStatic - fun isCallRecordingAvailable(user: User): Boolean { - if (hasSpreedFeatureCapability(user, "recording-v1") && - user.capabilities?.spreedCapability?.config?.containsKey("call") == true - ) { - val map: Map? = user.capabilities!!.spreedCapability!!.config!!["call"] - if (map != null && map.containsKey("recording")) { - return (map["recording"].toString()).toBoolean() - } - } - return false - } - - @JvmStatic - fun isUserStatusAvailable(user: User): Boolean { - return user.capabilities?.userStatusCapability?.enabled == true && - user.capabilities?.userStatusCapability?.supportsEmoji == true - } - - @JvmStatic - fun getAttachmentFolder(user: User): String { - if (user.capabilities?.spreedCapability?.config?.containsKey("attachments") == true) { - val map = user.capabilities!!.spreedCapability!!.config!!["attachments"] - if (map?.containsKey("folder") == true) { - return map["folder"].toString() - } - } - return "/Talk" - } - - fun getServerName(user: User?): String? { - if (user?.capabilities?.themingCapability != null) { - return user.capabilities!!.themingCapability!!.name - } - return "" - } - - // TODO later avatar can also be checked via user fields, for now it is in Talk capability - fun isAvatarEndpointAvailable(user: User): Boolean { - return user.capabilities?.spreedCapability?.features?.contains("temp-user-avatar-api") == true - } - - fun isConversationAvatarEndpointAvailable(user: User): Boolean { - return user.capabilities?.spreedCapability?.features?.contains("avatar") == true - } - - fun isConversationDescriptionEndpointAvailable(user: User): Boolean { - return user.capabilities?.spreedCapability?.features?.contains("room-description") == true - } - - fun canEditScopes(user: User): Boolean { - return user.capabilities?.provisioningCapability?.accountPropertyScopesVersion != null && - user.capabilities!!.provisioningCapability!!.accountPropertyScopesVersion!! > 1 - } - - fun isAbleToCall(user: User?): Boolean { - if (user?.capabilities != null) { - val capabilities = user.capabilities - return if ( - capabilities?.spreedCapability?.config?.containsKey("call") == true && - capabilities.spreedCapability!!.config!!["call"] != null && - capabilities.spreedCapability!!.config!!["call"]!!.containsKey("enabled") - ) { - java.lang.Boolean.parseBoolean(capabilities.spreedCapability!!.config!!["call"]!!["enabled"].toString()) - } else { - // older nextcloud versions without the capability can't disable the calls - true - } - } - return false - } - - fun isCallReactionsSupported(user: User?): Boolean { - if (user?.capabilities != null) { - val capabilities = user.capabilities - return capabilities?.spreedCapability?.config?.containsKey("call") == true && - capabilities.spreedCapability!!.config!!["call"] != null && - capabilities.spreedCapability!!.config!!["call"]!!.containsKey("supported-reactions") - } - return false - } - - @JvmStatic - fun isUnifiedSearchAvailable(user: User): Boolean { - return hasSpreedFeatureCapability(user, "unified-search") - } - - @JvmStatic - fun isLinkPreviewAvailable(user: User): Boolean { - return user.capabilities?.coreCapability?.referenceApi != null && - user.capabilities?.coreCapability?.referenceApi == "true" - } - - fun isTranslationsSupported(user: User?): Boolean { - if (user?.capabilities != null) { - val capabilities = user.capabilities - return capabilities?.spreedCapability?.config?.containsKey("chat") == true && - capabilities.spreedCapability!!.config!!["chat"] != null && - capabilities.spreedCapability!!.config!!["chat"]!!.containsKey("has-translation-providers") && - capabilities.spreedCapability!!.config!!["chat"]!!["has-translation-providers"] == true - } - - return false - } - - fun isRemindSupported(user: User?): Boolean { - if (user?.capabilities != null) { - val capabilities = user.capabilities - return capabilities?.spreedCapability?.features?.contains("remind-me-later") == true - } - - return false - } - - fun getRecordingConsentType(user: User?): Int { - if (user?.capabilities != null) { - val capabilities = user.capabilities - if ( - capabilities?.spreedCapability?.config?.containsKey("call") == true && - capabilities.spreedCapability!!.config!!["call"] != null && - capabilities.spreedCapability!!.config!!["call"]!!.containsKey("recording-consent") - ) { - return when ( - capabilities.spreedCapability!!.config!!["call"]!!["recording-consent"].toString() - .toInt() - ) { - 1 -> RECORDING_CONSENT_REQUIRED - 2 -> RECORDING_CONSENT_DEPEND_ON_CONVERSATION - else -> RECORDING_CONSENT_NOT_REQUIRED - } - } - } - return RECORDING_CONSENT_NOT_REQUIRED - } - - const val DEFAULT_CHAT_SIZE = 1000 - const val RECORDING_CONSENT_NOT_REQUIRED = 0 - const val RECORDING_CONSENT_REQUIRED = 1 - const val RECORDING_CONSENT_DEPEND_ON_CONVERSATION = 2 -} diff --git a/app/src/main/java/com/nextcloud/talk/utils/preferences/preferencestorage/DatabaseStorageModule.java b/app/src/main/java/com/nextcloud/talk/utils/preferences/preferencestorage/DatabaseStorageModule.java index cc4365c55..8652a99be 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/preferences/preferencestorage/DatabaseStorageModule.java +++ b/app/src/main/java/com/nextcloud/talk/utils/preferences/preferencestorage/DatabaseStorageModule.java @@ -35,7 +35,7 @@ import com.nextcloud.talk.data.user.model.User; import com.nextcloud.talk.models.json.generic.GenericOverall; import com.nextcloud.talk.utils.ApiUtils; import com.nextcloud.talk.utils.UserIdUtils; -import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew; +import com.nextcloud.talk.utils.CapabilitiesUtil; import javax.inject.Inject; @@ -158,7 +158,8 @@ public class DatabaseStorageModule { }); } else if ("conversation_info_message_notifications_dropdown".equals(key)) { - if (CapabilitiesUtilNew.hasSpreedFeatureCapability(conversationUser, "notification-levels")) { + if (CapabilitiesUtil.hasSpreedFeatureCapability(conversationUser.getCapabilities().getSpreedCapability(), "notification" + + "-levels")) { if (TextUtils.isEmpty(messageNotificationLevel) || !messageNotificationLevel.equals(value)) { int intValue; switch (value) { @@ -175,7 +176,7 @@ public class DatabaseStorageModule { intValue = 0; } - int apiVersion = ApiUtils.getConversationApiVersion(conversationUser, new int[]{ApiUtils.APIv4, 1}); + int apiVersion = ApiUtils.getConversationApiVersion(conversationUser, new int[]{ApiUtils.API_V4, 1}); ncApi.setNotificationLevel(ApiUtils.getCredentials(conversationUser.getUsername(), conversationUser.getToken()), diff --git a/app/src/main/java/com/nextcloud/talk/viewmodels/GeoCodingViewModel.kt b/app/src/main/java/com/nextcloud/talk/viewmodels/GeoCodingViewModel.kt index d66d9059c..bc08d85d6 100644 --- a/app/src/main/java/com/nextcloud/talk/viewmodels/GeoCodingViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/viewmodels/GeoCodingViewModel.kt @@ -25,7 +25,6 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import com.nextcloud.talk.activities.CallActivity.Companion.TAG -import com.nextcloud.talk.location.GeocodingActivity import fr.dudie.nominatim.client.TalkJsonNominatimClient import fr.dudie.nominatim.model.Address import kotlinx.coroutines.CoroutineScope @@ -70,9 +69,9 @@ class GeoCodingViewModel : ViewModel() { try { val results = nominatimClient.search(query) as ArrayList
for (address in results) { - Log.d(GeocodingActivity.TAG, address.displayName) - Log.d(GeocodingActivity.TAG, address.latitude.toString()) - Log.d(GeocodingActivity.TAG, address.longitude.toString()) + Log.d(TAG, address.displayName) + Log.d(TAG, address.latitude.toString()) + Log.d(TAG, address.longitude.toString()) } geocodingResults = results geocodingResultsLiveData.postValue(results) diff --git a/app/src/main/java/com/nextcloud/talk/webrtc/WebSocketConnectionHelper.java b/app/src/main/java/com/nextcloud/talk/webrtc/WebSocketConnectionHelper.java index 96b0a6fdb..adec118c3 100644 --- a/app/src/main/java/com/nextcloud/talk/webrtc/WebSocketConnectionHelper.java +++ b/app/src/main/java/com/nextcloud/talk/webrtc/WebSocketConnectionHelper.java @@ -113,7 +113,7 @@ public class WebSocketConnectionHelper { } HelloOverallWebSocketMessage getAssembledHelloModel(User user, String ticket) { - int apiVersion = ApiUtils.getSignalingApiVersion(user, new int[]{ApiUtils.APIv3, 2, 1}); + int apiVersion = ApiUtils.getSignalingApiVersion(user, new int[]{ApiUtils.API_V3, 2, 1}); HelloOverallWebSocketMessage helloOverallWebSocketMessage = new HelloOverallWebSocketMessage(); helloOverallWebSocketMessage.setType("hello"); diff --git a/app/src/test/java/com/nextcloud/talk/utils/ParticipantPermissionsTest.kt b/app/src/test/java/com/nextcloud/talk/utils/ParticipantPermissionsTest.kt index be1ed2cae..17c8dedf3 100644 --- a/app/src/test/java/com/nextcloud/talk/utils/ParticipantPermissionsTest.kt +++ b/app/src/test/java/com/nextcloud/talk/utils/ParticipantPermissionsTest.kt @@ -22,7 +22,7 @@ package com.nextcloud.talk.utils -import com.nextcloud.talk.data.user.model.User +import com.nextcloud.talk.models.json.capabilities.SpreedCapability import com.nextcloud.talk.models.json.conversations.Conversation import junit.framework.TestCase import org.junit.Test @@ -31,7 +31,7 @@ class ParticipantPermissionsTest : TestCase() { @Test fun test_areFlagsSet() { - val user = User() + val spreedCapability = SpreedCapability() val conversation = Conversation() conversation.permissions = ParticipantPermissions.PUBLISH_SCREEN or ParticipantPermissions.JOIN_CALL or @@ -39,7 +39,7 @@ class ParticipantPermissionsTest : TestCase() { val attendeePermissions = ParticipantPermissions( - user, + spreedCapability, conversation ) diff --git a/app/src/test/java/com/nextcloud/talk/utils/ShareUtilsTest.kt b/app/src/test/java/com/nextcloud/talk/utils/ShareUtilsTest.kt index 427890305..27084164b 100644 --- a/app/src/test/java/com/nextcloud/talk/utils/ShareUtilsTest.kt +++ b/app/src/test/java/com/nextcloud/talk/utils/ShareUtilsTest.kt @@ -23,7 +23,7 @@ import android.content.Context import android.content.res.Resources import com.nextcloud.talk.R import com.nextcloud.talk.data.user.model.User -import com.nextcloud.talk.models.json.conversations.Conversation +import com.nextcloud.talk.models.domain.ConversationModel import com.nextcloud.talk.users.UserManager import io.reactivex.Maybe import org.junit.Assert @@ -49,19 +49,19 @@ class ShareUtilsTest { private val baseUrl = "https://my.nextcloud.com" private val token = "2aotbrjr" - private lateinit var conversation: Conversation + private lateinit var conversation: ConversationModel @Before fun setUp() { MockitoAnnotations.openMocks(this) Mockito.`when`(userManager!!.currentUser).thenReturn(Maybe.just(user)) - Mockito.`when`(user!!.baseUrl).thenReturn(baseUrl) + Mockito.`when`(user!!.baseUrl!!).thenReturn(baseUrl) Mockito.`when`(context!!.resources).thenReturn(resources) Mockito.`when`(resources!!.getString(R.string.nc_share_text)) .thenReturn("Join the conversation at %1\$s/index.php/call/%2\$s") Mockito.`when`(resources.getString(R.string.nc_share_text_pass)).thenReturn("\nPassword: %1\$s") - conversation = Conversation(token = token) + conversation = ConversationModel(token = token) } @Test diff --git a/detekt.yml b/detekt.yml index 089cbd7f6..d38d10547 100644 --- a/detekt.yml +++ b/detekt.yml @@ -1,5 +1,5 @@ build: - maxIssues: 116 + maxIssues: 122 weights: # complexity: 2 # LongParameterList: 1 diff --git a/gradle/verification-keyring.keys b/gradle/verification-keyring.keys index ec2a8f58b..2904e0911 100644 --- a/gradle/verification-keyring.keys +++ b/gradle/verification-keyring.keys @@ -1313,8 +1313,6 @@ lQyC8nl8P5PgkEZ5CHcGymZlpzihR3ECrPJTk39Sb7D3SxCW4WrChV3kVfmLgvc= -----END PGP PUBLIC KEY BLOCK----- pub C21CE653B639E41A -uid Eric Kuck - sub 4F80368F9034B8D0 -----BEGIN PGP PUBLIC KEY BLOCK----- Version: BCPG v1.68 @@ -1324,21 +1322,20 @@ aBF7dud1bzw7voZo5ieGK923wUB+R9vQYd5DYfNLBHj9/TrTVCfKfUIeeEQRZYBz ufYcDwi4uVx9VPj2wRhkK+lzxphvosJCNFK8Vn82oY7eHQ1RA4AEhCeE/hz8maq6 NPoOPjpEN0DVnPIYdjPsdqd4UKQzkX/wMOxghz8SdcVROzUoL+9pZzx968OFuGrV lUD0su37S6To1IUn6WNEuy1uJTzT3Zqi0hfm31AqPxlLWDOwnuKvUJl3RObyli2k -CBtDa5xPE6GU6ZUEFUZ7qbk7iV5p8uTchKVbABEBAAG0IUVyaWMgS3VjayA8ZXJp -Y0BibHVlbGluZWxhYnMuY29tPrkBDQRU5ChoAQgAxC44rEZjgnJevvrzdL5vCVqC -1WI9cZ7L8DwF/pvm7NbRKC5GgXigul18UET80q4E3WIi0tTMG+pVWO+1v0dEu/Re -B/l+hc76iwJjOlwSiQ1jvq6/q0Nhne0/0khSYNWyd0AwJ2VZktcD93dJV4EqTm1O -Ck1gigAd/GN5wslQkMST/nUYUGm4cA/RZVSA8PSFZDZ2CxHyRyHgaOQNBUmWG2gf -ExUrrJA26iKowkNqZXWegnzYwlf8ZRE6MZM0JPLOUw/+r4ybI8ny+/U55s2sm0XZ -CcJvNda5N3SoaC/OgGWZFx1s9UksN7MmvhznaSUMeaeVFbGC3nu9dsQhV9RxMwAR -AQABiQEfBBgBAgAJBQJU5ChoAhsMAAoJEMIc5lO2OeQadSwH/07x1foZKkFRGMlj -wCmofKGSqZ9fu6ueOIV6fwHjrhlfkSyKN+96xbjhwIvWhKdSmWP/AsUqRDD/mTHw -ZMdlgmdXkGEvvCuJDL5FlQzl9OWeeplfVhLysx6dzj/G8AUXlfEIGBvb8Q56d5dK -MpId4H4vt+YIzS8x/ry+QTTDJAOu1cVJfwoX34yMcZ+IHTzly2XKi4zQ41DyfrgY -lCodWna10RtBdPZY41Jf4xSezX2q7KZBXXRgyVNYu3dDuNzhJAJ6jy7eMcb6urK6 -n73cz5uZPmWIbp4cAecZB0BfMj/PW37dK0oYdWKLxaDwpxvIV7T45Y64Md2FCC+d -nC2Xh7c= -=kzY9 +CBtDa5xPE6GU6ZUEFUZ7qbk7iV5p8uTchKVbABEBAAG5AQ0EVOQoaAEIAMQuOKxG +Y4JyXr7683S+bwlagtViPXGey/A8Bf6b5uzW0SguRoF4oLpdfFBE/NKuBN1iItLU +zBvqVVjvtb9HRLv0Xgf5foXO+osCYzpcEokNY76uv6tDYZ3tP9JIUmDVsndAMCdl +WZLXA/d3SVeBKk5tTgpNYIoAHfxjecLJUJDEk/51GFBpuHAP0WVUgPD0hWQ2dgsR +8kch4GjkDQVJlhtoHxMVK6yQNuoiqMJDamV1noJ82MJX/GUROjGTNCTyzlMP/q+M +myPJ8vv1OebNrJtF2QnCbzXWuTd0qGgvzoBlmRcdbPVJLDezJr4c52klDHmnlRWx +gt57vXbEIVfUcTMAEQEAAYkBHwQYAQIACQUCVOQoaAIbDAAKCRDCHOZTtjnkGnUs +B/9O8dX6GSpBURjJY8ApqHyhkqmfX7urnjiFen8B464ZX5EsijfvesW44cCL1oSn +Uplj/wLFKkQw/5kx8GTHZYJnV5BhL7wriQy+RZUM5fTlnnqZX1YS8rMenc4/xvAF +F5XxCBgb2/EOeneXSjKSHeB+L7fmCM0vMf68vkE0wyQDrtXFSX8KF9+MjHGfiB08 +5ctlyouM0ONQ8n64GJQqHVp2tdEbQXT2WONSX+MUns19quymQV10YMlTWLt3Q7jc +4SQCeo8u3jHG+rqyup+93M+bmT5liG6eHAHnGQdAXzI/z1t+3StKGHVii8Wg8Kcb +yFe0+OWOuDHdhQgvnZwtl4e3 +=DwNF -----END PGP PUBLIC KEY BLOCK----- pub C488A74FCAE540C6 @@ -1674,43 +1671,6 @@ fW1AkBVEk6siyL8PXfxmj9ev3H9xiQVLyJ6HpdHTLVjHjFkgNOLd =R7zg -----END PGP PUBLIC KEY BLOCK----- -pub D041CAD2E452550F -uid Deanna - -sub 5199F3DAE89C332D ------BEGIN PGP PUBLIC KEY BLOCK----- -Version: BCPG v1.68 - -mQGNBGCtdhoBDADdopjDt4eUNEqLJSw1ZICSR0oq09SOVtJSaSYdF8UiXjBfL1Ds -fhTDqSv5pT2a2gLj0OU3tFhWHvINLaKKCjQnHVcFXi2LTxt+XBOjRYkFjHVisbaZ -PZ6HnTMStPrvs+hQ168vU3VfYOsOLN22j53I/Ba+FA7E0G0bqkratuT5L7BTR1mC -fqDaeisWSCllfe6EEysaFF+/1RcRy+Yt+8ZWV0FZEF7UwQvqKHcYmlkqPIn3v/8y -J/yvmzIEtCQ1F+bvJbzaROmeJf254G2Uh7IfMYEm9WlqnGwNdbIhil7bdxq8Y/0H -XbQPaESxkki7yL5JTfH/+UzdklMe+Dga273L/cgzfjV3zJJ9vR94W5ABAbGYh4ZW -aKvNnT1m4vTbEMfo4r3NF2zc+K9Ly/JNaHqkR5M4SVElvN2lsC5KNUiRvExhg+h0 -mKyx61mu3gUIrC1UOmqhtx7RzQQf7ESMdzmNHY0P93lR0Ic10fyli0wfl7A6q7+q -zV2a1V2k9Yg6B9sAEQEAAbQgRGVhbm5hIDxkZWFubmFnYXJjaWFAZ29vZ2xlLmNv -bT65AY0EYK12GgEMAMgP3//QeBsTS3IrfSp3m44el96X6BWona2yo4DvVyuwqfUL -ZE+Nhj7I+kEZLrA29AOySOD/6quJ4MIJZfq/Do920Di8/10WQ00OdCM1wH7bMz2U -vcSqsr0iOgQtycuUf7JOHSTME9vqk+C3Lhn0r59AVaRdXEe6zBgNZyzZJeCr5F8w -RhglPlwvhOGs2aLEqlCxFnY4pLayQFoQyw1lDjHIXHg5JtfOHvqiNXVDcGpyKLG8 -SzImp62iL4sfuA0weVIQeS9kZiQabSYKvSf3TvNXYTgmFz/vjPbYhv9LTkBroTlV -g3l+UmAxLrHVuXMx0zX3jfNNHAqUjVhPYZhnifMkmGJgLeMIVqr5Q/tx8pzyYiiO -cqQ1zDg8ubJDGRue1JjlUGdw19OvhFDs+lydukt8Mmhb0gPkBLi2syZHgYHtEooX -PLwEsJ+SynZCFhZiWj8BsWNFJpaDd8ynNeWhMAcwi3B5ZeQiZaAlV0sItxsrzvbu -4ZYZtkjAkQdsaaTWSwARAQABiQG8BBgBCgAmFiEEaWthmaKp2MKc54zA0EHK0uRS -VQ8FAmCtdhoCGwwFCQPCZwAACgkQ0EHK0uRSVQ+G7wwAvaVPDgnM+i2pGQPwq6Mk -SzhKEG4H1pvBWyYR8H9D3p/dE33IjVu3EEy1h37Nzdyp46KtASGNe3KBodSsh6gv -PlV5pNGxMNbX6fo8ZGtS83C+6uTF1cYmuO1nmi8P4+7qtcNZg4xv/ujAZIC20kem -YKDth3FvPxEXsoxY+Ns7sxgd3SqoyLhjcyoczI8uyhim5nfvvbnEd6WrdiBPBtb/ -F1h/nfqdFj2TcZkAlnzGnlVlgU8J60u6zE+9VvBm0lJR73Ar55mQEwarGFPL1a3/ -A7ZEeNa0Dc3Oa5sKMYtxMlGKZ0WGUoGcDWiaDEsv5YyRnaSOaXKM1NkJCR013QAr -RcHrRBPo+0/RIZVE+b8oEcmGzdL8HNwnm7e06ruZryF9LQA5YBmCKE0urigmgEvC -zZsj/fMJ+OIZcAhE7UVae48GpW2kLATxmK01oSzvizIlmN3rVz2EnjOun2iuuEpF -/lmDbjK5n1r3f8npB1l1fT5cozzQJkPVYzhBWH1KXP5X -=nh9O ------END PGP PUBLIC KEY BLOCK----- - pub D364ABAA39A47320 sub 3F606403DCA455C8 -----BEGIN PGP PUBLIC KEY BLOCK----- diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 09f34c0ed..48d04192f 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -7,6 +7,9 @@ + + + @@ -2854,16 +2857,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + +