diff --git a/app/src/main/java/com/nextcloud/talk/api/NcApiCoroutines.kt b/app/src/main/java/com/nextcloud/talk/api/NcApiCoroutines.kt index af291d4cf9..8197025362 100644 --- a/app/src/main/java/com/nextcloud/talk/api/NcApiCoroutines.kt +++ b/app/src/main/java/com/nextcloud/talk/api/NcApiCoroutines.kt @@ -227,4 +227,7 @@ interface NcApiCoroutines { @Header("Authorization") authorization: String, @Url url: String ): UserAbsenceOverall + + @GET + suspend fun getRoom(@Header("Authorization") authorization: String?, @Url url: String?): RoomOverall } diff --git a/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt b/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt index 9af2aaa32d..6955bea95a 100644 --- a/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt @@ -311,7 +311,6 @@ class ChatActivity : var adapter: TalkMessagesListAdapter? = null var mentionAutocomplete: Autocomplete<*>? = null var layoutManager: LinearLayoutManager? = null - var pullChatMessagesPending = false var startCallFromNotification: Boolean = false var startCallFromRoomSwitch: Boolean = false @@ -434,20 +433,22 @@ class ChatActivity : super.onCreate(savedInstanceState) NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this) + chatViewModel = ViewModelProvider(this, viewModelFactory)[ChatViewModel::class.java] + binding = ActivityChatBinding.inflate(layoutInflater) - setupActionBar() + // setupActionBar() setContentView(binding.root) setupSystemColors() - conversationUser = currentUserProvider.currentUser.blockingGet() - handleIntent(intent) + conversationUser = currentUserProvider.currentUser.blockingGet() // TODO: -> ViewModel + handleIntent(intent) // TODO: -> ViewModel messageInputFragment = getMessageInputFragment() - chatViewModel = ViewModelProvider(this, viewModelFactory)[ChatViewModel::class.java] + chatViewModel.getRoom(roomToken) messageInputViewModel = ViewModelProvider(this, viewModelFactory)[MessageInputViewModel::class.java] - messageInputViewModel.setData(chatViewModel.getChatRepository()) + messageInputViewModel.setData(chatViewModel.getChatRepository()) // TODO: -> ViewModel this.lifecycleScope.launch { delay(DELAY_TO_SHOW_PROGRESS_BAR) @@ -462,8 +463,6 @@ class ChatActivity : chatViewModel.applyPlaybackSpeedPreferences(playbackSpeedPreferences) } - initObservers() - if (savedInstanceState != null) { // Restore value of members from saved state var voiceMessageId = savedInstanceState.getString(CURRENT_AUDIO_MESSAGE_KEY, "") @@ -517,7 +516,7 @@ class ChatActivity : } } - private fun handleIntent(intent: Intent) { + private fun handleIntent(intent: Intent) { // TODO: -> ViewModel val extras: Bundle? = intent.extras roomToken = extras?.getString(KEY_ROOM_TOKEN).orEmpty() @@ -578,53 +577,16 @@ class ChatActivity : private fun initObservers() { Log.d(TAG, "initObservers Called") - this.lifecycleScope.launch { - chatViewModel.getConversationFlow - .onEach { conversationModel -> - currentConversation = conversationModel - - val urlForChatting = ApiUtils.getUrlForChat(chatApiVersion, conversationUser?.baseUrl, roomToken) - val credentials = ApiUtils.getCredentials(conversationUser!!.username, conversationUser!!.token) - - chatViewModel.setData( - currentConversation!!, - credentials!!, - urlForChatting - ) - - logConversationInfos("GetRoomSuccessState") - - if (adapter == null) { - initAdapter() - binding.messagesListView.setAdapter(adapter) - layoutManager = binding.messagesListView.layoutManager as LinearLayoutManager? - } - - chatViewModel.getCapabilities(conversationUser!!, roomToken, currentConversation!!) - }.collect() - } - - chatViewModel.getRoomViewState.observe(this) { state -> - when (state) { - is ChatViewModel.GetRoomSuccessState -> { - // unused atm - } - - 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.GetCapabilitiesUpdateState -> { - if (currentConversation != null) { + if (chatViewModel.currentConversation != null) { spreedCapabilities = state.spreedCapabilities chatApiVersion = ApiUtils.getChatApiVersion(spreedCapabilities, intArrayOf(1)) - participantPermissions = ParticipantPermissions(spreedCapabilities, currentConversation!!) + participantPermissions = ParticipantPermissions( + spreedCapabilities, + chatViewModel.currentConversation!! + ) invalidateOptionsMenu() checkShowCallButtons() @@ -639,10 +601,21 @@ class ChatActivity : } is ChatViewModel.GetCapabilitiesInitialLoadState -> { - if (currentConversation != null) { + setupActionBar() + + if (adapter == null) { + initAdapter() + binding.messagesListView.setAdapter(adapter) + layoutManager = binding.messagesListView.layoutManager as LinearLayoutManager? + } + + if (chatViewModel.currentConversation != null) { spreedCapabilities = state.spreedCapabilities chatApiVersion = ApiUtils.getChatApiVersion(spreedCapabilities, intArrayOf(1)) - participantPermissions = ParticipantPermissions(spreedCapabilities, currentConversation!!) + participantPermissions = ParticipantPermissions( + spreedCapabilities, + chatViewModel.currentConversation!! + ) supportFragmentManager.commit { setReorderingAllowed(true) // optimizes out redundant replace operations @@ -662,15 +635,16 @@ class ChatActivity : setActionBarTitle() checkShowCallButtons() checkLobbyState() - if (currentConversation?.type == ConversationEnums.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL && - currentConversation?.status == "dnd" + if (chatViewModel.currentConversation?.type == + ConversationEnums.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL && + chatViewModel.currentConversation?.status == "dnd" ) { conversationUser?.let { user -> val credentials = ApiUtils.getCredentials(user.username, user.token) chatViewModel.outOfOfficeStatusOfUser( credentials!!, user.baseUrl!!, - currentConversation!!.name + chatViewModel.currentConversation!!.name ) } } @@ -705,11 +679,13 @@ class ChatActivity : chatViewModel.joinRoomViewState.observe(this) { state -> when (state) { is ChatViewModel.JoinRoomSuccessState -> { - currentConversation = state.conversationModel + chatViewModel.currentConversation = state.conversationModel - sessionIdAfterRoomJoined = currentConversation!!.sessionId - ApplicationWideCurrentRoomHolder.getInstance().session = currentConversation!!.sessionId - ApplicationWideCurrentRoomHolder.getInstance().currentRoomToken = currentConversation!!.token + sessionIdAfterRoomJoined = chatViewModel.currentConversation!!.sessionId + ApplicationWideCurrentRoomHolder.getInstance().session = + chatViewModel.currentConversation!!.sessionId + ApplicationWideCurrentRoomHolder.getInstance().currentRoomToken = + chatViewModel.currentConversation!!.token ApplicationWideCurrentRoomHolder.getInstance().userInRoom = conversationUser logConversationInfos("joinRoomWithPassword#onNext") @@ -744,7 +720,7 @@ class ChatActivity : getRoomInfoTimerHandler?.removeCallbacksAndMessages(null) } - if (webSocketInstance != null && currentConversation != null) { + if (webSocketInstance != null && chatViewModel.currentConversation != null) { webSocketInstance?.joinRoomWithRoomTokenAndSession( "", sessionIdAfterRoomJoined @@ -1159,17 +1135,15 @@ class ChatActivity : override fun onResume() { super.onResume() - logConversationInfos("onResume") + initObservers() - pullChatMessagesPending = false + // logConversationInfos("onResume") webSocketInstance?.getSignalingMessageReceiver()?.addListener(localParticipantMessageListener) webSocketInstance?.getSignalingMessageReceiver()?.addListener(conversationMessageListener) cancelNotificationsForCurrentConversation() - chatViewModel.getRoom(roomToken) - actionBar?.show() setupSwipeToReply() @@ -1211,8 +1185,8 @@ class ChatActivity : } }) - loadAvatarForStatusBar() - setActionBarTitle() + // loadAvatarForStatusBar() + // setActionBarTitle() viewThemeUtils.material.colorToolbarOverflowIcon(binding.chatToolbar) } @@ -1245,7 +1219,7 @@ class ChatActivity : val senderId = if (!conversationUser!!.userId.equals("?")) { "users/" + conversationUser!!.userId } else { - currentConversation?.actorType + "/" + currentConversation?.actorId + chatViewModel.currentConversation?.actorType + "/" + chatViewModel.currentConversation?.actorId } Log.d(TAG, "Initialize TalkMessagesListAdapter with senderId: $senderId") @@ -1322,7 +1296,7 @@ class ChatActivity : val payload = MessagePayload( roomToken, - ConversationUtils.isParticipantOwnerOrModerator(currentConversation!!), + ConversationUtils.isParticipantOwnerOrModerator(chatViewModel.currentConversation!!), profileBottomSheet ) @@ -1525,14 +1499,14 @@ class ChatActivity : } private fun loadAvatarForStatusBar() { - if (currentConversation == null) { + if (chatViewModel.currentConversation == null) { return } if (isOneToOneConversation()) { var url = ApiUtils.getUrlForAvatar( conversationUser!!.baseUrl!!, - currentConversation!!.name, + chatViewModel.currentConversation!!.name, true ) @@ -1549,7 +1523,7 @@ class ChatActivity : if (drawable != null && avatarSize > 0) { val bitmap = drawable.toBitmap(avatarSize, avatarSize) val status = StatusDrawable( - currentConversation!!.status, + chatViewModel.currentConversation!!.status, null, size, 0, @@ -1561,7 +1535,7 @@ class ChatActivity : binding.chatToolbar.findViewById(R.id.chat_toolbar_status) .setImageDrawable(status) binding.chatToolbar.findViewById(R.id.chat_toolbar_status).contentDescription = - currentConversation?.status + chatViewModel.currentConversation?.status binding.chatToolbar.findViewById(R.id.chat_toolbar_avatar_container) .visibility = View.VISIBLE } else { @@ -1599,19 +1573,19 @@ class ChatActivity : } fun isOneToOneConversation() = - currentConversation != null && - currentConversation?.type != null && - currentConversation?.type == ConversationEnums.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL + chatViewModel.currentConversation != null && + chatViewModel.currentConversation?.type != null && + chatViewModel.currentConversation?.type == ConversationEnums.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL private fun isGroupConversation() = - currentConversation != null && - currentConversation?.type != null && - currentConversation?.type == ConversationEnums.ConversationType.ROOM_GROUP_CALL + chatViewModel.currentConversation != null && + chatViewModel.currentConversation?.type != null && + chatViewModel.currentConversation?.type == ConversationEnums.ConversationType.ROOM_GROUP_CALL private fun isPublicConversation() = - currentConversation != null && - currentConversation?.type != null && - currentConversation?.type == ConversationEnums.ConversationType.ROOM_PUBLIC_CALL + chatViewModel.currentConversation != null && + chatViewModel.currentConversation?.type != null && + chatViewModel.currentConversation?.type == ConversationEnums.ConversationType.ROOM_PUBLIC_CALL private fun updateRoomTimerHandler(delay: Long = -1) { val delayForRecursiveCall = if (shouldShowLobby()) { @@ -1634,7 +1608,7 @@ class ChatActivity : private fun switchToRoom(token: String, startCallAfterRoomSwitch: Boolean, isVoiceOnlyCall: Boolean) { if (conversationUser != null) { runOnUiThread { - if (currentConversation?.objectType == ConversationEnums.ObjectType.ROOM) { + if (chatViewModel.currentConversation?.objectType == ConversationEnums.ObjectType.ROOM) { Snackbar.make( binding.root, context.resources.getString(R.string.switch_to_main_room), @@ -2105,7 +2079,7 @@ class ChatActivity : private fun checkShowCallButtons() { if (isReadOnlyConversation() || shouldShowLobby() || - ConversationUtils.isNoteToSelfConversation(currentConversation) + ConversationUtils.isNoteToSelfConversation(chatViewModel.currentConversation) ) { disableCallButtons() } else { @@ -2125,10 +2099,11 @@ class ChatActivity : } private fun shouldShowLobby(): Boolean { - if (currentConversation != null) { + if (chatViewModel.currentConversation != null) { return CapabilitiesUtil.hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.WEBINARY_LOBBY) && - currentConversation?.lobbyState == ConversationEnums.LobbyState.LOBBY_STATE_MODERATORS_ONLY && - !ConversationUtils.canModerate(currentConversation!!, spreedCapabilities) && + chatViewModel.currentConversation?.lobbyState == + ConversationEnums.LobbyState.LOBBY_STATE_MODERATORS_ONLY && + !ConversationUtils.canModerate(chatViewModel.currentConversation!!, spreedCapabilities) && !participantPermissions.canIgnoreLobby() } return false @@ -2161,13 +2136,13 @@ class ChatActivity : } private fun isReadOnlyConversation(): Boolean = - currentConversation?.conversationReadOnlyState != null && - currentConversation?.conversationReadOnlyState == + chatViewModel.currentConversation?.conversationReadOnlyState != null && + chatViewModel.currentConversation?.conversationReadOnlyState == ConversationEnums.ConversationReadOnlyState.CONVERSATION_READ_ONLY private fun checkLobbyState() { - if (currentConversation != null && - ConversationUtils.isLobbyViewApplicable(currentConversation!!, spreedCapabilities) && + if (chatViewModel.currentConversation != null && + ConversationUtils.isLobbyViewApplicable(chatViewModel.currentConversation!!, spreedCapabilities) && shouldShowLobby() ) { showLobbyView() @@ -2188,11 +2163,11 @@ class ChatActivity : sb.append(resources!!.getText(R.string.nc_lobby_waiting)) .append("\n\n") - if (currentConversation?.lobbyTimer != null && - currentConversation?.lobbyTimer != + if (chatViewModel.currentConversation?.lobbyTimer != null && + chatViewModel.currentConversation?.lobbyTimer != 0L ) { - val timestampMS = (currentConversation?.lobbyTimer ?: 0) * DateConstants.SECOND_DIVIDER + val timestampMS = (chatViewModel.currentConversation?.lobbyTimer ?: 0) * DateConstants.SECOND_DIVIDER val stringWithStartDate = String.format( resources!!.getString(R.string.nc_lobby_start_date), dateUtils.getLocalDateTimeStringFromTimestamp(timestampMS) @@ -2203,7 +2178,7 @@ class ChatActivity : .append("\n\n") } - sb.append(currentConversation!!.description) + sb.append(chatViewModel.currentConversation!!.description) binding.lobby.lobbyTextView.text = sb.toString() } @@ -2503,7 +2478,7 @@ class ChatActivity : if (token == "") room = roomToken else room = token - chatViewModel.uploadFile(fileUri, room, currentConversation?.displayName!!, metaData) + chatViewModel.uploadFile(fileUri, room, chatViewModel.currentConversation?.displayName!!, metaData) } private fun showLocalFilePicker() { @@ -2560,7 +2535,7 @@ class ChatActivity : } private fun validSessionId(): Boolean = - currentConversation != null && + chatViewModel.currentConversation != null && sessionIdAfterRoomJoined?.isNotEmpty() == true && sessionIdAfterRoomJoined != "0" @@ -2628,32 +2603,32 @@ class ChatActivity : viewThemeUtils.platform.colorTextView(title, ColorRole.ON_SURFACE) title.text = - if (currentConversation?.displayName != null) { + if (chatViewModel.currentConversation?.displayName != null) { try { - EmojiCompat.get().process(currentConversation?.displayName as CharSequence).toString() + EmojiCompat.get().process(chatViewModel.currentConversation?.displayName as CharSequence).toString() } catch (e: java.lang.IllegalStateException) { Log.e(TAG, "setActionBarTitle failed $e") - currentConversation?.displayName + chatViewModel.currentConversation?.displayName } } else { "" } - if (currentConversation?.type == ConversationEnums.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL) { + if (chatViewModel.currentConversation?.type == ConversationEnums.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL) { var statusMessage = "" - if (currentConversation?.statusIcon != null) { - statusMessage += currentConversation?.statusIcon + if (chatViewModel.currentConversation?.statusIcon != null) { + statusMessage += chatViewModel.currentConversation?.statusIcon } - if (currentConversation?.statusMessage != null) { - statusMessage += currentConversation?.statusMessage + if (chatViewModel.currentConversation?.statusMessage != null) { + statusMessage += chatViewModel.currentConversation?.statusMessage } statusMessageViewContents(statusMessage) } else { - if (currentConversation?.type == ConversationEnums.ConversationType.ROOM_GROUP_CALL || - currentConversation?.type == ConversationEnums.ConversationType.ROOM_PUBLIC_CALL + if (chatViewModel.currentConversation?.type == ConversationEnums.ConversationType.ROOM_GROUP_CALL || + chatViewModel.currentConversation?.type == ConversationEnums.ConversationType.ROOM_PUBLIC_CALL ) { var descriptionMessage = "" - descriptionMessage += currentConversation?.description + descriptionMessage += chatViewModel.currentConversation?.description statusMessageViewContents(descriptionMessage) } } @@ -2689,7 +2664,7 @@ class ChatActivity : private fun joinRoomWithPassword() { // if ApplicationWideCurrentRoomHolder contains a session (because a call is active), then keep the sessionId if (ApplicationWideCurrentRoomHolder.getInstance().currentRoomToken == - currentConversation!!.token + chatViewModel.currentConversation!!.token ) { sessionIdAfterRoomJoined = ApplicationWideCurrentRoomHolder.getInstance().session @@ -2733,11 +2708,11 @@ class ChatActivity : } private fun setupWebsocket() { - if (currentConversation == null || conversationUser == null) { + if (chatViewModel.currentConversation == null || conversationUser == null) { return } - if (currentConversation!!.remoteServer?.isNotEmpty() == true) { + if (chatViewModel.currentConversation!!.remoteServer?.isNotEmpty() == true) { val apiVersion = ApiUtils.getSignalingApiVersion(conversationUser!!, intArrayOf(ApiUtils.API_V3, 2, 1)) ncApi.getSignalingSettings( credentials, @@ -2918,9 +2893,12 @@ class ChatActivity : chatMessage.isGrouped = groupMessages(chatMessage, previousChatMessage) } chatMessage.isOneToOneConversation = - (currentConversation?.type == ConversationEnums.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL) + ( + chatViewModel.currentConversation?.type == + ConversationEnums.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL + ) chatMessage.isFormerOneToOneConversation = - (currentConversation?.type == ConversationEnums.ConversationType.FORMER_ONE_TO_ONE) + (chatViewModel.currentConversation?.type == ConversationEnums.ConversationType.FORMER_ONE_TO_ONE) Log.d(TAG, "chatMessage to add:" + chatMessage.message) it.addToStart(chatMessage, scrollToBottom) } @@ -2965,9 +2943,9 @@ class ChatActivity : val chatMessage = chatMessageList[i] chatMessage.isOneToOneConversation = - currentConversation?.type == ConversationEnums.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL + chatViewModel.currentConversation?.type == ConversationEnums.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL chatMessage.isFormerOneToOneConversation = - (currentConversation?.type == ConversationEnums.ConversationType.FORMER_ONE_TO_ONE) + (chatViewModel.currentConversation?.type == ConversationEnums.ConversationType.FORMER_ONE_TO_ONE) chatMessage.activeUser = conversationUser chatMessage.token = roomToken } @@ -3120,7 +3098,7 @@ class ChatActivity : withUrl = urlForChatting, withCredentials = credentials!!, withMessageLimit = MESSAGE_PULL_LIMIT, - roomToken = currentConversation!!.token + roomToken = chatViewModel.currentConversation!!.token ) } @@ -3157,9 +3135,9 @@ class ChatActivity : val searchItem = menu.findItem(R.id.conversation_search) searchItem.isVisible = CapabilitiesUtil.isUnifiedSearchAvailable(spreedCapabilities) && - currentConversation!!.remoteServer.isNullOrEmpty() + chatViewModel.currentConversation!!.remoteServer.isNullOrEmpty() - if (currentConversation!!.remoteServer != null || + if (chatViewModel.currentConversation!!.remoteServer != null || !CapabilitiesUtil.isSharedItemsAvailable(spreedCapabilities) ) { menu.removeItem(R.id.shared_items) @@ -3233,18 +3211,18 @@ class ChatActivity : private fun showSharedItems() { val intent = Intent(this, SharedItemsActivity::class.java) - intent.putExtra(KEY_CONVERSATION_NAME, currentConversation?.displayName) + intent.putExtra(KEY_CONVERSATION_NAME, chatViewModel.currentConversation?.displayName) intent.putExtra(KEY_ROOM_TOKEN, roomToken) intent.putExtra( SharedItemsActivity.KEY_USER_IS_OWNER_OR_MODERATOR, - ConversationUtils.isParticipantOwnerOrModerator(currentConversation!!) + ConversationUtils.isParticipantOwnerOrModerator(chatViewModel.currentConversation!!) ) startActivity(intent) } private fun startMessageSearch() { val intent = Intent(this, MessageSearchActivity::class.java) - intent.putExtra(KEY_CONVERSATION_NAME, currentConversation?.displayName) + intent.putExtra(KEY_CONVERSATION_NAME, chatViewModel.currentConversation?.displayName) intent.putExtra(KEY_ROOM_TOKEN, roomToken) startMessageSearchForResult.launch(intent) } @@ -3293,8 +3271,7 @@ class ChatActivity : private fun isInfoMessageAboutDeletion(currentMessage: MutableMap.MutableEntry): Boolean = currentMessage.value.parentMessageId != null && - currentMessage.value.systemMessageType == ChatMessage - .SystemMessageType.MESSAGE_DELETED + currentMessage.value.systemMessageType == ChatMessage.SystemMessageType.MESSAGE_DELETED private fun isReactionsMessage(currentMessage: MutableMap.MutableEntry): Boolean = currentMessage.value.systemMessageType == ChatMessage.SystemMessageType.REACTION || @@ -3303,17 +3280,16 @@ class ChatActivity : private fun isEditMessage(currentMessage: MutableMap.MutableEntry): Boolean = currentMessage.value.parentMessageId != null && - currentMessage.value.systemMessageType == ChatMessage - .SystemMessageType.MESSAGE_EDITED + currentMessage.value.systemMessageType == ChatMessage.SystemMessageType.MESSAGE_EDITED private fun isPollVotedMessage(currentMessage: MutableMap.MutableEntry): Boolean = currentMessage.value.systemMessageType == ChatMessage.SystemMessageType.POLL_VOTED private fun startACall(isVoiceOnlyCall: Boolean, callWithoutNotification: Boolean) { - currentConversation?.let { + chatViewModel.currentConversation?.let { if (conversationUser != null) { val pp = ParticipantPermissions(spreedCapabilities, it) - if (!pp.canStartCall() && currentConversation?.hasCall == false) { + if (!pp.canStartCall() && chatViewModel.currentConversation?.hasCall == false) { Snackbar.make(binding.root, R.string.startCallForbidden, Snackbar.LENGTH_LONG).show() } else { ApplicationWideCurrentRoomHolder.getInstance().isDialing = true @@ -3327,7 +3303,7 @@ class ChatActivity : } private fun getIntentForCall(isVoiceOnlyCall: Boolean, callWithoutNotification: Boolean): Intent? { - currentConversation?.let { + chatViewModel.currentConversation?.let { val bundle = Bundle() bundle.putString(KEY_ROOM_TOKEN, roomToken) bundle.putString(BundleKeys.KEY_CONVERSATION_PASSWORD, roomPassword) @@ -3411,7 +3387,7 @@ class ChatActivity : this, message, conversationUser, - currentConversation, + chatViewModel.currentConversation, isShowMessageDeletionButton(message), participantPermissions.hasChatPermission(), spreedCapabilities @@ -3612,7 +3588,7 @@ class ChatActivity : val lon = data["longitude"]!! metaData = "{\"type\":\"geo-location\",\"id\":\"geo:$lat,$lon\",\"latitude\":\"$lat\"," + - "\"longitude\":\"$lon\",\"name\":\"$name\"}" + "\"longitude\":\"$lon\",\"name\":\"$name\"}" } shareToNotes(shareUri, roomToken, message, objectId, metaData) @@ -3693,8 +3669,8 @@ class ChatActivity : conversationUser?.userId?.isNotEmpty() == true && conversationUser!!.userId != "?" && message.user.id.startsWith("users/") && - message.user.id.substring(ACTOR_LENGTH) != currentConversation?.actorId && - currentConversation?.type != ConversationEnums.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL || + message.user.id.substring(ACTOR_LENGTH) != chatViewModel.currentConversation?.actorId && + chatViewModel.currentConversation?.type != ConversationEnums.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL || isShowMessageDeletionButton(message) || // delete ChatMessage.MessageType.REGULAR_TEXT_MESSAGE == message.getCalculateMessageType() || @@ -3710,7 +3686,7 @@ class ChatActivity : messageTemp.message = getString(R.string.message_deleted_by_you) messageTemp.isOneToOneConversation = - currentConversation?.type == ConversationEnums.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL + chatViewModel.currentConversation?.type == ConversationEnums.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL messageTemp.activeUser = conversationUser adapter?.update(messageTemp) @@ -3728,7 +3704,7 @@ class ChatActivity : } messageTemp.isOneToOneConversation = - currentConversation?.type == ConversationEnums.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL + chatViewModel.currentConversation?.type == ConversationEnums.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL messageTemp.activeUser = conversationUser adapter?.update(messageTemp) @@ -3740,7 +3716,7 @@ class ChatActivity : // TODO is this needed? messageTemp.isOneToOneConversation = - currentConversation?.type == ConversationEnums.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL + chatViewModel.currentConversation?.type == ConversationEnums.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL messageTemp.activeUser = conversationUser adapter?.update(messageTemp) @@ -3815,7 +3791,7 @@ class ChatActivity : val isUserAllowedByPrivileges = if (message.actorId == conversationUser!!.userId) { true } else { - ConversationUtils.canModerate(currentConversation!!, spreedCapabilities) + ConversationUtils.canModerate(chatViewModel.currentConversation!!, spreedCapabilities) } return isUserAllowedByPrivileges } @@ -3877,8 +3853,8 @@ class ChatActivity : @Subscribe(threadMode = ThreadMode.BACKGROUND) fun onMessageEvent(userMentionClickEvent: UserMentionClickEvent) { - if (currentConversation?.type != ConversationEnums.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL || - currentConversation?.name != userMentionClickEvent.userId + if (chatViewModel.currentConversation?.type != ConversationEnums.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL || + chatViewModel.currentConversation?.name != userMentionClickEvent.userId ) { var apiVersion = 1 // FIXME Fix API checking with guests? @@ -3980,7 +3956,10 @@ class ChatActivity : Log.d(TAG, " | method: $methodName") Log.d(TAG, " | ChatActivity: " + System.identityHashCode(this).toString()) Log.d(TAG, " | roomToken: $roomToken") - Log.d(TAG, " | currentConversation?.displayName: ${currentConversation?.displayName}") + Log.d( + TAG, + " | chatViewModel.currentConversation?.displayName: ${chatViewModel.currentConversation?.displayName}" + ) Log.d(TAG, " | sessionIdAfterRoomJoined: $sessionIdAfterRoomJoined") Log.d(TAG, " |-----------------------------------------------") } diff --git a/app/src/main/java/com/nextcloud/talk/chat/data/network/ChatNetworkDataSource.kt b/app/src/main/java/com/nextcloud/talk/chat/data/network/ChatNetworkDataSource.kt index d808ac9a8b..47f530a150 100644 --- a/app/src/main/java/com/nextcloud/talk/chat/data/network/ChatNetworkDataSource.kt +++ b/app/src/main/java/com/nextcloud/talk/chat/data/network/ChatNetworkDataSource.kt @@ -21,6 +21,7 @@ import retrofit2.Response @Suppress("LongParameterList", "TooManyFunctions") interface ChatNetworkDataSource { fun getRoom(user: User, roomToken: String): Observable + suspend fun getRoomCoroutines(user: User, roomToken: String): ConversationModel fun getCapabilities(user: User, roomToken: String): Observable fun joinRoom(user: User, roomToken: String, roomPassword: String): Observable fun setReminder( diff --git a/app/src/main/java/com/nextcloud/talk/chat/data/network/RetrofitChatNetwork.kt b/app/src/main/java/com/nextcloud/talk/chat/data/network/RetrofitChatNetwork.kt index 6f857d2540..5a3295160a 100644 --- a/app/src/main/java/com/nextcloud/talk/chat/data/network/RetrofitChatNetwork.kt +++ b/app/src/main/java/com/nextcloud/talk/chat/data/network/RetrofitChatNetwork.kt @@ -36,6 +36,18 @@ class RetrofitChatNetwork( ).map { ConversationModel.mapToConversationModel(it.ocs?.data!!, user) } } + override suspend fun getRoomCoroutines(user: User, roomToken: String): ConversationModel { + val credentials: String = ApiUtils.getCredentials(user.username, user.token)!! + val apiVersion = ApiUtils.getConversationApiVersion(user, intArrayOf(ApiUtils.API_V4, ApiUtils.API_V3, 1)) + + val conversation = ncApiCoroutines.getRoom( + credentials, + ApiUtils.getUrlForRoom(apiVersion, user.baseUrl!!, roomToken) + ).ocs?.data!! + + return ConversationModel.mapToConversationModel(conversation, user) + } + override fun getCapabilities(user: User, roomToken: String): Observable { val credentials: String = ApiUtils.getCredentials(user.username, user.token)!! val apiVersion = ApiUtils.getConversationApiVersion(user, intArrayOf(ApiUtils.API_V4, ApiUtils.API_V3, 1)) diff --git a/app/src/main/java/com/nextcloud/talk/chat/viewmodels/ChatViewModel.kt b/app/src/main/java/com/nextcloud/talk/chat/viewmodels/ChatViewModel.kt index bb2abef36f..d049e3bb9d 100644 --- a/app/src/main/java/com/nextcloud/talk/chat/viewmodels/ChatViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/chat/viewmodels/ChatViewModel.kt @@ -38,6 +38,7 @@ import com.nextcloud.talk.models.json.reminder.Reminder import com.nextcloud.talk.models.json.userAbsence.UserAbsenceData import com.nextcloud.talk.repositories.reactions.ReactionsRepository import com.nextcloud.talk.ui.PlaybackSpeed +import com.nextcloud.talk.utils.ApiUtils import com.nextcloud.talk.utils.ConversationUtils import com.nextcloud.talk.utils.bundle.BundleKeys import com.nextcloud.talk.utils.database.user.CurrentUserProviderNew @@ -67,6 +68,12 @@ class ChatViewModel @Inject constructor( ) : ViewModel(), DefaultLifecycleObserver { + var chatApiVersion: Int = 1 + + val currentUser: User = userProvider.currentUser.blockingGet() + + lateinit var currentConversation: ConversationModel + enum class LifeCycleFlag { PAUSED, RESUMED, @@ -145,13 +152,6 @@ class ChatViewModel @Inject constructor( val getLastReadMessageFlow = chatRepository.lastReadMessageFlow - val getConversationFlow = conversationRepository.conversationFlow - .onEach { - _getRoomViewState.value = GetRoomSuccessState - }.catch { - _getRoomViewState.value = GetRoomErrorState - } - val getGeneralUIFlow = chatRepository.generalUIFlow sealed interface ViewState @@ -172,14 +172,6 @@ class ChatViewModel @Inject constructor( val getNoteToSelfAvailability: LiveData get() = _getNoteToSelfAvailability - object GetRoomStartState : ViewState - object GetRoomErrorState : ViewState - object GetRoomSuccessState : ViewState - - private val _getRoomViewState: MutableLiveData = MutableLiveData(GetRoomStartState) - val getRoomViewState: LiveData - get() = _getRoomViewState - object GetCapabilitiesStartState : ViewState object GetCapabilitiesErrorState : ViewState open class GetCapabilitiesInitialLoadState(val spreedCapabilities: SpreedCapability) : ViewState @@ -243,16 +235,31 @@ class ChatViewModel @Inject constructor( val reactionDeletedViewState: LiveData get() = _reactionDeletedViewState - fun setData(conversationModel: ConversationModel, credentials: String, urlForChatting: String) { - chatRepository.setData(conversationModel, credentials, urlForChatting) - } - fun getRoom(token: String) { - _getRoomViewState.value = GetRoomStartState - conversationRepository.getRoom(token) + viewModelScope.launch { + conversationRepository.getRoom(token).collect { conversation -> + currentConversation = conversation!! + // val chatApiVersion = ApiUtils.getChatApiVersion(spreedCapabilities, intArrayOf(1)) + + val urlForChatting = ApiUtils.getUrlForChat(chatApiVersion, currentUser.baseUrl, token) + val credentials = ApiUtils.getCredentials(currentUser.username, currentUser.token) + + chatRepository.setData(currentConversation, credentials!!, urlForChatting) + + // logConversationInfos("GetRoomSuccessState") + + // if (adapter == null) { // do later when capabilities are fetched? + // initAdapter() + // binding.messagesListView.setAdapter(adapter) + // layoutManager = binding.messagesListView.layoutManager as LinearLayoutManager? + // } + + getCapabilities(currentUser, currentConversation) + } + } } - fun getCapabilities(user: User, token: String, conversationModel: ConversationModel) { + fun getCapabilities(user: User, conversationModel: ConversationModel) { Log.d(TAG, "Remote server ${conversationModel.remoteServer}") if (conversationModel.remoteServer.isNullOrEmpty()) { if (_getCapabilitiesViewState.value == GetCapabilitiesStartState) { @@ -263,7 +270,7 @@ class ChatViewModel @Inject constructor( _getCapabilitiesViewState.value = GetCapabilitiesUpdateState(user.capabilities!!.spreedCapability!!) } } else { - chatNetworkDataSource.getCapabilities(user, token) + chatNetworkDataSource.getCapabilities(user, conversationModel.token) .subscribeOn(Schedulers.io()) ?.observeOn(AndroidSchedulers.mainThread()) ?.subscribe(object : Observer { @@ -362,7 +369,6 @@ class ChatViewModel @Inject constructor( override fun onNext(t: GenericOverall) { _leaveRoomViewState.value = LeaveRoomSuccessState(funToCallWhenLeaveSuccessful) _getCapabilitiesViewState.value = GetCapabilitiesStartState - _getRoomViewState.value = GetRoomStartState } }) } diff --git a/app/src/main/java/com/nextcloud/talk/conversationlist/data/OfflineConversationsRepository.kt b/app/src/main/java/com/nextcloud/talk/conversationlist/data/OfflineConversationsRepository.kt index 92591fe4e1..fd120e44d2 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationlist/data/OfflineConversationsRepository.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationlist/data/OfflineConversationsRepository.kt @@ -18,11 +18,6 @@ interface OfflineConversationsRepository { */ val roomListFlow: Flow> - /** - * Stream of a single conversation, for use in each conversations settings. - */ - val conversationFlow: Flow - /** * Loads rooms from local storage. If the rooms are not found, then it * synchronizes the database with the server, before retrying exactly once. Only @@ -35,5 +30,5 @@ interface OfflineConversationsRepository { * Called once onStart to emit a conversation to [conversationFlow] * to be handled asynchronously. */ - fun getRoom(roomToken: String): Job + fun getRoom(roomToken: String): Flow } diff --git a/app/src/main/java/com/nextcloud/talk/conversationlist/data/network/OfflineFirstConversationsRepository.kt b/app/src/main/java/com/nextcloud/talk/conversationlist/data/network/OfflineFirstConversationsRepository.kt index c4b9419624..0c6e3d0deb 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationlist/data/network/OfflineFirstConversationsRepository.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationlist/data/network/OfflineFirstConversationsRepository.kt @@ -20,9 +20,7 @@ import com.nextcloud.talk.data.user.model.User import com.nextcloud.talk.models.domain.ConversationModel import com.nextcloud.talk.utils.CapabilitiesUtil.isUserStatusAvailable import com.nextcloud.talk.utils.database.user.CurrentUserProviderNew -import io.reactivex.Observer import io.reactivex.android.schedulers.AndroidSchedulers -import io.reactivex.disposables.Disposable import io.reactivex.schedulers.Schedulers import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -30,9 +28,9 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking import javax.inject.Inject class OfflineFirstConversationsRepository @Inject constructor( @@ -46,10 +44,6 @@ class OfflineFirstConversationsRepository @Inject constructor( get() = _roomListFlow private val _roomListFlow: MutableSharedFlow> = MutableSharedFlow() - override val conversationFlow: Flow - get() = _conversationFlow - private val _conversationFlow: MutableSharedFlow = MutableSharedFlow() - private val scope = CoroutineScope(Dispatchers.IO) private var user: User = currentUserProviderNew.currentUser.blockingGet() @@ -67,41 +61,23 @@ class OfflineFirstConversationsRepository @Inject constructor( } } - override fun getRoom(roomToken: String): Job = - scope.launch { - chatNetworkDataSource.getRoom(user, roomToken) - .subscribeOn(Schedulers.io()) - ?.observeOn(AndroidSchedulers.mainThread()) - ?.subscribe(object : Observer { - override fun onSubscribe(p0: Disposable) { - // unused atm - } - - override fun onError(e: Throwable) { - runBlocking { - // In case network is offline or call fails - val id = user.id!! - val model = getConversation(id, roomToken) - if (model != null) { - _conversationFlow.emit(model) - } else { - Log.e(TAG, "Conversation model not found on device database") - } - } - } - - override fun onComplete() { - // unused atm - } - - override fun onNext(model: ConversationModel) { - runBlocking { - _conversationFlow.emit(model) - val entityList = listOf(model.asEntity()) - dao.upsertConversations(entityList) - } - } - }) + override fun getRoom(roomToken: String): Flow = + flow { + try { + val conversationModel = chatNetworkDataSource.getRoomCoroutines(user, roomToken) + emit(conversationModel) + val entityList = listOf(conversationModel.asEntity()) + dao.upsertConversations(entityList) + } catch (e: Exception) { + // In case network is offline or call fails + val id = user.id!! + val model = getConversation(id, roomToken) + if (model != null) { + emit(model) + } else { + Log.e(TAG, "Conversation model not found on device database") + } + } } @Suppress("Detekt.TooGenericExceptionCaught")