From 6a799387c8e676912a6fd9515dc6769fa622ee2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Calvi=C3=B1o=20S=C3=A1nchez?= Date: Thu, 26 Jan 2023 13:30:56 +0100 Subject: [PATCH 01/19] Extract method to process "update" events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Daniel Calviño Sánchez --- .../talk/signaling/SignalingMessageReceiver.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/nextcloud/talk/signaling/SignalingMessageReceiver.java b/app/src/main/java/com/nextcloud/talk/signaling/SignalingMessageReceiver.java index 47dd83ca9..fb2aaf9da 100644 --- a/app/src/main/java/com/nextcloud/talk/signaling/SignalingMessageReceiver.java +++ b/app/src/main/java/com/nextcloud/talk/signaling/SignalingMessageReceiver.java @@ -232,10 +232,18 @@ public abstract class SignalingMessageReceiver { } protected void processEvent(Map eventMap) { - if (!"update".equals(eventMap.get("type")) || !"participants".equals(eventMap.get("target"))) { + if (!"participants".equals(eventMap.get("target"))) { return; } + if ("update".equals(eventMap.get("type"))) { + processUpdateEvent(eventMap); + + return; + } + } + + private void processUpdateEvent(Map eventMap) { Map updateMap; try { updateMap = (Map) eventMap.get("update"); From 747a4646d37d538a44fead4894d83a33344a7c7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Calvi=C3=B1o=20S=C3=A1nchez?= Date: Thu, 26 Jan 2023 13:33:07 +0100 Subject: [PATCH 02/19] Add listener for local participant signaling messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Daniel Calviño Sánchez --- .../LocalParticipantMessageNotifier.java | 53 +++++ .../signaling/SignalingMessageReceiver.java | 80 +++++++- .../talk/webrtc/WebSocketInstance.kt | 4 +- ...ngMessageReceiverLocalParticipantTest.java | 193 ++++++++++++++++++ 4 files changed, 327 insertions(+), 3 deletions(-) create mode 100644 app/src/main/java/com/nextcloud/talk/signaling/LocalParticipantMessageNotifier.java create mode 100644 app/src/test/java/com/nextcloud/talk/signaling/SignalingMessageReceiverLocalParticipantTest.java diff --git a/app/src/main/java/com/nextcloud/talk/signaling/LocalParticipantMessageNotifier.java b/app/src/main/java/com/nextcloud/talk/signaling/LocalParticipantMessageNotifier.java new file mode 100644 index 000000000..b22ed5195 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/signaling/LocalParticipantMessageNotifier.java @@ -0,0 +1,53 @@ +/* + * Nextcloud Talk application + * + * @author Daniel Calviño Sánchez + * Copyright (C) 2023 Daniel Calviño Sánchez + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.nextcloud.talk.signaling; + +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.Set; + +/** + * Helper class to register and notify LocalParticipantMessageListeners. + * + * This class is only meant for internal use by SignalingMessageReceiver; listeners must register themselves against + * a SignalingMessageReceiver rather than against a LocalParticipantMessageNotifier. + */ +class LocalParticipantMessageNotifier { + + private final Set localParticipantMessageListeners = new LinkedHashSet<>(); + + public synchronized void addListener(SignalingMessageReceiver.LocalParticipantMessageListener listener) { + if (listener == null) { + throw new IllegalArgumentException("localParticipantMessageListener can not be null"); + } + + localParticipantMessageListeners.add(listener); + } + + public synchronized void removeListener(SignalingMessageReceiver.LocalParticipantMessageListener listener) { + localParticipantMessageListeners.remove(listener); + } + + public synchronized void notifySwitchTo(String token) { + for (SignalingMessageReceiver.LocalParticipantMessageListener listener : new ArrayList<>(localParticipantMessageListeners)) { + listener.onSwitchTo(token); + } + } +} diff --git a/app/src/main/java/com/nextcloud/talk/signaling/SignalingMessageReceiver.java b/app/src/main/java/com/nextcloud/talk/signaling/SignalingMessageReceiver.java index fb2aaf9da..a8e201817 100644 --- a/app/src/main/java/com/nextcloud/talk/signaling/SignalingMessageReceiver.java +++ b/app/src/main/java/com/nextcloud/talk/signaling/SignalingMessageReceiver.java @@ -118,6 +118,26 @@ public abstract class SignalingMessageReceiver { void onAllParticipantsUpdate(long inCall); } + /** + * Listener for local participant messages. + * + * The messages are implicitly bound to the local participant (or, rather, its session); listeners are expected + * to know the local participant. + * + * The messages are related to the conversation, so the local participant may or may not be in a call when they + * are received. + */ + public interface LocalParticipantMessageListener { + /** + * Request for the client to switch to the given conversation. + * + * This message is received only when the external signaling server is used. + * + * @param token the token of the conversation to switch to. + */ + void onSwitchTo(String token); + } + /** * Listener for call participant messages. * @@ -160,6 +180,8 @@ public abstract class SignalingMessageReceiver { private final ParticipantListMessageNotifier participantListMessageNotifier = new ParticipantListMessageNotifier(); + private final LocalParticipantMessageNotifier localParticipantMessageNotifier = new LocalParticipantMessageNotifier(); + private final CallParticipantMessageNotifier callParticipantMessageNotifier = new CallParticipantMessageNotifier(); private final OfferMessageNotifier offerMessageNotifier = new OfferMessageNotifier(); @@ -181,6 +203,21 @@ public abstract class SignalingMessageReceiver { participantListMessageNotifier.removeListener(listener); } + /** + * Adds a listener for local participant messages. + * + * A listener is expected to be added only once. If the same listener is added again it will be notified just once. + * + * @param listener the LocalParticipantMessageListener + */ + public void addListener(LocalParticipantMessageListener listener) { + localParticipantMessageNotifier.addListener(listener); + } + + public void removeListener(LocalParticipantMessageListener listener) { + localParticipantMessageNotifier.removeListener(listener); + } + /** * Adds a listener for call participant messages. * @@ -232,17 +269,56 @@ public abstract class SignalingMessageReceiver { } protected void processEvent(Map eventMap) { - if (!"participants".equals(eventMap.get("target"))) { + if ("room".equals(eventMap.get("target")) && "switchto".equals(eventMap.get("type"))) { + processSwitchToEvent(eventMap); + return; } - if ("update".equals(eventMap.get("type"))) { + if ("participants".equals(eventMap.get("target")) && "update".equals(eventMap.get("type"))) { processUpdateEvent(eventMap); return; } } + private void processSwitchToEvent(Map eventMap) { + // Message schema: + // { + // "type": "event", + // "event": { + // "target": "room", + // "type": "switchto", + // "switchto": { + // "roomid": #STRING#, + // }, + // }, + // } + + Map switchToMap; + try { + switchToMap = (Map) eventMap.get("switchto"); + } catch (RuntimeException e) { + // Broken message, this should not happen. + return; + } + + if (switchToMap == null) { + // Broken message, this should not happen. + return; + } + + String token; + try { + token = switchToMap.get("roomid").toString(); + } catch (RuntimeException e) { + // Broken message, this should not happen. + return; + } + + localParticipantMessageNotifier.notifySwitchTo(token); + } + private void processUpdateEvent(Map eventMap) { Map updateMap; try { diff --git a/app/src/main/java/com/nextcloud/talk/webrtc/WebSocketInstance.kt b/app/src/main/java/com/nextcloud/talk/webrtc/WebSocketInstance.kt index 46076f29d..4a3d630db 100644 --- a/app/src/main/java/com/nextcloud/talk/webrtc/WebSocketInstance.kt +++ b/app/src/main/java/com/nextcloud/talk/webrtc/WebSocketInstance.kt @@ -201,12 +201,14 @@ class WebSocketInstance internal constructor( val target = eventOverallWebSocketMessage.eventMap!!["target"] as String? if (target != null) { when (target) { - Globals.TARGET_ROOM -> + Globals.TARGET_ROOM -> { if ("message" == eventOverallWebSocketMessage.eventMap!!["type"]) { processRoomMessageMessage(eventOverallWebSocketMessage) } else if ("join" == eventOverallWebSocketMessage.eventMap!!["type"]) { processRoomJoinMessage(eventOverallWebSocketMessage) } + signalingMessageReceiver.process(eventOverallWebSocketMessage.eventMap) + } Globals.TARGET_PARTICIPANTS -> signalingMessageReceiver.process(eventOverallWebSocketMessage.eventMap) else -> diff --git a/app/src/test/java/com/nextcloud/talk/signaling/SignalingMessageReceiverLocalParticipantTest.java b/app/src/test/java/com/nextcloud/talk/signaling/SignalingMessageReceiverLocalParticipantTest.java new file mode 100644 index 000000000..4c6acee79 --- /dev/null +++ b/app/src/test/java/com/nextcloud/talk/signaling/SignalingMessageReceiverLocalParticipantTest.java @@ -0,0 +1,193 @@ +/* + * Nextcloud Talk application + * + * @author Daniel Calviño Sánchez + * Copyright (C) 2023 Daniel Calviño Sánchez + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.nextcloud.talk.signaling; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.InOrder; + +import java.util.HashMap; +import java.util.Map; + +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.only; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; + +public class SignalingMessageReceiverLocalParticipantTest { + + private SignalingMessageReceiver signalingMessageReceiver; + + @Before + public void setUp() { + // SignalingMessageReceiver is abstract to prevent direct instantiation without calling the appropriate + // protected methods. + signalingMessageReceiver = new SignalingMessageReceiver() { + }; + } + + @Test + public void testAddLocalParticipantMessageListenerWithNullListener() { + Assert.assertThrows(IllegalArgumentException.class, () -> { + signalingMessageReceiver.addListener((SignalingMessageReceiver.LocalParticipantMessageListener) null); + }); + } + + @Test + public void testExternalSignalingLocalParticipantMessageSwitchTo() { + SignalingMessageReceiver.LocalParticipantMessageListener mockedLocalParticipantMessageListener = + mock(SignalingMessageReceiver.LocalParticipantMessageListener.class); + + signalingMessageReceiver.addListener(mockedLocalParticipantMessageListener); + + Map eventMap = new HashMap<>(); + eventMap.put("type", "switchto"); + eventMap.put("target", "room"); + Map switchToMap = new HashMap<>(); + switchToMap.put("roomid", "theToken"); + eventMap.put("switchto", switchToMap); + signalingMessageReceiver.processEvent(eventMap); + + verify(mockedLocalParticipantMessageListener, only()).onSwitchTo("theToken"); + } + + @Test + public void testExternalSignalingLocalParticipantMessageAfterRemovingListener() { + SignalingMessageReceiver.LocalParticipantMessageListener mockedLocalParticipantMessageListener = + mock(SignalingMessageReceiver.LocalParticipantMessageListener.class); + + signalingMessageReceiver.addListener(mockedLocalParticipantMessageListener); + signalingMessageReceiver.removeListener(mockedLocalParticipantMessageListener); + + Map eventMap = new HashMap<>(); + eventMap.put("type", "switchto"); + eventMap.put("target", "room"); + HashMap switchToMap = new HashMap<>(); + switchToMap.put("roomid", "theToken"); + eventMap.put("switchto", switchToMap); + signalingMessageReceiver.processEvent(eventMap); + + verifyNoInteractions(mockedLocalParticipantMessageListener); + } + + @Test + public void testExternalSignalingLocalParticipantMessageAfterRemovingSingleListenerOfSeveral() { + SignalingMessageReceiver.LocalParticipantMessageListener mockedLocalParticipantMessageListener1 = + mock(SignalingMessageReceiver.LocalParticipantMessageListener.class); + SignalingMessageReceiver.LocalParticipantMessageListener mockedLocalParticipantMessageListener2 = + mock(SignalingMessageReceiver.LocalParticipantMessageListener.class); + SignalingMessageReceiver.LocalParticipantMessageListener mockedLocalParticipantMessageListener3 = + mock(SignalingMessageReceiver.LocalParticipantMessageListener.class); + + signalingMessageReceiver.addListener(mockedLocalParticipantMessageListener1); + signalingMessageReceiver.addListener(mockedLocalParticipantMessageListener2); + signalingMessageReceiver.addListener(mockedLocalParticipantMessageListener3); + signalingMessageReceiver.removeListener(mockedLocalParticipantMessageListener2); + + Map eventMap = new HashMap<>(); + eventMap.put("type", "switchto"); + eventMap.put("target", "room"); + HashMap switchToMap = new HashMap<>(); + switchToMap.put("roomid", "theToken"); + eventMap.put("switchto", switchToMap); + signalingMessageReceiver.processEvent(eventMap); + + verify(mockedLocalParticipantMessageListener1, only()).onSwitchTo("theToken"); + verify(mockedLocalParticipantMessageListener3, only()).onSwitchTo("theToken"); + verifyNoInteractions(mockedLocalParticipantMessageListener2); + } + + @Test + public void testExternalSignalingLocalParticipantMessageAfterAddingListenerAgain() { + SignalingMessageReceiver.LocalParticipantMessageListener mockedLocalParticipantMessageListener = + mock(SignalingMessageReceiver.LocalParticipantMessageListener.class); + + signalingMessageReceiver.addListener(mockedLocalParticipantMessageListener); + signalingMessageReceiver.addListener(mockedLocalParticipantMessageListener); + + Map eventMap = new HashMap<>(); + eventMap.put("type", "switchto"); + eventMap.put("target", "room"); + HashMap switchToMap = new HashMap<>(); + switchToMap.put("roomid", "theToken"); + eventMap.put("switchto", switchToMap); + signalingMessageReceiver.processEvent(eventMap); + + verify(mockedLocalParticipantMessageListener, only()).onSwitchTo("theToken"); + } + + @Test + public void testAddLocalParticipantMessageListenerWhenHandlingExternalSignalingLocalParticipantMessage() { + SignalingMessageReceiver.LocalParticipantMessageListener mockedLocalParticipantMessageListener1 = + mock(SignalingMessageReceiver.LocalParticipantMessageListener.class); + SignalingMessageReceiver.LocalParticipantMessageListener mockedLocalParticipantMessageListener2 = + mock(SignalingMessageReceiver.LocalParticipantMessageListener.class); + + doAnswer((invocation) -> { + signalingMessageReceiver.addListener(mockedLocalParticipantMessageListener2); + return null; + }).when(mockedLocalParticipantMessageListener1).onSwitchTo("theToken"); + + signalingMessageReceiver.addListener(mockedLocalParticipantMessageListener1); + + Map eventMap = new HashMap<>(); + eventMap.put("type", "switchto"); + eventMap.put("target", "room"); + HashMap switchToMap = new HashMap<>(); + switchToMap.put("roomid", "theToken"); + eventMap.put("switchto", switchToMap); + signalingMessageReceiver.processEvent(eventMap); + + verify(mockedLocalParticipantMessageListener1, only()).onSwitchTo("theToken"); + verifyNoInteractions(mockedLocalParticipantMessageListener2); + } + + @Test + public void testRemoveLocalParticipantMessageListenerWhenHandlingExternalSignalingLocalParticipantMessage() { + SignalingMessageReceiver.LocalParticipantMessageListener mockedLocalParticipantMessageListener1 = + mock(SignalingMessageReceiver.LocalParticipantMessageListener.class); + SignalingMessageReceiver.LocalParticipantMessageListener mockedLocalParticipantMessageListener2 = + mock(SignalingMessageReceiver.LocalParticipantMessageListener.class); + + doAnswer((invocation) -> { + signalingMessageReceiver.removeListener(mockedLocalParticipantMessageListener2); + return null; + }).when(mockedLocalParticipantMessageListener1).onSwitchTo("theToken"); + + signalingMessageReceiver.addListener(mockedLocalParticipantMessageListener1); + signalingMessageReceiver.addListener(mockedLocalParticipantMessageListener2); + + Map eventMap = new HashMap<>(); + eventMap.put("type", "switchto"); + eventMap.put("target", "room"); + HashMap switchToMap = new HashMap<>(); + switchToMap.put("roomid", "theToken"); + eventMap.put("switchto", switchToMap); + signalingMessageReceiver.processEvent(eventMap); + + InOrder inOrder = inOrder(mockedLocalParticipantMessageListener1, mockedLocalParticipantMessageListener2); + + inOrder.verify(mockedLocalParticipantMessageListener1).onSwitchTo("theToken"); + inOrder.verify(mockedLocalParticipantMessageListener2).onSwitchTo("theToken"); + } +} From b07aaee140fe1b3760c31762e99b1df52ba6e0d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Calvi=C3=B1o=20S=C3=A1nchez?= Date: Thu, 26 Jan 2023 14:09:50 +0100 Subject: [PATCH 03/19] Replace non null check with early return if null MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Daniel Calviño Sánchez --- .../talk/controllers/ChatController.kt | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt b/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt index a147bf18d..59225b3d5 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt +++ b/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt @@ -2147,15 +2147,17 @@ class ChatController(args: Bundle) : } private fun setupWebsocket() { - if (conversationUser != null) { - webSocketInstance = - if (WebSocketConnectionHelper.getMagicWebSocketInstanceForUserId(conversationUser.id!!) != null) { - WebSocketConnectionHelper.getMagicWebSocketInstanceForUserId(conversationUser.id!!) - } else { - Log.d(TAG, "magicWebSocketInstance became null") - null - } + if (conversationUser == null) { + return } + + webSocketInstance = + if (WebSocketConnectionHelper.getMagicWebSocketInstanceForUserId(conversationUser.id!!) != null) { + WebSocketConnectionHelper.getMagicWebSocketInstanceForUserId(conversationUser.id!!) + } else { + Log.d(TAG, "magicWebSocketInstance became null") + null + } } fun pullChatMessages(lookIntoFuture: Int, setReadMarker: Int = 1, xChatLastCommonRead: Int? = null) { From 3efcbe2a3929abfce521cec921ed73bb266e32e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Calvi=C3=B1o=20S=C3=A1nchez?= Date: Thu, 26 Jan 2023 14:12:04 +0100 Subject: [PATCH 04/19] Simplify assignment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Daniel Calviño Sánchez --- .../com/nextcloud/talk/controllers/ChatController.kt | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt b/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt index 59225b3d5..f279631aa 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt +++ b/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt @@ -2151,13 +2151,11 @@ class ChatController(args: Bundle) : return } - webSocketInstance = - if (WebSocketConnectionHelper.getMagicWebSocketInstanceForUserId(conversationUser.id!!) != null) { - WebSocketConnectionHelper.getMagicWebSocketInstanceForUserId(conversationUser.id!!) - } else { - Log.d(TAG, "magicWebSocketInstance became null") - null - } + webSocketInstance = WebSocketConnectionHelper.getMagicWebSocketInstanceForUserId(conversationUser.id!!) + + if (webSocketInstance == null) { + Log.d(TAG, "magicWebSocketInstance became null") + } } fun pullChatMessages(lookIntoFuture: Int, setReadMarker: Int = 1, xChatLastCommonRead: Int? = null) { From c67ce4253fe2250d4de2468e1e9f3d80bddf7aaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Calvi=C3=B1o=20S=C3=A1nchez?= Date: Thu, 26 Jan 2023 14:16:31 +0100 Subject: [PATCH 05/19] Use listeners for local participant signaling messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit only sets up the listeners, but the actual handling of the "switchto" event still needs to be added. Note that in CallActivity the SignalingMessageReceiver is available both when using the internal and the external signaling server. On the other hand, in ChatController it is available only for the external signaling server. Right now that is not a problem, as the message notified by the LocalParticipantMessageListener is currently sent only by the external signaling server, but this may need to be adjusted in the future. Signed-off-by: Daniel Calviño Sánchez --- .../nextcloud/talk/activities/CallActivity.java | 10 ++++++++++ .../talk/controllers/ChatController.kt | 17 +++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java b/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java index 0c1c1ff0b..c411d585c 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java +++ b/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java @@ -304,6 +304,13 @@ public class CallActivity extends CallBaseActivity { private CallParticipantList callParticipantList; + private SignalingMessageReceiver.LocalParticipantMessageListener localParticipantMessageListener = + new SignalingMessageReceiver.LocalParticipantMessageListener() { + @Override + public void onSwitchTo(String token) { + } + }; + private SignalingMessageReceiver.OfferMessageListener offerMessageListener = new SignalingMessageReceiver.OfferMessageListener() { @Override public void onOffer(String sessionId, String roomType, String sdp, String nick) { @@ -1319,6 +1326,7 @@ public class CallActivity extends CallBaseActivity { @Override public void onDestroy() { if (signalingMessageReceiver != null) { + signalingMessageReceiver.removeListener(localParticipantMessageListener); signalingMessageReceiver.removeListener(offerMessageListener); } @@ -1453,6 +1461,7 @@ public class CallActivity extends CallBaseActivity { setupAndInitiateWebSocketsConnection(); } else { signalingMessageReceiver = internalSignalingMessageReceiver; + signalingMessageReceiver.addListener(localParticipantMessageListener); signalingMessageReceiver.addListener(offerMessageListener); signalingMessageSender = internalSignalingMessageSender; joinRoomAndCall(); @@ -1662,6 +1671,7 @@ public class CallActivity extends CallBaseActivity { // Although setupAndInitiateWebSocketsConnection could be called several times the web socket is // initialized just once, so the message receiver is also initialized just once. signalingMessageReceiver = webSocketClient.getSignalingMessageReceiver(); + signalingMessageReceiver.addListener(localParticipantMessageListener); signalingMessageReceiver.addListener(offerMessageListener); signalingMessageSender = webSocketClient.getSignalingMessageSender(); } else { diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt b/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt index f279631aa..006400215 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt +++ b/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt @@ -151,6 +151,7 @@ import com.nextcloud.talk.presenters.MentionAutocompletePresenter import com.nextcloud.talk.remotefilebrowser.activities.RemoteFileBrowserActivity import com.nextcloud.talk.repositories.reactions.ReactionsRepository import com.nextcloud.talk.shareditems.activities.SharedItemsActivity +import com.nextcloud.talk.signaling.SignalingMessageReceiver import com.nextcloud.talk.ui.bottom.sheet.ProfileBottomSheet import com.nextcloud.talk.ui.dialog.AttachmentDialog import com.nextcloud.talk.ui.dialog.MessageActionsDialog @@ -299,6 +300,11 @@ class ChatController(args: Bundle) : private var videoURI: Uri? = null + private val localParticipantMessageListener = object : SignalingMessageReceiver.LocalParticipantMessageListener { + override fun onSwitchTo(token: String?) { + } + } + init { Log.d(TAG, "init ChatController: " + System.identityHashCode(this).toString()) @@ -1747,6 +1753,8 @@ class ChatController(args: Bundle) : eventBus.register(this) + webSocketInstance?.getSignalingMessageReceiver()?.addListener(localParticipantMessageListener) + if (conversationUser?.userId != "?" && CapabilitiesUtilNew.hasSpreedFeatureCapability(conversationUser, "mention-flag") && activity != null @@ -1833,6 +1841,8 @@ class ChatController(args: Bundle) : eventBus.unregister(this) + webSocketInstance?.getSignalingMessageReceiver()?.removeListener(localParticipantMessageListener) + if (activity != null) { activity?.findViewById(R.id.toolbar)?.setOnClickListener(null) } @@ -1935,8 +1945,15 @@ class ChatController(args: Bundle) : ApplicationWideCurrentRoomHolder.getInstance().session = currentConversation?.sessionId + // FIXME The web socket should be set up in onAttach(). It is currently setup after joining the + // room to "ensure" (rather, increase the chances) that the WebsocketConnectionsWorker job + // was able to finish and, therefore, that the web socket instance can be got. setupWebsocket() + // Ensure that the listener is added if the web socket instance was not set up yet when + // onAttach() was called. + webSocketInstance?.getSignalingMessageReceiver()?.addListener(localParticipantMessageListener) + checkLobbyState() if (isFirstMessagesProcessing) { From ebfd15e0015962daf90f990504001d774ca944fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Calvi=C3=B1o=20S=C3=A1nchez?= Date: Thu, 26 Jan 2023 13:30:56 +0100 Subject: [PATCH 06/19] WIP. Breakout to room from call and chat. Signed-off-by: Marcel Hibbe --- .../talk/activities/CallActivity.java | 38 +++++++++++++++- .../nextcloud/talk/activities/MainActivity.kt | 15 +++++++ .../talk/controllers/ChatController.kt | 44 +++++++++++++++++-- .../nextcloud/talk/utils/bundle/BundleKeys.kt | 1 + 4 files changed, 94 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java b/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java index c411d585c..4c16b8d80 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java +++ b/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java @@ -180,11 +180,14 @@ import static com.nextcloud.talk.utils.bundle.BundleKeys.KEY_PARTICIPANT_PERMISS import static com.nextcloud.talk.utils.bundle.BundleKeys.KEY_RECORDING_STATE; import static com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_ID; import static com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_TOKEN; +import static com.nextcloud.talk.utils.bundle.BundleKeys.KEY_SWITCH_TO_ROOM_AND_START_CALL; import static com.nextcloud.talk.utils.bundle.BundleKeys.KEY_USER_ENTITY; @AutoInjector(NextcloudTalkApplication.class) public class CallActivity extends CallBaseActivity { + public static boolean active = false; + public static final String VIDEO_STREAM_TYPE_SCREEN = "screen"; public static final String VIDEO_STREAM_TYPE_VIDEO = "video"; @@ -304,10 +307,14 @@ public class CallActivity extends CallBaseActivity { private CallParticipantList callParticipantList; + private String switchToRoomToken = ""; + private SignalingMessageReceiver.LocalParticipantMessageListener localParticipantMessageListener = new SignalingMessageReceiver.LocalParticipantMessageListener() { @Override public void onSwitchTo(String token) { + switchToRoomToken = token; + hangup(true); } }; @@ -456,6 +463,7 @@ public class CallActivity extends CallBaseActivity { @Override public void onStart() { super.onStart(); + active = true; initFeaturesVisibility(); try { @@ -465,6 +473,12 @@ public class CallActivity extends CallBaseActivity { } } + @Override + public void onStop() { + super.onStop(); + active = false; + } + @RequiresApi(api = Build.VERSION_CODES.S) private void requestBluetoothPermission() { if (ContextCompat.checkSelfPermission( @@ -1870,7 +1884,29 @@ public class CallActivity extends CallBaseActivity { @Override public void onNext(@io.reactivex.annotations.NonNull GenericOverall genericOverall) { - if (shutDownView) { + if (!switchToRoomToken.isEmpty()) { + Intent intent = new Intent(context, MainActivity.class); + + intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); + + Bundle bundle = new Bundle(); + bundle.putBoolean(KEY_SWITCH_TO_ROOM_AND_START_CALL, true); + bundle.putString(KEY_ROOM_TOKEN, switchToRoomToken); + +// bundle.putString(KEY_ROOM_ID, roomId); + bundle.putParcelable(KEY_USER_ENTITY, conversationUser); +// conversationName = extras.getString(KEY_CONVERSATION_NAME, ""); + bundle.putBoolean(KEY_CALL_VOICE_ONLY, isVoiceOnlyCall); + bundle.putBoolean(KEY_CALL_WITHOUT_NOTIFICATION, true); + bundle.putBoolean(KEY_PARTICIPANT_PERMISSION_CAN_PUBLISH_AUDIO, canPublishAudioStream); + bundle.putBoolean(KEY_PARTICIPANT_PERMISSION_CAN_PUBLISH_VIDEO, canPublishVideoStream); + intent.putExtras(bundle); + startActivity(intent); + + Toast.makeText(context, "going to breakout room...", Toast.LENGTH_LONG).show(); + + finish(); + } else if (shutDownView) { finish(); } else if (currentCallStatus == CallStatus.RECONNECTING || currentCallStatus == CallStatus.PUBLISHER_FAILED) { diff --git a/app/src/main/java/com/nextcloud/talk/activities/MainActivity.kt b/app/src/main/java/com/nextcloud/talk/activities/MainActivity.kt index 0baf80a4d..be2af0f49 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/MainActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/activities/MainActivity.kt @@ -354,6 +354,21 @@ class MainActivity : BaseActivity(), ActionBarProvider { private fun handleIntent(intent: Intent) { handleActionFromContact(intent) + + if (intent.getBooleanExtra(BundleKeys.KEY_SWITCH_TO_ROOM_AND_START_CALL, false)) { + + logRouterBackStack(router!!) + remapChatController( + router!!, + intent.getParcelableExtra(KEY_USER_ENTITY)!!.id!!, + intent.getStringExtra(KEY_ROOM_TOKEN)!!, + intent.extras!!, + true, + true + ) + logRouterBackStack(router!!) + } + if (intent.hasExtra(BundleKeys.KEY_FROM_NOTIFICATION_START_CALL)) { if (intent.getBooleanExtra(BundleKeys.KEY_FROM_NOTIFICATION_START_CALL, false)) { if (!router!!.hasRootController()) { diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt b/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt index 006400215..f553ce907 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt +++ b/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt @@ -266,6 +266,7 @@ class ChatController(args: Bundle) : private var lookingIntoFuture = false var newMessagesCount = 0 var startCallFromNotification: Boolean? = null + var startCallFromRoomSwitch: Boolean = false val roomId: String val voiceOnly: Boolean var isFirstMessagesProcessing = true @@ -302,6 +303,9 @@ class ChatController(args: Bundle) : private val localParticipantMessageListener = object : SignalingMessageReceiver.LocalParticipantMessageListener { override fun onSwitchTo(token: String?) { + if (token != null) { + switchToRoom(token) + } } } @@ -338,6 +342,10 @@ class ChatController(args: Bundle) : this.startCallFromNotification = args.getBoolean(BundleKeys.KEY_FROM_NOTIFICATION_START_CALL) } + if (args.containsKey(BundleKeys.KEY_SWITCH_TO_ROOM_AND_START_CALL)) { + this.startCallFromRoomSwitch = args.getBoolean(BundleKeys.KEY_SWITCH_TO_ROOM_AND_START_CALL) + } + this.voiceOnly = args.getBoolean(BundleKeys.KEY_CALL_VOICE_ONLY, false) } @@ -920,6 +928,30 @@ class ChatController(args: Bundle) : super.onViewBound(view) } + private fun switchToRoom(token: String) { + if (CallActivity.active) { + Log.d(TAG, "CallActivity is running. Ignore to switch chat in ChatController...") + return + } + + val conversationIntent = Intent(activity, CallActivity::class.java) + val bundle = Bundle() + bundle.putParcelable(KEY_USER_ENTITY, conversationUser) + bundle.putString(KEY_ROOM_TOKEN, token) + + if (conversationUser != null) { + conversationIntent.putExtras(bundle) + + ConductorRemapping.remapChatController( + router, + conversationUser.id!!, + token, + bundle, + true + ) + } + } + private fun showSendButtonMenu() { val popupMenu = PopupMenu( ContextThemeWrapper(view?.context, R.style.ChatSendButtonMenu), @@ -1972,6 +2004,10 @@ class ChatController(args: Bundle) : startCallFromNotification = false startACall(voiceOnly, false) } + + if (startCallFromRoomSwitch) { + startACall(voiceOnly, true) + } } override fun onError(e: Throwable) { @@ -2398,9 +2434,11 @@ class ChatController(args: Bundle) : } private fun updateReadStatusOfAllMessages(xChatLastCommonRead: Int?) { - for (message in adapter!!.items) { - xChatLastCommonRead?.let { - updateReadStatusOfMessage(message, it) + if (adapter != null) { + for (message in adapter!!.items) { + xChatLastCommonRead?.let { + updateReadStatusOfMessage(message, it) + } } } } diff --git a/app/src/main/java/com/nextcloud/talk/utils/bundle/BundleKeys.kt b/app/src/main/java/com/nextcloud/talk/utils/bundle/BundleKeys.kt index 5f2c4aa3a..b0e6d55ee 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/bundle/BundleKeys.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/bundle/BundleKeys.kt @@ -80,4 +80,5 @@ object BundleKeys { const val KEY_PARTICIPANT_PERMISSION_CAN_PUBLISH_AUDIO = "KEY_PARTICIPANT_PERMISSION_CAN_PUBLISH_AUDIO" const val KEY_PARTICIPANT_PERMISSION_CAN_PUBLISH_VIDEO = "KEY_PARTICIPANT_PERMISSION_CAN_PUBLISH_VIDEO" const val KEY_IS_MODERATOR = "KEY_IS_MODERATOR" + const val KEY_SWITCH_TO_ROOM_AND_START_CALL = "KEY_SWITCH_TO_ROOM_AND_START_CALL" } From dff37d0a66fbc768fc9b45c75126ffb9c1e4f9f5 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Wed, 1 Feb 2023 19:06:53 +0100 Subject: [PATCH 07/19] Fix content Description Signed-off-by: Marcel Hibbe --- app/src/main/res/layout/call_item.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/layout/call_item.xml b/app/src/main/res/layout/call_item.xml index 9d5e52547..d04556e54 100644 --- a/app/src/main/res/layout/call_item.xml +++ b/app/src/main/res/layout/call_item.xml @@ -79,7 +79,7 @@ android:layout_height="16dp" android:layout_marginStart="10dp" android:layout_marginBottom="6dp" - android:contentDescription="@string/nc_remote_audio_off" + android:contentDescription="@string/nc_call_raise_hand_description" android:src="@drawable/ic_hand_back_left" android:visibility="invisible" tools:visibility="visible" /> From 49571ca2290a1ba3e17543e0490deb7a28b926a5 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Wed, 1 Feb 2023 19:09:01 +0100 Subject: [PATCH 08/19] WIP. Send "raise hand" Building/sending of the signaling message is incomplete for now. Signed-off-by: Marcel Hibbe --- .../talk/activities/CallActivity.java | 24 ++++++++++++++ .../talk/ui/dialog/MoreCallActionsDialog.kt | 11 +++++++ .../talk/webrtc/PeerConnectionWrapper.java | 20 +++++++++++ .../res/drawable/ic_baseline_back_hand_24.xml | 5 +++ .../drawable/ic_baseline_do_not_touch_24.xml | 5 +++ .../res/layout/dialog_more_call_actions.xml | 33 +++++++++++++++++++ app/src/main/res/values/strings.xml | 2 ++ 7 files changed, 100 insertions(+) create mode 100644 app/src/main/res/drawable/ic_baseline_back_hand_24.xml create mode 100644 app/src/main/res/drawable/ic_baseline_do_not_touch_24.xml diff --git a/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java b/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java index 4c16b8d80..ba18c50d8 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java +++ b/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java @@ -210,6 +210,7 @@ public class CallActivity extends CallBaseActivity { public WebRtcAudioManager audioManager; public CallRecordingViewModel callRecordingViewModel; +// public RaiseHandViewModel raiseHandViewModel; private static final String[] PERMISSIONS_CALL = { Manifest.permission.CAMERA, @@ -1223,6 +1224,25 @@ public class CallActivity extends CallBaseActivity { } } + public void clickHand(Boolean raise) { + // TODO: fix how to build&send the message + + if (isConnectionEstablished() && peerConnectionWrapperList != null) { + if (!hasMCU) { + for (PeerConnectionWrapper peerConnectionWrapper : peerConnectionWrapperList) { + peerConnectionWrapper.raiseHand(raise); + } + } else { + for (PeerConnectionWrapper peerConnectionWrapper : peerConnectionWrapperList) { + if (peerConnectionWrapper.getSessionId().equals(webSocketClient.getSessionId())) { + peerConnectionWrapper.raiseHand(raise); + break; + } + } + } + } + } + private void animateCallControls(boolean show, long startDelay) { if (isVoiceOnlyCall) { @@ -3042,6 +3062,10 @@ public class CallActivity extends CallBaseActivity { && isModerator; } + public boolean isAllowedToRaiseHand() { + return CapabilitiesUtilNew.hasSpreedFeatureCapability(conversationUser, "raise-hand"); + } + private class SelfVideoTouchListener implements View.OnTouchListener { @SuppressLint("ClickableViewAccessibility") diff --git a/app/src/main/java/com/nextcloud/talk/ui/dialog/MoreCallActionsDialog.kt b/app/src/main/java/com/nextcloud/talk/ui/dialog/MoreCallActionsDialog.kt index 875cc3f4c..ae78c530b 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/dialog/MoreCallActionsDialog.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/dialog/MoreCallActionsDialog.kt @@ -72,12 +72,23 @@ class MoreCallActionsDialog(private val callActivity: CallActivity) : BottomShee } else { binding.recordCall.visibility = View.GONE } + + if (callActivity.isAllowedToRaiseHand) { + binding.raiseHand.visibility = View.VISIBLE + } else { + binding.raiseHand.visibility = View.GONE + } } private fun initClickListeners() { binding.recordCall.setOnClickListener { callActivity.callRecordingViewModel.clickRecordButton() } + + binding.raiseHand.setOnClickListener { + // TODO: save raised hand state & toggle... + callActivity.clickHand(true) + } } private fun initObservers() { diff --git a/app/src/main/java/com/nextcloud/talk/webrtc/PeerConnectionWrapper.java b/app/src/main/java/com/nextcloud/talk/webrtc/PeerConnectionWrapper.java index 2676c5746..7a6793c97 100644 --- a/app/src/main/java/com/nextcloud/talk/webrtc/PeerConnectionWrapper.java +++ b/app/src/main/java/com/nextcloud/talk/webrtc/PeerConnectionWrapper.java @@ -190,6 +190,26 @@ public class PeerConnectionWrapper { } } + public void raiseHand(Boolean raise) { + + // TODO: fix how to build&send the message + + NCMessagePayload ncMessagePayload = new NCMessagePayload(); + ncMessagePayload.setState(raise); + ncMessagePayload.setTimestamp(System.currentTimeMillis()); + + + NCSignalingMessage ncSignalingMessage = new NCSignalingMessage(); +// ncSignalingMessage.setFrom(); + ncSignalingMessage.setTo(sessionId); +// ncSignalingMessage.setSid(); + ncSignalingMessage.setType("raiseHand"); + ncSignalingMessage.setPayload(ncMessagePayload); + ncSignalingMessage.setRoomType(videoStreamType); + + signalingMessageSender.send(ncSignalingMessage); + } + /** * Adds a listener for data channel messages. * diff --git a/app/src/main/res/drawable/ic_baseline_back_hand_24.xml b/app/src/main/res/drawable/ic_baseline_back_hand_24.xml new file mode 100644 index 000000000..0e05b786e --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_back_hand_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_baseline_do_not_touch_24.xml b/app/src/main/res/drawable/ic_baseline_do_not_touch_24.xml new file mode 100644 index 000000000..6f844e82d --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_do_not_touch_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/layout/dialog_more_call_actions.xml b/app/src/main/res/layout/dialog_more_call_actions.xml index a44fdf80a..3ca078e46 100644 --- a/app/src/main/res/layout/dialog_more_call_actions.xml +++ b/app/src/main/res/layout/dialog_more_call_actions.xml @@ -37,6 +37,39 @@ android:textColor="@color/medium_emphasis_text_dark_background" android:textSize="@dimen/bottom_sheet_text_size" /> + + + + + + + + Answer as video call Switch to self video %1$s raised the hand + Raise hand + Lower hand Mute microphone From 99a4ca5e33207a8c381a6670e8590c4054203a2c Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Tue, 14 Feb 2023 15:23:10 +0100 Subject: [PATCH 09/19] comment out first try to raise hand this is not working yet. It's for now commented out in order to continue with "request help" feature for breakout rooms. Signed-off-by: Marcel Hibbe --- .../talk/activities/CallActivity.java | 29 +++++++++---------- .../talk/webrtc/PeerConnectionWrapper.java | 29 +++++++++---------- 2 files changed, 28 insertions(+), 30 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java b/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java index ba18c50d8..359dbf691 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java +++ b/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java @@ -1226,21 +1226,20 @@ public class CallActivity extends CallBaseActivity { public void clickHand(Boolean raise) { // TODO: fix how to build&send the message - - if (isConnectionEstablished() && peerConnectionWrapperList != null) { - if (!hasMCU) { - for (PeerConnectionWrapper peerConnectionWrapper : peerConnectionWrapperList) { - peerConnectionWrapper.raiseHand(raise); - } - } else { - for (PeerConnectionWrapper peerConnectionWrapper : peerConnectionWrapperList) { - if (peerConnectionWrapper.getSessionId().equals(webSocketClient.getSessionId())) { - peerConnectionWrapper.raiseHand(raise); - break; - } - } - } - } +// if (isConnectionEstablished() && peerConnectionWrapperList != null) { +// if (!hasMCU) { +// for (PeerConnectionWrapper peerConnectionWrapper : peerConnectionWrapperList) { +// peerConnectionWrapper.raiseHand(raise); +// } +// } else { +// for (PeerConnectionWrapper peerConnectionWrapper : peerConnectionWrapperList) { +// if (peerConnectionWrapper.getSessionId().equals(webSocketClient.getSessionId())) { +// peerConnectionWrapper.raiseHand(raise); +// break; +// } +// } +// } +// } } diff --git a/app/src/main/java/com/nextcloud/talk/webrtc/PeerConnectionWrapper.java b/app/src/main/java/com/nextcloud/talk/webrtc/PeerConnectionWrapper.java index 7a6793c97..228e24e93 100644 --- a/app/src/main/java/com/nextcloud/talk/webrtc/PeerConnectionWrapper.java +++ b/app/src/main/java/com/nextcloud/talk/webrtc/PeerConnectionWrapper.java @@ -193,21 +193,20 @@ public class PeerConnectionWrapper { public void raiseHand(Boolean raise) { // TODO: fix how to build&send the message - - NCMessagePayload ncMessagePayload = new NCMessagePayload(); - ncMessagePayload.setState(raise); - ncMessagePayload.setTimestamp(System.currentTimeMillis()); - - - NCSignalingMessage ncSignalingMessage = new NCSignalingMessage(); -// ncSignalingMessage.setFrom(); - ncSignalingMessage.setTo(sessionId); -// ncSignalingMessage.setSid(); - ncSignalingMessage.setType("raiseHand"); - ncSignalingMessage.setPayload(ncMessagePayload); - ncSignalingMessage.setRoomType(videoStreamType); - - signalingMessageSender.send(ncSignalingMessage); +// NCMessagePayload ncMessagePayload = new NCMessagePayload(); +// ncMessagePayload.setState(raise); +// ncMessagePayload.setTimestamp(System.currentTimeMillis()); +// +// +// NCSignalingMessage ncSignalingMessage = new NCSignalingMessage(); +//// ncSignalingMessage.setFrom(); +// ncSignalingMessage.setTo(sessionId); +//// ncSignalingMessage.setSid(); +// ncSignalingMessage.setType("raiseHand"); +// ncSignalingMessage.setPayload(ncMessagePayload); +// ncSignalingMessage.setRoomType(videoStreamType); +// +// signalingMessageSender.send(ncSignalingMessage); } /** From 2835bb6c02f7fd83804129eda2d09019402e7b78 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Wed, 15 Feb 2023 11:19:47 +0100 Subject: [PATCH 10/19] check if conversation is breakout room Signed-off-by: Marcel Hibbe --- .../talk/activities/CallActivity.java | 18 +++++++++++++++--- .../talk/controllers/ChatController.kt | 6 ++++++ .../nextcloud/talk/utils/bundle/BundleKeys.kt | 1 + .../talk/webrtc/PeerConnectionWrapper.java | 2 +- 4 files changed, 23 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java b/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java index 359dbf691..13e992e90 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java +++ b/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java @@ -173,6 +173,7 @@ import static com.nextcloud.talk.utils.bundle.BundleKeys.KEY_CALL_WITHOUT_NOTIFI import static com.nextcloud.talk.utils.bundle.BundleKeys.KEY_CONVERSATION_NAME; import static com.nextcloud.talk.utils.bundle.BundleKeys.KEY_CONVERSATION_PASSWORD; import static com.nextcloud.talk.utils.bundle.BundleKeys.KEY_FROM_NOTIFICATION_START_CALL; +import static com.nextcloud.talk.utils.bundle.BundleKeys.KEY_IS_BREAKOUT_ROOM; import static com.nextcloud.talk.utils.bundle.BundleKeys.KEY_IS_MODERATOR; import static com.nextcloud.talk.utils.bundle.BundleKeys.KEY_MODIFIED_BASE_URL; import static com.nextcloud.talk.utils.bundle.BundleKeys.KEY_PARTICIPANT_PERMISSION_CAN_PUBLISH_AUDIO; @@ -309,6 +310,7 @@ public class CallActivity extends CallBaseActivity { private CallParticipantList callParticipantList; private String switchToRoomToken = ""; + private boolean isBreakoutRoom = false; private SignalingMessageReceiver.LocalParticipantMessageListener localParticipantMessageListener = new SignalingMessageReceiver.LocalParticipantMessageListener() { @@ -390,6 +392,10 @@ public class CallActivity extends CallBaseActivity { isIncomingCallFromNotification = extras.getBoolean(KEY_FROM_NOTIFICATION_START_CALL); } + if (extras.containsKey(KEY_IS_BREAKOUT_ROOM)) { + isBreakoutRoom = extras.getBoolean(KEY_IS_BREAKOUT_ROOM); + } + credentials = ApiUtils.getCredentials(conversationUser.getUsername(), conversationUser.getToken()); baseUrl = extras.getString(KEY_MODIFIED_BASE_URL, ""); @@ -496,7 +502,7 @@ public class CallActivity extends CallBaseActivity { } private void initFeaturesVisibility() { - if (isAllowedToStartOrStopRecording()) { + if (isAllowedToStartOrStopRecording() || isAllowedToRaiseHand()) { binding.moreCallActions.setVisibility(View.VISIBLE); } else { binding.moreCallActions.setVisibility(View.GONE); @@ -1225,6 +1231,12 @@ public class CallActivity extends CallBaseActivity { } public void clickHand(Boolean raise) { + + if (isBreakoutRoom) { + Log.d(TAG, "send request to request help for breakout rooms."); + } +// + // TODO: fix how to build&send the message // if (isConnectionEstablished() && peerConnectionWrapperList != null) { // if (!hasMCU) { @@ -1911,7 +1923,6 @@ public class CallActivity extends CallBaseActivity { Bundle bundle = new Bundle(); bundle.putBoolean(KEY_SWITCH_TO_ROOM_AND_START_CALL, true); bundle.putString(KEY_ROOM_TOKEN, switchToRoomToken); - // bundle.putString(KEY_ROOM_ID, roomId); bundle.putParcelable(KEY_USER_ENTITY, conversationUser); // conversationName = extras.getString(KEY_CONVERSATION_NAME, ""); @@ -3062,7 +3073,8 @@ public class CallActivity extends CallBaseActivity { } public boolean isAllowedToRaiseHand() { - return CapabilitiesUtilNew.hasSpreedFeatureCapability(conversationUser, "raise-hand"); + return CapabilitiesUtilNew.hasSpreedFeatureCapability(conversationUser, "raise-hand") || + isBreakoutRoom; } private class SelfVideoTouchListener implements View.OnTouchListener { diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt b/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt index f553ce907..719084084 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt +++ b/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt @@ -173,6 +173,7 @@ import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ACTIVE_CONVERSATION import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_CONVERSATION_NAME import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_FILE_PATHS import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_INTERNAL_USER_ID +import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_IS_BREAKOUT_ROOM import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_IS_MODERATOR import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_RECORDING_STATE import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_ID @@ -2841,6 +2842,10 @@ class ChatController(args: Bundle) : bundle.putBoolean(BundleKeys.KEY_CALL_WITHOUT_NOTIFICATION, true) } + if (it.objectType == BREAKOUT_ROOM_TYPE) { + bundle.putBoolean(KEY_IS_BREAKOUT_ROOM, true) + } + return if (activity != null) { val callIntent = Intent(activity, CallActivity::class.java) callIntent.putExtras(bundle) @@ -3531,5 +3536,6 @@ class ChatController(args: Bundle) : private const val LOOKING_INTO_FUTURE_TIMEOUT = 30 private const val CHUNK_SIZE: Int = 10 private const val ONE_SECOND_IN_MILLIS = 1000 + private const val BREAKOUT_ROOM_TYPE = "room" } } diff --git a/app/src/main/java/com/nextcloud/talk/utils/bundle/BundleKeys.kt b/app/src/main/java/com/nextcloud/talk/utils/bundle/BundleKeys.kt index b0e6d55ee..e89647e3c 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/bundle/BundleKeys.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/bundle/BundleKeys.kt @@ -81,4 +81,5 @@ object BundleKeys { const val KEY_PARTICIPANT_PERMISSION_CAN_PUBLISH_VIDEO = "KEY_PARTICIPANT_PERMISSION_CAN_PUBLISH_VIDEO" const val KEY_IS_MODERATOR = "KEY_IS_MODERATOR" const val KEY_SWITCH_TO_ROOM_AND_START_CALL = "KEY_SWITCH_TO_ROOM_AND_START_CALL" + const val KEY_IS_BREAKOUT_ROOM = "KEY_IS_BREAKOUT_ROOM" } diff --git a/app/src/main/java/com/nextcloud/talk/webrtc/PeerConnectionWrapper.java b/app/src/main/java/com/nextcloud/talk/webrtc/PeerConnectionWrapper.java index 228e24e93..69ddb15e8 100644 --- a/app/src/main/java/com/nextcloud/talk/webrtc/PeerConnectionWrapper.java +++ b/app/src/main/java/com/nextcloud/talk/webrtc/PeerConnectionWrapper.java @@ -292,7 +292,7 @@ public class PeerConnectionWrapper { try { buffer = ByteBuffer.wrap(LoganSquare.serialize(dataChannelMessage).getBytes()); dataChannel.send(new DataChannel.Buffer(buffer, false)); - } catch (IOException e) { + } catch (Exception e) { Log.d(TAG, "Failed to send channel data, attempting regular " + dataChannelMessage); } } From 96dce63e200f560802f0857c31c9a45a97e83ac0 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Wed, 15 Feb 2023 12:59:31 +0100 Subject: [PATCH 11/19] Request assistance for breakout room Signed-off-by: Marcel Hibbe --- .../talk/activities/CallActivity.java | 11 +- .../java/com/nextcloud/talk/api/NcApi.java | 8 ++ .../talk/dagger/modules/RepositoryModule.kt | 8 ++ .../talk/dagger/modules/ViewModelModule.kt | 6 + .../talk/raisehand/RequestAssistanceModel.kt | 5 + .../raisehand/RequestAssistanceRepository.kt | 34 ++++++ .../RequestAssistanceRepositoryImpl.kt | 81 ++++++++++++ .../WithdrawRequestAssistanceModel.kt | 5 + .../raisehand/viewmodel/RaiseHandViewModel.kt | 115 ++++++++++++++++++ .../talk/ui/dialog/MoreCallActionsDialog.kt | 19 +++ .../com/nextcloud/talk/utils/ApiUtils.java | 4 + .../drawable/ic_baseline_do_not_touch_24.xml | 24 +++- app/src/main/res/values/strings.xml | 3 + 13 files changed, 315 insertions(+), 8 deletions(-) create mode 100644 app/src/main/java/com/nextcloud/talk/raisehand/RequestAssistanceModel.kt create mode 100644 app/src/main/java/com/nextcloud/talk/raisehand/RequestAssistanceRepository.kt create mode 100644 app/src/main/java/com/nextcloud/talk/raisehand/RequestAssistanceRepositoryImpl.kt create mode 100644 app/src/main/java/com/nextcloud/talk/raisehand/WithdrawRequestAssistanceModel.kt create mode 100644 app/src/main/java/com/nextcloud/talk/raisehand/viewmodel/RaiseHandViewModel.kt diff --git a/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java b/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java index 13e992e90..dbf23370e 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java +++ b/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java @@ -86,6 +86,7 @@ import com.nextcloud.talk.models.json.signaling.Signaling; import com.nextcloud.talk.models.json.signaling.SignalingOverall; import com.nextcloud.talk.models.json.signaling.settings.IceServer; import com.nextcloud.talk.models.json.signaling.settings.SignalingSettingsOverall; +import com.nextcloud.talk.raisehand.viewmodel.RaiseHandViewModel; import com.nextcloud.talk.signaling.SignalingMessageReceiver; import com.nextcloud.talk.signaling.SignalingMessageSender; import com.nextcloud.talk.ui.dialog.AudioOutputDialog; @@ -211,7 +212,7 @@ public class CallActivity extends CallBaseActivity { public WebRtcAudioManager audioManager; public CallRecordingViewModel callRecordingViewModel; -// public RaiseHandViewModel raiseHandViewModel; + public RaiseHandViewModel raiseHandViewModel; private static final String[] PERMISSIONS_CALL = { Manifest.permission.CAMERA, @@ -411,6 +412,9 @@ public class CallActivity extends CallBaseActivity { setCallState(CallStatus.CONNECTING); } + raiseHandViewModel = new ViewModelProvider(this, viewModelFactory).get((RaiseHandViewModel.class)); + raiseHandViewModel.setData(roomToken, isBreakoutRoom); + callRecordingViewModel = new ViewModelProvider(this, viewModelFactory).get((CallRecordingViewModel.class)); callRecordingViewModel.setData(roomToken); callRecordingViewModel.setRecordingState(extras.getInt(KEY_RECORDING_STATE)); @@ -1232,10 +1236,7 @@ public class CallActivity extends CallBaseActivity { public void clickHand(Boolean raise) { - if (isBreakoutRoom) { - Log.d(TAG, "send request to request help for breakout rooms."); - } -// + raiseHandViewModel.clickHandButton(); // TODO: fix how to build&send the message // if (isConnectionEstablished() && peerConnectionWrapperList != null) { diff --git a/app/src/main/java/com/nextcloud/talk/api/NcApi.java b/app/src/main/java/com/nextcloud/talk/api/NcApi.java index e2fb846ab..02c763aa8 100644 --- a/app/src/main/java/com/nextcloud/talk/api/NcApi.java +++ b/app/src/main/java/com/nextcloud/talk/api/NcApi.java @@ -587,4 +587,12 @@ public interface NcApi { @DELETE Observable stopRecording(@Header("Authorization") String authorization, @Url String url); + + @POST + Observable requestAssistance(@Header("Authorization") String authorization, + @Url String url); + + @DELETE + Observable withdrawRequestAssistance(@Header("Authorization") String authorization, + @Url String url); } diff --git a/app/src/main/java/com/nextcloud/talk/dagger/modules/RepositoryModule.kt b/app/src/main/java/com/nextcloud/talk/dagger/modules/RepositoryModule.kt index fb8c8fa0c..5fce4b3b2 100644 --- a/app/src/main/java/com/nextcloud/talk/dagger/modules/RepositoryModule.kt +++ b/app/src/main/java/com/nextcloud/talk/dagger/modules/RepositoryModule.kt @@ -33,6 +33,8 @@ import com.nextcloud.talk.data.user.UsersRepository import com.nextcloud.talk.data.user.UsersRepositoryImpl import com.nextcloud.talk.polls.repositories.PollRepository import com.nextcloud.talk.polls.repositories.PollRepositoryImpl +import com.nextcloud.talk.raisehand.RequestAssistanceRepository +import com.nextcloud.talk.raisehand.RequestAssistanceRepositoryImpl import com.nextcloud.talk.remotefilebrowser.repositories.RemoteFileBrowserItemsRepository import com.nextcloud.talk.remotefilebrowser.repositories.RemoteFileBrowserItemsRepositoryImpl import com.nextcloud.talk.repositories.callrecording.CallRecordingRepository @@ -99,4 +101,10 @@ class RepositoryModule { fun provideCallRecordingRepository(ncApi: NcApi, userProvider: CurrentUserProviderNew): CallRecordingRepository { return CallRecordingRepositoryImpl(ncApi, userProvider) } + + @Provides + fun provideRequestAssistanceRepository(ncApi: NcApi, userProvider: CurrentUserProviderNew): + RequestAssistanceRepository { + return RequestAssistanceRepositoryImpl(ncApi, userProvider) + } } diff --git a/app/src/main/java/com/nextcloud/talk/dagger/modules/ViewModelModule.kt b/app/src/main/java/com/nextcloud/talk/dagger/modules/ViewModelModule.kt index 990bdc32d..48228f412 100644 --- a/app/src/main/java/com/nextcloud/talk/dagger/modules/ViewModelModule.kt +++ b/app/src/main/java/com/nextcloud/talk/dagger/modules/ViewModelModule.kt @@ -28,6 +28,7 @@ import com.nextcloud.talk.polls.viewmodels.PollCreateViewModel import com.nextcloud.talk.polls.viewmodels.PollMainViewModel import com.nextcloud.talk.polls.viewmodels.PollResultsViewModel import com.nextcloud.talk.polls.viewmodels.PollVoteViewModel +import com.nextcloud.talk.raisehand.viewmodel.RaiseHandViewModel import com.nextcloud.talk.remotefilebrowser.viewmodels.RemoteFileBrowserItemsViewModel import com.nextcloud.talk.shareditems.viewmodels.SharedItemsViewModel import com.nextcloud.talk.viewmodels.CallRecordingViewModel @@ -95,4 +96,9 @@ abstract class ViewModelModule { @IntoMap @ViewModelKey(CallRecordingViewModel::class) abstract fun callRecordingViewModel(viewModel: CallRecordingViewModel): ViewModel + + @Binds + @IntoMap + @ViewModelKey(RaiseHandViewModel::class) + abstract fun raiseHandViewModel(viewModel: RaiseHandViewModel): ViewModel } diff --git a/app/src/main/java/com/nextcloud/talk/raisehand/RequestAssistanceModel.kt b/app/src/main/java/com/nextcloud/talk/raisehand/RequestAssistanceModel.kt new file mode 100644 index 000000000..1772cc469 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/raisehand/RequestAssistanceModel.kt @@ -0,0 +1,5 @@ +package com.nextcloud.talk.raisehand + +data class RequestAssistanceModel( + var success: Boolean +) diff --git a/app/src/main/java/com/nextcloud/talk/raisehand/RequestAssistanceRepository.kt b/app/src/main/java/com/nextcloud/talk/raisehand/RequestAssistanceRepository.kt new file mode 100644 index 000000000..2c39f9ae1 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/raisehand/RequestAssistanceRepository.kt @@ -0,0 +1,34 @@ +/* + * Nextcloud Talk application + * + * @author Marcel Hibbe + * Copyright (C) 2022 Marcel Hibbe + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.nextcloud.talk.raisehand + +import io.reactivex.Observable + +interface RequestAssistanceRepository { + + fun requestAssistance( + roomToken: String + ): Observable + + fun withdrawRequestAssistance( + roomToken: String + ): Observable +} diff --git a/app/src/main/java/com/nextcloud/talk/raisehand/RequestAssistanceRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/raisehand/RequestAssistanceRepositoryImpl.kt new file mode 100644 index 000000000..f737b02a2 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/raisehand/RequestAssistanceRepositoryImpl.kt @@ -0,0 +1,81 @@ +/* + * Nextcloud Talk application + * + * @author Marcel Hibbe + * Copyright (C) 2022 Marcel Hibbe + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.nextcloud.talk.raisehand + +import com.nextcloud.talk.api.NcApi +import com.nextcloud.talk.data.user.model.User +import com.nextcloud.talk.models.json.generic.GenericMeta +import com.nextcloud.talk.utils.ApiUtils +import com.nextcloud.talk.utils.database.user.CurrentUserProviderNew +import io.reactivex.Observable + +class RequestAssistanceRepositoryImpl(private val ncApi: NcApi, currentUserProvider: CurrentUserProviderNew) : + RequestAssistanceRepository { + + val currentUser: User = currentUserProvider.currentUser.blockingGet() + val credentials: String = ApiUtils.getCredentials(currentUser.username, currentUser.token) + + var apiVersion = 1 + + override fun requestAssistance(roomToken: String): Observable { + return ncApi.requestAssistance( + credentials, + ApiUtils.getUrlForRequestAssistance( + apiVersion, + currentUser.baseUrl, + roomToken + ) + ).map { mapToRequestAssistanceModel(it.ocs?.meta!!) } + } + + override fun withdrawRequestAssistance(roomToken: String): Observable { + return ncApi.withdrawRequestAssistance( + credentials, + ApiUtils.getUrlForRequestAssistance( + apiVersion, + currentUser.baseUrl, + roomToken + ) + ).map { mapToWithdrawRequestAssistanceModel(it.ocs?.meta!!) } + } + + private fun mapToRequestAssistanceModel( + response: GenericMeta + ): RequestAssistanceModel { + val success = response.statusCode == HTTP_OK + return RequestAssistanceModel( + success + ) + } + + private fun mapToWithdrawRequestAssistanceModel( + response: GenericMeta + ): WithdrawRequestAssistanceModel { + val success = response.statusCode == HTTP_OK + return WithdrawRequestAssistanceModel( + success + ) + } + + companion object { + private const val HTTP_OK: Int = 200 + } +} diff --git a/app/src/main/java/com/nextcloud/talk/raisehand/WithdrawRequestAssistanceModel.kt b/app/src/main/java/com/nextcloud/talk/raisehand/WithdrawRequestAssistanceModel.kt new file mode 100644 index 000000000..88b7f74b4 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/raisehand/WithdrawRequestAssistanceModel.kt @@ -0,0 +1,5 @@ +package com.nextcloud.talk.raisehand + +data class WithdrawRequestAssistanceModel( + var success: Boolean +) diff --git a/app/src/main/java/com/nextcloud/talk/raisehand/viewmodel/RaiseHandViewModel.kt b/app/src/main/java/com/nextcloud/talk/raisehand/viewmodel/RaiseHandViewModel.kt new file mode 100644 index 000000000..c79fb0349 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/raisehand/viewmodel/RaiseHandViewModel.kt @@ -0,0 +1,115 @@ +package com.nextcloud.talk.raisehand.viewmodel + +import android.util.Log +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import com.nextcloud.talk.raisehand.RequestAssistanceModel +import com.nextcloud.talk.raisehand.RequestAssistanceRepository +import com.nextcloud.talk.raisehand.WithdrawRequestAssistanceModel +import com.nextcloud.talk.users.UserManager +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 RaiseHandViewModel @Inject constructor(private val repository: RequestAssistanceRepository) : ViewModel() { + + @Inject + lateinit var userManager: UserManager + + lateinit var roomToken: String + private var isBreakoutRoom: Boolean = false + + sealed interface ViewState + + object RaisedHandState : ViewState + object LoweredHandState : ViewState + object ErrorState : ViewState + + private val _viewState: MutableLiveData = MutableLiveData(LoweredHandState) + val viewState: LiveData + get() = _viewState + + fun clickHandButton() { + when (viewState.value) { + LoweredHandState -> { + raiseHand() + } + RaisedHandState -> { + lowerHand() + } + else -> {} + } + } + + private fun raiseHand() { + _viewState.value = RaisedHandState + if (isBreakoutRoom) { + repository.requestAssistance(roomToken) + .subscribeOn(Schedulers.io()) + ?.observeOn(AndroidSchedulers.mainThread()) + ?.subscribe(RequestAssistanceObserver()) + } + } + + private fun lowerHand() { + _viewState.value = LoweredHandState + if (isBreakoutRoom) { + repository.withdrawRequestAssistance(roomToken) + .subscribeOn(Schedulers.io()) + ?.observeOn(AndroidSchedulers.mainThread()) + ?.subscribe(WithdrawRequestAssistanceObserver()) + } + } + + fun setData(roomToken: String, isBreakoutRoom: Boolean) { + this.roomToken = roomToken + this.isBreakoutRoom = isBreakoutRoom + } + + inner class RequestAssistanceObserver : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } + + override fun onNext(requestAssistanceModel: RequestAssistanceModel) { + // RaisedHandState was already set because it's also used for signaling message + Log.d(TAG, "requestAssistance successful") + } + + override fun onError(e: Throwable) { + Log.e(TAG, "failure in RequestAssistanceObserver", e) + _viewState.value = ErrorState + } + + override fun onComplete() { + // dismiss() + } + } + + inner class WithdrawRequestAssistanceObserver : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } + + override fun onNext(withdrawRequestAssistanceModel: WithdrawRequestAssistanceModel) { + // LoweredHandState was already set because it's also used for signaling message + Log.d(TAG, "withdrawRequestAssistance successful") + } + + override fun onError(e: Throwable) { + Log.e(TAG, "failure in WithdrawRequestAssistanceObserver", e) + _viewState.value = ErrorState + } + + override fun onComplete() { + // dismiss() + } + } + + companion object { + private val TAG = RaiseHandViewModel::class.java.simpleName + } +} diff --git a/app/src/main/java/com/nextcloud/talk/ui/dialog/MoreCallActionsDialog.kt b/app/src/main/java/com/nextcloud/talk/ui/dialog/MoreCallActionsDialog.kt index ae78c530b..3da2934b9 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/dialog/MoreCallActionsDialog.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/dialog/MoreCallActionsDialog.kt @@ -32,6 +32,7 @@ import com.nextcloud.talk.R import com.nextcloud.talk.activities.CallActivity import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.databinding.DialogMoreCallActionsBinding +import com.nextcloud.talk.raisehand.viewmodel.RaiseHandViewModel import com.nextcloud.talk.ui.theme.ViewThemeUtils import com.nextcloud.talk.viewmodels.CallRecordingViewModel import javax.inject.Inject @@ -122,6 +123,24 @@ class MoreCallActionsDialog(private val callActivity: CallActivity) : BottomShee } } } + + callActivity.raiseHandViewModel.viewState.observe(this) { state -> + when (state) { + is RaiseHandViewModel.RaisedHandState -> { + binding.raiseHandText.text = context.getText(R.string.lower_hand) + binding.raiseHandIcon.setImageDrawable( + ContextCompat.getDrawable(context, R.drawable.ic_baseline_do_not_touch_24) + ) + } + is RaiseHandViewModel.LoweredHandState -> { + binding.raiseHandText.text = context.getText(R.string.raise_hand) + binding.raiseHandIcon.setImageDrawable( + ContextCompat.getDrawable(context, R.drawable.ic_hand_back_left) + ) + } + else -> {} + } + } } companion object { diff --git a/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java b/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java index fe40815d9..6c2dd557b 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java +++ b/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java @@ -498,4 +498,8 @@ public class ApiUtils { 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"; + } } diff --git a/app/src/main/res/drawable/ic_baseline_do_not_touch_24.xml b/app/src/main/res/drawable/ic_baseline_do_not_touch_24.xml index 6f844e82d..a6ba60c39 100644 --- a/app/src/main/res/drawable/ic_baseline_do_not_touch_24.xml +++ b/app/src/main/res/drawable/ic_baseline_do_not_touch_24.xml @@ -1,5 +1,23 @@ - + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e19b85751..6b2eb1dca 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -573,6 +573,9 @@ Do you really want to stop the recording? The call is being recorded + Raise hand + Lower hand + Media File From f2b312a11851b43e90c0f51f80261c5973e0332d Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Wed, 15 Feb 2023 14:59:21 +0100 Subject: [PATCH 12/19] add floating lower hand button Signed-off-by: Marcel Hibbe --- .../nextcloud/talk/activities/CallActivity.java | 12 ++++++++++++ .../raisehand/viewmodel/RaiseHandViewModel.kt | 2 +- app/src/main/res/layout/call_activity.xml | 16 ++++++++++++++++ 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java b/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java index dbf23370e..6ecb46288 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java +++ b/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java @@ -415,6 +415,14 @@ public class CallActivity extends CallBaseActivity { raiseHandViewModel = new ViewModelProvider(this, viewModelFactory).get((RaiseHandViewModel.class)); raiseHandViewModel.setData(roomToken, isBreakoutRoom); + raiseHandViewModel.getViewState().observe(this, viewState -> { + if (viewState instanceof RaiseHandViewModel.RaisedHandState) { + binding.lowerHandButton.setVisibility(View.VISIBLE); + } else if (viewState instanceof RaiseHandViewModel.LoweredHandState) { + binding.lowerHandButton.setVisibility(View.GONE); + } + }); + callRecordingViewModel = new ViewModelProvider(this, viewModelFactory).get((CallRecordingViewModel.class)); callRecordingViewModel.setData(roomToken); callRecordingViewModel.setRecordingState(extras.getInt(KEY_RECORDING_STATE)); @@ -584,6 +592,10 @@ public class CallActivity extends CallBaseActivity { Toast.makeText(context, context.getResources().getString(R.string.record_active_info), Toast.LENGTH_LONG).show(); } }); + + binding.lowerHandButton.setOnClickListener(l -> { + raiseHandViewModel.lowerHand(); + }); } private void createCameraEnumerator() { diff --git a/app/src/main/java/com/nextcloud/talk/raisehand/viewmodel/RaiseHandViewModel.kt b/app/src/main/java/com/nextcloud/talk/raisehand/viewmodel/RaiseHandViewModel.kt index c79fb0349..a835f8c3a 100644 --- a/app/src/main/java/com/nextcloud/talk/raisehand/viewmodel/RaiseHandViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/raisehand/viewmodel/RaiseHandViewModel.kt @@ -54,7 +54,7 @@ class RaiseHandViewModel @Inject constructor(private val repository: RequestAssi } } - private fun lowerHand() { + fun lowerHand() { _viewState.value = LoweredHandState if (isBreakoutRoom) { repository.withdrawRequestAssistance(roomToken) diff --git a/app/src/main/res/layout/call_activity.xml b/app/src/main/res/layout/call_activity.xml index 2d3a5bb75..a409b68e9 100644 --- a/app/src/main/res/layout/call_activity.xml +++ b/app/src/main/res/layout/call_activity.xml @@ -159,6 +159,22 @@ android:layout_height="wrap_content" android:layout_alignTop="@id/verticalCenter" android:layout_marginTop="-50dp" /> + + + From 0ea13c1ec7be15349f656de3fedd7d6cbcef08e3 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Wed, 15 Feb 2023 15:09:08 +0100 Subject: [PATCH 13/19] remove unused parameter Signed-off-by: Marcel Hibbe --- .../main/java/com/nextcloud/talk/activities/CallActivity.java | 3 +-- .../java/com/nextcloud/talk/ui/dialog/MoreCallActionsDialog.kt | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java b/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java index 6ecb46288..16f0de4c7 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java +++ b/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java @@ -1246,8 +1246,7 @@ public class CallActivity extends CallBaseActivity { } } - public void clickHand(Boolean raise) { - + public void clickRaiseOrLowerHandButton() { raiseHandViewModel.clickHandButton(); // TODO: fix how to build&send the message diff --git a/app/src/main/java/com/nextcloud/talk/ui/dialog/MoreCallActionsDialog.kt b/app/src/main/java/com/nextcloud/talk/ui/dialog/MoreCallActionsDialog.kt index 3da2934b9..620c4171d 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/dialog/MoreCallActionsDialog.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/dialog/MoreCallActionsDialog.kt @@ -87,8 +87,7 @@ class MoreCallActionsDialog(private val callActivity: CallActivity) : BottomShee } binding.raiseHand.setOnClickListener { - // TODO: save raised hand state & toggle... - callActivity.clickHand(true) + callActivity.clickRaiseOrLowerHandButton() } } From 2637884a835636e555160c07f4a7939151a594b4 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Wed, 15 Feb 2023 15:16:32 +0100 Subject: [PATCH 14/19] add comments where to implement the raise hand signaling message Signed-off-by: Marcel Hibbe --- .../talk/activities/CallActivity.java | 32 +++++++++---------- .../talk/webrtc/PeerConnectionWrapper.java | 3 +- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java b/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java index 16f0de4c7..f1dc883cb 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java +++ b/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java @@ -421,6 +421,22 @@ public class CallActivity extends CallBaseActivity { } else if (viewState instanceof RaiseHandViewModel.LoweredHandState) { binding.lowerHandButton.setVisibility(View.GONE); } + + // TODO: build&send raiseHand message (if not possible in RaiseHandViewModel, just do it here..) +// if (isConnectionEstablished() && peerConnectionWrapperList != null) { +// if (!hasMCU) { +// for (PeerConnectionWrapper peerConnectionWrapper : peerConnectionWrapperList) { +// peerConnectionWrapper.raiseHand(...); +// } +// } else { +// for (PeerConnectionWrapper peerConnectionWrapper : peerConnectionWrapperList) { +// if (peerConnectionWrapper.getSessionId().equals(webSocketClient.getSessionId())) { +// peerConnectionWrapper.raiseHand(...); +// break; +// } +// } +// } +// } }); callRecordingViewModel = new ViewModelProvider(this, viewModelFactory).get((CallRecordingViewModel.class)); @@ -1248,22 +1264,6 @@ public class CallActivity extends CallBaseActivity { public void clickRaiseOrLowerHandButton() { raiseHandViewModel.clickHandButton(); - - // TODO: fix how to build&send the message -// if (isConnectionEstablished() && peerConnectionWrapperList != null) { -// if (!hasMCU) { -// for (PeerConnectionWrapper peerConnectionWrapper : peerConnectionWrapperList) { -// peerConnectionWrapper.raiseHand(raise); -// } -// } else { -// for (PeerConnectionWrapper peerConnectionWrapper : peerConnectionWrapperList) { -// if (peerConnectionWrapper.getSessionId().equals(webSocketClient.getSessionId())) { -// peerConnectionWrapper.raiseHand(raise); -// break; -// } -// } -// } -// } } diff --git a/app/src/main/java/com/nextcloud/talk/webrtc/PeerConnectionWrapper.java b/app/src/main/java/com/nextcloud/talk/webrtc/PeerConnectionWrapper.java index 69ddb15e8..13f26a83c 100644 --- a/app/src/main/java/com/nextcloud/talk/webrtc/PeerConnectionWrapper.java +++ b/app/src/main/java/com/nextcloud/talk/webrtc/PeerConnectionWrapper.java @@ -191,8 +191,7 @@ public class PeerConnectionWrapper { } public void raiseHand(Boolean raise) { - - // TODO: fix how to build&send the message + // TODO: build&send raiseHand message (either here or via RaiseHandViewModel) // NCMessagePayload ncMessagePayload = new NCMessagePayload(); // ncMessagePayload.setState(raise); // ncMessagePayload.setTimestamp(System.currentTimeMillis()); From 3abb9db9dce9cabeb880a9ccb169b46ee7bca04f Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Thu, 16 Feb 2023 13:49:56 +0100 Subject: [PATCH 15/19] remove useless bundle entries etc. - add strings for breakout room toasts - remove useless boilerplate code - dismiss call actions dialog when chlicked raise/lower hand Signed-off-by: Marcel Hibbe --- .../talk/activities/CallActivity.java | 13 +++--- .../nextcloud/talk/activities/MainActivity.kt | 1 - .../talk/controllers/ChatController.kt | 45 ++++++++++++------- .../talk/ui/dialog/MoreCallActionsDialog.kt | 2 + app/src/main/res/values/strings.xml | 4 ++ 5 files changed, 42 insertions(+), 23 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java b/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java index f1dc883cb..cfc286f84 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java +++ b/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java @@ -1935,17 +1935,18 @@ public class CallActivity extends CallBaseActivity { Bundle bundle = new Bundle(); bundle.putBoolean(KEY_SWITCH_TO_ROOM_AND_START_CALL, true); bundle.putString(KEY_ROOM_TOKEN, switchToRoomToken); -// bundle.putString(KEY_ROOM_ID, roomId); bundle.putParcelable(KEY_USER_ENTITY, conversationUser); -// conversationName = extras.getString(KEY_CONVERSATION_NAME, ""); bundle.putBoolean(KEY_CALL_VOICE_ONLY, isVoiceOnlyCall); - bundle.putBoolean(KEY_CALL_WITHOUT_NOTIFICATION, true); - bundle.putBoolean(KEY_PARTICIPANT_PERMISSION_CAN_PUBLISH_AUDIO, canPublishAudioStream); - bundle.putBoolean(KEY_PARTICIPANT_PERMISSION_CAN_PUBLISH_VIDEO, canPublishVideoStream); intent.putExtras(bundle); startActivity(intent); - Toast.makeText(context, "going to breakout room...", Toast.LENGTH_LONG).show(); + if (isBreakoutRoom) { + Toast.makeText(context, context.getResources().getString(R.string.switch_to_main_room), + Toast.LENGTH_LONG).show(); + } else { + Toast.makeText(context, context.getResources().getString(R.string.switch_to_breakout_room), + Toast.LENGTH_LONG).show(); + } finish(); } else if (shutDownView) { diff --git a/app/src/main/java/com/nextcloud/talk/activities/MainActivity.kt b/app/src/main/java/com/nextcloud/talk/activities/MainActivity.kt index be2af0f49..a95a9d8e1 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/MainActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/activities/MainActivity.kt @@ -356,7 +356,6 @@ class MainActivity : BaseActivity(), ActionBarProvider { handleActionFromContact(intent) if (intent.getBooleanExtra(BundleKeys.KEY_SWITCH_TO_ROOM_AND_START_CALL, false)) { - logRouterBackStack(router!!) remapChatController( router!!, diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt b/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt index 719084084..01badf1ab 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt +++ b/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt @@ -316,10 +316,10 @@ class ChatController(args: Bundle) : setHasOptionsMenu(true) NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this) - this.conversationUser = args.getParcelable(KEY_USER_ENTITY) - this.roomId = args.getString(KEY_ROOM_ID, "") - this.roomToken = args.getString(KEY_ROOM_TOKEN, "") - this.sharedText = args.getString(BundleKeys.KEY_SHARED_TEXT, "") + conversationUser = args.getParcelable(KEY_USER_ENTITY) + roomId = args.getString(KEY_ROOM_ID, "") + roomToken = args.getString(KEY_ROOM_TOKEN, "") + sharedText = args.getString(BundleKeys.KEY_SHARED_TEXT, "") Log.d(TAG, " roomToken = $roomToken") if (roomToken.isNullOrEmpty()) { @@ -327,11 +327,11 @@ class ChatController(args: Bundle) : } if (args.containsKey(KEY_ACTIVE_CONVERSATION)) { - this.currentConversation = Parcels.unwrap(args.getParcelable(KEY_ACTIVE_CONVERSATION)) - this.participantPermissions = ParticipantPermissions(conversationUser!!, currentConversation!!) + currentConversation = Parcels.unwrap(args.getParcelable(KEY_ACTIVE_CONVERSATION)) + participantPermissions = ParticipantPermissions(conversationUser!!, currentConversation!!) } - this.roomPassword = args.getString(BundleKeys.KEY_CONVERSATION_PASSWORD, "") + roomPassword = args.getString(BundleKeys.KEY_CONVERSATION_PASSWORD, "") credentials = if (conversationUser?.userId == "?") { null @@ -340,14 +340,14 @@ class ChatController(args: Bundle) : } if (args.containsKey(BundleKeys.KEY_FROM_NOTIFICATION_START_CALL)) { - this.startCallFromNotification = args.getBoolean(BundleKeys.KEY_FROM_NOTIFICATION_START_CALL) + startCallFromNotification = args.getBoolean(BundleKeys.KEY_FROM_NOTIFICATION_START_CALL) } if (args.containsKey(BundleKeys.KEY_SWITCH_TO_ROOM_AND_START_CALL)) { - this.startCallFromRoomSwitch = args.getBoolean(BundleKeys.KEY_SWITCH_TO_ROOM_AND_START_CALL) + startCallFromRoomSwitch = args.getBoolean(BundleKeys.KEY_SWITCH_TO_ROOM_AND_START_CALL) } - this.voiceOnly = args.getBoolean(BundleKeys.KEY_CALL_VOICE_ONLY, false) + voiceOnly = args.getBoolean(BundleKeys.KEY_CALL_VOICE_ONLY, false) } private fun getRoomInfo() { @@ -935,13 +935,26 @@ class ChatController(args: Bundle) : return } - val conversationIntent = Intent(activity, CallActivity::class.java) - val bundle = Bundle() - bundle.putParcelable(KEY_USER_ENTITY, conversationUser) - bundle.putString(KEY_ROOM_TOKEN, token) - if (conversationUser != null) { - conversationIntent.putExtras(bundle) + activity?.runOnUiThread { + if (currentConversation?.objectType == BREAKOUT_ROOM_TYPE) { + Toast.makeText( + context, + context.resources.getString(R.string.switch_to_main_room), + Toast.LENGTH_LONG + ).show() + } else { + Toast.makeText( + context, + context.resources.getString(R.string.switch_to_breakout_room), + Toast.LENGTH_LONG + ).show() + } + } + + val bundle = Bundle() + bundle.putParcelable(KEY_USER_ENTITY, conversationUser) + bundle.putString(KEY_ROOM_TOKEN, token) ConductorRemapping.remapChatController( router, diff --git a/app/src/main/java/com/nextcloud/talk/ui/dialog/MoreCallActionsDialog.kt b/app/src/main/java/com/nextcloud/talk/ui/dialog/MoreCallActionsDialog.kt index 620c4171d..717fec352 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/dialog/MoreCallActionsDialog.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/dialog/MoreCallActionsDialog.kt @@ -130,12 +130,14 @@ class MoreCallActionsDialog(private val callActivity: CallActivity) : BottomShee binding.raiseHandIcon.setImageDrawable( ContextCompat.getDrawable(context, R.drawable.ic_baseline_do_not_touch_24) ) + dismiss() } is RaiseHandViewModel.LoweredHandState -> { binding.raiseHandText.text = context.getText(R.string.raise_hand) binding.raiseHandIcon.setImageDrawable( ContextCompat.getDrawable(context, R.drawable.ic_hand_back_left) ) + dismiss() } else -> {} } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6b2eb1dca..5460faa6c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -633,6 +633,10 @@ 1 hour Chat messages can be expired after a certain time. Note: Files shared in chat will not be deleted for the owner, but will no longer be shared in the conversation. + + Switch to main room + Switch to breakout room + You are not allowed to activate audio! You are not allowed to activate video! Scroll to bottom From 38d1a377846a361686fea589adffae46196e97c4 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Thu, 16 Feb 2023 15:16:16 +0100 Subject: [PATCH 16/19] Add system messages for breakout rooms Signed-off-by: Marcel Hibbe --- .../java/com/nextcloud/talk/models/json/chat/ChatMessage.kt | 4 +++- .../json/converters/EnumSystemMessageTypeConverter.kt | 6 ++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatMessage.kt b/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatMessage.kt index 9cebbf32e..e60e3314d 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatMessage.kt +++ b/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatMessage.kt @@ -529,7 +529,9 @@ data class ChatMessage( RECORDING_STARTED, RECORDING_STOPPED, AUDIO_RECORDING_STARTED, - AUDIO_RECORDING_STOPPED + AUDIO_RECORDING_STOPPED, + BREAKOUT_ROOMS_STARTED, + BREAKOUT_ROOMS_STOPPED } companion object { diff --git a/app/src/main/java/com/nextcloud/talk/models/json/converters/EnumSystemMessageTypeConverter.kt b/app/src/main/java/com/nextcloud/talk/models/json/converters/EnumSystemMessageTypeConverter.kt index 8e276a703..dded74d64 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/converters/EnumSystemMessageTypeConverter.kt +++ b/app/src/main/java/com/nextcloud/talk/models/json/converters/EnumSystemMessageTypeConverter.kt @@ -28,6 +28,8 @@ import com.bluelinelabs.logansquare.typeconverters.StringBasedTypeConverter import com.nextcloud.talk.models.json.chat.ChatMessage import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.AUDIO_RECORDING_STARTED import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.AUDIO_RECORDING_STOPPED +import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.BREAKOUT_ROOMS_STARTED +import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.BREAKOUT_ROOMS_STOPPED import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.CALL_ENDED import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.CALL_ENDED_EVERYONE import com.nextcloud.talk.models.json.chat.ChatMessage.SystemMessageType.CALL_JOINED @@ -141,6 +143,8 @@ class EnumSystemMessageTypeConverter : StringBasedTypeConverter RECORDING_STOPPED "audio_recording_started" -> AUDIO_RECORDING_STARTED "audio_recording_stopped" -> AUDIO_RECORDING_STOPPED + "breakout_rooms_started" -> BREAKOUT_ROOMS_STARTED + "breakout_rooms_stopped" -> BREAKOUT_ROOMS_STOPPED else -> DUMMY } } @@ -202,6 +206,8 @@ class EnumSystemMessageTypeConverter : StringBasedTypeConverter "recording_stopped" AUDIO_RECORDING_STARTED -> "audio_recording_started" AUDIO_RECORDING_STOPPED -> "audio_recording_stopped" + BREAKOUT_ROOMS_STARTED -> "breakout_rooms_started" + BREAKOUT_ROOMS_STOPPED -> "breakout_rooms_stopped" else -> "" } } From a0997e699ffbb395ff34b539e0d8e87880cde2a0 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Thu, 16 Feb 2023 15:33:10 +0100 Subject: [PATCH 17/19] Remove unused resources Signed-off-by: Marcel Hibbe --- app/src/main/res/drawable/ic_baseline_back_hand_24.xml | 5 ----- app/src/main/res/layout/call_item.xml | 2 +- app/src/main/res/layout/dialog_more_call_actions.xml | 2 +- app/src/main/res/values/strings.xml | 7 ++----- 4 files changed, 4 insertions(+), 12 deletions(-) delete mode 100644 app/src/main/res/drawable/ic_baseline_back_hand_24.xml diff --git a/app/src/main/res/drawable/ic_baseline_back_hand_24.xml b/app/src/main/res/drawable/ic_baseline_back_hand_24.xml deleted file mode 100644 index 0e05b786e..000000000 --- a/app/src/main/res/drawable/ic_baseline_back_hand_24.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/app/src/main/res/layout/call_item.xml b/app/src/main/res/layout/call_item.xml index d04556e54..8901e054b 100644 --- a/app/src/main/res/layout/call_item.xml +++ b/app/src/main/res/layout/call_item.xml @@ -79,7 +79,7 @@ android:layout_height="16dp" android:layout_marginStart="10dp" android:layout_marginBottom="6dp" - android:contentDescription="@string/nc_call_raise_hand_description" + android:contentDescription="@string/raise_hand" android:src="@drawable/ic_hand_back_left" android:visibility="invisible" tools:visibility="visible" /> diff --git a/app/src/main/res/layout/dialog_more_call_actions.xml b/app/src/main/res/layout/dialog_more_call_actions.xml index 3ca078e46..9bb0bac78 100644 --- a/app/src/main/res/layout/dialog_more_call_actions.xml +++ b/app/src/main/res/layout/dialog_more_call_actions.xml @@ -63,7 +63,7 @@ android:layout_gravity="start|center_vertical" android:paddingStart="@dimen/standard_double_padding" android:paddingEnd="@dimen/zero" - android:text="@string/nc_call_raise_hand_description" + android:text="@string/raise_hand" android:textAlignment="viewStart" android:textColor="@color/high_emphasis_text_dark_background" android:textSize="@dimen/bottom_sheet_text_size" /> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5460faa6c..96eb7f3fa 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -223,8 +223,8 @@ Answer as video call Switch to self video %1$s raised the hand - Raise hand - Lower hand + Raise hand + Lower hand Mute microphone @@ -573,9 +573,6 @@ Do you really want to stop the recording? The call is being recorded - Raise hand - Lower hand - Media File From c55f9fdf79f18d3ff1ad5dda6a546f594054516a Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Thu, 16 Feb 2023 15:42:00 +0100 Subject: [PATCH 18/19] Add/fix copyright headers Signed-off-by: Marcel Hibbe --- .../talk/raisehand/RequestAssistanceModel.kt | 20 +++++++++++++++++++ .../raisehand/RequestAssistanceRepository.kt | 2 +- .../RequestAssistanceRepositoryImpl.kt | 20 +++++++++++++++++++ .../WithdrawRequestAssistanceModel.kt | 20 +++++++++++++++++++ .../raisehand/viewmodel/RaiseHandViewModel.kt | 20 +++++++++++++++++++ 5 files changed, 81 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/nextcloud/talk/raisehand/RequestAssistanceModel.kt b/app/src/main/java/com/nextcloud/talk/raisehand/RequestAssistanceModel.kt index 1772cc469..471838284 100644 --- a/app/src/main/java/com/nextcloud/talk/raisehand/RequestAssistanceModel.kt +++ b/app/src/main/java/com/nextcloud/talk/raisehand/RequestAssistanceModel.kt @@ -1,3 +1,23 @@ +/* + * Nextcloud Talk application + * + * @author Marcel Hibbe + * Copyright (C) 2023 Marcel Hibbe + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + package com.nextcloud.talk.raisehand data class RequestAssistanceModel( diff --git a/app/src/main/java/com/nextcloud/talk/raisehand/RequestAssistanceRepository.kt b/app/src/main/java/com/nextcloud/talk/raisehand/RequestAssistanceRepository.kt index 2c39f9ae1..826dc72e1 100644 --- a/app/src/main/java/com/nextcloud/talk/raisehand/RequestAssistanceRepository.kt +++ b/app/src/main/java/com/nextcloud/talk/raisehand/RequestAssistanceRepository.kt @@ -2,7 +2,7 @@ * Nextcloud Talk application * * @author Marcel Hibbe - * Copyright (C) 2022 Marcel Hibbe + * Copyright (C) 2023 Marcel Hibbe * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/app/src/main/java/com/nextcloud/talk/raisehand/RequestAssistanceRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/raisehand/RequestAssistanceRepositoryImpl.kt index f737b02a2..b1f2b40ea 100644 --- a/app/src/main/java/com/nextcloud/talk/raisehand/RequestAssistanceRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/raisehand/RequestAssistanceRepositoryImpl.kt @@ -18,6 +18,26 @@ * along with this program. If not, see . */ +/* + * Nextcloud Talk application + * + * @author Marcel Hibbe + * Copyright (C) 2023 Marcel Hibbe + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + package com.nextcloud.talk.raisehand import com.nextcloud.talk.api.NcApi diff --git a/app/src/main/java/com/nextcloud/talk/raisehand/WithdrawRequestAssistanceModel.kt b/app/src/main/java/com/nextcloud/talk/raisehand/WithdrawRequestAssistanceModel.kt index 88b7f74b4..f8bba900d 100644 --- a/app/src/main/java/com/nextcloud/talk/raisehand/WithdrawRequestAssistanceModel.kt +++ b/app/src/main/java/com/nextcloud/talk/raisehand/WithdrawRequestAssistanceModel.kt @@ -1,3 +1,23 @@ +/* + * Nextcloud Talk application + * + * @author Marcel Hibbe + * Copyright (C) 2023 Marcel Hibbe + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + package com.nextcloud.talk.raisehand data class WithdrawRequestAssistanceModel( diff --git a/app/src/main/java/com/nextcloud/talk/raisehand/viewmodel/RaiseHandViewModel.kt b/app/src/main/java/com/nextcloud/talk/raisehand/viewmodel/RaiseHandViewModel.kt index a835f8c3a..2d8a8a36a 100644 --- a/app/src/main/java/com/nextcloud/talk/raisehand/viewmodel/RaiseHandViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/raisehand/viewmodel/RaiseHandViewModel.kt @@ -1,3 +1,23 @@ +/* + * Nextcloud Talk application + * + * @author Marcel Hibbe + * Copyright (C) 2023 Marcel Hibbe + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + package com.nextcloud.talk.raisehand.viewmodel import android.util.Log From 78e3c801241b89d837e113a5ee5b096f00337cfa Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Mon, 20 Feb 2023 12:47:44 +0100 Subject: [PATCH 19/19] fix license header Signed-off-by: Marcel Hibbe --- .../RequestAssistanceRepositoryImpl.kt | 20 ------------------- 1 file changed, 20 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/raisehand/RequestAssistanceRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/raisehand/RequestAssistanceRepositoryImpl.kt index b1f2b40ea..aa42daa7d 100644 --- a/app/src/main/java/com/nextcloud/talk/raisehand/RequestAssistanceRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/raisehand/RequestAssistanceRepositoryImpl.kt @@ -1,23 +1,3 @@ -/* - * Nextcloud Talk application - * - * @author Marcel Hibbe - * Copyright (C) 2022 Marcel Hibbe - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - /* * Nextcloud Talk application *