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 <dev@mhibbe.de>
This commit is contained in:
Marcel Hibbe 2024-02-23 16:54:31 +01:00
parent 513127e481
commit 754b825096
No known key found for this signature in database
GPG key ID: C793F8B59F43CE7B
101 changed files with 2115 additions and 1556 deletions

View file

@ -199,7 +199,7 @@ class AccountVerificationActivity : BaseActivity() {
val credentials = ApiUtils.getCredentials(username, token) val credentials = ApiUtils.getCredentials(username, token)
cookieManager.cookieStore.removeAll() cookieManager.cookieStore.removeAll()
ncApi.getCapabilities(credentials, ApiUtils.getUrlForCapabilities(baseUrl)) ncApi.getCapabilities(credentials, ApiUtils.getUrlForCapabilities(baseUrl!!))
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.subscribe(object : Observer<CapabilitiesOverall> { .subscribe(object : Observer<CapabilitiesOverall> {
override fun onSubscribe(d: Disposable) { 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 != null &&
!capabilitiesOverall.ocs!!.data!!.capabilities!!.spreedCapability!!.features!!.isEmpty() !capabilitiesOverall.ocs!!.data!!.capabilities!!.spreedCapability!!.features!!.isEmpty()
if (hasTalk) { if (hasTalk) {
fetchProfile(credentials, capabilitiesOverall) fetchProfile(credentials!!, capabilitiesOverall)
} else { } else {
if (resources != null) { if (resources != null) {
runOnUiThread { runOnUiThread {
@ -305,7 +305,7 @@ class AccountVerificationActivity : BaseActivity() {
private fun fetchProfile(credentials: String, capabilitiesOverall: CapabilitiesOverall) { private fun fetchProfile(credentials: String, capabilitiesOverall: CapabilitiesOverall) {
ncApi.getUserProfile( ncApi.getUserProfile(
credentials, credentials,
ApiUtils.getUrlForUserProfile(baseUrl) ApiUtils.getUrlForUserProfile(baseUrl!!)
) )
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.subscribe(object : Observer<UserProfileOverall> { .subscribe(object : Observer<UserProfileOverall> {

View file

@ -52,7 +52,7 @@ import com.nextcloud.talk.utils.UriUtils
import com.nextcloud.talk.utils.bundle.BundleKeys import com.nextcloud.talk.utils.bundle.BundleKeys
import com.nextcloud.talk.utils.bundle.BundleKeys.ADD_ADDITIONAL_ACCOUNT 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.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 com.nextcloud.talk.utils.singletons.ApplicationWideMessageHolder
import io.reactivex.Observer import io.reactivex.Observer
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
@ -336,7 +336,7 @@ class ServerSelectionActivity : BaseActivity() {
if (hasTalk) { if (hasTalk) {
runOnUiThread { runOnUiThread {
if (CapabilitiesUtilNew.isServerEOL(capabilities)) { if (CapabilitiesUtil.isServerEOL(capabilitiesOverall.ocs?.data?.serverVersion?.major!!)) {
if (resources != null) { if (resources != null) {
runOnUiThread { runOnUiThread {
setErrorText(resources!!.getString(R.string.nc_settings_server_eol)) setErrorText(resources!!.getString(R.string.nc_settings_server_eol))

View file

@ -110,6 +110,7 @@ import com.nextcloud.talk.ui.dialog.AudioOutputDialog
import com.nextcloud.talk.ui.dialog.MoreCallActionsDialog import com.nextcloud.talk.ui.dialog.MoreCallActionsDialog
import com.nextcloud.talk.users.UserManager import com.nextcloud.talk.users.UserManager
import com.nextcloud.talk.utils.ApiUtils import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.SpreedFeatures
import com.nextcloud.talk.utils.DisplayUtils import com.nextcloud.talk.utils.DisplayUtils
import com.nextcloud.talk.utils.NotificationUtils.cancelExistingNotificationsForRoom import com.nextcloud.talk.utils.NotificationUtils.cancelExistingNotificationsForRoom
import com.nextcloud.talk.utils.NotificationUtils.getCallRingtoneUri 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_ROOM_TOKEN
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_START_CALL_AFTER_ROOM_SWITCH 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.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.CapabilitiesUtilNew.hasSpreedFeatureCapability import com.nextcloud.talk.utils.CapabilitiesUtil.hasSpreedFeatureCapability
import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew.isCallRecordingAvailable import com.nextcloud.talk.utils.CapabilitiesUtil.isCallRecordingAvailable
import com.nextcloud.talk.utils.database.user.CurrentUserProviderNew import com.nextcloud.talk.utils.database.user.CurrentUserProviderNew
import com.nextcloud.talk.utils.permissions.PlatformPermissionUtil import com.nextcloud.talk.utils.permissions.PlatformPermissionUtil
import com.nextcloud.talk.utils.power.PowerManagerUtils import com.nextcloud.talk.utils.power.PowerManagerUtils
@ -234,7 +235,7 @@ class CallActivity : CallBaseActivity() {
private var iceServers: MutableList<PeerConnection.IceServer>? = null private var iceServers: MutableList<PeerConnection.IceServer>? = null
private var cameraEnumerator: CameraEnumerator? = null private var cameraEnumerator: CameraEnumerator? = null
private var roomToken: String? = null private var roomToken: String? = null
var conversationUser: User? = null lateinit var conversationUser: User
private var conversationName: String? = null private var conversationName: String? = null
private var callSession: String? = null private var callSession: String? = null
private var localStream: MediaStream? = null private var localStream: MediaStream? = null
@ -530,13 +531,13 @@ class CallActivity : CallBaseActivity() {
) )
} }
when (CapabilitiesUtilNew.getRecordingConsentType(conversationUser)) { when (CapabilitiesUtil.getRecordingConsentType(conversationUser!!.capabilities!!.spreedCapability!!)) {
CapabilitiesUtilNew.RECORDING_CONSENT_NOT_REQUIRED -> initiateCall() CapabilitiesUtil.RECORDING_CONSENT_NOT_REQUIRED -> initiateCall()
CapabilitiesUtilNew.RECORDING_CONSENT_REQUIRED -> askForRecordingConsent() CapabilitiesUtil.RECORDING_CONSENT_REQUIRED -> askForRecordingConsent()
CapabilitiesUtilNew.RECORDING_CONSENT_DEPEND_ON_CONVERSATION -> { CapabilitiesUtil.RECORDING_CONSENT_DEPEND_ON_CONVERSATION -> {
val getRoomApiVersion = ApiUtils.getConversationApiVersion( val getRoomApiVersion = ApiUtils.getConversationApiVersion(
conversationUser, conversationUser!!,
intArrayOf(ApiUtils.APIv4, 1) intArrayOf(ApiUtils.API_V4, 1)
) )
ncApi!!.getRoom(credentials, ApiUtils.getUrlForRoom(getRoomApiVersion, baseUrl, roomToken)) ncApi!!.getRoom(credentials, ApiUtils.getUrlForRoom(getRoomApiVersion, baseUrl, roomToken))
.retry(API_RETRIES) .retry(API_RETRIES)
@ -571,7 +572,10 @@ class CallActivity : CallBaseActivity() {
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
if (hasSpreedFeatureCapability(conversationUser, "recording-v1") && if (hasSpreedFeatureCapability(
conversationUser.capabilities!!.spreedCapability!!,
SpreedFeatures.RECORDING_V1
) &&
othersInCall && othersInCall &&
elapsedSeconds.toInt() >= CALL_TIME_ONE_HOUR elapsedSeconds.toInt() >= CALL_TIME_ONE_HOUR
) { ) {
@ -1468,7 +1472,7 @@ class CallActivity : CallBaseActivity() {
private fun fetchSignalingSettings() { private fun fetchSignalingSettings() {
Log.d(TAG, "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)) ncApi!!.getSignalingSettings(credentials, ApiUtils.getUrlForSignalingSettings(apiVersion, baseUrl))
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.retry(API_RETRIES) .retry(API_RETRIES)
@ -1531,7 +1535,7 @@ class CallActivity : CallBaseActivity() {
private fun addIceServers(signalingSettingsOverall: SignalingSettingsOverall, apiVersion: Int) { private fun addIceServers(signalingSettingsOverall: SignalingSettingsOverall, apiVersion: Int) {
if (signalingSettingsOverall.ocs!!.settings!!.stunServers != null) { if (signalingSettingsOverall.ocs!!.settings!!.stunServers != null) {
val stunServers = signalingSettingsOverall.ocs!!.settings!!.stunServers val stunServers = signalingSettingsOverall.ocs!!.settings!!.stunServers
if (apiVersion == ApiUtils.APIv3) { if (apiVersion == ApiUtils.API_V3) {
for ((_, urls) in stunServers!!) { for ((_, urls) in stunServers!!) {
if (urls != null) { if (urls != null) {
for (url in urls) { for (url in urls) {
@ -1564,7 +1568,7 @@ class CallActivity : CallBaseActivity() {
} }
private fun checkCapabilities() { private fun checkCapabilities() {
ncApi!!.getCapabilities(credentials, ApiUtils.getUrlForCapabilities(baseUrl)) ncApi!!.getCapabilities(credentials, ApiUtils.getUrlForCapabilities(baseUrl!!))
.retry(API_RETRIES) .retry(API_RETRIES)
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
@ -1600,7 +1604,7 @@ class CallActivity : CallBaseActivity() {
private fun joinRoomAndCall() { private fun joinRoomAndCall() {
callSession = ApplicationWideCurrentRoomHolder.getInstance().session 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, "joinRoomAndCall")
Log.d(TAG, " baseUrl= $baseUrl") Log.d(TAG, " baseUrl= $baseUrl")
Log.d(TAG, " roomToken= $roomToken") Log.d(TAG, " roomToken= $roomToken")
@ -1656,7 +1660,7 @@ class CallActivity : CallBaseActivity() {
fun getRoomAndContinue() { fun getRoomAndContinue() {
val getRoomApiVersion = ApiUtils.getConversationApiVersion( val getRoomApiVersion = ApiUtils.getConversationApiVersion(
conversationUser, conversationUser,
intArrayOf(ApiUtils.APIv4, 1) intArrayOf(ApiUtils.API_V4, 1)
) )
ncApi!!.getRoom(credentials, ApiUtils.getUrlForRoom(getRoomApiVersion, baseUrl, roomToken)) ncApi!!.getRoom(credentials, ApiUtils.getUrlForRoom(getRoomApiVersion, baseUrl, roomToken))
.retry(API_RETRIES) .retry(API_RETRIES)
@ -1715,10 +1719,10 @@ class CallActivity : CallBaseActivity() {
callParticipantList = CallParticipantList(signalingMessageReceiver) callParticipantList = CallParticipantList(signalingMessageReceiver)
callParticipantList!!.addObserver(callParticipantListObserver) callParticipantList!!.addObserver(callParticipantListObserver)
val apiVersion = ApiUtils.getCallApiVersion(conversationUser, intArrayOf(ApiUtils.APIv4, 1)) val apiVersion = ApiUtils.getCallApiVersion(conversationUser, intArrayOf(ApiUtils.API_V4, 1))
ncApi!!.joinCall( ncApi!!.joinCall(
credentials, credentials,
ApiUtils.getUrlForCall(apiVersion, baseUrl, roomToken), ApiUtils.getUrlForCall(apiVersion, baseUrl, roomToken!!),
inCallFlag, inCallFlag,
isCallWithoutNotification, isCallWithoutNotification,
recordingConsentGiven recordingConsentGiven
@ -1756,7 +1760,10 @@ class CallActivity : CallBaseActivity() {
} }
private fun startCallTimeCounter(callStartTime: Long?) { 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 binding!!.callDuration.visibility = View.VISIBLE
val currentTimeInSec = System.currentTimeMillis() / SECOND_IN_MILLIES val currentTimeInSec = System.currentTimeMillis() / SECOND_IN_MILLIES
elapsedSeconds = currentTimeInSec - callStartTime elapsedSeconds = currentTimeInSec - callStartTime
@ -1793,7 +1800,7 @@ class CallActivity : CallBaseActivity() {
} }
private fun pullSignalingMessages() { 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) val delayOnError = AtomicInteger(0)
ncApi!!.pullSignalingMessages( ncApi!!.pullSignalingMessages(
@ -1801,7 +1808,7 @@ class CallActivity : CallBaseActivity() {
ApiUtils.getUrlForSignaling( ApiUtils.getUrlForSignaling(
signalingApiVersion, signalingApiVersion,
baseUrl, baseUrl,
roomToken roomToken!!
) )
) )
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
@ -2031,12 +2038,12 @@ class CallActivity : CallBaseActivity() {
private fun hangupNetworkCalls(shutDownView: Boolean) { private fun hangupNetworkCalls(shutDownView: Boolean) {
Log.d(TAG, "hangupNetworkCalls. shutDownView=$shutDownView") 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) { if (callParticipantList != null) {
callParticipantList!!.removeObserver(callParticipantListObserver) callParticipantList!!.removeObserver(callParticipantListObserver)
callParticipantList!!.destroy() callParticipantList!!.destroy()
} }
ncApi!!.leaveCall(credentials, ApiUtils.getUrlForCall(apiVersion, baseUrl, roomToken)) ncApi!!.leaveCall(credentials, ApiUtils.getUrlForCall(apiVersion, baseUrl, roomToken!!))
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Observer<GenericOverall> { .subscribe(object : Observer<GenericOverall> {
@ -2919,10 +2926,10 @@ class CallActivity : CallBaseActivity() {
val strings: MutableList<String> = ArrayList() val strings: MutableList<String> = ArrayList()
val stringToSend = stringBuilder.toString() val stringToSend = stringBuilder.toString()
strings.add(stringToSend) 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( ncApi!!.sendSignalingMessages(
credentials, credentials,
ApiUtils.getUrlForSignaling(apiVersion, baseUrl, roomToken), ApiUtils.getUrlForSignaling(apiVersion, baseUrl, roomToken!!),
strings.toString() strings.toString()
) )
.retry(API_RETRIES) .retry(API_RETRIES)
@ -3099,12 +3106,14 @@ class CallActivity : CallBaseActivity() {
val isAllowedToStartOrStopRecording: Boolean val isAllowedToStartOrStopRecording: Boolean
get() = ( get() = (
isCallRecordingAvailable(conversationUser!!) && isCallRecordingAvailable(conversationUser!!.capabilities!!.spreedCapability!!) &&
isModerator isModerator
) )
val isAllowedToRaiseHand: Boolean val isAllowedToRaiseHand: Boolean
get() = hasSpreedFeatureCapability(conversationUser, "raise-hand") || get() = hasSpreedFeatureCapability(
isBreakoutRoom conversationUser.capabilities!!.spreedCapability!!,
SpreedFeatures.RAISE_HAND
) || isBreakoutRoom
private inner class SelfVideoTouchListener : OnTouchListener { private inner class SelfVideoTouchListener : OnTouchListener {
@SuppressLint("ClickableViewAccessibility") @SuppressLint("ClickableViewAccessibility")

View file

@ -181,7 +181,7 @@ class MainActivity : BaseActivity(), ActionBarProvider {
val user = userId.substringBeforeLast("@") val user = userId.substringBeforeLast("@")
val baseUrl = userId.substringAfterLast("@") val baseUrl = userId.substringAfterLast("@")
if (userManager.currentUser.blockingGet()?.baseUrl?.endsWith(baseUrl) == true) { if (userManager.currentUser.blockingGet()?.baseUrl!!.endsWith(baseUrl) == true) {
startConversation(user) startConversation(user)
} else { } else {
Snackbar.make( Snackbar.make(
@ -200,11 +200,11 @@ class MainActivity : BaseActivity(), ActionBarProvider {
val currentUser = userManager.currentUser.blockingGet() 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 credentials = ApiUtils.getCredentials(currentUser?.username, currentUser?.token)
val retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom( val retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(
apiVersion, apiVersion,
currentUser?.baseUrl, currentUser?.baseUrl!!,
roomType, roomType,
null, null,
userId, userId,

View file

@ -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.models.json.conversations.Conversation.ConversationType
import com.nextcloud.talk.ui.StatusDrawable import com.nextcloud.talk.ui.StatusDrawable
import com.nextcloud.talk.ui.theme.ViewThemeUtils import com.nextcloud.talk.ui.theme.ViewThemeUtils
import com.nextcloud.talk.utils.SpreedFeatures
import com.nextcloud.talk.utils.ConversationUtils import com.nextcloud.talk.utils.ConversationUtils
import com.nextcloud.talk.utils.DisplayUtils 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.FlexibleAdapter
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import eu.davidea.flexibleadapter.items.IFilterable import eu.davidea.flexibleadapter.items.IFilterable
@ -312,7 +313,7 @@ class ConversationItem(
if (model.type === ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL) { if (model.type === ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL) {
viewThemeUtils.material.colorChipBackground(holder.binding.dialogUnreadBubble) viewThemeUtils.material.colorChipBackground(holder.binding.dialogUnreadBubble)
} else if (model.unreadMention) { } else if (model.unreadMention) {
if (hasSpreedFeatureCapability(user, "direct-mention-flag")) { if (hasSpreedFeatureCapability(user.capabilities?.spreedCapability!!, SpreedFeatures.DIRECT_MENTION_FLAG)) {
if (model.unreadMentionDirect!!) { if (model.unreadMentionDirect!!) {
viewThemeUtils.material.colorChipBackground(holder.binding.dialogUnreadBubble) viewThemeUtils.material.colorChipBackground(holder.binding.dialogUnreadBubble)
} else { } else {

View file

@ -77,12 +77,12 @@ class CallStartedViewHolder(incomingView: View, payload: Any) :
val user = userManager.currentUser.blockingGet() val user = userManager.currentUser.blockingGet()
val url: String = if (message.actorType == "guests" || message.actorType == "guest") { val url: String = if (message.actorType == "guests" || message.actorType == "guest") {
ApiUtils.getUrlForGuestAvatar( ApiUtils.getUrlForGuestAvatar(
user!!.baseUrl, user!!.baseUrl!!,
message.actorDisplayName, message.actorDisplayName,
true true
) )
} else { } else {
ApiUtils.getUrlForAvatar(user!!.baseUrl, message.actorDisplayName, false) ApiUtils.getUrlForAvatar(user!!.baseUrl!!, message.actorDisplayName, false)
} }
val imageRequest: ImageRequest = ImageRequest.Builder(context) val imageRequest: ImageRequest = ImageRequest.Builder(context)

View file

@ -188,7 +188,7 @@ class IncomingLinkPreviewMessageViewHolder(incomingView: View, payload: Any) :
binding.messageQuote.quotedMessageImage.load(it) { binding.messageQuote.quotedMessageImage.load(it) {
addHeader( addHeader(
"Authorization", "Authorization",
ApiUtils.getCredentials(message.activeUser!!.username, message.activeUser!!.token) ApiUtils.getCredentials(message.activeUser!!.username, message.activeUser!!.token)!!
) )
} }
} ?: run { } ?: run {

View file

@ -172,7 +172,7 @@ class IncomingLocationMessageViewHolder(incomingView: View, payload: Any) :
binding.messageQuote.quotedMessageImage.load(it) { binding.messageQuote.quotedMessageImage.load(it) {
addHeader( addHeader(
"Authorization", "Authorization",
ApiUtils.getCredentials(message.activeUser!!.username, message.activeUser!!.token) ApiUtils.getCredentials(message.activeUser!!.username, message.activeUser!!.token)!!
) )
} }
} ?: run { } ?: run {

View file

@ -195,7 +195,7 @@ class IncomingPollMessageViewHolder(incomingView: View, payload: Any) :
binding.messageQuote.quotedMessageImage.load(it) { binding.messageQuote.quotedMessageImage.load(it) {
addHeader( addHeader(
"Authorization", "Authorization",
ApiUtils.getCredentials(message.activeUser!!.username, message.activeUser!!.token) ApiUtils.getCredentials(message.activeUser!!.username, message.activeUser!!.token)!!
) )
} }
} ?: run { } ?: run {

View file

@ -197,7 +197,7 @@ class IncomingTextMessageViewHolder(itemView: View, payload: Any) :
binding.messageQuote.quotedMessageImage.load(it) { binding.messageQuote.quotedMessageImage.load(it) {
addHeader( addHeader(
"Authorization", "Authorization",
ApiUtils.getCredentials(message.activeUser!!.username, message.activeUser!!.token) ApiUtils.getCredentials(message.activeUser!!.username, message.activeUser!!.token)!!
) )
} }
} ?: run { } ?: run {

View file

@ -301,7 +301,7 @@ class IncomingVoiceMessageViewHolder(incomingView: View, payload: Any) :
binding.messageQuote.quotedMessageImage.load(it) { binding.messageQuote.quotedMessageImage.load(it) {
addHeader( addHeader(
"Authorization", "Authorization",
ApiUtils.getCredentials(message.activeUser!!.username, message.activeUser!!.token) ApiUtils.getCredentials(message.activeUser!!.username, message.activeUser!!.token)!!
) )
} }
} ?: run { } ?: run {

View file

@ -45,8 +45,8 @@ class LinkPreview {
binding.referenceThumbImage.setImageDrawable(null) binding.referenceThumbImage.setImageDrawable(null)
if (!message.extractedUrlToPreview.isNullOrEmpty()) { if (!message.extractedUrlToPreview.isNullOrEmpty()) {
val credentials: String = ApiUtils.getCredentials(message.activeUser?.username, message.activeUser?.token) val credentials: String = ApiUtils.getCredentials(message.activeUser?.username, message.activeUser?.token)!!
val openGraphLink = ApiUtils.getUrlForOpenGraph(message.activeUser?.baseUrl) val openGraphLink = ApiUtils.getUrlForOpenGraph(message.activeUser?.baseUrl!!)
ncApi.getOpenGraph( ncApi.getOpenGraph(
credentials, credentials,
openGraphLink, openGraphLink,

View file

@ -161,7 +161,7 @@ class OutcomingLinkPreviewMessageViewHolder(outcomingView: View, payload: Any) :
binding.messageQuote.quotedMessageImage.load(it) { binding.messageQuote.quotedMessageImage.load(it) {
addHeader( addHeader(
"Authorization", "Authorization",
ApiUtils.getCredentials(message.activeUser!!.username, message.activeUser!!.token) ApiUtils.getCredentials(message.activeUser!!.username, message.activeUser!!.token)!!
) )
} }
} ?: run { } ?: run {

View file

@ -213,7 +213,7 @@ class OutcomingLocationMessageViewHolder(incomingView: View) :
binding.messageQuote.quotedMessageImage.load(it) { binding.messageQuote.quotedMessageImage.load(it) {
addHeader( addHeader(
"Authorization", "Authorization",
ApiUtils.getCredentials(message.activeUser!!.username, message.activeUser!!.token) ApiUtils.getCredentials(message.activeUser!!.username, message.activeUser!!.token)!!
) )
} }
} ?: run { } ?: run {

View file

@ -175,7 +175,7 @@ class OutcomingPollMessageViewHolder(outcomingView: View, payload: Any) :
binding.messageQuote.quotedMessageImage.load(it) { binding.messageQuote.quotedMessageImage.load(it) {
addHeader( addHeader(
"Authorization", "Authorization",
ApiUtils.getCredentials(message.activeUser!!.username, message.activeUser!!.token) ApiUtils.getCredentials(message.activeUser!!.username, message.activeUser!!.token)!!
) )
} }
} ?: run { } ?: run {

View file

@ -170,7 +170,7 @@ class OutcomingTextMessageViewHolder(itemView: View) : OutcomingTextMessageViewH
binding.messageQuote.quotedMessageImage.load(it) { binding.messageQuote.quotedMessageImage.load(it) {
addHeader( addHeader(
"Authorization", "Authorization",
ApiUtils.getCredentials(message.activeUser!!.username, message.activeUser!!.token) ApiUtils.getCredentials(message.activeUser!!.username, message.activeUser!!.token)!!
) )
} }
} ?: run { } ?: run {

View file

@ -285,7 +285,7 @@ class OutcomingVoiceMessageViewHolder(outcomingView: View) :
binding.messageQuote.quotedMessageImage.load(it) { binding.messageQuote.quotedMessageImage.load(it) {
addHeader( addHeader(
"Authorization", "Authorization",
ApiUtils.getCredentials(message.activeUser!!.username, message.activeUser!!.token) ApiUtils.getCredentials(message.activeUser!!.username, message.activeUser!!.token)!!
) )
} }
} ?: run { } ?: run {

View file

@ -24,6 +24,7 @@
package com.nextcloud.talk.api; package com.nextcloud.talk.api;
import com.nextcloud.talk.models.json.capabilities.CapabilitiesOverall; 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.ChatOverall;
import com.nextcloud.talk.models.json.chat.ChatOverallSingleMessage; import com.nextcloud.talk.models.json.chat.ChatOverallSingleMessage;
import com.nextcloud.talk.models.json.chat.ChatShareOverall; import com.nextcloud.talk.models.json.chat.ChatShareOverall;
@ -367,6 +368,10 @@ public interface NcApi {
@GET @GET
Observable<CapabilitiesOverall> getCapabilities(@Url String url); Observable<CapabilitiesOverall> getCapabilities(@Url String url);
@GET
Observable<RoomCapabilitiesOverall> getRoomCapabilities(@Header("Authorization") String authorization,
@Url String url);
/* /*
QueryMap items are as follows: QueryMap items are as follows:
- "lookIntoFuture": int (0 or 1), - "lookIntoFuture": int (0 or 1),

View file

@ -50,6 +50,7 @@ import com.nextcloud.talk.models.domain.ConversationType
import com.nextcloud.talk.models.json.participants.Participant import com.nextcloud.talk.models.json.participants.Participant
import com.nextcloud.talk.users.UserManager import com.nextcloud.talk.users.UserManager
import com.nextcloud.talk.utils.ApiUtils import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.SpreedFeatures
import com.nextcloud.talk.utils.ConversationUtils import com.nextcloud.talk.utils.ConversationUtils
import com.nextcloud.talk.utils.NotificationUtils import com.nextcloud.talk.utils.NotificationUtils
import com.nextcloud.talk.utils.ParticipantPermissions 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_CALL_VOICE_ONLY
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_CONVERSATION_NAME 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.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 io.reactivex.disposables.Disposable
import okhttp3.Cache import okhttp3.Cache
import java.io.IOException import java.io.IOException
@ -148,10 +149,10 @@ class CallNotificationActivity : CallBaseActivity() {
private fun initObservers() { private fun initObservers() {
val apiVersion = ApiUtils.getConversationApiVersion( val apiVersion = ApiUtils.getConversationApiVersion(
userBeingCalled, userBeingCalled!!,
intArrayOf( intArrayOf(
ApiUtils.APIv4, ApiUtils.API_V4,
ApiUtils.APIv3, ApiUtils.API_V3,
1 1
) )
) )
@ -186,10 +187,10 @@ class CallNotificationActivity : CallBaseActivity() {
showAnswerControls() showAnswerControls()
if (apiVersion >= ApiUtils.APIv3) { if (apiVersion >= ApiUtils.API_V3) {
val hasCallFlags = hasSpreedFeatureCapability( val hasCallFlags = hasSpreedFeatureCapability(
userBeingCalled, userBeingCalled?.capabilities?.spreedCapability!!,
"conversation-call-flags" SpreedFeatures.CONVERSATION_CALL_FLAGS
) )
if (hasCallFlags) { if (hasCallFlags) {
if (isInCallWithVideo(currentConversation!!.callFlag)) { if (isInCallWithVideo(currentConversation!!.callFlag)) {
@ -243,7 +244,7 @@ class CallNotificationActivity : CallBaseActivity() {
originalBundle!!.putString(KEY_CONVERSATION_NAME, currentConversation!!.displayName) originalBundle!!.putString(KEY_CONVERSATION_NAME, currentConversation!!.displayName)
val participantPermission = ParticipantPermissions( val participantPermission = ParticipantPermissions(
userBeingCalled!!, userBeingCalled!!.capabilities!!.spreedCapability!!,
currentConversation!! currentConversation!!
) )
originalBundle!!.putBoolean( originalBundle!!.putBoolean(

View file

@ -167,6 +167,7 @@ import com.nextcloud.talk.models.domain.ConversationReadOnlyState
import com.nextcloud.talk.models.domain.ConversationType import com.nextcloud.talk.models.domain.ConversationType
import com.nextcloud.talk.models.domain.LobbyState import com.nextcloud.talk.models.domain.LobbyState
import com.nextcloud.talk.models.domain.ObjectType 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.ChatMessage
import com.nextcloud.talk.models.json.chat.ChatOverall import com.nextcloud.talk.models.json.chat.ChatOverall
import com.nextcloud.talk.models.json.chat.ReadStatus 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.ui.recyclerview.MessageSwipeCallback
import com.nextcloud.talk.utils.ApiUtils import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.AudioUtils import com.nextcloud.talk.utils.AudioUtils
import com.nextcloud.talk.utils.SpreedFeatures
import com.nextcloud.talk.utils.ContactUtils import com.nextcloud.talk.utils.ContactUtils
import com.nextcloud.talk.utils.ConversationUtils import com.nextcloud.talk.utils.ConversationUtils
import com.nextcloud.talk.utils.DateConstants 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_ROOM_TOKEN
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_START_CALL_AFTER_ROOM_SWITCH 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.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.database.user.CurrentUserProviderNew
import com.nextcloud.talk.utils.permissions.PlatformPermissionUtil import com.nextcloud.talk.utils.permissions.PlatformPermissionUtil
import com.nextcloud.talk.utils.rx.DisposableSet import com.nextcloud.talk.utils.rx.DisposableSet
@ -301,6 +303,8 @@ class ChatActivity :
var sessionIdAfterRoomJoined: String? = null var sessionIdAfterRoomJoined: String? = null
lateinit var roomToken: String lateinit var roomToken: String
var conversationUser: User? = null var conversationUser: User? = null
lateinit var spreedCapabilities: SpreedCapability
var chatApiVersion: Int = 1
private var roomPassword: String = "" private var roomPassword: String = ""
var credentials: String? = null var credentials: String? = null
var currentConversation: ConversationModel? = null var currentConversation: ConversationModel? = null
@ -351,6 +355,7 @@ class ChatActivity :
RELEASED, RELEASED,
ERROR ERROR
} }
private val editableBehaviorSubject = BehaviorSubject.createDefault(false) private val editableBehaviorSubject = BehaviorSubject.createDefault(false)
private val editedTextBehaviorSubject = BehaviorSubject.createDefault("") private val editedTextBehaviorSubject = BehaviorSubject.createDefault("")
@ -541,14 +546,6 @@ class ChatActivity :
} }
this.lifecycle.addObserver(AudioUtils) this.lifecycle.addObserver(AudioUtils)
this.lifecycle.addObserver(ChatViewModel.LifeCycleObserver) this.lifecycle.addObserver(ChatViewModel.LifeCycleObserver)
chatViewModel.refreshChatParams(
setupFieldsForPullChatMessages(
false,
0,
false
)
)
} }
override fun onStop() { override fun onStop() {
@ -587,6 +584,30 @@ class ChatActivity :
is ChatViewModel.GetRoomSuccessState -> { is ChatViewModel.GetRoomSuccessState -> {
currentConversation = state.conversationModel currentConversation = state.conversationModel
logConversationInfos("GetRoomSuccessState") 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) { if (adapter == null) {
initAdapter() initAdapter()
@ -597,7 +618,7 @@ class ChatActivity :
loadAvatarForStatusBar() loadAvatarForStatusBar()
setActionBarTitle() setActionBarTitle()
participantPermissions = ParticipantPermissions(conversationUser!!, currentConversation!!) participantPermissions = ParticipantPermissions(spreedCapabilities, currentConversation!!)
setupSwipeToReply() setupSwipeToReply()
setupMentionAutocomplete() setupMentionAutocomplete()
@ -626,9 +647,17 @@ class ChatActivity :
}, },
delayForRecursiveCall 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() Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show()
} }
@ -716,6 +745,7 @@ class ChatActivity :
} }
binding.messagesListView.smoothScrollToPosition(0) binding.messagesListView.smoothScrollToPosition(0)
} }
is ChatViewModel.SendChatMessageErrorState -> { is ChatViewModel.SendChatMessageErrorState -> {
if (state.e is HttpException) { if (state.e is HttpException) {
val code = state.e.code() val code = state.e.code()
@ -730,6 +760,7 @@ class ChatActivity :
} }
} }
} }
else -> {} else -> {}
} }
} }
@ -753,9 +784,11 @@ class ChatActivity :
) )
) )
} }
is ChatViewModel.DeleteChatMessageErrorState -> { is ChatViewModel.DeleteChatMessageErrorState -> {
Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show() Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show()
} }
else -> {} else -> {}
} }
} }
@ -774,24 +807,22 @@ class ChatActivity :
startActivity(chatIntent) startActivity(chatIntent)
} }
} }
is ChatViewModel.CreateRoomErrorState -> { is ChatViewModel.CreateRoomErrorState -> {
Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show() Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show()
} }
else -> {} else -> {}
} }
} }
var apiVersion = 1 chatViewModel.getFieldMapForChat.observe(this) { fieldMap ->
// FIXME this is a best guess, guests would need to get the capabilities themselves if (fieldMap.isNotEmpty()) {
if (conversationUser != null) { chatViewModel.pullChatMessages(
apiVersion = ApiUtils.getChatApiVersion(conversationUser, intArrayOf(1)) credentials!!,
} ApiUtils.getUrlForChat(chatApiVersion, conversationUser?.baseUrl, roomToken)
)
chatViewModel.getFieldMapForChat.observe(this) { _ -> }
chatViewModel.pullChatMessages(
credentials!!,
ApiUtils.getUrlForChat(apiVersion, conversationUser?.baseUrl, roomToken)
)
} }
chatViewModel.pullChatMessageViewState.observe(this) { state -> chatViewModel.pullChatMessageViewState.observe(this) { state ->
@ -865,6 +896,7 @@ class ChatActivity :
) )
) )
} }
HTTP_CODE_NOT_MODIFIED -> { HTTP_CODE_NOT_MODIFIED -> {
processHeaderChatLastGiven(state.response, state.lookIntoFuture) processHeaderChatLastGiven(state.response, state.lookIntoFuture)
chatViewModel.refreshChatParams( chatViewModel.refreshChatParams(
@ -875,6 +907,7 @@ class ChatActivity :
) )
) )
} }
HTTP_CODE_PRECONDITION_FAILED -> { HTTP_CODE_PRECONDITION_FAILED -> {
processHeaderChatLastGiven(state.response, state.lookIntoFuture) processHeaderChatLastGiven(state.response, state.lookIntoFuture)
chatViewModel.refreshChatParams( chatViewModel.refreshChatParams(
@ -885,6 +918,7 @@ class ChatActivity :
) )
) )
} }
else -> {} else -> {}
} }
@ -898,12 +932,15 @@ class ChatActivity :
collapseSystemMessages() collapseSystemMessages()
} }
} }
is ChatViewModel.PullChatMessageCompleteState -> { is ChatViewModel.PullChatMessageCompleteState -> {
Log.d(TAG, "PullChatMessageCompleted") Log.d(TAG, "PullChatMessageCompleted")
} }
is ChatViewModel.PullChatMessageErrorState -> { is ChatViewModel.PullChatMessageErrorState -> {
Log.d(TAG, "PullChatMessageError") Log.d(TAG, "PullChatMessageError")
} }
else -> {} else -> {}
} }
} }
@ -916,6 +953,7 @@ class ChatActivity :
state.reactionDeletedModel.emoji state.reactionDeletedModel.emoji
) )
} }
else -> {} else -> {}
} }
} }
@ -928,6 +966,7 @@ class ChatActivity :
state.reactionAddedModel.emoji state.reactionAddedModel.emoji
) )
} }
else -> {} else -> {}
} }
} }
@ -943,6 +982,7 @@ class ChatActivity :
Snackbar.LENGTH_LONG Snackbar.LENGTH_LONG
).show() ).show()
} }
HTTP_FORBIDDEN -> { HTTP_FORBIDDEN -> {
Snackbar.make( Snackbar.make(
binding.root, binding.root,
@ -950,6 +990,7 @@ class ChatActivity :
Snackbar.LENGTH_LONG Snackbar.LENGTH_LONG
).show() ).show()
} }
HTTP_NOT_FOUND -> { HTTP_NOT_FOUND -> {
Snackbar.make( Snackbar.make(
binding.root, binding.root,
@ -960,9 +1001,11 @@ class ChatActivity :
} }
clearEditUI() clearEditUI()
} }
is ChatViewModel.EditMessageErrorState -> { is ChatViewModel.EditMessageErrorState -> {
Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show() Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show()
} }
else -> {} else -> {}
} }
} }
@ -980,12 +1023,6 @@ class ChatActivity :
webSocketInstance?.getSignalingMessageReceiver()?.addListener(localParticipantMessageListener) webSocketInstance?.getSignalingMessageReceiver()?.addListener(localParticipantMessageListener)
webSocketInstance?.getSignalingMessageReceiver()?.addListener(conversationMessageListener) webSocketInstance?.getSignalingMessageReceiver()?.addListener(conversationMessageListener)
if (conversationUser?.userId != "?" &&
CapabilitiesUtilNew.hasSpreedFeatureCapability(conversationUser, "mention-flag")
) {
binding.chatToolbar.setOnClickListener { v -> showConversationInfoScreen() }
}
initSmileyKeyboardToggler() initSmileyKeyboardToggler()
themeMessageInputView() themeMessageInputView()
@ -1053,7 +1090,6 @@ class ChatActivity :
} }
}) })
initMessageInputView()
loadAvatarForStatusBar() loadAvatarForStatusBar()
setActionBarTitle() setActionBarTitle()
viewThemeUtils.material.colorToolbarOverflowIcon(binding.chatToolbar) viewThemeUtils.material.colorToolbarOverflowIcon(binding.chatToolbar)
@ -1061,7 +1097,7 @@ class ChatActivity :
private fun initMessageInputView() { private fun initMessageInputView() {
val filters = arrayOfNulls<InputFilter>(1) val filters = arrayOfNulls<InputFilter>(1)
val lengthFilter = CapabilitiesUtilNew.getMessageMaxLength(conversationUser) val lengthFilter = CapabilitiesUtil.getMessageMaxLength(spreedCapabilities)
binding.editView.editMessageView.visibility = View.GONE binding.editView.editMessageView.visibility = View.GONE
@ -1160,7 +1196,7 @@ class ChatActivity :
clearEditUI() clearEditUI()
} }
if (CapabilitiesUtilNew.hasSpreedFeatureCapability(conversationUser, "silent-send")) { if (CapabilitiesUtil.hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.SILENT_SEND)) {
binding.messageInputView.button?.setOnLongClickListener { binding.messageInputView.button?.setOnLongClickListener {
showSendButtonMenu() showSendButtonMenu()
true true
@ -1175,14 +1211,14 @@ class ChatActivity :
var apiVersion = 1 var apiVersion = 1
// FIXME Fix API checking with guests? // FIXME Fix API checking with guests?
if (conversationUser != null) { if (conversationUser != null) {
apiVersion = ApiUtils.getChatApiVersion(conversationUser, intArrayOf(1)) apiVersion = ApiUtils.getChatApiVersion(spreedCapabilities, intArrayOf(1))
} }
chatViewModel.editChatMessage( chatViewModel.editChatMessage(
credentials!!, credentials!!,
ApiUtils.getUrlForChatMessage( ApiUtils.getUrlForChatMessage(
apiVersion, apiVersion,
conversationUser?.baseUrl, conversationUser?.baseUrl!!,
roomToken, roomToken,
message.id message.id
), ),
@ -2016,7 +2052,7 @@ class ChatActivity :
private fun isTypingStatusEnabled(): Boolean { private fun isTypingStatusEnabled(): Boolean {
return webSocketInstance != null && return webSocketInstance != null &&
!CapabilitiesUtilNew.isTypingStatusPrivate(conversationUser!!) !CapabilitiesUtil.isTypingStatusPrivate(conversationUser!!)
} }
private fun setupSwipeToReply() { private fun setupSwipeToReply() {
@ -2048,7 +2084,7 @@ class ChatActivity :
if (isOneToOneConversation()) { if (isOneToOneConversation()) {
var url = ApiUtils.getUrlForAvatar( var url = ApiUtils.getUrlForAvatar(
conversationUser!!.baseUrl, conversationUser!!.baseUrl!!,
currentConversation!!.name, currentConversation!!.name,
true true
) )
@ -2097,18 +2133,19 @@ class ChatActivity :
} }
val credentials = ApiUtils.getCredentials(conversationUser!!.username, conversationUser!!.token) val credentials = ApiUtils.getCredentials(conversationUser!!.username, conversationUser!!.token)
if (credentials != null) {
context.imageLoader.enqueue( context.imageLoader.enqueue(
ImageRequest.Builder(context) ImageRequest.Builder(context)
.data(url) .data(url)
.addHeader("Authorization", credentials) .addHeader("Authorization", credentials)
.transformations(CircleCropTransformation()) .transformations(CircleCropTransformation())
.crossfade(true) .crossfade(true)
.target(target) .target(target)
.memoryCachePolicy(CachePolicy.DISABLED) .memoryCachePolicy(CachePolicy.DISABLED)
.diskCachePolicy(CachePolicy.DISABLED) .diskCachePolicy(CachePolicy.DISABLED)
.build() .build()
) )
}
} else { } else {
binding.chatToolbar.findViewById<FrameLayout>(R.id.chat_toolbar_avatar_container).visibility = View.GONE binding.chatToolbar.findViewById<FrameLayout>(R.id.chat_toolbar_avatar_container).visibility = View.GONE
} }
@ -2463,7 +2500,10 @@ class ChatActivity :
val baseUrl = message.activeUser!!.baseUrl val baseUrl = message.activeUser!!.baseUrl
val userId = message.activeUser!!.userId val userId = message.activeUser!!.userId
val attachmentFolder = CapabilitiesUtilNew.getAttachmentFolder(message.activeUser!!) val attachmentFolder = CapabilitiesUtil.getAttachmentFolder(
message.activeUser!!.capabilities!!
.spreedCapability!!
)
val fileName = message.selectedIndividualHashMap!!["name"] val fileName = message.selectedIndividualHashMap!!["name"]
var size = message.selectedIndividualHashMap!!["size"] var size = message.selectedIndividualHashMap!!["size"]
if (size == null) { if (size == null) {
@ -2801,16 +2841,16 @@ class ChatActivity :
private fun shouldShowLobby(): Boolean { private fun shouldShowLobby(): Boolean {
if (currentConversation != null) { if (currentConversation != null) {
return CapabilitiesUtilNew.hasSpreedFeatureCapability(conversationUser, "webinary-lobby") && return CapabilitiesUtil.hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.WEBINARY_LOBBY) &&
currentConversation?.lobbyState == LobbyState.LOBBY_STATE_MODERATORS_ONLY && currentConversation?.lobbyState == LobbyState.LOBBY_STATE_MODERATORS_ONLY &&
!ConversationUtils.canModerate(currentConversation!!, conversationUser!!) && !ConversationUtils.canModerate(currentConversation!!, spreedCapabilities) &&
!participantPermissions.canIgnoreLobby() !participantPermissions.canIgnoreLobby()
} }
return false return false
} }
private fun disableCallButtons() { private fun disableCallButtons() {
if (CapabilitiesUtilNew.isAbleToCall(conversationUser)) { if (CapabilitiesUtil.isAbleToCall(spreedCapabilities)) {
if (conversationVoiceCallMenuItem != null && conversationVideoMenuItem != null) { if (conversationVoiceCallMenuItem != null && conversationVideoMenuItem != null) {
conversationVoiceCallMenuItem?.icon?.alpha = SEMI_TRANSPARENT_INT conversationVoiceCallMenuItem?.icon?.alpha = SEMI_TRANSPARENT_INT
conversationVideoMenuItem?.icon?.alpha = SEMI_TRANSPARENT_INT conversationVideoMenuItem?.icon?.alpha = SEMI_TRANSPARENT_INT
@ -2823,7 +2863,7 @@ class ChatActivity :
} }
private fun enableCallButtons() { private fun enableCallButtons() {
if (CapabilitiesUtilNew.isAbleToCall(conversationUser)) { if (CapabilitiesUtil.isAbleToCall(spreedCapabilities)) {
if (conversationVoiceCallMenuItem != null && conversationVideoMenuItem != null) { if (conversationVoiceCallMenuItem != null && conversationVideoMenuItem != null) {
conversationVoiceCallMenuItem?.icon?.alpha = FULLY_OPAQUE_INT conversationVoiceCallMenuItem?.icon?.alpha = FULLY_OPAQUE_INT
conversationVideoMenuItem?.icon?.alpha = FULLY_OPAQUE_INT conversationVideoMenuItem?.icon?.alpha = FULLY_OPAQUE_INT
@ -2843,7 +2883,7 @@ class ChatActivity :
private fun checkLobbyState() { private fun checkLobbyState() {
if (currentConversation != null && if (currentConversation != null &&
ConversationUtils.isLobbyViewApplicable(currentConversation!!, conversationUser!!) ConversationUtils.isLobbyViewApplicable(currentConversation!!, spreedCapabilities)
) { ) {
if (shouldShowLobby()) { if (shouldShowLobby()) {
binding.lobby.lobbyView.visibility = View.VISIBLE binding.lobby.lobbyView.visibility = View.VISIBLE
@ -3252,6 +3292,7 @@ class ChatActivity :
val intent = Intent(this, LocationPickerActivity::class.java) val intent = Intent(this, LocationPickerActivity::class.java)
intent.putExtra(KEY_ROOM_TOKEN, roomToken) intent.putExtra(KEY_ROOM_TOKEN, roomToken)
intent.putExtra(BundleKeys.KEY_CHAT_API_VERSION, chatApiVersion)
startActivity(intent) startActivity(intent)
} }
@ -3270,7 +3311,7 @@ class ChatActivity :
val elevation = MENTION_AUTO_COMPLETE_ELEVATION val elevation = MENTION_AUTO_COMPLETE_ELEVATION
resources?.let { resources?.let {
val backgroundDrawable = ColorDrawable(it.getColor(R.color.bg_default, null)) val backgroundDrawable = ColorDrawable(it.getColor(R.color.bg_default, null))
val presenter = MentionAutocompletePresenter(this, roomToken) val presenter = MentionAutocompletePresenter(this, roomToken, chatApiVersion)
val callback = MentionAutocompleteCallback( val callback = MentionAutocompleteCallback(
this, this,
conversationUser!!, conversationUser!!,
@ -3430,12 +3471,6 @@ class ChatActivity :
if (!validSessionId()) { if (!validSessionId()) {
Log.d(TAG, "sessionID was not valid -> joinRoom") 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() val startNanoTime = System.nanoTime()
Log.d(TAG, "joinRoomWithPassword - joinRoom - calling: $startNanoTime") Log.d(TAG, "joinRoomWithPassword - joinRoom - calling: $startNanoTime")
@ -3458,7 +3493,7 @@ class ChatActivity :
var apiVersion = 1 var apiVersion = 1
// FIXME Fix API checking with guests? // FIXME Fix API checking with guests?
if (conversationUser != null) { if (conversationUser != null) {
apiVersion = ApiUtils.getConversationApiVersion(conversationUser, intArrayOf(ApiUtils.APIv4, 1)) apiVersion = ApiUtils.getConversationApiVersion(conversationUser!!, intArrayOf(ApiUtils.API_V4, 1))
} }
val startNanoTime = System.nanoTime() val startNanoTime = System.nanoTime()
@ -3467,7 +3502,7 @@ class ChatActivity :
credentials!!, credentials!!,
ApiUtils.getUrlForParticipantsActive( ApiUtils.getUrlForParticipantsActive(
apiVersion, apiVersion,
conversationUser?.baseUrl, conversationUser?.baseUrl!!,
roomToken roomToken
), ),
funToCallWhenLeaveSuccessful funToCallWhenLeaveSuccessful
@ -3513,11 +3548,9 @@ class ChatActivity :
private fun sendMessage(message: CharSequence, replyTo: Int?, sendWithoutNotification: Boolean) { private fun sendMessage(message: CharSequence, replyTo: Int?, sendWithoutNotification: Boolean) {
if (conversationUser != null) { if (conversationUser != null) {
val apiVersion = ApiUtils.getChatApiVersion(conversationUser, intArrayOf(1))
chatViewModel.sendChatMessage( chatViewModel.sendChatMessage(
credentials!!, credentials!!,
ApiUtils.getUrlForChat(apiVersion, conversationUser!!.baseUrl, roomToken), ApiUtils.getUrlForChat(chatApiVersion, conversationUser!!.baseUrl!!, roomToken),
message, message,
conversationUser!!.displayName ?: "", conversationUser!!.displayName ?: "",
replyTo ?: 0, replyTo ?: 0,
@ -3637,7 +3670,7 @@ class ChatActivity :
} }
} }
if (CapabilitiesUtilNew.hasSpreedFeatureCapability(conversationUser, "message-expiration")) { if (CapabilitiesUtil.hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.MESSAGE_EXPIRATION)) {
deleteExpiredMessages() deleteExpiredMessages()
} }
} }
@ -3871,53 +3904,58 @@ class ChatActivity :
if (conversationUser?.userId == "?") { if (conversationUser?.userId == "?") {
menu.removeItem(R.id.conversation_info) menu.removeItem(R.id.conversation_info)
} else { } 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() loadAvatarForStatusBar()
setActionBarTitle() 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<View?>(R.id.conversation_voice_call)?.setOnLongClickListener {
showCallButtonMenu(true)
true
}
}
Handler().post {
findViewById<View?>(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 return true
} }
override fun onPrepareOptionsMenu(menu: Menu): Boolean { override fun onPrepareOptionsMenu(menu: Menu): Boolean {
super.onPrepareOptionsMenu(menu) 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() checkShowCallButtons()
} }
val searchItem = menu.findItem(R.id.conversation_search) 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<View?>(R.id.conversation_voice_call)?.setOnLongClickListener {
showCallButtonMenu(true)
true
}
}
Handler().post {
findViewById<View?>(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 return true
} }
@ -4054,7 +4092,7 @@ class ChatActivity :
private fun startACall(isVoiceOnlyCall: Boolean, callWithoutNotification: Boolean) { private fun startACall(isVoiceOnlyCall: Boolean, callWithoutNotification: Boolean) {
currentConversation?.let { currentConversation?.let {
if (conversationUser != null) { if (conversationUser != null) {
val pp = ParticipantPermissions(conversationUser!!, it) val pp = ParticipantPermissions(spreedCapabilities, it)
if (!pp.canStartCall() && currentConversation?.hasCall == false) { if (!pp.canStartCall() && currentConversation?.hasCall == false) {
Snackbar.make(binding.root, R.string.startCallForbidden, Snackbar.LENGTH_LONG).show() Snackbar.make(binding.root, R.string.startCallForbidden, Snackbar.LENGTH_LONG).show()
} else { } else {
@ -4074,7 +4112,7 @@ class ChatActivity :
bundle.putString(KEY_ROOM_TOKEN, roomToken) bundle.putString(KEY_ROOM_TOKEN, roomToken)
bundle.putString(KEY_ROOM_ID, roomId) bundle.putString(KEY_ROOM_ID, roomId)
bundle.putString(BundleKeys.KEY_CONVERSATION_PASSWORD, roomPassword) 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.putString(KEY_CONVERSATION_NAME, it.displayName)
bundle.putInt(KEY_RECORDING_STATE, it.callRecording) bundle.putInt(KEY_RECORDING_STATE, it.callRecording)
bundle.putBoolean(KEY_IS_MODERATOR, ConversationUtils.isParticipantOwnerOrModerator(it)) bundle.putBoolean(KEY_IS_MODERATOR, ConversationUtils.isParticipantOwnerOrModerator(it))
@ -4147,7 +4185,8 @@ class ChatActivity :
conversationUser, conversationUser,
currentConversation, currentConversation,
isShowMessageDeletionButton(message), isShowMessageDeletionButton(message),
participantPermissions.hasChatPermission() participantPermissions.hasChatPermission(),
spreedCapabilities
).show() ).show()
} }
} }
@ -4156,7 +4195,7 @@ class ChatActivity :
return ChatMessage.MessageType.SYSTEM_MESSAGE == message.getCalculateMessageType() return ChatMessage.MessageType.SYSTEM_MESSAGE == message.getCalculateMessageType()
} }
fun deleteMessage(message: IMessage?) { fun deleteMessage(message: IMessage) {
if (!participantPermissions.hasChatPermission()) { if (!participantPermissions.hasChatPermission()) {
Log.w( Log.w(
TAG, TAG,
@ -4168,28 +4207,28 @@ class ChatActivity :
var apiVersion = 1 var apiVersion = 1
// FIXME Fix API checking with guests? // FIXME Fix API checking with guests?
if (conversationUser != null) { if (conversationUser != null) {
apiVersion = ApiUtils.getChatApiVersion(conversationUser, intArrayOf(1)) apiVersion = ApiUtils.getChatApiVersion(spreedCapabilities, intArrayOf(1))
} }
chatViewModel.deleteChatMessages( chatViewModel.deleteChatMessages(
credentials!!, credentials!!,
ApiUtils.getUrlForChatMessage( ApiUtils.getUrlForChatMessage(
apiVersion, apiVersion,
conversationUser?.baseUrl, conversationUser?.baseUrl!!,
roomToken, roomToken,
message?.id message.id!!
), ),
message?.id!! message.id!!
) )
} }
} }
fun replyPrivately(message: IMessage?) { fun replyPrivately(message: IMessage?) {
val apiVersion = val apiVersion =
ApiUtils.getConversationApiVersion(conversationUser, intArrayOf(ApiUtils.APIv4, 1)) ApiUtils.getConversationApiVersion(conversationUser!!, intArrayOf(ApiUtils.API_V4, 1))
val retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom( val retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(
apiVersion, apiVersion,
conversationUser?.baseUrl, conversationUser?.baseUrl!!,
"1", "1",
null, null,
message?.user?.id?.substring(INVITE_LENGTH), message?.user?.id?.substring(INVITE_LENGTH),
@ -4215,10 +4254,14 @@ class ChatActivity :
fun remindMeLater(message: ChatMessage?) { fun remindMeLater(message: ChatMessage?) {
Log.d(TAG, "remindMeLater called") Log.d(TAG, "remindMeLater called")
val chatApiVersion = ApiUtils.getChatApiVersion(spreedCapabilities, intArrayOf(ApiUtils.API_V1, 1))
val newFragment: DialogFragment = DateTimePickerFragment.newInstance( val newFragment: DialogFragment = DateTimePickerFragment.newInstance(
roomToken, roomToken,
message!!.id, message!!.id,
chatViewModel chatViewModel,
chatApiVersion
) )
newFragment.show(supportFragmentManager, DateTimePickerFragment.TAG) newFragment.show(supportFragmentManager, DateTimePickerFragment.TAG)
} }
@ -4229,8 +4272,8 @@ class ChatActivity :
chatViewModel.setChatReadMarker( chatViewModel.setChatReadMarker(
credentials!!, credentials!!,
ApiUtils.getUrlForChatReadMarker( ApiUtils.getUrlForChatReadMarker(
ApiUtils.getChatApiVersion(conversationUser, intArrayOf(ApiUtils.APIv1)), ApiUtils.getChatApiVersion(spreedCapabilities, intArrayOf(ApiUtils.API_V1)),
conversationUser?.baseUrl, conversationUser?.baseUrl!!,
roomToken roomToken
), ),
chatMessage.previousMessageId chatMessage.previousMessageId
@ -4312,10 +4355,10 @@ class ChatActivity :
} }
fun shareToNotes(message: ChatMessage, roomToken: String) { fun shareToNotes(message: ChatMessage, roomToken: String) {
val apiVersion = ApiUtils.getChatApiVersion(conversationUser, intArrayOf(1)) val apiVersion = ApiUtils.getChatApiVersion(spreedCapabilities, intArrayOf(1))
val type = message.getCalculateMessageType() val type = message.getCalculateMessageType()
var shareUri: Uri? = null var shareUri: Uri? = null
var data: HashMap<String?, String?>? val data: HashMap<String?, String?>?
var metaData: String = "" var metaData: String = ""
var objectId: String = "" var objectId: String = ""
if (message.hasFileAttachment()) { if (message.hasFileAttachment()) {
@ -4335,9 +4378,9 @@ class ChatActivity :
} else if (message.hasGeoLocation()) { } else if (message.hasGeoLocation()) {
data = message.messageParameters?.get("object") data = message.messageParameters?.get("object")
objectId = data?.get("id")!! objectId = data?.get("id")!!
val name = data.get("name")!! val name = data["name"]!!
val lat = data.get("latitude")!! val lat = data["latitude"]!!
val lon = data.get("longitude")!! val lon = data["longitude"]!!
metaData = metaData =
"{\"type\":\"geo-location\",\"id\":\"geo:$lat,$lon\",\"latitude\":\"$lat\"," + "{\"type\":\"geo-location\",\"id\":\"geo:$lat,$lon\",\"latitude\":\"$lat\"," +
"\"longitude\":\"$lon\",\"name\":\"$name\"}" "\"longitude\":\"$lon\",\"name\":\"$name\"}"
@ -4348,6 +4391,7 @@ class ChatActivity :
uploadFile(shareUri.toString(), true, token = roomToken) uploadFile(shareUri.toString(), true, token = roomToken)
Snackbar.make(binding.root, R.string.nc_message_sent, Snackbar.LENGTH_SHORT).show() Snackbar.make(binding.root, R.string.nc_message_sent, Snackbar.LENGTH_SHORT).show()
} }
ChatMessage.MessageType.SINGLE_NC_ATTACHMENT_MESSAGE -> { ChatMessage.MessageType.SINGLE_NC_ATTACHMENT_MESSAGE -> {
val caption = if (message.message != "{file}") message.message else "" val caption = if (message.message != "{file}") message.message else ""
if (null != shareUri) { if (null != shareUri) {
@ -4364,25 +4408,28 @@ class ChatActivity :
} }
} }
} }
ChatMessage.MessageType.SINGLE_NC_GEOLOCATION_MESSAGE -> { ChatMessage.MessageType.SINGLE_NC_GEOLOCATION_MESSAGE -> {
chatViewModel.shareLocationToNotes( chatViewModel.shareLocationToNotes(
credentials!!, credentials!!,
ApiUtils.getUrlToSendLocation(apiVersion, conversationUser!!.baseUrl, roomToken), ApiUtils.getUrlToSendLocation(apiVersion, conversationUser!!.baseUrl!!, roomToken),
"geo-location", "geo-location",
objectId, objectId,
metaData metaData
) )
Snackbar.make(binding.root, R.string.nc_message_sent, Snackbar.LENGTH_SHORT).show() Snackbar.make(binding.root, R.string.nc_message_sent, Snackbar.LENGTH_SHORT).show()
} }
ChatMessage.MessageType.REGULAR_TEXT_MESSAGE -> { ChatMessage.MessageType.REGULAR_TEXT_MESSAGE -> {
chatViewModel.shareToNotes( chatViewModel.shareToNotes(
credentials!!, credentials!!,
ApiUtils.getUrlForChat(apiVersion, conversationUser!!.baseUrl, roomToken), ApiUtils.getUrlForChat(apiVersion, conversationUser!!.baseUrl!!, roomToken),
message.message!!, message.message!!,
conversationUser!!.displayName!! conversationUser!!.displayName!!
) )
Snackbar.make(binding.root, R.string.nc_message_sent, Snackbar.LENGTH_SHORT).show() Snackbar.make(binding.root, R.string.nc_message_sent, Snackbar.LENGTH_SHORT).show()
} }
else -> {} else -> {}
} }
} }
@ -4456,7 +4503,10 @@ class ChatActivity :
} }
private fun showMicrophoneButton(show: Boolean) { 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") Log.d(TAG, "Microphone shown")
binding.messageInputView.messageSendButton.visibility = View.GONE binding.messageInputView.messageSendButton.visibility = View.GONE
binding.messageInputView.recordAudioButton.visibility = View.VISIBLE binding.messageInputView.recordAudioButton.visibility = View.VISIBLE
@ -4542,7 +4592,7 @@ class ChatActivity :
!isUserAllowedByPrivileges -> false !isUserAllowedByPrivileges -> false
message.systemMessageType != ChatMessage.SystemMessageType.DUMMY -> false message.systemMessageType != ChatMessage.SystemMessageType.DUMMY -> false
message.isDeleted -> false message.isDeleted -> false
!CapabilitiesUtilNew.hasSpreedFeatureCapability(conversationUser, "delete-messages") -> false !CapabilitiesUtil.hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.DELETE_MESSAGES) -> false
!participantPermissions.hasChatPermission() -> false !participantPermissions.hasChatPermission() -> false
else -> true else -> true
} }
@ -4554,7 +4604,7 @@ class ChatActivity :
val isUserAllowedByPrivileges = if (message.actorId == conversationUser!!.userId) { val isUserAllowedByPrivileges = if (message.actorId == conversationUser!!.userId) {
true true
} else { } else {
ConversationUtils.canModerate(currentConversation!!, conversationUser!!) ConversationUtils.canModerate(currentConversation!!, spreedCapabilities)
} }
return isUserAllowedByPrivileges return isUserAllowedByPrivileges
} }
@ -4628,12 +4678,12 @@ class ChatActivity :
var apiVersion = 1 var apiVersion = 1
// FIXME Fix API checking with guests? // FIXME Fix API checking with guests?
if (conversationUser != null) { if (conversationUser != null) {
apiVersion = ApiUtils.getConversationApiVersion(conversationUser, intArrayOf(ApiUtils.APIv4, 1)) apiVersion = ApiUtils.getConversationApiVersion(conversationUser!!, intArrayOf(ApiUtils.API_V4, 1))
} }
val retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom( val retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(
apiVersion, apiVersion,
conversationUser?.baseUrl, conversationUser?.baseUrl!!,
"1", "1",
null, null,
userMentionClickEvent.userId, userMentionClickEvent.userId,

View file

@ -22,6 +22,7 @@ package com.nextcloud.talk.chat.data
import com.nextcloud.talk.data.user.model.User import com.nextcloud.talk.data.user.model.User
import com.nextcloud.talk.models.domain.ConversationModel 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.chat.ChatOverallSingleMessage
import com.nextcloud.talk.models.json.conversations.RoomOverall import com.nextcloud.talk.models.json.conversations.RoomOverall
import com.nextcloud.talk.models.json.conversations.RoomsOverall import com.nextcloud.talk.models.json.conversations.RoomsOverall
@ -33,10 +34,17 @@ import retrofit2.Response
@Suppress("LongParameterList", "TooManyFunctions") @Suppress("LongParameterList", "TooManyFunctions")
interface ChatRepository { interface ChatRepository {
fun getRoom(user: User, roomToken: String): Observable<ConversationModel> fun getRoom(user: User, roomToken: String): Observable<ConversationModel>
fun getCapabilities(user: User, roomToken: String): Observable<SpreedCapability>
fun joinRoom(user: User, roomToken: String, roomPassword: String): Observable<ConversationModel> fun joinRoom(user: User, roomToken: String, roomPassword: String): Observable<ConversationModel>
fun setReminder(user: User, roomToken: String, messageId: String, timeStamp: Int): Observable<Reminder> fun setReminder(
fun getReminder(user: User, roomToken: String, messageId: String): Observable<Reminder> user: User,
fun deleteReminder(user: User, roomToken: String, messageId: String): Observable<GenericOverall> roomToken: String,
messageId: String,
timeStamp: Int,
chatApiVersion: Int
): Observable<Reminder>
fun getReminder(user: User, roomToken: String, messageId: String, apiVersion: Int): Observable<Reminder>
fun deleteReminder(user: User, roomToken: String, messageId: String, apiVersion: Int): Observable<GenericOverall>
fun shareToNotes( fun shareToNotes(
credentials: String, credentials: String,
url: String, url: String,

View file

@ -24,6 +24,7 @@ import com.nextcloud.talk.api.NcApi
import com.nextcloud.talk.chat.data.ChatRepository import com.nextcloud.talk.chat.data.ChatRepository
import com.nextcloud.talk.data.user.model.User import com.nextcloud.talk.data.user.model.User
import com.nextcloud.talk.models.domain.ConversationModel 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.chat.ChatOverallSingleMessage
import com.nextcloud.talk.models.json.conversations.RoomOverall import com.nextcloud.talk.models.json.conversations.RoomOverall
import com.nextcloud.talk.models.json.conversations.RoomsOverall import com.nextcloud.talk.models.json.conversations.RoomsOverall
@ -35,55 +36,78 @@ import retrofit2.Response
class NetworkChatRepositoryImpl(private val ncApi: NcApi) : ChatRepository { class NetworkChatRepositoryImpl(private val ncApi: NcApi) : ChatRepository {
override fun getRoom(user: User, roomToken: String): Observable<ConversationModel> { override fun getRoom(user: User, roomToken: String): Observable<ConversationModel> {
val credentials: String = ApiUtils.getCredentials(user.username, user.token) val credentials: String = ApiUtils.getCredentials(user.username, user.token)!!
val apiVersion = ApiUtils.getConversationApiVersion(user, intArrayOf(ApiUtils.APIv4, ApiUtils.APIv3, 1)) val apiVersion = ApiUtils.getConversationApiVersion(user, intArrayOf(ApiUtils.API_V4, ApiUtils.API_V3, 1))
return ncApi.getRoom( return ncApi.getRoom(
credentials, credentials,
ApiUtils.getUrlForRoom(apiVersion, user.baseUrl, roomToken) ApiUtils.getUrlForRoom(apiVersion, user.baseUrl!!, roomToken)
).map { ConversationModel.mapToConversationModel(it.ocs?.data!!) } ).map { ConversationModel.mapToConversationModel(it.ocs?.data!!) }
} }
override fun getCapabilities(user: User, roomToken: String): Observable<SpreedCapability> {
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<ConversationModel> { override fun joinRoom(user: User, roomToken: String, roomPassword: String): Observable<ConversationModel> {
val credentials: String = ApiUtils.getCredentials(user.username, user.token) val credentials: String = ApiUtils.getCredentials(user.username, user.token)!!
val apiVersion = ApiUtils.getConversationApiVersion(user, intArrayOf(ApiUtils.APIv4, 1)) val apiVersion = ApiUtils.getConversationApiVersion(user, intArrayOf(ApiUtils.API_V4, 1))
return ncApi.joinRoom( return ncApi.joinRoom(
credentials, credentials,
ApiUtils.getUrlForParticipantsActive(apiVersion, user.baseUrl, roomToken), ApiUtils.getUrlForParticipantsActive(apiVersion, user.baseUrl!!, roomToken),
roomPassword roomPassword
).map { ConversationModel.mapToConversationModel(it.ocs?.data!!) } ).map { ConversationModel.mapToConversationModel(it.ocs?.data!!) }
} }
override fun setReminder(user: User, roomToken: String, messageId: String, timeStamp: Int): Observable<Reminder> { override fun setReminder(
val credentials: String = ApiUtils.getCredentials(user.username, user.token) user: User,
val apiVersion = ApiUtils.getChatApiVersion(user, intArrayOf(ApiUtils.APIv1, 1)) roomToken: String,
messageId: String,
timeStamp: Int,
chatApiVersion: Int
): Observable<Reminder> {
val credentials: String = ApiUtils.getCredentials(user.username, user.token)!!
return ncApi.setReminder( return ncApi.setReminder(
credentials, credentials,
ApiUtils.getUrlForReminder(user, roomToken, messageId, apiVersion), ApiUtils.getUrlForReminder(user, roomToken, messageId, chatApiVersion),
timeStamp timeStamp
).map { ).map {
it.ocs!!.data it.ocs!!.data
} }
} }
override fun getReminder(user: User, roomToken: String, messageId: String): Observable<Reminder> { override fun getReminder(
val credentials: String = ApiUtils.getCredentials(user.username, user.token) user: User,
val apiVersion = ApiUtils.getChatApiVersion(user, intArrayOf(ApiUtils.APIv1, 1)) roomToken: String,
messageId: String,
chatApiVersion: Int
): Observable<Reminder> {
val credentials: String = ApiUtils.getCredentials(user.username, user.token)!!
return ncApi.getReminder( return ncApi.getReminder(
credentials, credentials,
ApiUtils.getUrlForReminder(user, roomToken, messageId, apiVersion) ApiUtils.getUrlForReminder(user, roomToken, messageId, chatApiVersion)
).map { ).map {
it.ocs!!.data it.ocs!!.data
} }
} }
override fun deleteReminder(user: User, roomToken: String, messageId: String): Observable<GenericOverall> { override fun deleteReminder(
val credentials: String = ApiUtils.getCredentials(user.username, user.token) user: User,
val apiVersion = ApiUtils.getChatApiVersion(user, intArrayOf(ApiUtils.APIv1, 1)) roomToken: String,
messageId: String,
chatApiVersion: Int
): Observable<GenericOverall> {
val credentials: String = ApiUtils.getCredentials(user.username, user.token)!!
return ncApi.deleteReminder( return ncApi.deleteReminder(
credentials, credentials,
ApiUtils.getUrlForReminder(user, roomToken, messageId, apiVersion) ApiUtils.getUrlForReminder(user, roomToken, messageId, chatApiVersion)
).map { ).map {
it it
} }

View file

@ -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.ConversationModel
import com.nextcloud.talk.models.domain.ReactionAddedModel import com.nextcloud.talk.models.domain.ReactionAddedModel
import com.nextcloud.talk.models.domain.ReactionDeletedModel 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.ChatMessage
import com.nextcloud.talk.models.json.chat.ChatOverallSingleMessage import com.nextcloud.talk.models.json.chat.ChatOverallSingleMessage
import com.nextcloud.talk.models.json.conversations.RoomOverall import com.nextcloud.talk.models.json.conversations.RoomOverall
@ -105,6 +106,14 @@ class ChatViewModel @Inject constructor(
val getRoomViewState: LiveData<ViewState> val getRoomViewState: LiveData<ViewState>
get() = _getRoomViewState get() = _getRoomViewState
object GetCapabilitiesStartState : ViewState
object GetCapabilitiesErrorState : ViewState
open class GetCapabilitiesSuccessState(val spreedCapabilities: SpreedCapability) : ViewState
private val _getCapabilitiesViewState: MutableLiveData<ViewState> = MutableLiveData(GetCapabilitiesStartState)
val getCapabilitiesViewState: LiveData<ViewState>
get() = _getCapabilitiesViewState
object JoinRoomStartState : ViewState object JoinRoomStartState : ViewState
object JoinRoomErrorState : ViewState object JoinRoomErrorState : ViewState
open class JoinRoomSuccessState(val conversationModel: ConversationModel) : ViewState open class JoinRoomSuccessState(val conversationModel: ConversationModel) : ViewState
@ -184,6 +193,36 @@ class ChatViewModel @Inject constructor(
?.subscribe(GetRoomObserver()) ?.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<SpreedCapability> {
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) { fun joinRoom(user: User, token: String, roomPassword: String) {
_joinRoomViewState.value = JoinRoomStartState _joinRoomViewState.value = JoinRoomStartState
chatRepository.joinRoom(user, token, roomPassword) chatRepository.joinRoom(user, token, roomPassword)
@ -193,6 +232,43 @@ class ChatViewModel @Inject constructor(
?.subscribe(JoinRoomObserver()) ?.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<GenericOverall> {
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)?) { fun leaveRoom(credentials: String, url: String, funToCallWhenLeaveSuccessful: (() -> Unit)?) {
val startNanoTime = System.nanoTime() val startNanoTime = System.nanoTime()
chatRepository.leaveRoom(credentials, url) 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<GenericOverall> {
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) { fun shareToNotes(credentials: String, url: String, message: String, displayName: String) {
chatRepository.shareToNotes(credentials, url, message, displayName) chatRepository.shareToNotes(credentials, url, message, displayName)
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
@ -522,7 +561,7 @@ class ChatViewModel @Inject constructor(
inner class GetRoomObserver : Observer<ConversationModel> { inner class GetRoomObserver : Observer<ConversationModel> {
override fun onSubscribe(d: Disposable) { override fun onSubscribe(d: Disposable) {
LifeCycleObserver.disposableSet.add(d) // unused atm
} }
override fun onNext(conversationModel: ConversationModel) { override fun onNext(conversationModel: ConversationModel) {

View file

@ -65,7 +65,7 @@ class ReadFolderListingOperation(okHttpClient: OkHttpClient, currentUser: User,
ApiUtils.getCredentials( ApiUtils.getCredentials(
currentUser.username, currentUser.username,
currentUser.token currentUser.token
), )!!,
"Authorization" "Authorization"
) )
) )

View file

@ -68,9 +68,10 @@ import com.nextcloud.talk.models.json.participants.Participant
import com.nextcloud.talk.openconversations.ListOpenConversationsActivity import com.nextcloud.talk.openconversations.ListOpenConversationsActivity
import com.nextcloud.talk.users.UserManager import com.nextcloud.talk.users.UserManager
import com.nextcloud.talk.utils.ApiUtils import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.SpreedFeatures
import com.nextcloud.talk.utils.UserIdUtils.getIdForUser import com.nextcloud.talk.utils.UserIdUtils.getIdForUser
import com.nextcloud.talk.utils.bundle.BundleKeys 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.FlexibleAdapter
import eu.davidea.flexibleadapter.SelectableAdapter import eu.davidea.flexibleadapter.SelectableAdapter
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
@ -318,10 +319,10 @@ class ContactsActivity :
} }
private fun createRoom(roomType: String, sourceType: String?, userId: String) { 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( val retrofitBucket: RetrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(
apiVersion, apiVersion,
currentUser!!.baseUrl, currentUser!!.baseUrl!!,
roomType, roomType,
sourceType, sourceType,
userId, userId,
@ -438,7 +439,7 @@ class ContactsActivity :
userHeaderItems = HashMap() userHeaderItems = HashMap()
val query = adapter!!.getFilter(String::class.java) val query = adapter!!.getFilter(String::class.java)
val retrofitBucket: RetrofitBucket = val retrofitBucket: RetrofitBucket =
ApiUtils.getRetrofitBucketForContactsSearchFor14(currentUser!!.baseUrl, query) ApiUtils.getRetrofitBucketForContactsSearchFor14(currentUser!!.baseUrl!!, query)
val modifiedQueryMap: HashMap<String, Any?> = HashMap(retrofitBucket.queryMap) val modifiedQueryMap: HashMap<String, Any?> = HashMap(retrofitBucket.queryMap)
modifiedQueryMap["limit"] = CONTACTS_BATCH_SIZE modifiedQueryMap["limit"] = CONTACTS_BATCH_SIZE
if (isAddingParticipantsView) { if (isAddingParticipantsView) {
@ -450,13 +451,21 @@ class ContactsActivity :
if (!isAddingParticipantsView) { if (!isAddingParticipantsView) {
// groups // groups
shareTypesList.add("1") 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 // groups
shareTypesList.add("1") shareTypesList.add("1")
// emails // emails
shareTypesList.add("4") shareTypesList.add("4")
} }
if (CapabilitiesUtilNew.hasSpreedFeatureCapability(currentUser, "circles-support")) { if (CapabilitiesUtil.hasSpreedFeatureCapability(
currentUser?.capabilities?.spreedCapability!!,
SpreedFeatures.CIRCLES_SUPPORT
)
) {
// circles // circles
shareTypesList.add("7") shareTypesList.add("7")
} }
@ -745,8 +754,12 @@ class ContactsActivity :
private fun updateSelection(contactItem: ContactItem) { private fun updateSelection(contactItem: ContactItem) {
contactItem.model.selected = !contactItem.model.selected contactItem.model.selected = !contactItem.model.selected
updateSelectionLists(contactItem.model) updateSelectionLists(contactItem.model)
if (CapabilitiesUtilNew.hasSpreedFeatureCapability(currentUser, "last-room-activity") && if (CapabilitiesUtil.hasSpreedFeatureCapability(
!CapabilitiesUtilNew.hasSpreedFeatureCapability(currentUser, "invite-groups-and-mails") && currentUser?.capabilities?.spreedCapability!!, SpreedFeatures.LAST_ROOM_ACTIVITY
) &&
!CapabilitiesUtil.hasSpreedFeatureCapability(
currentUser?.capabilities?.spreedCapability!!, SpreedFeatures.INVITE_GROUPS_AND_MAILS
) &&
isValidGroupSelection(contactItem, contactItem.model, adapter) isValidGroupSelection(contactItem, contactItem.model, adapter)
) { ) {
val currentItems: List<ContactItem> = adapter?.currentItems as List<ContactItem> val currentItems: List<ContactItem> = adapter?.currentItems as List<ContactItem>
@ -771,10 +784,10 @@ class ContactsActivity :
if ("groups" == contactItem.model.source) { if ("groups" == contactItem.model.source) {
roomType = "2" 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( val retrofitBucket: RetrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(
apiVersion, apiVersion,
currentUser!!.baseUrl, currentUser!!.baseUrl!!,
roomType, roomType,
null, null,
contactItem.model.calculatedActorId, contactItem.model.calculatedActorId,

View file

@ -36,16 +36,16 @@ class ConversationRepositoryImpl(private val ncApi: NcApi, currentUserProvider:
ConversationRepository { ConversationRepository {
val currentUser: User = currentUserProvider.currentUser.blockingGet() 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<GenericOverall> { override fun renameConversation(roomToken: String, roomNameNew: String): Observable<GenericOverall> {
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( return ncApi.renameRoom(
credentials, credentials,
ApiUtils.getUrlForRoom( ApiUtils.getUrlForRoom(
apiVersion, apiVersion,
currentUser.baseUrl, currentUser.baseUrl!!,
roomToken roomToken
), ),
roomNameNew roomNameNew
@ -59,12 +59,12 @@ class ConversationRepositoryImpl(private val ncApi: NcApi, currentUserProvider:
roomName: String, roomName: String,
conversationType: Conversation.ConversationType? conversationType: Conversation.ConversationType?
): Observable<RoomOverall> { ): Observable<RoomOverall> {
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) { val retrofitBucket: RetrofitBucket = if (conversationType == Conversation.ConversationType.ROOM_PUBLIC_CALL) {
ApiUtils.getRetrofitBucketForCreateRoom( ApiUtils.getRetrofitBucketForCreateRoom(
apiVersion, apiVersion,
currentUser.baseUrl, currentUser.baseUrl!!,
ROOM_TYPE_PUBLIC, ROOM_TYPE_PUBLIC,
null, null,
null, null,
@ -73,7 +73,7 @@ class ConversationRepositoryImpl(private val ncApi: NcApi, currentUserProvider:
} else { } else {
ApiUtils.getRetrofitBucketForCreateRoom( ApiUtils.getRetrofitBucketForCreateRoom(
apiVersion, apiVersion,
currentUser.baseUrl, currentUser.baseUrl!!,
ROOM_TYPE_GROUP, ROOM_TYPE_GROUP,
null, null,
null, null,

View file

@ -40,6 +40,7 @@ import android.view.View
import android.view.View.GONE import android.view.View.GONE
import android.view.View.VISIBLE import android.view.View.VISIBLE
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.lifecycle.ViewModelProvider
import androidx.work.Data import androidx.work.Data
import androidx.work.OneTimeWorkRequest import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager 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.bottomsheet.items.listItemsWithImage
import com.nextcloud.talk.contacts.ContactsActivity import com.nextcloud.talk.contacts.ContactsActivity
import com.nextcloud.talk.conversationinfoedit.ConversationInfoEditActivity 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.data.user.model.User
import com.nextcloud.talk.databinding.ActivityConversationInfoBinding import com.nextcloud.talk.databinding.ActivityConversationInfoBinding
import com.nextcloud.talk.events.EventStatus 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.DeleteConversationWorker
import com.nextcloud.talk.jobs.LeaveConversationWorker import com.nextcloud.talk.jobs.LeaveConversationWorker
import com.nextcloud.talk.models.domain.ConversationModel import com.nextcloud.talk.models.domain.ConversationModel
import com.nextcloud.talk.models.json.conversations.Conversation import com.nextcloud.talk.models.domain.ConversationType
import com.nextcloud.talk.models.json.conversations.RoomOverall import com.nextcloud.talk.models.domain.LobbyState
import com.nextcloud.talk.models.json.converters.EnumNotificationLevelConverter 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.generic.GenericOverall
import com.nextcloud.talk.models.json.participants.Participant import com.nextcloud.talk.models.json.participants.Participant
import com.nextcloud.talk.models.json.participants.Participant.ActorType.CIRCLES 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.repositories.conversations.ConversationsRepository
import com.nextcloud.talk.shareditems.activities.SharedItemsActivity import com.nextcloud.talk.shareditems.activities.SharedItemsActivity
import com.nextcloud.talk.utils.ApiUtils import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.SpreedFeatures
import com.nextcloud.talk.utils.ConversationUtils import com.nextcloud.talk.utils.ConversationUtils
import com.nextcloud.talk.utils.DateConstants import com.nextcloud.talk.utils.DateConstants
import com.nextcloud.talk.utils.DateUtils import com.nextcloud.talk.utils.DateUtils
import com.nextcloud.talk.utils.bundle.BundleKeys 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.database.user.CurrentUserProviderNew
import com.nextcloud.talk.utils.preferences.preferencestorage.DatabaseStorageModule import com.nextcloud.talk.utils.preferences.preferencestorage.DatabaseStorageModule
import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.FlexibleAdapter
@ -109,6 +114,9 @@ class ConversationInfoActivity :
FlexibleAdapter.OnItemClickListener { FlexibleAdapter.OnItemClickListener {
private lateinit var binding: ActivityConversationInfoBinding private lateinit var binding: ActivityConversationInfoBinding
@Inject
lateinit var viewModelFactory: ViewModelProvider.Factory
@Inject @Inject
lateinit var ncApi: NcApi lateinit var ncApi: NcApi
@ -121,6 +129,10 @@ class ConversationInfoActivity :
@Inject @Inject
lateinit var dateUtils: DateUtils lateinit var dateUtils: DateUtils
lateinit var viewModel: ConversationInfoViewModel
private lateinit var spreedCapabilities: SpreedCapability
private lateinit var conversationToken: String private lateinit var conversationToken: String
private lateinit var conversationUser: User private lateinit var conversationUser: User
private var hasAvatarSpacing: Boolean = false private var hasAvatarSpacing: Boolean = false
@ -129,7 +141,9 @@ class ConversationInfoActivity :
private var participantsDisposable: Disposable? = null private var participantsDisposable: Disposable? = null
private var databaseStorageModule: DatabaseStorageModule? = 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<ParticipantItem>? = null private var adapter: FlexibleAdapter<ParticipantItem>? = null
private var userItems: MutableList<ParticipantItem> = ArrayList() private var userItems: MutableList<ParticipantItem> = ArrayList()
@ -157,11 +171,26 @@ class ConversationInfoActivity :
setContentView(binding.root) setContentView(binding.root)
setupSystemColors() setupSystemColors()
viewModel =
ViewModelProvider(this, viewModelFactory)[ConversationInfoViewModel::class.java]
conversationUser = currentUserProvider.currentUser.blockingGet() conversationUser = currentUserProvider.currentUser.blockingGet()
conversationToken = intent.getStringExtra(BundleKeys.KEY_ROOM_TOKEN)!! conversationToken = intent.getStringExtra(BundleKeys.KEY_ROOM_TOKEN)!!
hasAvatarSpacing = intent.getBooleanExtra(BundleKeys.KEY_ROOM_ONE_TO_ONE, false) 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() { override fun onResume() {
@ -176,13 +205,7 @@ class ConversationInfoActivity :
binding.clearConversationHistory.setOnClickListener { showClearHistoryDialog() } binding.clearConversationHistory.setOnClickListener { showClearHistoryDialog() }
binding.addParticipantsAction.setOnClickListener { addParticipants() } binding.addParticipantsAction.setOnClickListener { addParticipants() }
if (CapabilitiesUtilNew.hasSpreedFeatureCapability(conversationUser, "rich-object-list-media")) { viewModel.getRoom(conversationUser, conversationToken)
binding.sharedItemsButton.setOnClickListener { showSharedItems() }
} else {
binding.sharedItems.visibility = GONE
}
fetchRoomInfo()
themeTextViews() themeTextViews()
themeSwitchPreferences() themeSwitchPreferences()
@ -192,6 +215,35 @@ class ConversationInfoActivity :
binding.progressBar.let { viewThemeUtils.platform.colorCircularProgressBar(it, ColorRole.PRIMARY) } 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() { private fun setupActionBar() {
setSupportActionBar(binding.conversationInfoToolbar) setSupportActionBar(binding.conversationInfoToolbar)
binding.conversationInfoToolbar.setNavigationOnClickListener { binding.conversationInfoToolbar.setNavigationOnClickListener {
@ -217,7 +269,7 @@ class ConversationInfoActivity :
fun showOptionsMenu() { fun showOptionsMenu() {
if (::optionsMenu.isInitialized) { if (::optionsMenu.isInitialized) {
optionsMenu.clear() optionsMenu.clear()
if (CapabilitiesUtilNew.isConversationAvatarEndpointAvailable(conversationUser)) { if (CapabilitiesUtil.hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.AVATAR)) {
menuInflater.inflate(R.menu.menu_conversation_info, optionsMenu) menuInflater.inflate(R.menu.menu_conversation_info, optionsMenu)
} }
} }
@ -273,19 +325,22 @@ class ConversationInfoActivity :
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
intent.putExtra(BundleKeys.KEY_CONVERSATION_NAME, conversation?.displayName) intent.putExtra(BundleKeys.KEY_CONVERSATION_NAME, conversation?.displayName)
intent.putExtra(BundleKeys.KEY_ROOM_TOKEN, conversationToken) 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) startActivity(intent)
} }
private fun setupWebinaryView() { private fun setupWebinaryView() {
if (CapabilitiesUtilNew.hasSpreedFeatureCapability(conversationUser, "webinary-lobby") && if (CapabilitiesUtil.hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.WEBINARY_LOBBY) &&
webinaryRoomType(conversation!!) && webinaryRoomType(conversation!!) &&
conversation!!.canModerate(conversationUser) ConversationUtils.canModerate(conversation!!, spreedCapabilities)
) { ) {
binding.webinarInfoView.webinarSettings.visibility = VISIBLE binding.webinarInfoView.webinarSettings.visibility = VISIBLE
val isLobbyOpenToModeratorsOnly = val isLobbyOpenToModeratorsOnly =
conversation!!.lobbyState == Conversation.LobbyState.LOBBY_STATE_MODERATORS_ONLY conversation!!.lobbyState == LobbyState.LOBBY_STATE_MODERATORS_ONLY
binding.webinarInfoView.lobbySwitch.isChecked = isLobbyOpenToModeratorsOnly binding.webinarInfoView.lobbySwitch.isChecked = isLobbyOpenToModeratorsOnly
reconfigureLobbyTimerView() reconfigureLobbyTimerView()
@ -320,9 +375,9 @@ class ConversationInfoActivity :
} }
} }
private fun webinaryRoomType(conversation: Conversation): Boolean { private fun webinaryRoomType(conversation: ConversationModel): Boolean {
return conversation.type == Conversation.ConversationType.ROOM_GROUP_CALL || return conversation.type == ConversationType.ROOM_GROUP_CALL ||
conversation.type == Conversation.ConversationType.ROOM_PUBLIC_CALL conversation.type == ConversationType.ROOM_PUBLIC_CALL
} }
private fun reconfigureLobbyTimerView(dateTime: Calendar? = null) { private fun reconfigureLobbyTimerView(dateTime: Calendar? = null) {
@ -337,9 +392,9 @@ class ConversationInfoActivity :
} }
conversation!!.lobbyState = if (isChecked) { conversation!!.lobbyState = if (isChecked) {
Conversation.LobbyState.LOBBY_STATE_MODERATORS_ONLY LobbyState.LOBBY_STATE_MODERATORS_ONLY
} else { } else {
Conversation.LobbyState.LOBBY_STATE_ALL_PARTICIPANTS LobbyState.LOBBY_STATE_ALL_PARTICIPANTS
} }
if ( if (
@ -370,11 +425,11 @@ class ConversationInfoActivity :
0 0
} }
val apiVersion = ApiUtils.getConversationApiVersion(conversationUser, intArrayOf(ApiUtils.APIv4, 1)) val apiVersion = ApiUtils.getConversationApiVersion(conversationUser, intArrayOf(ApiUtils.API_V4, 1))
ncApi.setLobbyForConversation( ncApi.setLobbyForConversation(
ApiUtils.getCredentials(conversationUser.username, conversationUser.token), ApiUtils.getCredentials(conversationUser.username, conversationUser.token),
ApiUtils.getUrlForRoomWebinaryLobby(apiVersion, conversationUser.baseUrl, conversation!!.token), ApiUtils.getUrlForRoomWebinaryLobby(apiVersion, conversationUser.baseUrl!!, conversation!!.token),
state, state,
conversation!!.lobbyTimer conversation!!.lobbyTimer
) )
@ -487,7 +542,7 @@ class ConversationInfoActivity :
private fun getListOfParticipants() { private fun getListOfParticipants() {
// FIXME Fix API checking with guests? // 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<String, Boolean>() val fieldMap = HashMap<String, Boolean>()
fieldMap["includeStatus"] = true fieldMap["includeStatus"] = true
@ -496,7 +551,7 @@ class ConversationInfoActivity :
credentials, credentials,
ApiUtils.getUrlForParticipants( ApiUtils.getUrlForParticipants(
apiVersion, apiVersion,
conversationUser.baseUrl, conversationUser.baseUrl!!,
conversationToken conversationToken
), ),
fieldMap fieldMap
@ -586,11 +641,11 @@ class ConversationInfoActivity :
} }
private fun clearHistory() { private fun clearHistory() {
val apiVersion = ApiUtils.getChatApiVersion(conversationUser, intArrayOf(1)) val apiVersion = ApiUtils.getChatApiVersion(spreedCapabilities, intArrayOf(1))
ncApi.clearChatHistory( ncApi.clearChatHistory(
credentials, credentials,
ApiUtils.getUrlForChat(apiVersion, conversationUser.baseUrl, conversationToken) ApiUtils.getUrlForChat(apiVersion, conversationUser.baseUrl!!, conversationToken)
) )
?.subscribeOn(Schedulers.io()) ?.subscribeOn(Schedulers.io())
?.observeOn(AndroidSchedulers.mainThread()) ?.observeOn(AndroidSchedulers.mainThread())
@ -631,123 +686,99 @@ class ConversationInfoActivity :
} }
} }
private fun fetchRoomInfo() { @Suppress("LongMethod")
val apiVersion: Int private fun handleConversation() {
// FIXME Fix API checking with guests? val conversationCopy = conversation!!
apiVersion = ApiUtils.getConversationApiVersion(conversationUser, intArrayOf(ApiUtils.APIv4, 1))
ncApi.getRoom(credentials, ApiUtils.getUrlForRoom(apiVersion, conversationUser.baseUrl, conversationToken)) if (CapabilitiesUtil.hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.RICH_OBJECT_LIST_MEDIA)) {
?.subscribeOn(Schedulers.io()) binding.sharedItemsButton.setOnClickListener { showSharedItems() }
?.observeOn(AndroidSchedulers.mainThread()) } else {
?.subscribe(object : Observer<RoomOverall> { binding.sharedItems.visibility = GONE
override fun onSubscribe(d: Disposable) { }
roomDisposable = d
}
@Suppress("Detekt.TooGenericExceptionCaught") if (ConversationUtils.canModerate(conversationCopy, spreedCapabilities)) {
override fun onNext(roomOverall: RoomOverall) { binding.addParticipantsAction.visibility = VISIBLE
conversation = roomOverall.ocs!!.data 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)) { if (!isDestroyed) {
binding.addParticipantsAction.visibility = VISIBLE binding.dangerZoneOptions.visibility = VISIBLE
if (CapabilitiesUtilNew.hasSpreedFeatureCapability(
conversationUser,
"clear-history"
)
) {
binding.clearConversationHistory.visibility = VISIBLE
} else {
binding.clearConversationHistory.visibility = GONE
}
showOptionsMenu()
} else {
binding.addParticipantsAction.visibility = GONE
if (ConversationUtils.isNoteToSelfConversation( setupWebinaryView()
ConversationModel.mapToConversationModel(conversation!!)
)
) {
binding.notificationSettingsView.notificationSettings.visibility = VISIBLE
} else {
binding.clearConversationHistory.visibility = GONE
}
}
if (!isDestroyed) { if (ConversationUtils.canLeave(conversation!!)) {
binding.dangerZoneOptions.visibility = VISIBLE 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()) { if (ConversationType.ROOM_SYSTEM == conversation!!.type) {
binding.leaveConversationAction.visibility = GONE binding.notificationSettingsView.callNotificationsSwitch.visibility = GONE
} else { }
binding.leaveConversationAction.visibility = VISIBLE
}
if (!conversation!!.canDelete(conversationUser)) { if (conversation!!.notificationCalls === null) {
binding.deleteConversationAction.visibility = GONE binding.notificationSettingsView.callNotificationsSwitch.visibility = GONE
} else { } else {
binding.deleteConversationAction.visibility = VISIBLE binding.notificationSettingsView.callNotificationsSwitch.isChecked =
} (conversationCopy.notificationCalls == 1)
}
if (Conversation.ConversationType.ROOM_SYSTEM == conversation!!.type) { getListOfParticipants()
binding.notificationSettingsView.callNotificationsSwitch.visibility = GONE
}
if (conversation!!.notificationCalls === null) { binding.progressBar.visibility = GONE
binding.notificationSettingsView.callNotificationsSwitch.visibility = GONE
} else {
binding.notificationSettingsView.callNotificationsSwitch.isChecked =
(conversationCopy.notificationCalls == 1)
}
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.let {
binding.descriptionText.text = conversation!!.description GuestAccessHelper(
binding.conversationDescription.visibility = VISIBLE this@ConversationInfoActivity,
} it,
conversation!!,
loadConversationAvatar() spreedCapabilities,
adjustNotificationLevelUI() conversationUser
initRecordingConsentOption() ).setupGuestAccess()
initExpiringMessageOption() }
if (ConversationUtils.isNoteToSelfConversation(conversation!!)) {
binding.let { binding.notificationSettingsView.notificationSettings.visibility = GONE
GuestAccessHelper( } else {
this@ConversationInfoActivity, binding.notificationSettingsView.notificationSettings.visibility = VISIBLE
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()
}
})
} }
private fun initRecordingConsentOption() { private fun initRecordingConsentOption() {
@ -781,13 +812,13 @@ class ConversationInfoActivity :
} }
} }
if (conversation!!.isParticipantOwnerOrModerator && if (ConversationUtils.isParticipantOwnerOrModerator(conversation!!) &&
!ConversationUtils.isNoteToSelfConversation(ConversationModel.mapToConversationModel(conversation!!)) !ConversationUtils.isNoteToSelfConversation(conversation!!)
) { ) {
when (CapabilitiesUtilNew.getRecordingConsentType(conversationUser)) { when (CapabilitiesUtil.getRecordingConsentType(spreedCapabilities)) {
CapabilitiesUtilNew.RECORDING_CONSENT_NOT_REQUIRED -> hide() CapabilitiesUtil.RECORDING_CONSENT_NOT_REQUIRED -> hide()
CapabilitiesUtilNew.RECORDING_CONSENT_REQUIRED -> showAlwaysRequiredInfo() CapabilitiesUtil.RECORDING_CONSENT_REQUIRED -> showAlwaysRequiredInfo()
CapabilitiesUtilNew.RECORDING_CONSENT_DEPEND_ON_CONVERSATION -> showSwitch() CapabilitiesUtil.RECORDING_CONSENT_DEPEND_ON_CONVERSATION -> showSwitch()
} }
} else { } else {
hide() hide()
@ -801,11 +832,11 @@ class ConversationInfoActivity :
RECORDING_CONSENT_NOT_REQUIRED_FOR_CONVERSATION 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( ncApi.setRecordingConsent(
ApiUtils.getCredentials(conversationUser.username, conversationUser.token), ApiUtils.getCredentials(conversationUser.username, conversationUser.token),
ApiUtils.getUrlForRecordingConsent(apiVersion, conversationUser.baseUrl, conversation!!.token), ApiUtils.getUrlForRecordingConsent(apiVersion, conversationUser.baseUrl!!, conversation!!.token),
state state
) )
?.subscribeOn(Schedulers.io()) ?.subscribeOn(Schedulers.io())
@ -831,8 +862,8 @@ class ConversationInfoActivity :
} }
private fun initExpiringMessageOption() { private fun initExpiringMessageOption() {
if (conversation!!.isParticipantOwnerOrModerator && if (ConversationUtils.isParticipantOwnerOrModerator(conversation!!) &&
CapabilitiesUtilNew.hasSpreedFeatureCapability(conversationUser, "message-expiration") CapabilitiesUtil.hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.MESSAGE_EXPIRATION)
) { ) {
databaseStorageModule?.setMessageExpiration(conversation!!.messageExpiration) databaseStorageModule?.setMessageExpiration(conversation!!.messageExpiration)
val value = databaseStorageModule!!.getString("conversation_settings_dropdown", "") val value = databaseStorageModule!!.getString("conversation_settings_dropdown", "")
@ -855,13 +886,16 @@ class ConversationInfoActivity :
private fun adjustNotificationLevelUI() { private fun adjustNotificationLevelUI() {
if (conversation != null) { if (conversation != null) {
if (CapabilitiesUtilNew.hasSpreedFeatureCapability(conversationUser, "notification-levels")) { if (CapabilitiesUtil.hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.NOTIFICATION_LEVELS)) {
binding.notificationSettingsView.conversationInfoMessageNotificationsDropdown.isEnabled = true binding.notificationSettingsView.conversationInfoMessageNotificationsDropdown.isEnabled = true
binding.notificationSettingsView.conversationInfoMessageNotificationsDropdown.alpha = 1.0f binding.notificationSettingsView.conversationInfoMessageNotificationsDropdown.alpha = 1.0f
if (conversation!!.notificationLevel != Conversation.NotificationLevel.DEFAULT) { if (conversation!!.notificationLevel != NotificationLevel.DEFAULT) {
val stringValue: String = 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_ALWAYS -> resources.getString(R.string.nc_notify_me_always)
NOTIFICATION_LEVEL_MENTION -> resources.getString(R.string.nc_notify_me_mention) NOTIFICATION_LEVEL_MENTION -> resources.getString(R.string.nc_notify_me_mention)
NOTIFICATION_LEVEL_NEVER -> resources.getString(R.string.nc_notify_me_never) NOTIFICATION_LEVEL_NEVER -> resources.getString(R.string.nc_notify_me_never)
@ -885,9 +919,9 @@ class ConversationInfoActivity :
} }
} }
private fun setProperNotificationValue(conversation: Conversation?) { private fun setProperNotificationValue(conversation: ConversationModel?) {
if (conversation!!.type == Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL) { if (conversation!!.type == ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL) {
if (CapabilitiesUtilNew.hasSpreedFeatureCapability(conversationUser, "mention-flag")) { if (CapabilitiesUtil.hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.MENTION_FLAG)) {
binding.notificationSettingsView.conversationInfoMessageNotificationsDropdown.setText( binding.notificationSettingsView.conversationInfoMessageNotificationsDropdown.setText(
resources.getString(R.string.nc_notify_me_always) resources.getString(R.string.nc_notify_me_always)
) )
@ -905,7 +939,7 @@ class ConversationInfoActivity :
private fun loadConversationAvatar() { private fun loadConversationAvatar() {
when (conversation!!.type) { 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 { conversation!!.name?.let {
binding.avatarImage.loadUserAvatar( binding.avatarImage.loadUserAvatar(
conversationUser, 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( binding.avatarImage.loadConversationAvatar(
conversationUser, conversationUser,
conversation!!, conversation!!,
@ -925,15 +959,12 @@ class ConversationInfoActivity :
) )
} }
Conversation.ConversationType.ROOM_SYSTEM -> { ConversationType.ROOM_SYSTEM -> {
binding.avatarImage.loadSystemAvatar() binding.avatarImage.loadSystemAvatar()
} }
Conversation.ConversationType.DUMMY -> { ConversationType.DUMMY -> {
if (ConversationUtils.isNoteToSelfConversation( if (ConversationUtils.isNoteToSelfConversation(conversation!!)) {
ConversationModel.mapToConversationModel(conversation!!)
)
) {
binding.avatarImage.loadNoteToSelfAvatar() binding.avatarImage.loadNoteToSelfAvatar()
} }
} }
@ -971,7 +1002,7 @@ class ConversationInfoActivity :
credentials, credentials,
ApiUtils.getUrlForRoomModerators( ApiUtils.getUrlForRoomModerators(
apiVersion, apiVersion,
conversationUser.baseUrl, conversationUser.baseUrl!!,
conversation!!.token conversation!!.token
), ),
participant.attendeeId participant.attendeeId
@ -986,7 +1017,7 @@ class ConversationInfoActivity :
credentials, credentials,
ApiUtils.getUrlForRoomModerators( ApiUtils.getUrlForRoomModerators(
apiVersion, apiVersion,
conversationUser.baseUrl, conversationUser.baseUrl!!,
conversation!!.token conversation!!.token
), ),
participant.attendeeId participant.attendeeId
@ -1022,7 +1053,7 @@ class ConversationInfoActivity :
credentials, credentials,
ApiUtils.getUrlForRoomModerators( ApiUtils.getUrlForRoomModerators(
apiVersion, apiVersion,
conversationUser.baseUrl, conversationUser.baseUrl!!,
conversation!!.token conversation!!.token
), ),
participant.userId participant.userId
@ -1035,7 +1066,7 @@ class ConversationInfoActivity :
credentials, credentials,
ApiUtils.getUrlForRoomModerators( ApiUtils.getUrlForRoomModerators(
apiVersion, apiVersion,
conversationUser.baseUrl, conversationUser.baseUrl!!,
conversation!!.token conversation!!.token
), ),
participant.userId participant.userId
@ -1047,12 +1078,12 @@ class ConversationInfoActivity :
} }
private fun removeAttendeeFromConversation(apiVersion: Int, participant: Participant) { private fun removeAttendeeFromConversation(apiVersion: Int, participant: Participant) {
if (apiVersion >= ApiUtils.APIv4) { if (apiVersion >= ApiUtils.API_V4) {
ncApi.removeAttendeeFromConversation( ncApi.removeAttendeeFromConversation(
credentials, credentials,
ApiUtils.getUrlForAttendees( ApiUtils.getUrlForAttendees(
apiVersion, apiVersion,
conversationUser.baseUrl, conversationUser.baseUrl!!,
conversation!!.token conversation!!.token
), ),
participant.attendeeId participant.attendeeId
@ -1084,7 +1115,7 @@ class ConversationInfoActivity :
ncApi.removeParticipantFromConversation( ncApi.removeParticipantFromConversation(
credentials, credentials,
ApiUtils.getUrlForRemovingParticipantFromConversation( ApiUtils.getUrlForRemovingParticipantFromConversation(
conversationUser.baseUrl, conversationUser.baseUrl!!,
conversation!!.token, conversation!!.token,
true true
), ),
@ -1114,7 +1145,7 @@ class ConversationInfoActivity :
ncApi.removeParticipantFromConversation( ncApi.removeParticipantFromConversation(
credentials, credentials,
ApiUtils.getUrlForRemovingParticipantFromConversation( ApiUtils.getUrlForRemovingParticipantFromConversation(
conversationUser.baseUrl, conversationUser.baseUrl!!,
conversation!!.token, conversation!!.token,
false false
), ),
@ -1146,14 +1177,14 @@ class ConversationInfoActivity :
@SuppressLint("CheckResult") @SuppressLint("CheckResult")
override fun onItemClick(view: View?, position: Int): Boolean { override fun onItemClick(view: View?, position: Int): Boolean {
if (!conversation!!.canModerate(conversationUser)) { if (ConversationUtils.canModerate(conversation!!, spreedCapabilities)) {
return true return true
} }
val userItem = adapter?.getItem(position) as ParticipantItem val userItem = adapter?.getItem(position) as ParticipantItem
val participant = userItem.model 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.calculatedActorType == USERS && participant.calculatedActorId == conversationUser.userId) {
if (participant.attendeePin?.isNotEmpty() == true) { if (participant.attendeePin?.isNotEmpty() == true) {
@ -1277,7 +1308,7 @@ class ConversationInfoActivity :
// Pin, nothing to do // Pin, nothing to do
} else if (actionToTrigger == 1) { } else if (actionToTrigger == 1) {
// Promote/demote // Promote/demote
if (apiVersion >= ApiUtils.APIv4) { if (apiVersion >= ApiUtils.API_V4) {
toggleModeratorStatus(apiVersion, participant) toggleModeratorStatus(apiVersion, participant)
} else { } else {
toggleModeratorStatusLegacy(apiVersion, participant) toggleModeratorStatusLegacy(apiVersion, participant)

View file

@ -11,8 +11,11 @@ import com.nextcloud.talk.R
import com.nextcloud.talk.data.user.model.User import com.nextcloud.talk.data.user.model.User
import com.nextcloud.talk.databinding.ActivityConversationInfoBinding import com.nextcloud.talk.databinding.ActivityConversationInfoBinding
import com.nextcloud.talk.databinding.DialogPasswordBinding 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.repositories.conversations.ConversationsRepository
import com.nextcloud.talk.utils.ConversationUtils
import com.nextcloud.talk.utils.Mimetype import com.nextcloud.talk.utils.Mimetype
import com.nextcloud.talk.utils.ShareUtils import com.nextcloud.talk.utils.ShareUtils
import io.reactivex.Observer import io.reactivex.Observer
@ -23,7 +26,8 @@ import io.reactivex.schedulers.Schedulers
class GuestAccessHelper( class GuestAccessHelper(
private val activity: ConversationInfoActivity, private val activity: ConversationInfoActivity,
private val binding: ActivityConversationInfoBinding, private val binding: ActivityConversationInfoBinding,
private val conversation: Conversation, private val conversation: ConversationModel,
private val spreedCapabilities: SpreedCapability,
private val conversationUser: User private val conversationUser: User
) { ) {
@ -32,13 +36,13 @@ class GuestAccessHelper(
private val context = activity.context private val context = activity.context
fun setupGuestAccess() { fun setupGuestAccess() {
if (conversation.canModerate(conversationUser)) { if (ConversationUtils.canModerate(conversation, spreedCapabilities)) {
binding.guestAccessView.guestAccessSettings.visibility = View.VISIBLE binding.guestAccessView.guestAccessSettings.visibility = View.VISIBLE
} else { } else {
binding.guestAccessView.guestAccessSettings.visibility = View.GONE 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 binding.guestAccessView.allowGuestsSwitch.isChecked = true
showAllOptions() showAllOptions()
if (conversation.hasPassword) { if (conversation.hasPassword) {

View file

@ -0,0 +1,142 @@
/*
* Nextcloud Talk application
*
* @author Marcel Hibbe
* Copyright (C) 2023 Marcel Hibbe <dev@mhibbe.de>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.nextcloud.talk.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<Disposable>()
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<ViewState> = MutableLiveData(GetRoomStartState)
val viewState: LiveData<ViewState>
get() = _viewState
object GetCapabilitiesStartState : ViewState
object GetCapabilitiesErrorState : ViewState
open class GetCapabilitiesSuccessState(val spreedCapabilities: SpreedCapability) : ViewState
private val _getCapabilitiesViewState: MutableLiveData<ViewState> = MutableLiveData(GetCapabilitiesStartState)
val getCapabilitiesViewState: LiveData<ViewState>
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<SpreedCapability> {
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<ConversationModel> {
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
}
}

View file

@ -49,11 +49,12 @@ import com.nextcloud.talk.extensions.loadSystemAvatar
import com.nextcloud.talk.extensions.loadUserAvatar import com.nextcloud.talk.extensions.loadUserAvatar
import com.nextcloud.talk.models.domain.ConversationModel import com.nextcloud.talk.models.domain.ConversationModel
import com.nextcloud.talk.models.domain.ConversationType 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.models.json.generic.GenericOverall
import com.nextcloud.talk.utils.ApiUtils import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.CapabilitiesUtil
import com.nextcloud.talk.utils.PickImage import com.nextcloud.talk.utils.PickImage
import com.nextcloud.talk.utils.bundle.BundleKeys import com.nextcloud.talk.utils.bundle.BundleKeys
import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew
import com.nextcloud.talk.utils.database.user.CurrentUserProviderNew import com.nextcloud.talk.utils.database.user.CurrentUserProviderNew
import io.reactivex.Observer import io.reactivex.Observer
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
@ -87,6 +88,8 @@ class ConversationInfoEditActivity :
private lateinit var pickImage: PickImage private lateinit var pickImage: PickImage
private lateinit var spreedCapabilities: SpreedCapability
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this) NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this)
@ -110,7 +113,7 @@ class ConversationInfoEditActivity :
viewThemeUtils.material.colorTextInputLayout(binding.conversationNameInputLayout) viewThemeUtils.material.colorTextInputLayout(binding.conversationNameInputLayout)
viewThemeUtils.material.colorTextInputLayout(binding.conversationDescriptionInputLayout) viewThemeUtils.material.colorTextInputLayout(binding.conversationDescriptionInputLayout)
credentials = ApiUtils.getCredentials(conversationUser.username, conversationUser.token) credentials = ApiUtils.getCredentials(conversationUser.username, conversationUser.token)!!
pickImage = PickImage(this, conversationUser) pickImage = PickImage(this, conversationUser)
@ -127,13 +130,15 @@ class ConversationInfoEditActivity :
is ConversationInfoEditViewModel.GetRoomSuccessState -> { is ConversationInfoEditViewModel.GetRoomSuccessState -> {
conversation = state.conversationModel conversation = state.conversationModel
spreedCapabilities = conversationUser.capabilities!!.spreedCapability!!
binding.conversationName.setText(conversation!!.displayName) binding.conversationName.setText(conversation!!.displayName)
if (conversation!!.description != null && conversation!!.description!!.isNotEmpty()) { if (conversation!!.description != null && conversation!!.description!!.isNotEmpty()) {
binding.conversationDescription.setText(conversation!!.description) binding.conversationDescription.setText(conversation!!.description)
} }
if (!CapabilitiesUtilNew.isConversationDescriptionEndpointAvailable(conversationUser)) { if (!CapabilitiesUtil.isConversationDescriptionEndpointAvailable(spreedCapabilities)) {
binding.conversationDescription.isEnabled = false binding.conversationDescription.isEnabled = false
} }
@ -221,13 +226,13 @@ class ConversationInfoEditActivity :
private fun saveConversationNameAndDescription() { private fun saveConversationNameAndDescription() {
val apiVersion = val apiVersion =
ApiUtils.getConversationApiVersion(conversationUser, intArrayOf(ApiUtils.APIv4, ApiUtils.APIv1)) ApiUtils.getConversationApiVersion(conversationUser, intArrayOf(ApiUtils.API_V4, ApiUtils.API_V1))
ncApi.renameRoom( ncApi.renameRoom(
credentials, credentials,
ApiUtils.getUrlForRoom( ApiUtils.getUrlForRoom(
apiVersion, apiVersion,
conversationUser.baseUrl, conversationUser.baseUrl!!,
conversation!!.token conversation!!.token
), ),
binding.conversationName.text.toString() binding.conversationName.text.toString()
@ -241,7 +246,7 @@ class ConversationInfoEditActivity :
} }
override fun onNext(genericOverall: GenericOverall) { override fun onNext(genericOverall: GenericOverall) {
if (CapabilitiesUtilNew.isConversationDescriptionEndpointAvailable(conversationUser)) { if (CapabilitiesUtil.isConversationDescriptionEndpointAvailable(spreedCapabilities)) {
saveConversationDescription() saveConversationDescription()
} else { } else {
finish() finish()
@ -265,13 +270,13 @@ class ConversationInfoEditActivity :
fun saveConversationDescription() { fun saveConversationDescription() {
val apiVersion = val apiVersion =
ApiUtils.getConversationApiVersion(conversationUser, intArrayOf(ApiUtils.APIv4, ApiUtils.APIv1)) ApiUtils.getConversationApiVersion(conversationUser, intArrayOf(ApiUtils.API_V4, ApiUtils.API_V1))
ncApi.setConversationDescription( ncApi.setConversationDescription(
credentials, credentials,
ApiUtils.getUrlForConversationDescription( ApiUtils.getUrlForConversationDescription(
apiVersion, apiVersion,
conversationUser.baseUrl, conversationUser.baseUrl!!,
conversation!!.token conversation!!.token
), ),
binding.conversationDescription.text.toString() binding.conversationDescription.text.toString()

View file

@ -36,9 +36,9 @@ class ConversationInfoEditRepositoryImpl(private val ncApi: NcApi, currentUserPr
ConversationInfoEditRepository { ConversationInfoEditRepository {
val currentUser: User = currentUserProvider.currentUser.blockingGet() 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<ConversationModel> { override fun uploadConversationAvatar(user: User, file: File, roomToken: String): Observable<ConversationModel> {
val builder = MultipartBody.Builder() val builder = MultipartBody.Builder()
@ -56,7 +56,7 @@ class ConversationInfoEditRepositoryImpl(private val ncApi: NcApi, currentUserPr
return ncApi.uploadConversationAvatar( return ncApi.uploadConversationAvatar(
credentials, credentials,
ApiUtils.getUrlForConversationAvatar(1, user.baseUrl, roomToken), ApiUtils.getUrlForConversationAvatar(1, user.baseUrl!!, roomToken),
filePart filePart
).map { ConversationModel.mapToConversationModel(it.ocs?.data!!) } ).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<ConversationModel> { override fun deleteConversationAvatar(user: User, roomToken: String): Observable<ConversationModel> {
return ncApi.deleteConversationAvatar( return ncApi.deleteConversationAvatar(
credentials, credentials,
ApiUtils.getUrlForConversationAvatar(1, user.baseUrl, roomToken) ApiUtils.getUrlForConversationAvatar(1, user.baseUrl!!, roomToken)
).map { ConversationModel.mapToConversationModel(it.ocs?.data!!) } ).map { ConversationModel.mapToConversationModel(it.ocs?.data!!) }
} }
} }

View file

@ -115,6 +115,7 @@ import com.nextcloud.talk.ui.dialog.ConversationsListBottomDialog
import com.nextcloud.talk.ui.dialog.FilterConversationFragment import com.nextcloud.talk.ui.dialog.FilterConversationFragment
import com.nextcloud.talk.users.UserManager import com.nextcloud.talk.users.UserManager
import com.nextcloud.talk.utils.ApiUtils import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.SpreedFeatures
import com.nextcloud.talk.utils.ClosedInterfaceImpl import com.nextcloud.talk.utils.ClosedInterfaceImpl
import com.nextcloud.talk.utils.FileUtils import com.nextcloud.talk.utils.FileUtils
import com.nextcloud.talk.utils.Mimetype 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_ID
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_TOKEN 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.bundle.BundleKeys.KEY_SHARED_TEXT
import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew.hasSpreedFeatureCapability import com.nextcloud.talk.utils.CapabilitiesUtil.hasSpreedFeatureCapability
import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew.isServerEOL import com.nextcloud.talk.utils.CapabilitiesUtil.isServerEOL
import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew.isUnifiedSearchAvailable import com.nextcloud.talk.utils.CapabilitiesUtil.isUnifiedSearchAvailable
import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew.isUserStatusAvailable import com.nextcloud.talk.utils.CapabilitiesUtil.isUserStatusAvailable
import com.nextcloud.talk.utils.permissions.PlatformPermissionUtil import com.nextcloud.talk.utils.permissions.PlatformPermissionUtil
import com.nextcloud.talk.utils.power.PowerManagerUtils import com.nextcloud.talk.utils.power.PowerManagerUtils
import com.nextcloud.talk.utils.rx.SearchViewObservable.Companion.observeSearchView import com.nextcloud.talk.utils.rx.SearchViewObservable.Companion.observeSearchView
@ -283,11 +284,11 @@ class ConversationsListActivity :
} }
currentUser = userManager.currentUser.blockingGet() currentUser = userManager.currentUser.blockingGet()
if (currentUser != null) { if (currentUser != null) {
if (isServerEOL(currentUser!!.capabilities)) { if (isServerEOL(currentUser!!.serverVersion!!.major)) {
showServerEOLDialog() showServerEOLDialog()
return return
} }
if (isUnifiedSearchAvailable(currentUser!!)) { if (isUnifiedSearchAvailable(currentUser!!.capabilities!!.spreedCapability!!)) {
searchHelper = MessageSearchHelper(unifiedSearchRepository) searchHelper = MessageSearchHelper(unifiedSearchRepository)
} }
credentials = ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token) credentials = ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token)
@ -423,7 +424,7 @@ class ConversationsListActivity :
private fun loadUserAvatar(target: Target) { private fun loadUserAvatar(target: Target) {
if (currentUser != null) { if (currentUser != null) {
val url = ApiUtils.getUrlForAvatar( val url = ApiUtils.getUrlForAvatar(
currentUser!!.baseUrl, currentUser!!.baseUrl!!,
currentUser!!.userId, currentUser!!.userId,
true true
) )
@ -433,7 +434,7 @@ class ConversationsListActivity :
context.imageLoader.enqueue( context.imageLoader.enqueue(
ImageRequest.Builder(context) ImageRequest.Builder(context)
.data(url) .data(url)
.addHeader("Authorization", credentials) .addHeader("Authorization", credentials!!)
.placeholder(R.drawable.ic_user) .placeholder(R.drawable.ic_user)
.transformations(CircleCropTransformation()) .transformations(CircleCropTransformation())
.crossfade(true) .crossfade(true)
@ -698,7 +699,10 @@ class ConversationsListActivity :
isRefreshing = true isRefreshing = true
conversationItems = ArrayList() conversationItems = ArrayList()
conversationItemsWithHeader = 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() val startNanoTime = System.nanoTime()
Log.d(TAG, "fetchData - getRooms - calling: $startNanoTime") Log.d(TAG, "fetchData - getRooms - calling: $startNanoTime")
roomsQueryDisposable = ncApi.getRooms( roomsQueryDisposable = ncApi.getRooms(
@ -868,11 +872,15 @@ class ConversationsListActivity :
private fun fetchOpenConversations(apiVersion: Int) { private fun fetchOpenConversations(apiVersion: Int) {
searchableConversationItems.clear() searchableConversationItems.clear()
searchableConversationItems.addAll(conversationItemsWithHeader) searchableConversationItems.addAll(conversationItemsWithHeader)
if (hasSpreedFeatureCapability(currentUser, "listable-rooms")) { if (hasSpreedFeatureCapability(
currentUser!!.capabilities!!.spreedCapability!!,
SpreedFeatures.LISTABLE_ROOMS
)
) {
val openConversationItems: MutableList<AbstractFlexibleItem<*>> = ArrayList() val openConversationItems: MutableList<AbstractFlexibleItem<*>> = ArrayList()
openConversationsQueryDisposable = ncApi.getOpenConversations( openConversationsQueryDisposable = ncApi.getOpenConversations(
credentials, credentials,
ApiUtils.getUrlForOpenConversations(apiVersion, currentUser!!.baseUrl) ApiUtils.getUrlForOpenConversations(apiVersion, currentUser!!.baseUrl!!)
) )
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
@ -1087,7 +1095,7 @@ class ConversationsListActivity :
clearMessageSearchResults() clearMessageSearchResults()
adapter!!.setFilter(filter) adapter!!.setFilter(filter)
adapter!!.filterItems() adapter!!.filterItems()
if (isUnifiedSearchAvailable(currentUser!!)) { if (isUnifiedSearchAvailable(currentUser!!.capabilities!!.spreedCapability!!)) {
startMessageSearch(filter) startMessageSearch(filter)
} }
} else { } else {
@ -1173,7 +1181,11 @@ class ConversationsListActivity :
private fun handleConversation(conversation: Conversation?) { private fun handleConversation(conversation: Conversation?) {
selectedConversation = conversation selectedConversation = conversation
if (selectedConversation != null) { if (selectedConversation != null) {
val hasChatPermission = ParticipantPermissions(currentUser!!, selectedConversation!!).hasChatPermission() val hasChatPermission = ParticipantPermissions(
currentUser!!.capabilities!!.spreedCapability!!,
selectedConversation!!
)
.hasChatPermission()
if (showShareToScreen) { if (showShareToScreen) {
if (hasChatPermission && if (hasChatPermission &&
!isReadOnlyConversation(selectedConversation!!) && !isReadOnlyConversation(selectedConversation!!) &&
@ -1197,7 +1209,10 @@ class ConversationsListActivity :
} }
private fun shouldShowLobby(conversation: Conversation): Boolean { 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 && return conversation.lobbyState == Conversation.LobbyState.LOBBY_STATE_MODERATORS_ONLY &&
!conversation.canModerate(currentUser!!) && !conversation.canModerate(currentUser!!) &&
!participantPermissions.canIgnoreLobby() !participantPermissions.canIgnoreLobby()
@ -1511,7 +1526,7 @@ class ConversationsListActivity :
.setNegativeButton(R.string.nc_settings_reauthorize) { _, _ -> .setNegativeButton(R.string.nc_settings_reauthorize) { _, _ ->
val intent = Intent(context, WebViewLoginActivity::class.java) val intent = Intent(context, WebViewLoginActivity::class.java)
val bundle = Bundle() 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) bundle.putBoolean(BundleKeys.KEY_REAUTHORIZE_ACCOUNT, true)
intent.putExtras(bundle) intent.putExtras(bundle)
startActivity(intent) startActivity(intent)

View file

@ -27,6 +27,7 @@ import com.nextcloud.talk.callnotification.viewmodel.CallNotificationViewModel
import com.nextcloud.talk.chat.viewmodels.ChatViewModel import com.nextcloud.talk.chat.viewmodels.ChatViewModel
import com.nextcloud.talk.conversation.viewmodel.ConversationViewModel import com.nextcloud.talk.conversation.viewmodel.ConversationViewModel
import com.nextcloud.talk.conversation.viewmodel.RenameConversationViewModel 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.conversationinfoedit.viewmodel.ConversationInfoEditViewModel
import com.nextcloud.talk.conversationlist.viewmodels.ConversationsListViewModel import com.nextcloud.talk.conversationlist.viewmodels.ConversationsListViewModel
import com.nextcloud.talk.invitation.viewmodels.InvitationsViewModel import com.nextcloud.talk.invitation.viewmodels.InvitationsViewModel
@ -137,6 +138,11 @@ abstract class ViewModelModule {
@ViewModelKey(CallNotificationViewModel::class) @ViewModelKey(CallNotificationViewModel::class)
abstract fun callNotificationViewModel(viewModel: CallNotificationViewModel): ViewModel abstract fun callNotificationViewModel(viewModel: CallNotificationViewModel): ViewModel
@Binds
@IntoMap
@ViewModelKey(ConversationInfoViewModel::class)
abstract fun conversationInfoViewModel(viewModel: ConversationInfoViewModel): ViewModel
@Binds @Binds
@IntoMap @IntoMap
@ViewModelKey(ConversationInfoEditViewModel::class) @ViewModelKey(ConversationInfoEditViewModel::class)

View file

@ -36,7 +36,7 @@ object UserMapper {
entity.id, entity.id,
entity.userId, entity.userId,
entity.username, entity.username,
entity.baseUrl, entity.baseUrl!!,
entity.token, entity.token,
entity.displayName, entity.displayName,
entity.pushConfigurationState, entity.pushConfigurationState,
@ -52,8 +52,8 @@ object UserMapper {
fun toEntity(model: User): UserEntity { fun toEntity(model: User): UserEntity {
val userEntity = when (val id = model.id) { val userEntity = when (val id = model.id) {
null -> UserEntity(userId = model.userId, username = model.username, baseUrl = model.baseUrl) null -> UserEntity(userId = model.userId, username = model.username, baseUrl = model.baseUrl!!)
else -> UserEntity(id, model.userId, model.username, model.baseUrl) else -> UserEntity(id, model.userId, model.username, model.baseUrl!!)
} }
userEntity.apply { userEntity.apply {
token = model.token token = model.token

View file

@ -45,7 +45,7 @@ data class User(
var scheduledForDeletion: Boolean = FALSE var scheduledForDeletion: Boolean = FALSE
) : Parcelable { ) : Parcelable {
fun getCredentials(): String = ApiUtils.getCredentials(username, token) fun getCredentials(): String = ApiUtils.getCredentials(username, token)!!
fun hasSpreedFeatureCapability(capabilityName: String): Boolean { fun hasSpreedFeatureCapability(capabilityName: String): Boolean {
return capabilities?.spreedCapability?.features?.contains(capabilityName) ?: false return capabilities?.spreedCapability?.features?.contains(capabilityName) ?: false

View file

@ -118,7 +118,7 @@ fun ImageView.loadUserAvatar(
ignoreCache: Boolean ignoreCache: Boolean
): io.reactivex.disposables.Disposable { ): io.reactivex.disposables.Disposable {
val imageRequestUri = ApiUtils.getUrlForAvatar( val imageRequestUri = ApiUtils.getUrlForAvatar(
user.baseUrl, user.baseUrl!!,
avatarId, avatarId,
requestBigSize requestBigSize
) )
@ -155,7 +155,7 @@ private fun ImageView.loadAvatarInternal(
user?.let { user?.let {
addHeader( addHeader(
"Authorization", "Authorization",
ApiUtils.getCredentials(user.username, user.token) ApiUtils.getCredentials(user.username, user.token)!!
) )
} }
transformations(CircleCropTransformation()) transformations(CircleCropTransformation())
@ -196,7 +196,7 @@ fun ImageView.loadThumbnail(url: String, user: User): io.reactivex.disposables.D
) { ) {
requestBuilder.addHeader( requestBuilder.addHeader(
"Authorization", "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( requestBuilder.addHeader(
"Authorization", "Authorization",
ApiUtils.getCredentials(user.username, user.token) ApiUtils.getCredentials(user.username, user.token)!!
) )
} }

View file

@ -29,29 +29,29 @@ class InvitationsRepositoryImpl(private val ncApi: NcApi) :
InvitationsRepository { InvitationsRepository {
override fun fetchInvitations(user: User): Observable<InvitationsModel> { override fun fetchInvitations(user: User): Observable<InvitationsModel> {
val credentials: String = ApiUtils.getCredentials(user.username, user.token) val credentials: String = ApiUtils.getCredentials(user.username, user.token)!!
return ncApi.getInvitations( return ncApi.getInvitations(
credentials, credentials,
ApiUtils.getUrlForInvitation(user.baseUrl) ApiUtils.getUrlForInvitation(user.baseUrl!!)
).map { mapToInvitationsModel(user, it.ocs?.data!!) } ).map { mapToInvitationsModel(user, it.ocs?.data!!) }
} }
override fun acceptInvitation(user: User, invitation: Invitation): Observable<InvitationActionModel> { override fun acceptInvitation(user: User, invitation: Invitation): Observable<InvitationActionModel> {
val credentials: String = ApiUtils.getCredentials(user.username, user.token) val credentials: String = ApiUtils.getCredentials(user.username, user.token)!!
return ncApi.acceptInvitation( return ncApi.acceptInvitation(
credentials, credentials,
ApiUtils.getUrlForInvitationAccept(user.baseUrl, invitation.id) ApiUtils.getUrlForInvitationAccept(user.baseUrl!!, invitation.id)
).map { InvitationActionModel(ActionEnum.ACCEPT, it.ocs?.meta?.statusCode!!, invitation) } ).map { InvitationActionModel(ActionEnum.ACCEPT, it.ocs?.meta?.statusCode!!, invitation) }
} }
override fun rejectInvitation(user: User, invitation: Invitation): Observable<InvitationActionModel> { override fun rejectInvitation(user: User, invitation: Invitation): Observable<InvitationActionModel> {
val credentials: String = ApiUtils.getCredentials(user.username, user.token) val credentials: String = ApiUtils.getCredentials(user.username, user.token)!!
return ncApi.rejectInvitation( return ncApi.rejectInvitation(
credentials, credentials,
ApiUtils.getUrlForInvitationReject(user.baseUrl, invitation.id) ApiUtils.getUrlForInvitationReject(user.baseUrl!!, invitation.id)
).map { InvitationActionModel(ActionEnum.REJECT, it.ocs?.meta?.statusCode!!, invitation) } ).map { InvitationActionModel(ActionEnum.REJECT, it.ocs?.meta?.statusCode!!, invitation) }
} }

View file

@ -70,7 +70,7 @@ public class AddParticipantsToConversation extends Worker {
data.getLong(BundleKeys.KEY_INTERNAL_USER_ID, -1)) data.getLong(BundleKeys.KEY_INTERNAL_USER_ID, -1))
.blockingGet(); .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 conversationToken = data.getString(BundleKeys.KEY_TOKEN);
String credentials = ApiUtils.getCredentials(user.getUsername(), user.getToken()); String credentials = ApiUtils.getCredentials(user.getUsername(), user.getToken());

View file

@ -129,7 +129,7 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar
ncApi.searchContactsByPhoneNumber( ncApi.searchContactsByPhoneNumber(
ApiUtils.getCredentials(currentUser.username, currentUser.token), ApiUtils.getCredentials(currentUser.username, currentUser.token),
ApiUtils.getUrlForSearchByNumber(currentUser.baseUrl), ApiUtils.getUrlForSearchByNumber(currentUser.baseUrl!!),
json.toRequestBody("application/json".toMediaTypeOrNull()) json.toRequestBody("application/json".toMediaTypeOrNull())
) )
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())

View file

@ -80,7 +80,7 @@ public class DeleteConversationWorker extends Worker {
User operationUser = userManager.getUserWithId(operationUserId).blockingGet(); User operationUser = userManager.getUserWithId(operationUserId).blockingGet();
if (operationUser != null) { 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()); String credentials = ApiUtils.getCredentials(operationUser.getUsername(), operationUser.getToken());
ncApi = retrofit ncApi = retrofit

View file

@ -92,7 +92,7 @@ public class LeaveConversationWorker extends Worker {
EventStatus.EventType.CONVERSATION_UPDATE, EventStatus.EventType.CONVERSATION_UPDATE,
true); 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, ncApi.removeSelfFromRoom(credentials, ApiUtils.getUrlForParticipantsSelf(apiVersion,
operationUser.getBaseUrl(), operationUser.getBaseUrl(),

View file

@ -248,7 +248,7 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor
val soundUri = getCallRingtoneUri(applicationContext, appPreferences) val soundUri = getCallRingtoneUri(applicationContext, appPreferences)
val notificationChannelId = NotificationUtils.NotificationChannels.NOTIFICATION_CHANNEL_CALLS_V4.name 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 baseUrl = uri.host
val notification = val notification =
@ -279,7 +279,7 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor
credentials = ApiUtils.getCredentials( credentials = ApiUtils.getCredentials(
signatureVerification.user!!.username, signatureVerification.user!!.username,
signatureVerification.user!!.token signatureVerification.user!!.token
) )!!
ncApi = retrofit!!.newBuilder().client( ncApi = retrofit!!.newBuilder().client(
okHttpClient!!.newBuilder().cookieJar( okHttpClient!!.newBuilder().cookieJar(
JavaNetCookieJar( JavaNetCookieJar(
@ -338,7 +338,7 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor
ncApi.getNcNotification( ncApi.getNcNotification(
credentials, credentials,
ApiUtils.getUrlForNcNotificationWithId( ApiUtils.getUrlForNcNotificationWithId(
user!!.baseUrl, user!!.baseUrl!!,
(pushMessage.notificationId!!).toString() (pushMessage.notificationId!!).toString()
) )
) )
@ -451,7 +451,7 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor
0 0
} }
val pendingIntent = PendingIntent.getActivity(context, requestCode, intent, intentFlag) 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 val baseUrl = uri.host
var contentTitle: CharSequence? = "" var contentTitle: CharSequence? = ""
@ -601,12 +601,12 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor
val baseUrl = signatureVerification.user!!.baseUrl val baseUrl = signatureVerification.user!!.baseUrl
val avatarUrl = if ("user" == userType) { val avatarUrl = if ("user" == userType) {
ApiUtils.getUrlForAvatar( ApiUtils.getUrlForAvatar(
baseUrl, baseUrl!!,
notificationUser.id, notificationUser.id,
false false
) )
} else { } else {
ApiUtils.getUrlForGuestAvatar(baseUrl, notificationUser.name, false) ApiUtils.getUrlForGuestAvatar(baseUrl!!, notificationUser.name, false)
} }
person.setIcon(loadAvatarSync(avatarUrl, context!!)) person.setIcon(loadAvatarSync(avatarUrl, context!!))
} }
@ -840,8 +840,8 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor
var inCallOnDifferentDevice = false var inCallOnDifferentDevice = false
val apiVersion = ApiUtils.getConversationApiVersion( val apiVersion = ApiUtils.getConversationApiVersion(
signatureVerification.user, signatureVerification.user!!,
intArrayOf(ApiUtils.APIv4, 1) intArrayOf(ApiUtils.API_V4, 1)
) )
var isCallNotificationVisible = true var isCallNotificationVisible = true
@ -850,8 +850,8 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor
credentials, credentials,
ApiUtils.getUrlForCall( ApiUtils.getUrlForCall(
apiVersion, apiVersion,
signatureVerification.user!!.baseUrl, signatureVerification.user!!.baseUrl!!,
pushMessage.id pushMessage.id!!
) )
) )
.repeatWhen { completed -> .repeatWhen { completed ->
@ -920,10 +920,10 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor
if (isOngoingCallNotificationVisible) { if (isOngoingCallNotificationVisible) {
val apiVersion = ApiUtils.getConversationApiVersion( val apiVersion = ApiUtils.getConversationApiVersion(
signatureVerification.user, signatureVerification.user!!,
intArrayOf( intArrayOf(
ApiUtils.APIv4, ApiUtils.API_V4,
ApiUtils.APIv3, ApiUtils.API_V3,
1 1
) )
) )
@ -931,7 +931,7 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor
credentials, credentials,
ApiUtils.getUrlForRoom( ApiUtils.getUrlForRoom(
apiVersion, apiVersion,
signatureVerification.user?.baseUrl, signatureVerification.user?.baseUrl!!,
pushMessage.id pushMessage.id
) )
) )

View file

@ -62,7 +62,7 @@ class ShareOperationWorker(context: Context, workerParams: WorkerParameters) : W
for (filePath in filesArray) { for (filePath in filesArray) {
ncApi.createRemoteShare( ncApi.createRemoteShare(
credentials, credentials,
ApiUtils.getSharingUrl(baseUrl), ApiUtils.getSharingUrl(baseUrl!!),
filePath, filePath,
roomToken, roomToken,
"10", "10",
@ -87,7 +87,7 @@ class ShareOperationWorker(context: Context, workerParams: WorkerParameters) : W
val operationsUser = userManager.getUserWithId(userId).blockingGet() val operationsUser = userManager.getUserWithId(userId).blockingGet()
baseUrl = operationsUser.baseUrl baseUrl = operationsUser.baseUrl
credentials = ApiUtils.getCredentials(operationsUser.username, operationsUser.token) credentials = ApiUtils.getCredentials(operationsUser.username, operationsUser.token)!!
} }
companion object { companion object {

View file

@ -85,7 +85,7 @@ public class SignalingSettingsWorker extends Worker {
for (User user : userEntityObjectList) { 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( ncApi.getSignalingSettings(
ApiUtils.getCredentials(user.getUsername(), user.getToken()), ApiUtils.getCredentials(user.getUsername(), user.getToken()),

View file

@ -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_FROM_NOTIFICATION_START_CALL
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_INTERNAL_USER_ID 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.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.permissions.PlatformPermissionUtil
import com.nextcloud.talk.utils.preferences.AppPreferences import com.nextcloud.talk.utils.preferences.AppPreferences
import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.MediaType.Companion.toMediaTypeOrNull
@ -186,7 +186,9 @@ class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerPa
} }
private fun getRemotePath(currentUser: User): String { private fun getRemotePath(currentUser: User): String {
var remotePath = CapabilitiesUtilNew.getAttachmentFolder(currentUser)!! + "/" + fileName var remotePath = CapabilitiesUtil.getAttachmentFolder(
currentUser.capabilities!!.spreedCapability!!
) + "/" + fileName
remotePath = RemoteFileUtils.getNewPathIfFileExists( remotePath = RemoteFileUtils.getNewPathIfFileExists(
ncApi, ncApi,
currentUser, currentUser,

View file

@ -64,6 +64,7 @@ class GeocodingActivity :
lateinit var okHttpClient: OkHttpClient lateinit var okHttpClient: OkHttpClient
lateinit var roomToken: String lateinit var roomToken: String
private var chatApiVersion: Int = 1
private var nominatimClient: TalkJsonNominatimClient? = null private var nominatimClient: TalkJsonNominatimClient? = null
private var searchItem: MenuItem? = null private var searchItem: MenuItem? = null
@ -86,6 +87,7 @@ class GeocodingActivity :
Configuration.getInstance().load(context, PreferenceManager.getDefaultSharedPreferences(context)) Configuration.getInstance().load(context, PreferenceManager.getDefaultSharedPreferences(context))
roomToken = intent.getStringExtra(BundleKeys.KEY_ROOM_TOKEN)!! roomToken = intent.getStringExtra(BundleKeys.KEY_ROOM_TOKEN)!!
chatApiVersion = intent.getIntExtra(BundleKeys.KEY_CHAT_API_VERSION, 1)
recyclerView = findViewById(R.id.geocoding_results) recyclerView = findViewById(R.id.geocoding_results)
recyclerView.layoutManager = LinearLayoutManager(this) recyclerView.layoutManager = LinearLayoutManager(this)
@ -130,6 +132,7 @@ class GeocodingActivity :
val intent = Intent(this@GeocodingActivity, LocationPickerActivity::class.java) val intent = Intent(this@GeocodingActivity, LocationPickerActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
intent.putExtra(BundleKeys.KEY_ROOM_TOKEN, roomToken) intent.putExtra(BundleKeys.KEY_ROOM_TOKEN, roomToken)
intent.putExtra(BundleKeys.KEY_CHAT_API_VERSION, chatApiVersion)
intent.putExtra(BundleKeys.KEY_GEOCODING_RESULT, geocodingResult) intent.putExtra(BundleKeys.KEY_GEOCODING_RESULT, geocodingResult)
startActivity(intent) startActivity(intent)
} }
@ -158,6 +161,7 @@ class GeocodingActivity :
val intent = Intent(this@GeocodingActivity, LocationPickerActivity::class.java) val intent = Intent(this@GeocodingActivity, LocationPickerActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
intent.putExtra(BundleKeys.KEY_ROOM_TOKEN, roomToken) intent.putExtra(BundleKeys.KEY_ROOM_TOKEN, roomToken)
intent.putExtra(BundleKeys.KEY_CHAT_API_VERSION, chatApiVersion)
intent.putExtra(BundleKeys.KEY_GEOCODING_RESULT, geocodingResult) intent.putExtra(BundleKeys.KEY_GEOCODING_RESULT, geocodingResult)
startActivity(intent) startActivity(intent)
} }
@ -217,6 +221,7 @@ class GeocodingActivity :
val intent = Intent(context, LocationPickerActivity::class.java) val intent = Intent(context, LocationPickerActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
intent.putExtra(BundleKeys.KEY_ROOM_TOKEN, roomToken) intent.putExtra(BundleKeys.KEY_ROOM_TOKEN, roomToken)
intent.putExtra(BundleKeys.KEY_CHAT_API_VERSION, chatApiVersion)
startActivity(intent) startActivity(intent)
return true return true
} }

View file

@ -58,6 +58,7 @@ import com.nextcloud.talk.users.UserManager
import com.nextcloud.talk.utils.ApiUtils import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.DisplayUtils import com.nextcloud.talk.utils.DisplayUtils
import com.nextcloud.talk.utils.bundle.BundleKeys 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_GEOCODING_RESULT
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_TOKEN import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_TOKEN
import fr.dudie.nominatim.client.TalkJsonNominatimClient import fr.dudie.nominatim.client.TalkJsonNominatimClient
@ -103,6 +104,7 @@ class LocationPickerActivity :
var nominatimClient: TalkJsonNominatimClient? = null var nominatimClient: TalkJsonNominatimClient? = null
lateinit var roomToken: String lateinit var roomToken: String
private var chatApiVersion: Int = 1
var geocodingResult: GeocodingResult? = null var geocodingResult: GeocodingResult? = null
var myLocation: GeoPoint = GeoPoint(COORDINATE_ZERO, COORDINATE_ZERO) var myLocation: GeoPoint = GeoPoint(COORDINATE_ZERO, COORDINATE_ZERO)
@ -130,6 +132,7 @@ class LocationPickerActivity :
NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this) NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this)
roomToken = intent.getStringExtra(KEY_ROOM_TOKEN)!! roomToken = intent.getStringExtra(KEY_ROOM_TOKEN)!!
chatApiVersion = intent.getIntExtra(KEY_CHAT_API_VERSION, 1)
geocodingResult = intent.getParcelableExtra(KEY_GEOCODING_RESULT) geocodingResult = intent.getParcelableExtra(KEY_GEOCODING_RESULT)
if (savedInstanceState != null) { if (savedInstanceState != null) {
@ -244,6 +247,7 @@ class LocationPickerActivity :
val intent = Intent(this, GeocodingActivity::class.java) val intent = Intent(this, GeocodingActivity::class.java)
intent.putExtra(BundleKeys.KEY_GEOCODING_QUERY, query) intent.putExtra(BundleKeys.KEY_GEOCODING_QUERY, query)
intent.putExtra(KEY_ROOM_TOKEN, roomToken) intent.putExtra(KEY_ROOM_TOKEN, roomToken)
intent.putExtra(KEY_CHAT_API_VERSION, chatApiVersion)
startActivity(intent) startActivity(intent)
} }
return true return true
@ -465,11 +469,10 @@ class LocationPickerActivity :
"\"longitude\":\"$selectedLon\",\"name\":\"$locationNameToShare\"}" "\"longitude\":\"$selectedLon\",\"name\":\"$locationNameToShare\"}"
val currentUser = userManager.currentUser.blockingGet() val currentUser = userManager.currentUser.blockingGet()
val apiVersion = ApiUtils.getChatApiVersion(currentUser, intArrayOf(1))
ncApi.sendLocation( ncApi.sendLocation(
ApiUtils.getCredentials(currentUser.username, currentUser.token), ApiUtils.getCredentials(currentUser.username, currentUser.token),
ApiUtils.getUrlToSendLocation(apiVersion, currentUser.baseUrl, roomToken), ApiUtils.getUrlToSendLocation(chatApiVersion, currentUser.baseUrl!!, roomToken),
"geo-location", "geo-location",
objectId, objectId,
metaData metaData

View file

@ -27,5 +27,5 @@ import kotlinx.parcelize.Parcelize
@Parcelize @Parcelize
data class RetrofitBucket( data class RetrofitBucket(
var url: String? = null, var url: String? = null,
var queryMap: Map<String, String>? = null var queryMap: MutableMap<String, String>? = null
) : Parcelable ) : Parcelable

View file

@ -3,7 +3,7 @@ package com.nextcloud.talk.models.domain
import com.nextcloud.talk.models.json.conversations.Conversation import com.nextcloud.talk.models.json.conversations.Conversation
class ConversationModel( class ConversationModel(
var roomId: String?, var roomId: String? = null,
var token: String? = null, var token: String? = null,
var name: String? = null, var name: String? = null,
var displayName: String? = null, var displayName: String? = null,
@ -42,7 +42,11 @@ class ConversationModel(
var statusClearAt: Long? = 0, var statusClearAt: Long? = 0,
var callRecording: Int = 0, var callRecording: Int = 0,
var avatarVersion: String? = null, 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 { companion object {
@ -95,7 +99,11 @@ class ConversationModel(
statusClearAt = conversation.statusClearAt, statusClearAt = conversation.statusClearAt,
callRecording = conversation.callRecording, callRecording = conversation.callRecording,
avatarVersion = conversation.avatarVersion, avatarVersion = conversation.avatarVersion,
hasCustomAvatar = conversation.hasCustomAvatar hasCustomAvatar = conversation.hasCustomAvatar,
callStartTime = conversation.callStartTime,
recordingConsentRequired = conversation.recordingConsentRequired,
remoteServer = conversation.remoteServer,
remoteToken = conversation.remoteToken
) )
} }
} }

View file

@ -0,0 +1,45 @@
/*
* Nextcloud Talk application
*
* @author Mario Danic
* Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.nextcloud.talk.models.domain.converters
import com.bluelinelabs.logansquare.typeconverters.IntBasedTypeConverter
import com.nextcloud.talk.models.domain.NotificationLevel
class DomainEnumNotificationLevelConverter : IntBasedTypeConverter<NotificationLevel>() {
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
}
}
}

View file

@ -0,0 +1,42 @@
/*
* Nextcloud Talk application
*
* @author Mario Danic
* @author Tim Krüger
* @author Andy Scherzinger
* Copyright (C) 2022 Andy Scherzinger <info@andy-scherzinger.de>
* Copyright (C) 2022 Tim Krüger <t@timkrueger.me>
* Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
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)
}

View file

@ -0,0 +1,37 @@
/*
* Nextcloud Talk application
*
* @author Mario Danic
* @author Tim Krüger
* Copyright (C) 2022 Tim Krüger <t@timkrueger.me>
* Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
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)
}

View file

@ -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.chat.ChatUtils.Companion.getParsedMessage
import com.nextcloud.talk.models.json.converters.EnumSystemMessageTypeConverter import com.nextcloud.talk.models.json.converters.EnumSystemMessageTypeConverter
import com.nextcloud.talk.utils.ApiUtils 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.IUser
import com.stfalcon.chatkit.commons.models.MessageContentType import com.stfalcon.chatkit.commons.models.MessageContentType
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
@ -213,7 +213,7 @@ data class ChatMessage(
@Suppress("ReturnCount") @Suppress("ReturnCount")
fun isLinkPreview(): Boolean { fun isLinkPreview(): Boolean {
if (CapabilitiesUtilNew.isLinkPreviewAvailable(activeUser!!)) { if (CapabilitiesUtil.isLinkPreviewAvailable(activeUser!!)) {
val regexStringFromServer = activeUser?.capabilities?.coreCapability?.referenceRegex val regexStringFromServer = activeUser?.capabilities?.coreCapability?.referenceRegex
val regexFromServer = regexStringFromServer?.toRegex(setOf(RegexOption.MULTILINE, RegexOption.IGNORE_CASE)) val regexFromServer = regexStringFromServer?.toRegex(setOf(RegexOption.MULTILINE, RegexOption.IGNORE_CASE))
@ -249,8 +249,8 @@ data class ChatMessage(
if (!isVoiceMessage) { if (!isVoiceMessage) {
if (activeUser != null && activeUser!!.baseUrl != null) { if (activeUser != null && activeUser!!.baseUrl != null) {
return ApiUtils.getUrlForFilePreviewWithFileId( return ApiUtils.getUrlForFilePreviewWithFileId(
activeUser!!.baseUrl, activeUser!!.baseUrl!!,
individualHashMap["id"], individualHashMap["id"]!!,
sharedApplication!!.resources.getDimensionPixelSize(R.dimen.maximum_file_preview_size) sharedApplication!!.resources.getDimensionPixelSize(R.dimen.maximum_file_preview_size)
) )
} else { } else {
@ -413,11 +413,11 @@ data class ChatMessage(
null null
} }
actorType == "users" -> { actorType == "users" -> {
ApiUtils.getUrlForAvatar(activeUser!!.baseUrl, actorId, true) ApiUtils.getUrlForAvatar(activeUser!!.baseUrl!!, actorId, true)
} }
actorType == "bridged" -> { actorType == "bridged" -> {
ApiUtils.getUrlForAvatar( ApiUtils.getUrlForAvatar(
activeUser!!.baseUrl, activeUser!!.baseUrl!!,
"bridge-bot", "bridge-bot",
true true
) )
@ -427,7 +427,7 @@ data class ChatMessage(
if (!TextUtils.isEmpty(actorDisplayName)) { if (!TextUtils.isEmpty(actorDisplayName)) {
apiId = actorDisplayName apiId = actorDisplayName
} }
ApiUtils.getUrlForGuestAvatar(activeUser!!.baseUrl, apiId, true) ApiUtils.getUrlForGuestAvatar(activeUser!!.baseUrl!!, apiId, true)
} }
} }
} }

View file

@ -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.EnumReadOnlyConversationConverter
import com.nextcloud.talk.models.json.converters.EnumRoomTypeConverter import com.nextcloud.talk.models.json.converters.EnumRoomTypeConverter
import com.nextcloud.talk.models.json.participants.Participant.ParticipantType 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.ConversationUtils
import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew import com.nextcloud.talk.utils.CapabilitiesUtil
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
@Parcelize @Parcelize
@ -160,7 +161,13 @@ data class Conversation(
var callStartTime: Long? = null, var callStartTime: Long? = null,
@JsonField(name = ["recordingConsent"]) @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 { ) : Parcelable {
// This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject' // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject'
@ -185,13 +192,20 @@ data class Conversation(
@Deprecated("Use ConversationUtil") @Deprecated("Use ConversationUtil")
private fun isLockedOneToOne(conversationUser: User): Boolean { private fun isLockedOneToOne(conversationUser: User): Boolean {
return type == ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL && 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") @Deprecated("Use ConversationUtil")
fun canModerate(conversationUser: User): Boolean { fun canModerate(conversationUser: User): Boolean {
return isParticipantOwnerOrModerator && return isParticipantOwnerOrModerator &&
!isLockedOneToOne(conversationUser) && ConversationUtils.isLockedOneToOne(
ConversationModel.mapToConversationModel(this),
conversationUser
.capabilities?.spreedCapability!!
) &&
type != ConversationType.FORMER_ONE_TO_ONE && type != ConversationType.FORMER_ONE_TO_ONE &&
!ConversationUtils.isNoteToSelfConversation(ConversationModel.mapToConversationModel(this)) !ConversationUtils.isNoteToSelfConversation(ConversationModel.mapToConversationModel(this))
} }

View file

@ -31,14 +31,14 @@ class OpenConversationsRepositoryImpl(private val ncApi: NcApi, currentUserProvi
OpenConversationsRepository { OpenConversationsRepository {
val currentUser: User = currentUserProvider.currentUser.blockingGet() 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<OpenConversationsModel> { override fun fetchConversations(): Observable<OpenConversationsModel> {
return ncApi.getOpenConversations( return ncApi.getOpenConversations(
credentials, credentials,
ApiUtils.getUrlForOpenConversations(apiVersion, currentUser.baseUrl) ApiUtils.getUrlForOpenConversations(apiVersion, currentUser.baseUrl!!)
).map { mapToOpenConversationsModel(it.ocs?.data!!) } ).map { mapToOpenConversationsModel(it.ocs?.data!!) }
} }

View file

@ -37,7 +37,7 @@ class PollRepositoryImpl(private val ncApi: NcApi, private val currentUserProvid
PollRepository { PollRepository {
val currentUser: User = currentUserProvider.currentUser.blockingGet() 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( override fun createPoll(
roomToken: String, roomToken: String,
@ -49,7 +49,7 @@ class PollRepositoryImpl(private val ncApi: NcApi, private val currentUserProvid
return ncApi.createPoll( return ncApi.createPoll(
credentials, credentials,
ApiUtils.getUrlForPoll( ApiUtils.getUrlForPoll(
currentUser.baseUrl, currentUser.baseUrl!!,
roomToken roomToken
), ),
question, question,
@ -63,7 +63,7 @@ class PollRepositoryImpl(private val ncApi: NcApi, private val currentUserProvid
return ncApi.getPoll( return ncApi.getPoll(
credentials, credentials,
ApiUtils.getUrlForPoll( ApiUtils.getUrlForPoll(
currentUser.baseUrl, currentUser.baseUrl!!,
roomToken, roomToken,
pollId pollId
) )
@ -74,7 +74,7 @@ class PollRepositoryImpl(private val ncApi: NcApi, private val currentUserProvid
return ncApi.votePoll( return ncApi.votePoll(
credentials, credentials,
ApiUtils.getUrlForPoll( ApiUtils.getUrlForPoll(
currentUser.baseUrl, currentUser.baseUrl!!,
roomToken, roomToken,
pollId pollId
), ),
@ -86,7 +86,7 @@ class PollRepositoryImpl(private val ncApi: NcApi, private val currentUserProvid
return ncApi.closePoll( return ncApi.closePoll(
credentials, credentials,
ApiUtils.getUrlForPoll( ApiUtils.getUrlForPoll(
currentUser.baseUrl, currentUser.baseUrl!!,
roomToken, roomToken,
pollId pollId
) )

View file

@ -77,6 +77,7 @@ public class MentionAutocompletePresenter extends RecyclerViewPresenter<Mention>
private Context context; private Context context;
private String roomToken; private String roomToken;
private int chatApiVersion;
private List<AbstractFlexibleItem> abstractFlexibleItemList = new ArrayList<>(); private List<AbstractFlexibleItem> abstractFlexibleItemList = new ArrayList<>();
@ -87,10 +88,11 @@ public class MentionAutocompletePresenter extends RecyclerViewPresenter<Mention>
currentUser = userManager.getCurrentUser().blockingGet(); currentUser = userManager.getCurrentUser().blockingGet();
} }
public MentionAutocompletePresenter(Context context, String roomToken) { public MentionAutocompletePresenter(Context context, String roomToken, int chatApiVersion) {
super(context); super(context);
this.roomToken = roomToken; this.roomToken = roomToken;
this.context = context; this.context = context;
this.chatApiVersion = chatApiVersion;
NextcloudTalkApplication.Companion.getSharedApplication().getComponentApplication().inject(this); NextcloudTalkApplication.Companion.getSharedApplication().getComponentApplication().inject(this);
currentUser = userManager.getCurrentUser().blockingGet(); currentUser = userManager.getCurrentUser().blockingGet();
} }
@ -120,8 +122,6 @@ public class MentionAutocompletePresenter extends RecyclerViewPresenter<Mention>
queryString = ""; queryString = "";
} }
int apiVersion = ApiUtils.getChatApiVersion(currentUser, new int[] {1});
adapter.setFilter(queryString); adapter.setFilter(queryString);
Map<String, String> queryMap = new HashMap<>(); Map<String, String> queryMap = new HashMap<>();
@ -129,7 +129,7 @@ public class MentionAutocompletePresenter extends RecyclerViewPresenter<Mention>
ncApi.getMentionAutocompleteSuggestions( ncApi.getMentionAutocompleteSuggestions(
ApiUtils.getCredentials(currentUser.getUsername(), currentUser.getToken()), ApiUtils.getCredentials(currentUser.getUsername(), currentUser.getToken()),
ApiUtils.getUrlForMentionSuggestions(apiVersion, currentUser.getBaseUrl(), roomToken), ApiUtils.getUrlForMentionSuggestions(chatApiVersion, currentUser.getBaseUrl(), roomToken),
queryString, 5, queryMap) queryString, 5, queryMap)
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())

View file

@ -66,12 +66,13 @@ import com.nextcloud.talk.ui.dialog.ScopeDialog
import com.nextcloud.talk.ui.theme.ViewThemeUtils import com.nextcloud.talk.ui.theme.ViewThemeUtils
import com.nextcloud.talk.users.UserManager import com.nextcloud.talk.users.UserManager
import com.nextcloud.talk.utils.ApiUtils import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.SpreedFeatures
import com.nextcloud.talk.utils.DisplayUtils import com.nextcloud.talk.utils.DisplayUtils
import com.nextcloud.talk.utils.Mimetype.IMAGE_JPG import com.nextcloud.talk.utils.Mimetype.IMAGE_JPG
import com.nextcloud.talk.utils.Mimetype.IMAGE_PREFIX_GENERIC import com.nextcloud.talk.utils.Mimetype.IMAGE_PREFIX_GENERIC
import com.nextcloud.talk.utils.PickImage import com.nextcloud.talk.utils.PickImage
import com.nextcloud.talk.utils.PickImage.Companion.REQUEST_PERMISSION_CAMERA 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.Observer
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable import io.reactivex.disposables.Disposable
@ -127,7 +128,7 @@ class ProfileActivity : BaseActivity() {
binding.avatarDelete.setOnClickListener { binding.avatarDelete.setOnClickListener {
ncApi.deleteAvatar( ncApi.deleteAvatar(
credentials, credentials,
ApiUtils.getUrlForTempAvatar(currentUser!!.baseUrl) ApiUtils.getUrlForTempAvatar(currentUser!!.baseUrl!!)
) )
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
@ -154,7 +155,7 @@ class ProfileActivity : BaseActivity() {
}) })
} }
binding.avatarImage.let { ViewCompat.setTransitionName(it, "userAvatar.transitionTag") } 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) .retry(DEFAULT_RETRIES)
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
@ -226,13 +227,17 @@ class ProfileActivity : BaseActivity() {
item.icon = ContextCompat.getDrawable(this, R.drawable.ic_check) item.icon = ContextCompat.getDrawable(this, R.drawable.ic_check)
binding.emptyList.root.visibility = View.GONE binding.emptyList.root.visibility = View.GONE
binding.userinfoList.visibility = View.VISIBLE 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 // TODO later avatar can also be checked via user fields, for now it is in Talk capability
binding.avatarButtons.visibility = View.VISIBLE binding.avatarButtons.visibility = View.VISIBLE
} }
ncApi.getEditableUserProfileFields( ncApi.getEditableUserProfileFields(
ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token), ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token),
ApiUtils.getUrlForUserFields(currentUser!!.baseUrl) ApiUtils.getUrlForUserFields(currentUser!!.baseUrl!!)
) )
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
@ -292,7 +297,7 @@ class ProfileActivity : BaseActivity() {
private fun showUserProfile() { private fun showUserProfile() {
if (currentUser!!.baseUrl != null) { 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) DisplayUtils.loadAvatarImage(currentUser, binding.avatarImage, false)
if (!TextUtils.isEmpty(userInfo?.displayName)) { if (!TextUtils.isEmpty(userInfo?.displayName)) {
@ -327,10 +332,10 @@ class ProfileActivity : BaseActivity() {
} }
// show edit button // show edit button
if (CapabilitiesUtilNew.canEditScopes(currentUser!!)) { if (CapabilitiesUtil.canEditScopes(currentUser!!)) {
ncApi.getEditableUserProfileFields( ncApi.getEditableUserProfileFields(
ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token), ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token),
ApiUtils.getUrlForUserFields(currentUser!!.baseUrl) ApiUtils.getUrlForUserFields(currentUser!!.baseUrl!!)
) )
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
@ -438,7 +443,7 @@ class ProfileActivity : BaseActivity() {
val credentials = ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token) val credentials = ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token)
ncApi.setUserData( ncApi.setUserData(
credentials, credentials,
ApiUtils.getUrlForUserData(currentUser!!.baseUrl, currentUser!!.userId), ApiUtils.getUrlForUserData(currentUser!!.baseUrl!!, currentUser!!.userId!!),
item.field.fieldName, item.field.fieldName,
item.text item.text
) )
@ -535,7 +540,7 @@ class ProfileActivity : BaseActivity() {
// upload file // upload file
ncApi.uploadAvatar( ncApi.uploadAvatar(
ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token), ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token),
ApiUtils.getUrlForTempAvatar(currentUser!!.baseUrl), ApiUtils.getUrlForTempAvatar(currentUser!!.baseUrl!!),
filePart filePart
) )
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
@ -569,7 +574,7 @@ class ProfileActivity : BaseActivity() {
val credentials = ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token) val credentials = ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token)
ncApi.setUserData( ncApi.setUserData(
credentials, credentials,
ApiUtils.getUrlForUserData(currentUser!!.baseUrl, currentUser!!.userId), ApiUtils.getUrlForUserData(currentUser!!.baseUrl!!, currentUser!!.userId!!),
item.field.scopeName, item.field.scopeName,
item.scope!!.name item.scope!!.name
) )

View file

@ -31,7 +31,7 @@ class RequestAssistanceRepositoryImpl(private val ncApi: NcApi, currentUserProvi
RequestAssistanceRepository { RequestAssistanceRepository {
val currentUser: User = currentUserProvider.currentUser.blockingGet() 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 var apiVersion = 1

View file

@ -91,8 +91,8 @@ class DirectReplyReceiver : BroadcastReceiver() {
private fun sendDirectReply() { private fun sendDirectReply() {
val credentials = ApiUtils.getCredentials(currentUser.username, currentUser.token) 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.getUrlForChat(apiVersion, currentUser.baseUrl, roomToken) val url = ApiUtils.getUrlForChat(apiVersion, currentUser.baseUrl!!, roomToken!!)
ncApi.sendChatMessage(credentials, url, replyMessage, currentUser.displayName, null, false) ncApi.sendChatMessage(credentials, url, replyMessage, currentUser.displayName, null, false)
?.subscribeOn(Schedulers.io()) ?.subscribeOn(Schedulers.io())
@ -153,7 +153,7 @@ class DirectReplyReceiver : BroadcastReceiver() {
// Add reply // Add reply
Single.fromCallable { Single.fromCallable {
val avatarUrl = ApiUtils.getUrlForAvatar(currentUser.baseUrl, currentUser.userId, false) val avatarUrl = ApiUtils.getUrlForAvatar(currentUser.baseUrl!!, currentUser.userId, false)
val me = Person.Builder() val me = Person.Builder()
.setName(currentUser.displayName) .setName(currentUser.displayName)
.setIcon(NotificationUtils.loadAvatarSync(avatarUrl, context)) .setIcon(NotificationUtils.loadAvatarSync(avatarUrl, context))

View file

@ -80,11 +80,11 @@ class MarkAsReadReceiver : BroadcastReceiver() {
private fun markAsRead() { private fun markAsRead() {
val credentials = ApiUtils.getCredentials(currentUser.username, currentUser.token) 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( val url = ApiUtils.getUrlForChatReadMarker(
apiVersion, apiVersion,
currentUser.baseUrl, currentUser.baseUrl!!,
roomToken roomToken!!
) )
ncApi.setChatReadMarker(credentials, url, messageId) ncApi.setChatReadMarker(credentials, url, messageId)

View file

@ -94,7 +94,7 @@ class RemoteFileBrowserItemsListViewHolder(
if (item.hasPreview) { if (item.hasPreview) {
val path = ApiUtils.getUrlForFilePreviewWithRemotePath( val path = ApiUtils.getUrlForFilePreviewWithRemotePath(
currentUser.baseUrl, currentUser.baseUrl!!,
item.path, item.path,
fileIcon.context.resources.getDimensionPixelSize(R.dimen.small_item_height) fileIcon.context.resources.getDimensionPixelSize(R.dimen.small_item_height)
) )

View file

@ -33,7 +33,7 @@ class CallRecordingRepositoryImpl(private val ncApi: NcApi, currentUserProvider:
CallRecordingRepository { CallRecordingRepository {
val currentUser: User = currentUserProvider.currentUser.blockingGet() 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 var apiVersion = 1
@ -42,7 +42,7 @@ class CallRecordingRepositoryImpl(private val ncApi: NcApi, currentUserProvider:
credentials, credentials,
ApiUtils.getUrlForRecording( ApiUtils.getUrlForRecording(
apiVersion, apiVersion,
currentUser.baseUrl, currentUser.baseUrl!!,
roomToken roomToken
), ),
1 1
@ -54,7 +54,7 @@ class CallRecordingRepositoryImpl(private val ncApi: NcApi, currentUserProvider:
credentials, credentials,
ApiUtils.getUrlForRecording( ApiUtils.getUrlForRecording(
apiVersion, apiVersion,
currentUser.baseUrl, currentUser.baseUrl!!,
roomToken roomToken
) )
).map { mapToStopCallRecordingModel(it.ocs?.meta!!) } ).map { mapToStopCallRecordingModel(it.ocs?.meta!!) }

View file

@ -38,12 +38,12 @@ class ConversationsRepositoryImpl(private val api: NcApi, private val userProvid
get() = userProvider.currentUser.blockingGet() get() = userProvider.currentUser.blockingGet()
private val credentials: String 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<AllowGuestsResult> { override fun allowGuests(token: String, allow: Boolean): Observable<AllowGuestsResult> {
val url = ApiUtils.getUrlForRoomPublic( val url = ApiUtils.getUrlForRoomPublic(
apiVersion(), apiVersion(),
user.baseUrl, user.baseUrl!!,
token token
) )
@ -100,7 +100,7 @@ class ConversationsRepositoryImpl(private val api: NcApi, private val userProvid
} }
private fun apiVersion(): Int { private fun apiVersion(): Int {
return ApiUtils.getConversationApiVersion(user, intArrayOf(ApiUtils.APIv4)) return ApiUtils.getConversationApiVersion(user, intArrayOf(ApiUtils.API_V4))
} }
companion object { companion object {

View file

@ -34,13 +34,13 @@ class ReactionsRepositoryImpl(private val ncApi: NcApi, currentUserProvider: Cur
ReactionsRepository { ReactionsRepository {
val currentUser: User = currentUserProvider.currentUser.blockingGet() 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<ReactionAddedModel> { override fun addReaction(roomToken: String, message: ChatMessage, emoji: String): Observable<ReactionAddedModel> {
return ncApi.sendReaction( return ncApi.sendReaction(
credentials, credentials,
ApiUtils.getUrlForMessageReaction( ApiUtils.getUrlForMessageReaction(
currentUser.baseUrl, currentUser.baseUrl!!,
roomToken, roomToken,
message.id message.id
), ),
@ -56,7 +56,7 @@ class ReactionsRepositoryImpl(private val ncApi: NcApi, currentUserProvider: Cur
return ncApi.deleteReaction( return ncApi.deleteReaction(
credentials, credentials,
ApiUtils.getUrlForMessageReaction( ApiUtils.getUrlForMessageReaction(
currentUser.baseUrl, currentUser.baseUrl!!,
roomToken, roomToken,
message.id message.id
), ),

View file

@ -37,7 +37,7 @@ class UnifiedSearchRepositoryImpl(private val api: NcApi, private val userProvid
get() = userProvider.currentUser.blockingGet() get() = userProvider.currentUser.blockingGet()
private val credentials: String private val credentials: String
get() = ApiUtils.getCredentials(user.username, user.token) get() = ApiUtils.getCredentials(user.username, user.token)!!
override fun searchMessages( override fun searchMessages(
searchTerm: String, searchTerm: String,

View file

@ -90,6 +90,7 @@ import com.nextcloud.talk.models.json.userprofile.UserProfileOverall
import com.nextcloud.talk.profile.ProfileActivity import com.nextcloud.talk.profile.ProfileActivity
import com.nextcloud.talk.users.UserManager import com.nextcloud.talk.users.UserManager
import com.nextcloud.talk.utils.ApiUtils import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.SpreedFeatures
import com.nextcloud.talk.utils.ClosedInterfaceImpl import com.nextcloud.talk.utils.ClosedInterfaceImpl
import com.nextcloud.talk.utils.DisplayUtils import com.nextcloud.talk.utils.DisplayUtils
import com.nextcloud.talk.utils.LoggingUtils.sendMailWithAttachment 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.getCallRingtoneUri
import com.nextcloud.talk.utils.NotificationUtils.getMessageRingtoneUri import com.nextcloud.talk.utils.NotificationUtils.getMessageRingtoneUri
import com.nextcloud.talk.utils.SecurityUtils 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.database.user.CurrentUserProviderNew
import com.nextcloud.talk.utils.permissions.PlatformPermissionUtil import com.nextcloud.talk.utils.permissions.PlatformPermissionUtil
import com.nextcloud.talk.utils.power.PowerManagerUtils import com.nextcloud.talk.utils.power.PowerManagerUtils
@ -266,7 +267,11 @@ class SettingsActivity : BaseActivity() {
} }
private fun setupPhoneBookIntegration() { private fun setupPhoneBookIntegration() {
if (CapabilitiesUtilNew.isPhoneBookIntegrationAvailable(currentUser!!)) { if (CapabilitiesUtil.hasSpreedFeatureCapability(
currentUser?.capabilities?.spreedCapability!!,
SpreedFeatures.PHONEBOOK_SEARCH
)
) {
binding.settingsPhoneBookIntegration.visibility = View.VISIBLE binding.settingsPhoneBookIntegration.visibility = View.VISIBLE
} else { } else {
binding.settingsPhoneBookIntegration.visibility = View.GONE binding.settingsPhoneBookIntegration.visibility = View.GONE
@ -507,7 +512,7 @@ class SettingsActivity : BaseActivity() {
var port = -1 var port = -1
val uri: URI val uri: URI
try { try {
uri = URI(currentUser!!.baseUrl) uri = URI(currentUser!!.baseUrl!!)
host = uri.host host = uri.host
port = uri.port port = uri.port
Log.d(TAG, "uri is $uri") Log.d(TAG, "uri is $uri")
@ -823,7 +828,7 @@ class SettingsActivity : BaseActivity() {
private fun setupProfileQueryDisposable() { private fun setupProfileQueryDisposable() {
profileQueryDisposable = ncApi.getUserProfile( profileQueryDisposable = ncApi.getUserProfile(
credentials, credentials,
ApiUtils.getUrlForUserProfile(currentUser!!.baseUrl) ApiUtils.getUrlForUserProfile(currentUser!!.baseUrl!!)
) )
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
@ -854,7 +859,7 @@ class SettingsActivity : BaseActivity() {
private fun setupServerAgeWarning() { private fun setupServerAgeWarning() {
when { when {
CapabilitiesUtilNew.isServerEOL(currentUser!!.capabilities) -> { CapabilitiesUtil.isServerEOL(currentUser!!.serverVersion!!.major) -> {
binding.serverAgeWarningText.setTextColor(ContextCompat.getColor((context), R.color.nc_darkRed)) binding.serverAgeWarningText.setTextColor(ContextCompat.getColor((context), R.color.nc_darkRed))
binding.serverAgeWarningText.setText(R.string.nc_settings_server_eol) binding.serverAgeWarningText.setText(R.string.nc_settings_server_eol)
binding.serverAgeWarningIcon.setColorFilter( binding.serverAgeWarningIcon.setColorFilter(
@ -863,7 +868,7 @@ class SettingsActivity : BaseActivity() {
) )
} }
CapabilitiesUtilNew.isServerAlmostEOL(currentUser!!) -> { CapabilitiesUtil.isServerAlmostEOL(currentUser!!.serverVersion!!.major) -> {
binding.serverAgeWarningText.setTextColor( binding.serverAgeWarningText.setTextColor(
ContextCompat.getColor((context), R.color.nc_darkYellow) ContextCompat.getColor((context), R.color.nc_darkYellow)
) )
@ -889,8 +894,8 @@ class SettingsActivity : BaseActivity() {
binding.settingsIncognitoKeyboardSwitch.visibility = View.GONE binding.settingsIncognitoKeyboardSwitch.visibility = View.GONE
} }
if (CapabilitiesUtilNew.isReadStatusAvailable(currentUser!!)) { if (CapabilitiesUtil.isReadStatusAvailable(currentUser!!.capabilities!!.spreedCapability!!)) {
binding.settingsReadPrivacySwitch.isChecked = !CapabilitiesUtilNew.isReadStatusPrivate(currentUser!!) binding.settingsReadPrivacySwitch.isChecked = !CapabilitiesUtil.isReadStatusPrivate(currentUser!!)
} else { } else {
binding.settingsReadPrivacy.visibility = View.GONE binding.settingsReadPrivacy.visibility = View.GONE
} }
@ -954,10 +959,10 @@ class SettingsActivity : BaseActivity() {
private fun setupTypingStatusSetting() { private fun setupTypingStatusSetting() {
if (currentUser!!.externalSignalingServer?.externalSignalingServer?.isNotEmpty() == true) { if (currentUser!!.externalSignalingServer?.externalSignalingServer?.isNotEmpty() == true) {
binding.settingsTypingStatusOnlyWithHpb.visibility = View.GONE 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!!)) { if (CapabilitiesUtil.isTypingStatusAvailable(currentUser!!)) {
binding.settingsTypingStatusSwitch.isChecked = !CapabilitiesUtilNew.isTypingStatusPrivate(currentUser!!) binding.settingsTypingStatusSwitch.isChecked = !CapabilitiesUtil.isTypingStatusPrivate(currentUser!!)
} else { } else {
binding.settingsTypingStatus.visibility = View.GONE binding.settingsTypingStatus.visibility = View.GONE
} }
@ -1209,7 +1214,7 @@ class SettingsActivity : BaseActivity() {
private fun checkForPhoneNumber() { private fun checkForPhoneNumber() {
ncApi.getUserData( ncApi.getUserData(
ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token), ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token),
ApiUtils.getUrlForUserProfile(currentUser!!.baseUrl) ApiUtils.getUrlForUserProfile(currentUser!!.baseUrl!!)
).subscribeOn(Schedulers.io()) ).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Observer<UserProfileOverall> { .subscribe(object : Observer<UserProfileOverall> {
@ -1294,7 +1299,7 @@ class SettingsActivity : BaseActivity() {
val phoneNumber = textInputLayout.editText!!.text.toString() val phoneNumber = textInputLayout.editText!!.text.toString()
ncApi.setUserData( ncApi.setUserData(
ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token), ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token),
ApiUtils.getUrlForUserData(currentUser!!.baseUrl, currentUser!!.userId), ApiUtils.getUrlForUserData(currentUser!!.baseUrl!!, currentUser!!.userId!!),
"phone", "phone",
phoneNumber phoneNumber
).subscribeOn(Schedulers.io()) ).subscribeOn(Schedulers.io())
@ -1349,7 +1354,7 @@ class SettingsActivity : BaseActivity() {
val json = "{\"key\": \"read_status_privacy\", \"value\" : $booleanValue}" val json = "{\"key\": \"read_status_privacy\", \"value\" : $booleanValue}"
ncApi.setReadStatusPrivacy( ncApi.setReadStatusPrivacy(
ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token), ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token),
ApiUtils.getUrlForUserSettings(currentUser!!.baseUrl), ApiUtils.getUrlForUserSettings(currentUser!!.baseUrl!!),
json.toRequestBody("application/json".toMediaTypeOrNull()) json.toRequestBody("application/json".toMediaTypeOrNull())
) )
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
@ -1387,7 +1392,7 @@ class SettingsActivity : BaseActivity() {
val json = "{\"key\": \"typing_privacy\", \"value\" : $booleanValue}" val json = "{\"key\": \"typing_privacy\", \"value\" : $booleanValue}"
ncApi.setTypingStatusPrivacy( ncApi.setTypingStatusPrivacy(
ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token), ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token),
ApiUtils.getUrlForUserSettings(currentUser!!.baseUrl), ApiUtils.getUrlForUserSettings(currentUser!!.baseUrl!!),
json.toRequestBody("application/json".toMediaTypeOrNull()) json.toRequestBody("application/json".toMediaTypeOrNull())
) )
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())

View file

@ -105,7 +105,7 @@ class SharedItemsRepositoryImpl @Inject constructor(private val ncApi: NcApi, pr
fileParameters["link"]!!, fileParameters["link"]!!,
fileParameters["mimetype"]!!, fileParameters["mimetype"]!!,
previewAvailable, previewAvailable,
previewLink(fileParameters["id"], parameters.baseUrl) previewLink(fileParameters["id"], parameters.baseUrl!!)
) )
} else if (it.value.messageParameters?.containsKey("object") == true) { } else if (it.value.messageParameters?.containsKey("object") == true) {
val objectParameters = it.value.messageParameters!!["object"]!! val objectParameters = it.value.messageParameters!!["object"]!!
@ -184,7 +184,7 @@ class SharedItemsRepositoryImpl @Inject constructor(private val ncApi: NcApi, pr
return ncApi.getSharedItemsOverview( return ncApi.getSharedItemsOverview(
credentials, credentials,
ApiUtils.getUrlForChatSharedItemsOverview(1, parameters.baseUrl, parameters.roomToken), ApiUtils.getUrlForChatSharedItemsOverview(1, parameters.baseUrl!!, parameters.roomToken),
1 1
).map { ).map {
val types = mutableSetOf<SharedItemType>() val types = mutableSetOf<SharedItemType>()
@ -206,7 +206,7 @@ class SharedItemsRepositoryImpl @Inject constructor(private val ncApi: NcApi, pr
private fun previewLink(fileId: String?, baseUrl: String): String { private fun previewLink(fileId: String?, baseUrl: String): String {
return ApiUtils.getUrlForFilePreviewWithFileId( return ApiUtils.getUrlForFilePreviewWithFileId(
baseUrl, baseUrl,
fileId, fileId!!,
sharedApplication!!.resources.getDimensionPixelSize(R.dimen.maximum_file_preview_size) sharedApplication!!.resources.getDimensionPixelSize(R.dimen.maximum_file_preview_size)
) )
} }

View file

@ -37,8 +37,8 @@ class TranslateViewModel @Inject constructor(
fun translateMessage(toLanguage: String, fromLanguage: String?, text: String) { fun translateMessage(toLanguage: String, fromLanguage: String?, text: String) {
val currentUser: User = userManager.currentUser.blockingGet() val currentUser: User = userManager.currentUser.blockingGet()
val authorization: String = ApiUtils.getCredentials(currentUser.username, currentUser.token) val authorization: String = ApiUtils.getCredentials(currentUser.username, currentUser.token)!!
val url: String = ApiUtils.getUrlForTranslation(currentUser.baseUrl) val url: String = ApiUtils.getUrlForTranslation(currentUser.baseUrl!!)
val calculatedFromLanguage = val calculatedFromLanguage =
if (fromLanguage == null || fromLanguage == "") { if (fromLanguage == null || fromLanguage == "") {
null null
@ -60,8 +60,8 @@ class TranslateViewModel @Inject constructor(
fun getLanguages() { fun getLanguages() {
val currentUser: User = userManager.currentUser.blockingGet() val currentUser: User = userManager.currentUser.blockingGet()
val authorization: String = ApiUtils.getCredentials(currentUser.username, currentUser.token) val authorization: String = ApiUtils.getCredentials(currentUser.username, currentUser.token)!!
val url: String = ApiUtils.getUrlForLanguages(currentUser.baseUrl) val url: String = ApiUtils.getUrlForLanguages(currentUser.baseUrl!!)
Log.d(TAG, "URL is: $url") Log.d(TAG, "URL is: $url")
repository.getLanguages(authorization, url) repository.getLanguages(authorization, url)
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())

View file

@ -57,7 +57,7 @@ class ProfileBottomSheet(val ncApi: NcApi, val userModel: User, val viewThemeUti
fun showFor(user: String, context: Context) { fun showFor(user: String, context: Context) {
ncApi.hoverCard( ncApi.hoverCard(
ApiUtils.getCredentials(userModel.username, userModel.token), ApiUtils.getCredentials(userModel.username, userModel.token),
ApiUtils.getUrlForHoverCard(userModel.baseUrl, user) ApiUtils.getUrlForHoverCard(userModel.baseUrl!!, user)
).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()) ).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Observer<HoverCardOverall> { .subscribe(object : Observer<HoverCardOverall> {
override fun onSubscribe(d: Disposable) { 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) { private fun talkTo(userId: String, context: Context) {
val apiVersion = val apiVersion =
ApiUtils.getConversationApiVersion(userModel, intArrayOf(ApiUtils.APIv4, 1)) ApiUtils.getConversationApiVersion(userModel, intArrayOf(ApiUtils.API_V4, 1))
val retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom( val retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(
apiVersion, apiVersion,
userModel.baseUrl, userModel.baseUrl!!,
"1", "1",
null, null,
userId, userId,

View file

@ -35,7 +35,8 @@ import com.nextcloud.talk.application.NextcloudTalkApplication
import com.nextcloud.talk.chat.ChatActivity import com.nextcloud.talk.chat.ChatActivity
import com.nextcloud.talk.databinding.DialogAttachmentBinding import com.nextcloud.talk.databinding.DialogAttachmentBinding
import com.nextcloud.talk.ui.theme.ViewThemeUtils 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 import javax.inject.Inject
@AutoInjector(NextcloudTalkApplication::class) @AutoInjector(NextcloudTalkApplication::class)
@ -61,7 +62,7 @@ class AttachmentDialog(val activity: Activity, var chatActivity: ChatActivity) :
} }
private fun initItemsStrings() { private fun initItemsStrings() {
var serverName = CapabilitiesUtilNew.getServerName(chatActivity.conversationUser) var serverName = CapabilitiesUtil.getServerName(chatActivity.conversationUser)
dialogAttachmentBinding.txtAttachFileFromCloud.text = chatActivity.resources?.let { dialogAttachmentBinding.txtAttachFileFromCloud.text = chatActivity.resources?.let {
if (serverName.isNullOrEmpty()) { if (serverName.isNullOrEmpty()) {
serverName = it.getString(R.string.nc_server_product_name) serverName = it.getString(R.string.nc_server_product_name)
@ -71,15 +72,15 @@ class AttachmentDialog(val activity: Activity, var chatActivity: ChatActivity) :
} }
private fun initItemsVisibility() { private fun initItemsVisibility() {
if (!CapabilitiesUtilNew.hasSpreedFeatureCapability( if (!CapabilitiesUtil.hasSpreedFeatureCapability(
chatActivity.conversationUser, chatActivity.spreedCapabilities,
"geo-location-sharing" SpreedFeatures.GEO_LOCATION_SHARING
) )
) { ) {
dialogAttachmentBinding.menuShareLocation.visibility = View.GONE dialogAttachmentBinding.menuShareLocation.visibility = View.GONE
} }
if (!CapabilitiesUtilNew.hasSpreedFeatureCapability(chatActivity.conversationUser, "talk-polls") || if (!CapabilitiesUtil.hasSpreedFeatureCapability(chatActivity.spreedCapabilities, SpreedFeatures.TALK_POLLS) ||
chatActivity.isOneToOneConversation() chatActivity.isOneToOneConversation()
) { ) {
dialogAttachmentBinding.menuAttachPoll.visibility = View.GONE dialogAttachmentBinding.menuAttachPoll.visibility = View.GONE

View file

@ -54,7 +54,7 @@ import com.nextcloud.talk.ui.theme.ViewThemeUtils;
import com.nextcloud.talk.users.UserManager; import com.nextcloud.talk.users.UserManager;
import com.nextcloud.talk.utils.ApiUtils; import com.nextcloud.talk.utils.ApiUtils;
import com.nextcloud.talk.utils.DisplayUtils; 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.net.CookieManager;
import java.util.ArrayList; import java.util.ArrayList;
@ -262,7 +262,7 @@ public class ChooseAccountDialogFragment extends DialogFragment {
private void loadCurrentStatus(User user) { private void loadCurrentStatus(User user) {
String credentials = ApiUtils.getCredentials(user.getUsername(), user.getToken()); 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); binding.statusView.setVisibility(View.VISIBLE);
ncApi.status(credentials, ApiUtils.getUrlForStatus(user.getBaseUrl())). ncApi.status(credentials, ApiUtils.getUrlForStatus(user.getBaseUrl())).

View file

@ -89,7 +89,7 @@ class ChooseAccountShareToDialogFragment : DialogFragment() {
if (user != null) { if (user != null) {
binding!!.currentAccount.userName.text = user.displayName binding!!.currentAccount.userName.text = user.displayName
binding!!.currentAccount.ticker.visibility = View.GONE 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) viewThemeUtils!!.platform.colorImageView(binding!!.currentAccount.accountMenu, ColorRole.PRIMARY)
if (user.baseUrl != null && if (user.baseUrl != null &&
(user.baseUrl!!.startsWith("http://") || user.baseUrl!!.startsWith("https://")) (user.baseUrl!!.startsWith("http://") || user.baseUrl!!.startsWith("https://"))

View file

@ -46,7 +46,7 @@ import com.nextcloud.talk.users.UserManager
import com.nextcloud.talk.utils.ApiUtils 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_INTERNAL_USER_ID
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_TOKEN 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.Observer
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable import io.reactivex.disposables.Disposable
@ -86,7 +86,7 @@ class ConversationsListBottomDialog(
initItemsVisibility() initItemsVisibility()
initClickListeners() initClickListeners()
credentials = ApiUtils.getCredentials(currentUser.username, currentUser.token) credentials = ApiUtils.getCredentials(currentUser.username, currentUser.token)!!
} }
override fun onStart() { override fun onStart() {
@ -105,7 +105,10 @@ class ConversationsListBottomDialog(
} }
private fun initItemsVisibility() { private fun initItemsVisibility() {
val hasFavoritesCapability = CapabilitiesUtilNew.hasSpreedFeatureCapability(currentUser, "favorites") val hasFavoritesCapability = CapabilitiesUtil.hasSpreedFeatureCapability(
currentUser.capabilities?.spreedCapability!!,
"favorites"
)
val canModerate = conversation.canModerate(currentUser) val canModerate = conversation.canModerate(currentUser)
binding.conversationRemoveFromFavorites.visibility = setVisibleIf( binding.conversationRemoveFromFavorites.visibility = setVisibleIf(
@ -116,11 +119,19 @@ class ConversationsListBottomDialog(
) )
binding.conversationMarkAsRead.visibility = setVisibleIf( 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( binding.conversationMarkAsUnread.visibility = setVisibleIf(
conversation.unreadMessages <= 0 && CapabilitiesUtilNew.canMarkRoomAsUnread(currentUser) conversation.unreadMessages <= 0 && CapabilitiesUtil.hasSpreedFeatureCapability(
currentUser
.capabilities?.spreedCapability!!,
"chat-unread"
)
) )
binding.conversationOperationRename.visibility = setVisibleIf( binding.conversationOperationRename.visibility = setVisibleIf(
@ -178,12 +189,12 @@ class ConversationsListBottomDialog(
} }
private fun addConversationToFavorites() { 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( ncApi.addConversationToFavorites(
credentials, credentials,
ApiUtils.getUrlForRoomFavorite( ApiUtils.getUrlForRoomFavorite(
apiVersion, apiVersion,
currentUser.baseUrl, currentUser.baseUrl!!,
conversation.token conversation.token
) )
) )
@ -218,12 +229,12 @@ class ConversationsListBottomDialog(
} }
private fun removeConversationFromFavorites() { 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( ncApi.removeConversationFromFavorites(
credentials, credentials,
ApiUtils.getUrlForRoomFavorite( ApiUtils.getUrlForRoomFavorite(
apiVersion, apiVersion,
currentUser.baseUrl, currentUser.baseUrl!!,
conversation.token conversation.token
) )
) )
@ -262,8 +273,8 @@ class ConversationsListBottomDialog(
credentials, credentials,
ApiUtils.getUrlForChatReadMarker( ApiUtils.getUrlForChatReadMarker(
chatApiVersion(), chatApiVersion(),
currentUser.baseUrl, currentUser.baseUrl!!,
conversation.token conversation.token!!
) )
) )
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
@ -301,8 +312,8 @@ class ConversationsListBottomDialog(
credentials, credentials,
ApiUtils.getUrlForChatReadMarker( ApiUtils.getUrlForChatReadMarker(
chatApiVersion(), chatApiVersion(),
currentUser.baseUrl, currentUser.baseUrl!!,
conversation.token conversation.token!!
), ),
conversation.lastMessage!!.jsonMessageId conversation.lastMessage!!.jsonMessageId
) )
@ -396,7 +407,7 @@ class ConversationsListBottomDialog(
} }
private fun chatApiVersion(): Int { private fun chatApiVersion(): Int {
return ApiUtils.getChatApiVersion(currentUser, intArrayOf(ApiUtils.APIv1)) return ApiUtils.getChatApiVersion(currentUser.capabilities!!.spreedCapability!!, intArrayOf(ApiUtils.API_V1))
} }
companion object { companion object {

View file

@ -50,7 +50,8 @@ import javax.inject.Inject
class DateTimePickerFragment( class DateTimePickerFragment(
token: String, token: String,
id: String, id: String,
chatViewModel: ChatViewModel chatViewModel: ChatViewModel,
private val chatApiVersion: Int
) : DialogFragment() { ) : DialogFragment() {
lateinit var binding: DialogDateTimePickerBinding lateinit var binding: DialogDateTimePickerBinding
private var dialogView: View? = null private var dialogView: View? = null
@ -144,7 +145,7 @@ class DateTimePickerFragment(
} }
private fun getReminder() { private fun getReminder() {
viewModel.getReminder(userManager.currentUser.blockingGet(), roomToken, messageId) viewModel.getReminder(userManager.currentUser.blockingGet(), roomToken, messageId, chatApiVersion)
} }
private fun showDelete(value: Boolean) { private fun showDelete(value: Boolean) {
@ -221,12 +222,18 @@ class DateTimePickerFragment(
binding.buttonClose.setOnClickListener { dismiss() } binding.buttonClose.setOnClickListener { dismiss() }
binding.buttonSet.setOnClickListener { binding.buttonSet.setOnClickListener {
currentTimeStamp?.let { time -> currentTimeStamp?.let { time ->
viewModel.setReminder(userManager.currentUser.blockingGet(), roomToken, messageId, time.toInt()) viewModel.setReminder(
userManager.currentUser.blockingGet(),
roomToken,
messageId,
time.toInt(),
chatApiVersion
)
} }
dismiss() dismiss()
} }
binding.buttonDelete.setOnClickListener { 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 private const val HOUR_SIX_PM = 18
@JvmStatic @JvmStatic
fun newInstance(token: String, id: String, chatViewModel: ChatViewModel) = fun newInstance(token: String, id: String, chatViewModel: ChatViewModel, chatApiVersion: Int) =
DateTimePickerFragment( DateTimePickerFragment(
token, token,
id, id,
chatViewModel chatViewModel,
chatApiVersion
) )
} }
} }

View file

@ -46,14 +46,17 @@ import com.nextcloud.talk.models.domain.ConversationReadOnlyState
import com.nextcloud.talk.models.domain.ConversationType import com.nextcloud.talk.models.domain.ConversationType
import com.nextcloud.talk.models.domain.ReactionAddedModel import com.nextcloud.talk.models.domain.ReactionAddedModel
import com.nextcloud.talk.models.domain.ReactionDeletedModel 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.ChatMessage
import com.nextcloud.talk.repositories.reactions.ReactionsRepository import com.nextcloud.talk.repositories.reactions.ReactionsRepository
import com.nextcloud.talk.ui.theme.ViewThemeUtils import com.nextcloud.talk.ui.theme.ViewThemeUtils
import com.nextcloud.talk.utils.ApiUtils import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.SpreedFeatures
import com.nextcloud.talk.utils.ConversationUtils import com.nextcloud.talk.utils.ConversationUtils
import com.nextcloud.talk.utils.DateConstants import com.nextcloud.talk.utils.DateConstants
import com.nextcloud.talk.utils.DateUtils 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.EmojiPopup
import com.vanniktech.emoji.EmojiTextView import com.vanniktech.emoji.EmojiTextView
import com.vanniktech.emoji.installDisableKeyboardInput import com.vanniktech.emoji.installDisableKeyboardInput
@ -72,7 +75,8 @@ class MessageActionsDialog(
private val user: User?, private val user: User?,
private val currentConversation: ConversationModel?, private val currentConversation: ConversationModel?,
private val showMessageDeletionButton: Boolean, private val showMessageDeletionButton: Boolean,
private val hasChatPermission: Boolean private val hasChatPermission: Boolean,
private val spreedCapabilities: SpreedCapability
) : BottomSheetDialog(chatActivity) { ) : BottomSheetDialog(chatActivity) {
@Inject @Inject
@ -100,8 +104,8 @@ class MessageActionsDialog(
private val isUserAllowedToEdit = chatActivity.userAllowedByPrivilages(message) private val isUserAllowedToEdit = chatActivity.userAllowedByPrivilages(message)
private val isMessageEditable = CapabilitiesUtilNew.hasSpreedFeatureCapability( private val isMessageEditable = CapabilitiesUtil.hasSpreedFeatureCapability(
user, spreedCapabilities,
"edit-messages" "edit-messages"
) && messageHasRegularText && !isOlderThanTwentyFourHours && isUserAllowedToEdit ) && messageHasRegularText && !isOlderThanTwentyFourHours && isUserAllowedToEdit
@ -116,9 +120,9 @@ class MessageActionsDialog(
viewThemeUtils.platform.themeDialog(dialogMessageActionsBinding.root) viewThemeUtils.platform.themeDialog(dialogMessageActionsBinding.root)
initEmojiBar(hasChatPermission) initEmojiBar(hasChatPermission)
initMenuItemCopy(!message.isDeleted) 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( chatActivity.chatViewModel.checkForNoteToSelf(
ApiUtils.getCredentials(user!!.username, user.token), ApiUtils.getCredentials(user!!.username, user.token)!!,
ApiUtils.getUrlForRooms( ApiUtils.getUrlForRooms(
apiVersion, apiVersion,
user.baseUrl user.baseUrl
@ -144,7 +148,7 @@ class MessageActionsDialog(
initMenuItemTranslate( initMenuItemTranslate(
!message.isDeleted && !message.isDeleted &&
ChatMessage.MessageType.REGULAR_TEXT_MESSAGE == message.getCalculateMessageType() && ChatMessage.MessageType.REGULAR_TEXT_MESSAGE == message.getCalculateMessageType() &&
CapabilitiesUtilNew.isTranslationsSupported(user) CapabilitiesUtil.isTranslationsSupported(spreedCapabilities)
) )
initMenuEditorDetails(message.lastEditTimestamp != 0L && !message.isDeleted) initMenuEditorDetails(message.lastEditTimestamp != 0L && !message.isDeleted)
initMenuReplyToMessage(message.replyable && hasChatPermission) initMenuReplyToMessage(message.replyable && hasChatPermission)
@ -160,7 +164,10 @@ class MessageActionsDialog(
ChatMessage.MessageType.REGULAR_TEXT_MESSAGE == message.getCalculateMessageType() && ChatMessage.MessageType.REGULAR_TEXT_MESSAGE == message.getCalculateMessageType() &&
!(message.isDeletedCommentMessage || message.isDeleted) !(message.isDeletedCommentMessage || message.isDeleted)
) )
initMenuRemindMessage(!message.isDeleted && CapabilitiesUtilNew.isRemindSupported(user)) initMenuRemindMessage(
!message.isDeleted && CapabilitiesUtil.hasSpreedFeatureCapability
(spreedCapabilities, "remind-me-later")
)
initMenuMarkAsUnread( initMenuMarkAsUnread(
message.previousMessageId > NO_PREVIOUS_MESSAGE_ID && message.previousMessageId > NO_PREVIOUS_MESSAGE_ID &&
ChatMessage.MessageType.SYSTEM_MESSAGE != message.getCalculateMessageType() ChatMessage.MessageType.SYSTEM_MESSAGE != message.getCalculateMessageType()
@ -242,7 +249,7 @@ class MessageActionsDialog(
} }
private fun initEmojiBar(hasChatPermission: Boolean) { private fun initEmojiBar(hasChatPermission: Boolean) {
if (CapabilitiesUtilNew.hasSpreedFeatureCapability(user, "reactions") && if (hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.REACTIONS) &&
isPermitted(hasChatPermission) && isPermitted(hasChatPermission) &&
isReactableMessageType(message) isReactableMessageType(message)
) { ) {

View file

@ -35,7 +35,7 @@ import com.nextcloud.talk.application.NextcloudTalkApplication
import com.nextcloud.talk.databinding.DialogMoreCallActionsBinding import com.nextcloud.talk.databinding.DialogMoreCallActionsBinding
import com.nextcloud.talk.raisehand.viewmodel.RaiseHandViewModel import com.nextcloud.talk.raisehand.viewmodel.RaiseHandViewModel
import com.nextcloud.talk.ui.theme.ViewThemeUtils 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.nextcloud.talk.viewmodels.CallRecordingViewModel
import com.vanniktech.emoji.EmojiTextView import com.vanniktech.emoji.EmojiTextView
import javax.inject.Inject import javax.inject.Inject
@ -72,7 +72,7 @@ class MoreCallActionsDialog(private val callActivity: CallActivity) : BottomShee
} }
private fun initItemsVisibility() { private fun initItemsVisibility() {
if (CapabilitiesUtilNew.isCallReactionsSupported(callActivity.conversationUser)) { if (CapabilitiesUtil.isCallReactionsSupported(callActivity.conversationUser)) {
binding.callEmojiBar.visibility = View.VISIBLE binding.callEmojiBar.visibility = View.VISIBLE
} else { } else {
binding.callEmojiBar.visibility = View.GONE binding.callEmojiBar.visibility = View.GONE
@ -102,7 +102,7 @@ class MoreCallActionsDialog(private val callActivity: CallActivity) : BottomShee
} }
private fun initEmojiBar() { private fun initEmojiBar() {
if (CapabilitiesUtilNew.isCallReactionsSupported(callActivity.conversationUser)) { if (CapabilitiesUtil.isCallReactionsSupported(callActivity.conversationUser)) {
binding.advancedCallOptionsTitle.visibility = View.GONE binding.advancedCallOptionsTitle.visibility = View.GONE
val capabilities = callActivity.conversationUser?.capabilities val capabilities = callActivity.conversationUser?.capabilities

View file

@ -128,8 +128,8 @@ class SetStatusDialogFragment :
currentUser = currentUserProvider?.currentUser?.blockingGet() currentUser = currentUserProvider?.currentUser?.blockingGet()
currentStatus = it.getParcelable(ARG_CURRENT_STATUS_PARAM) currentStatus = it.getParcelable(ARG_CURRENT_STATUS_PARAM)
credentials = ApiUtils.getCredentials(currentUser?.username, currentUser?.token) credentials = ApiUtils.getCredentials(currentUser?.username, currentUser?.token)!!
ncApi.getPredefinedStatuses(credentials, ApiUtils.getUrlForPredefinedStatuses(currentUser?.baseUrl)) ncApi.getPredefinedStatuses(credentials, ApiUtils.getUrlForPredefinedStatuses(currentUser?.baseUrl!!))
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Observer<ResponseBody> { .subscribe(object : Observer<ResponseBody> {
@ -369,7 +369,7 @@ class SetStatusDialogFragment :
private fun clearStatus() { private fun clearStatus() {
val credentials = ApiUtils.getCredentials(currentUser?.username, currentUser?.token) 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()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()).subscribe(object : Observer<GenericOverall> { .observeOn(AndroidSchedulers.mainThread()).subscribe(object : Observer<GenericOverall> {
override fun onSubscribe(d: Disposable) { override fun onSubscribe(d: Disposable) {
@ -393,7 +393,7 @@ class SetStatusDialogFragment :
private fun setStatus(statusType: StatusType) { private fun setStatus(statusType: StatusType) {
visualizeStatus(statusType) visualizeStatus(statusType)
ncApi.setStatusType(credentials, ApiUtils.getUrlForSetStatusType(currentUser?.baseUrl), statusType.string) ncApi.setStatusType(credentials, ApiUtils.getUrlForSetStatusType(currentUser?.baseUrl!!), statusType.string)
.subscribeOn( .subscribeOn(
Schedulers Schedulers
.io() .io()
@ -468,7 +468,7 @@ class SetStatusDialogFragment :
) { ) {
ncApi.setCustomStatusMessage( ncApi.setCustomStatusMessage(
credentials, credentials,
ApiUtils.getUrlForSetCustomStatus(currentUser?.baseUrl), ApiUtils.getUrlForSetCustomStatus(currentUser?.baseUrl!!),
statusIcon, statusIcon,
inputText, inputText,
clearAt clearAt
@ -499,7 +499,7 @@ class SetStatusDialogFragment :
ncApi.setPredefinedStatusMessage( ncApi.setPredefinedStatusMessage(
credentials, credentials,
ApiUtils.getUrlForSetPredefinedStatus(currentUser?.baseUrl), ApiUtils.getUrlForSetPredefinedStatus(currentUser?.baseUrl!!),
selectedPredefinedStatus!!.id, selectedPredefinedStatus!!.id,
if (clearAt == -1L) null else clearAt if (clearAt == -1L) null else clearAt
) )

View file

@ -154,7 +154,7 @@ class ShowReactionsDialog(
ncApi.getReactions( ncApi.getReactions(
credentials, credentials,
ApiUtils.getUrlForMessageReaction( ApiUtils.getUrlForMessageReaction(
user?.baseUrl, user?.baseUrl!!,
roomToken, roomToken,
chatMessage.id chatMessage.id
), ),
@ -209,7 +209,7 @@ class ShowReactionsDialog(
ncApi.deleteReaction( ncApi.deleteReaction(
credentials, credentials,
ApiUtils.getUrlForMessageReaction( ApiUtils.getUrlForMessageReaction(
user?.baseUrl, user?.baseUrl!!,
roomToken, roomToken,
message.id message.id
), ),

View file

@ -81,7 +81,7 @@ class ChunkedFileUploader(
init { init {
initHttpClient(okHttpClient, currentUser) initHttpClient(okHttpClient, currentUser)
remoteChunkUrl = ApiUtils.getUrlForChunkedUpload(currentUser.baseUrl, currentUser.userId) remoteChunkUrl = ApiUtils.getUrlForChunkedUpload(currentUser.baseUrl!!, currentUser.userId!!)
} }
@Suppress("Detekt.TooGenericExceptionCaught") @Suppress("Detekt.TooGenericExceptionCaught")
@ -295,7 +295,7 @@ class ChunkedFileUploader(
ApiUtils.getCredentials( ApiUtils.getCredentials(
currentUser.username, currentUser.username,
currentUser.token currentUser.token
), )!!,
"Authorization" "Authorization"
) )
) )
@ -304,8 +304,8 @@ class ChunkedFileUploader(
private fun assembleChunks(uploadFolderUri: String, targetPath: String) { private fun assembleChunks(uploadFolderUri: String, targetPath: String) {
val destinationUri: String = ApiUtils.getUrlForFileUpload( val destinationUri: String = ApiUtils.getUrlForFileUpload(
currentUser.baseUrl, currentUser.baseUrl!!,
currentUser.userId, currentUser.userId!!,
targetPath targetPath
) )
val originUri = "$uploadFolderUri/.file" val originUri = "$uploadFolderUri/.file"

View file

@ -24,7 +24,7 @@ class FileUploader(
fun upload(sourceFileUri: Uri, fileName: String, remotePath: String, metaData: String?): Observable<Boolean> { fun upload(sourceFileUri: Uri, fileName: String, remotePath: String, metaData: String?): Observable<Boolean> {
return ncApi.uploadFile( return ncApi.uploadFile(
ApiUtils.getCredentials(currentUser.username, currentUser.token), ApiUtils.getCredentials(currentUser.username, currentUser.token),
ApiUtils.getUrlForFileUpload(currentUser.baseUrl, currentUser.userId, remotePath), ApiUtils.getUrlForFileUpload(currentUser.baseUrl!!, currentUser.userId!!, remotePath),
createRequestBody(sourceFileUri) createRequestBody(sourceFileUri)
) )
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())

View file

@ -69,7 +69,7 @@ object AccountUtils {
private fun matchAccounts(importAccount: ImportAccount, user: User): Boolean { private fun matchAccounts(importAccount: ImportAccount, user: User): Boolean {
var accountFound = false var accountFound = false
if (importAccount.token != null) { if (importAccount.token != null) {
if (UriUtils.hasHttpProtocolPrefixed(importAccount.baseUrl)) { if (UriUtils.hasHttpProtocolPrefixed(importAccount.baseUrl!!)) {
if ( if (
user.username == importAccount.username && user.username == importAccount.username &&
user.baseUrl == importAccount.baseUrl user.baseUrl == importAccount.baseUrl

View file

@ -1,559 +0,0 @@
/*
* Nextcloud Talk application
*
* @author Mario Danic
* @author Marcel Hibbe
* @author Tim Krüger
* Copyright (C) 2021 Tim Krüger <t@timkrueger.me>
* Copyright (C) 2021-2022 Marcel Hibbe <dev@mhibbe.de>
* Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
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<String, String> 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<String, String> 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<String, String> 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;
}
}

View file

@ -0,0 +1,577 @@
/*
* Nextcloud Talk application
*
* @author Mario Danic
* @author Marcel Hibbe
* @author Tim Krüger
* Copyright (C) 2021 Tim Krüger <t@timkrueger.me>
* Copyright (C) 2021-2022 Marcel Hibbe <dev@mhibbe.de>
* Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
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<String, String> = 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<String, String> = 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<String, String> = 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"
}
}

View file

@ -0,0 +1,275 @@
/*
* Nextcloud Talk application
*
* @author Andy Scherzinger
* @author Mario Danic
* @author Marcel Hibbe
* Copyright (C) 2023-2024 Marcel Hibbe <dev@mhibbe.de>
* Copyright (C) 2021 Andy Scherzinger (info@andy-scherzinger.de)
* Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
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<String, Any>? = 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<String, Any>? = 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
}

View file

@ -1,10 +1,9 @@
package com.nextcloud.talk.utils 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.ConversationModel
import com.nextcloud.talk.models.domain.ConversationType import com.nextcloud.talk.models.domain.ConversationType
import com.nextcloud.talk.models.domain.ParticipantType 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 * Nextcloud Talk application
@ -45,28 +44,28 @@ object ConversationUtils {
ParticipantType.MODERATOR == conversation.participantType 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 && 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) && return isParticipantOwnerOrModerator(conversation) &&
!isLockedOneToOne(conversation, conversationUser) && !isLockedOneToOne(conversation, spreedCapabilities) &&
conversation.type != ConversationType.FORMER_ONE_TO_ONE && conversation.type != ConversationType.FORMER_ONE_TO_ONE &&
!isNoteToSelfConversation(conversation) !isNoteToSelfConversation(conversation)
} }
fun isLobbyViewApplicable(conversation: ConversationModel, conversationUser: User): Boolean { fun isLobbyViewApplicable(conversation: ConversationModel, spreedCapabilities: SpreedCapability): Boolean {
return !canModerate(conversation, conversationUser) && return !canModerate(conversation, spreedCapabilities) &&
( (
conversation.type == ConversationType.ROOM_GROUP_CALL || conversation.type == ConversationType.ROOM_GROUP_CALL ||
conversation.type == ConversationType.ROOM_PUBLIC_CALL conversation.type == ConversationType.ROOM_PUBLIC_CALL
) )
} }
fun isNameEditable(conversation: ConversationModel, conversationUser: User): Boolean { fun isNameEditable(conversation: ConversationModel, spreedCapabilities: SpreedCapability): Boolean {
return canModerate(conversation, conversationUser) && return canModerate(conversation, spreedCapabilities) &&
ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL != conversation.type 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) { return if (conversation.canDeleteConversation != null) {
// Available since APIv2 // Available since APIv2
conversation.canDeleteConversation!! conversation.canDeleteConversation!!
} else { } else {
canModerate(conversation, conversationUser) canModerate(conversation, spreedCapability)
// Fallback for APIv1 // Fallback for APIv1
} }
} }

View file

@ -61,7 +61,6 @@ import com.nextcloud.talk.utils.MimetypeUtils.isGif
import com.nextcloud.talk.utils.MimetypeUtils.isMarkdown import com.nextcloud.talk.utils.MimetypeUtils.isMarkdown
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ACCOUNT import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ACCOUNT
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_FILE_ID import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_FILE_ID
import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew
import java.io.File import java.io.File
import java.util.concurrent.ExecutionException 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_USER_ID, user.userId)
.putString( .putString(
DownloadFileToCacheWorker.KEY_ATTACHMENT_FOLDER, DownloadFileToCacheWorker.KEY_ATTACHMENT_FOLDER,
CapabilitiesUtilNew.getAttachmentFolder(user) CapabilitiesUtil.getAttachmentFolder(user.capabilities!!.spreedCapability!!)
) )
.putString(DownloadFileToCacheWorker.KEY_FILE_NAME, fileInfo.fileName) .putString(DownloadFileToCacheWorker.KEY_FILE_NAME, fileInfo.fileName)
.putString(DownloadFileToCacheWorker.KEY_FILE_PATH, path) .putString(DownloadFileToCacheWorker.KEY_FILE_PATH, path)

View file

@ -22,22 +22,21 @@
package com.nextcloud.talk.utils 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.ConversationModel
import com.nextcloud.talk.models.json.capabilities.SpreedCapability
import com.nextcloud.talk.models.json.conversations.Conversation 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 * see https://nextcloud-talk.readthedocs.io/en/latest/constants/#attendee-permissions
*/ */
class ParticipantPermissions( class ParticipantPermissions(
private val user: User, private val spreedCapabilities: SpreedCapability,
private val conversation: ConversationModel private val conversation: ConversationModel
) { ) {
@Deprecated("Use ChatRepository.ConversationModel") @Deprecated("Use ChatRepository.ConversationModel")
constructor(user: User, conversation: Conversation) : this( constructor(spreedCapabilities: SpreedCapability, conversation: Conversation) : this(
user, spreedCapabilities,
ConversationModel.mapToConversationModel(conversation) ConversationModel.mapToConversationModel(conversation)
) )
@ -52,8 +51,8 @@ class ParticipantPermissions(
private val hasChatPermission = (conversation.permissions and CHAT) == CHAT private val hasChatPermission = (conversation.permissions and CHAT) == CHAT
private fun hasConversationPermissions(): Boolean { private fun hasConversationPermissions(): Boolean {
return CapabilitiesUtilNew.hasSpreedFeatureCapability( return CapabilitiesUtil.hasSpreedFeatureCapability(
user, spreedCapabilities,
"conversation-permissions" "conversation-permissions"
) )
} }
@ -91,7 +90,7 @@ class ParticipantPermissions(
} }
fun hasChatPermission(): Boolean { fun hasChatPermission(): Boolean {
if (CapabilitiesUtilNew.hasSpreedFeatureCapability(user, "chat-permission")) { if (CapabilitiesUtil.hasSpreedFeatureCapability(spreedCapabilities, "chat-permission")) {
return hasChatPermission return hasChatPermission
} }
// if capability is not available then the spreed version doesn't support to restrict this // if capability is not available then the spreed version doesn't support to restrict this

View file

@ -238,7 +238,7 @@ class PushUtils {
val credentials = ApiUtils.getCredentials(user.username, user.token) val credentials = ApiUtils.getCredentials(user.username, user.token)
ncApi.registerDeviceForNotificationsWithNextcloud( ncApi.registerDeviceForNotificationsWithNextcloud(
credentials, credentials,
ApiUtils.getUrlNextcloudPush(user.baseUrl), ApiUtils.getUrlNextcloudPush(user.baseUrl!!),
nextcloudRegisterPushMap nextcloudRegisterPushMap
) )
.subscribe(object : Observer<PushRegistrationOverall> { .subscribe(object : Observer<PushRegistrationOverall> {

View file

@ -53,8 +53,8 @@ object RemoteFileUtils {
return ncApi.checkIfFileExists( return ncApi.checkIfFileExists(
ApiUtils.getCredentials(currentUser.username, currentUser.token), ApiUtils.getCredentials(currentUser.username, currentUser.token),
ApiUtils.getUrlForFileUpload( ApiUtils.getUrlForFileUpload(
currentUser.baseUrl, currentUser.baseUrl!!,
currentUser.userId, currentUser.userId!!,
remotePath remotePath
) )
) )

View file

@ -22,10 +22,10 @@ package com.nextcloud.talk.utils
import android.content.Context import android.content.Context
import com.nextcloud.talk.R import com.nextcloud.talk.R
import com.nextcloud.talk.data.user.model.User 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 { object ShareUtils {
fun getStringForIntent(context: Context, user: User, conversation: Conversation?): String { fun getStringForIntent(context: Context, user: User, conversation: ConversationModel?): String {
return String.format( return String.format(
context.resources.getString(R.string.nc_share_text), context.resources.getString(R.string.nc_share_text),
user.baseUrl, user.baseUrl,

View file

@ -89,4 +89,5 @@ object BundleKeys {
const val KEY_REAUTHORIZE_ACCOUNT = "KEY_REAUTHORIZE_ACCOUNT" const val KEY_REAUTHORIZE_ACCOUNT = "KEY_REAUTHORIZE_ACCOUNT"
const val KEY_PASSWORD = "KEY_PASSWORD" const val KEY_PASSWORD = "KEY_PASSWORD"
const val KEY_REMOTE_TALK_SHARE = "KEY_REMOTE_TALK_SHARE" const val KEY_REMOTE_TALK_SHARE = "KEY_REMOTE_TALK_SHARE"
const val KEY_CHAT_API_VERSION = "KEY_CHAT_API_VERSION"
} }

View file

@ -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 <mario@lovelyhq.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.nextcloud.talk.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<String, Any>? = 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<String, Any>? = 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
}

View file

@ -35,7 +35,7 @@ import com.nextcloud.talk.data.user.model.User;
import com.nextcloud.talk.models.json.generic.GenericOverall; import com.nextcloud.talk.models.json.generic.GenericOverall;
import com.nextcloud.talk.utils.ApiUtils; import com.nextcloud.talk.utils.ApiUtils;
import com.nextcloud.talk.utils.UserIdUtils; import com.nextcloud.talk.utils.UserIdUtils;
import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew; import com.nextcloud.talk.utils.CapabilitiesUtil;
import javax.inject.Inject; import javax.inject.Inject;
@ -158,7 +158,8 @@ public class DatabaseStorageModule {
}); });
} else if ("conversation_info_message_notifications_dropdown".equals(key)) { } 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)) { if (TextUtils.isEmpty(messageNotificationLevel) || !messageNotificationLevel.equals(value)) {
int intValue; int intValue;
switch (value) { switch (value) {
@ -175,7 +176,7 @@ public class DatabaseStorageModule {
intValue = 0; 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(), ncApi.setNotificationLevel(ApiUtils.getCredentials(conversationUser.getUsername(),
conversationUser.getToken()), conversationUser.getToken()),

View file

@ -25,7 +25,6 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import com.nextcloud.talk.activities.CallActivity.Companion.TAG import com.nextcloud.talk.activities.CallActivity.Companion.TAG
import com.nextcloud.talk.location.GeocodingActivity
import fr.dudie.nominatim.client.TalkJsonNominatimClient import fr.dudie.nominatim.client.TalkJsonNominatimClient
import fr.dudie.nominatim.model.Address import fr.dudie.nominatim.model.Address
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
@ -70,9 +69,9 @@ class GeoCodingViewModel : ViewModel() {
try { try {
val results = nominatimClient.search(query) as ArrayList<Address> val results = nominatimClient.search(query) as ArrayList<Address>
for (address in results) { for (address in results) {
Log.d(GeocodingActivity.TAG, address.displayName) Log.d(TAG, address.displayName)
Log.d(GeocodingActivity.TAG, address.latitude.toString()) Log.d(TAG, address.latitude.toString())
Log.d(GeocodingActivity.TAG, address.longitude.toString()) Log.d(TAG, address.longitude.toString())
} }
geocodingResults = results geocodingResults = results
geocodingResultsLiveData.postValue(results) geocodingResultsLiveData.postValue(results)

View file

@ -113,7 +113,7 @@ public class WebSocketConnectionHelper {
} }
HelloOverallWebSocketMessage getAssembledHelloModel(User user, String ticket) { 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 helloOverallWebSocketMessage = new HelloOverallWebSocketMessage();
helloOverallWebSocketMessage.setType("hello"); helloOverallWebSocketMessage.setType("hello");

View file

@ -22,7 +22,7 @@
package com.nextcloud.talk.utils 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 com.nextcloud.talk.models.json.conversations.Conversation
import junit.framework.TestCase import junit.framework.TestCase
import org.junit.Test import org.junit.Test
@ -31,7 +31,7 @@ class ParticipantPermissionsTest : TestCase() {
@Test @Test
fun test_areFlagsSet() { fun test_areFlagsSet() {
val user = User() val spreedCapability = SpreedCapability()
val conversation = Conversation() val conversation = Conversation()
conversation.permissions = ParticipantPermissions.PUBLISH_SCREEN or conversation.permissions = ParticipantPermissions.PUBLISH_SCREEN or
ParticipantPermissions.JOIN_CALL or ParticipantPermissions.JOIN_CALL or
@ -39,7 +39,7 @@ class ParticipantPermissionsTest : TestCase() {
val attendeePermissions = val attendeePermissions =
ParticipantPermissions( ParticipantPermissions(
user, spreedCapability,
conversation conversation
) )

View file

@ -23,7 +23,7 @@ import android.content.Context
import android.content.res.Resources import android.content.res.Resources
import com.nextcloud.talk.R import com.nextcloud.talk.R
import com.nextcloud.talk.data.user.model.User 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 com.nextcloud.talk.users.UserManager
import io.reactivex.Maybe import io.reactivex.Maybe
import org.junit.Assert import org.junit.Assert
@ -49,19 +49,19 @@ class ShareUtilsTest {
private val baseUrl = "https://my.nextcloud.com" private val baseUrl = "https://my.nextcloud.com"
private val token = "2aotbrjr" private val token = "2aotbrjr"
private lateinit var conversation: Conversation private lateinit var conversation: ConversationModel
@Before @Before
fun setUp() { fun setUp() {
MockitoAnnotations.openMocks(this) MockitoAnnotations.openMocks(this)
Mockito.`when`(userManager!!.currentUser).thenReturn(Maybe.just(user)) 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`(context!!.resources).thenReturn(resources)
Mockito.`when`(resources!!.getString(R.string.nc_share_text)) Mockito.`when`(resources!!.getString(R.string.nc_share_text))
.thenReturn("Join the conversation at %1\$s/index.php/call/%2\$s") .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") Mockito.`when`(resources.getString(R.string.nc_share_text_pass)).thenReturn("\nPassword: %1\$s")
conversation = Conversation(token = token) conversation = ConversationModel(token = token)
} }
@Test @Test

View file

@ -1,5 +1,5 @@
build: build:
maxIssues: 116 maxIssues: 122
weights: weights:
# complexity: 2 # complexity: 2
# LongParameterList: 1 # LongParameterList: 1

View file

@ -1313,8 +1313,6 @@ lQyC8nl8P5PgkEZ5CHcGymZlpzihR3ECrPJTk39Sb7D3SxCW4WrChV3kVfmLgvc=
-----END PGP PUBLIC KEY BLOCK----- -----END PGP PUBLIC KEY BLOCK-----
pub C21CE653B639E41A pub C21CE653B639E41A
uid Eric Kuck <eric@bluelinelabs.com>
sub 4F80368F9034B8D0 sub 4F80368F9034B8D0
-----BEGIN PGP PUBLIC KEY BLOCK----- -----BEGIN PGP PUBLIC KEY BLOCK-----
Version: BCPG v1.68 Version: BCPG v1.68
@ -1324,21 +1322,20 @@ aBF7dud1bzw7voZo5ieGK923wUB+R9vQYd5DYfNLBHj9/TrTVCfKfUIeeEQRZYBz
ufYcDwi4uVx9VPj2wRhkK+lzxphvosJCNFK8Vn82oY7eHQ1RA4AEhCeE/hz8maq6 ufYcDwi4uVx9VPj2wRhkK+lzxphvosJCNFK8Vn82oY7eHQ1RA4AEhCeE/hz8maq6
NPoOPjpEN0DVnPIYdjPsdqd4UKQzkX/wMOxghz8SdcVROzUoL+9pZzx968OFuGrV NPoOPjpEN0DVnPIYdjPsdqd4UKQzkX/wMOxghz8SdcVROzUoL+9pZzx968OFuGrV
lUD0su37S6To1IUn6WNEuy1uJTzT3Zqi0hfm31AqPxlLWDOwnuKvUJl3RObyli2k lUD0su37S6To1IUn6WNEuy1uJTzT3Zqi0hfm31AqPxlLWDOwnuKvUJl3RObyli2k
CBtDa5xPE6GU6ZUEFUZ7qbk7iV5p8uTchKVbABEBAAG0IUVyaWMgS3VjayA8ZXJp CBtDa5xPE6GU6ZUEFUZ7qbk7iV5p8uTchKVbABEBAAG5AQ0EVOQoaAEIAMQuOKxG
Y0BibHVlbGluZWxhYnMuY29tPrkBDQRU5ChoAQgAxC44rEZjgnJevvrzdL5vCVqC Y4JyXr7683S+bwlagtViPXGey/A8Bf6b5uzW0SguRoF4oLpdfFBE/NKuBN1iItLU
1WI9cZ7L8DwF/pvm7NbRKC5GgXigul18UET80q4E3WIi0tTMG+pVWO+1v0dEu/Re zBvqVVjvtb9HRLv0Xgf5foXO+osCYzpcEokNY76uv6tDYZ3tP9JIUmDVsndAMCdl
B/l+hc76iwJjOlwSiQ1jvq6/q0Nhne0/0khSYNWyd0AwJ2VZktcD93dJV4EqTm1O WZLXA/d3SVeBKk5tTgpNYIoAHfxjecLJUJDEk/51GFBpuHAP0WVUgPD0hWQ2dgsR
Ck1gigAd/GN5wslQkMST/nUYUGm4cA/RZVSA8PSFZDZ2CxHyRyHgaOQNBUmWG2gf 8kch4GjkDQVJlhtoHxMVK6yQNuoiqMJDamV1noJ82MJX/GUROjGTNCTyzlMP/q+M
ExUrrJA26iKowkNqZXWegnzYwlf8ZRE6MZM0JPLOUw/+r4ybI8ny+/U55s2sm0XZ myPJ8vv1OebNrJtF2QnCbzXWuTd0qGgvzoBlmRcdbPVJLDezJr4c52klDHmnlRWx
CcJvNda5N3SoaC/OgGWZFx1s9UksN7MmvhznaSUMeaeVFbGC3nu9dsQhV9RxMwAR gt57vXbEIVfUcTMAEQEAAYkBHwQYAQIACQUCVOQoaAIbDAAKCRDCHOZTtjnkGnUs
AQABiQEfBBgBAgAJBQJU5ChoAhsMAAoJEMIc5lO2OeQadSwH/07x1foZKkFRGMlj B/9O8dX6GSpBURjJY8ApqHyhkqmfX7urnjiFen8B464ZX5EsijfvesW44cCL1oSn
wCmofKGSqZ9fu6ueOIV6fwHjrhlfkSyKN+96xbjhwIvWhKdSmWP/AsUqRDD/mTHw Uplj/wLFKkQw/5kx8GTHZYJnV5BhL7wriQy+RZUM5fTlnnqZX1YS8rMenc4/xvAF
ZMdlgmdXkGEvvCuJDL5FlQzl9OWeeplfVhLysx6dzj/G8AUXlfEIGBvb8Q56d5dK F5XxCBgb2/EOeneXSjKSHeB+L7fmCM0vMf68vkE0wyQDrtXFSX8KF9+MjHGfiB08
MpId4H4vt+YIzS8x/ry+QTTDJAOu1cVJfwoX34yMcZ+IHTzly2XKi4zQ41DyfrgY 5ctlyouM0ONQ8n64GJQqHVp2tdEbQXT2WONSX+MUns19quymQV10YMlTWLt3Q7jc
lCodWna10RtBdPZY41Jf4xSezX2q7KZBXXRgyVNYu3dDuNzhJAJ6jy7eMcb6urK6 4SQCeo8u3jHG+rqyup+93M+bmT5liG6eHAHnGQdAXzI/z1t+3StKGHVii8Wg8Kcb
n73cz5uZPmWIbp4cAecZB0BfMj/PW37dK0oYdWKLxaDwpxvIV7T45Y64Md2FCC+d yFe0+OWOuDHdhQgvnZwtl4e3
nC2Xh7c= =DwNF
=kzY9
-----END PGP PUBLIC KEY BLOCK----- -----END PGP PUBLIC KEY BLOCK-----
pub C488A74FCAE540C6 pub C488A74FCAE540C6
@ -1674,43 +1671,6 @@ fW1AkBVEk6siyL8PXfxmj9ev3H9xiQVLyJ6HpdHTLVjHjFkgNOLd
=R7zg =R7zg
-----END PGP PUBLIC KEY BLOCK----- -----END PGP PUBLIC KEY BLOCK-----
pub D041CAD2E452550F
uid Deanna <deannagarcia@google.com>
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 pub D364ABAA39A47320
sub 3F606403DCA455C8 sub 3F606403DCA455C8
-----BEGIN PGP PUBLIC KEY BLOCK----- -----BEGIN PGP PUBLIC KEY BLOCK-----

Some files were not shown because too many files have changed in this diff Show more