Merge pull request #3663 from nextcloud/feature/noid/handleFederationRoomCapabilities

Feature/noid/handle federation room capabilities
This commit is contained in:
Marcel Hibbe 2024-03-01 11:28:04 +01:00 committed by GitHub
commit f11b426421
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
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