Note that the thread used to handle and notify messages from the
external signaling server does not change; the EventBus subscriber mode
was "BACKGROUND", but as the message was posted from a WebSocket
handler, which runs in a worker thread rather than in the main thread,
the subscriber was executed in the same thread as the poster.
Signed-off-by: Daniel Calviño Sánchez <danxuliu@gmail.com>
This will no longer log an error if the room type of the received
message is neither "video" nor "screen". However, that should never
happen, and it would be useful only while debugging, so it is fine to
lose that.
Note that the check is not added to SignalingMessageReceiver itself to
keep it as generic as possible (and due to the low value of adding it as
explained above). Nevertheless, if needed in the future it would be
possible to add a special listener that receives raw messages in order
to validate them and log the errors, if any.
Signed-off-by: Daniel Calviño Sánchez <danxuliu@gmail.com>
Although "unshareScreen" is technically bound to a specific peer
connection it is instead treated as a general message on the call
participant.
Nevertheless, call participant messages will make possible (at a later
point) to listen to events like "raise hand" or "mute" (which, again,
could be technically bound to a specific peer connection, but at least
for now are treated as a general message on the call participant).
Signed-off-by: Daniel Calviño Sánchez <danxuliu@gmail.com>
Unlike the WebRtcMessageListener, which is bound to a specific peer
connection, an OfferMessageListener listens to all offer messages, no
matter which peer connection they are bound to. This can be used, for
example, to create a new peer connection when a remote offer for which
there is no previous connection is received.
Signed-off-by: Daniel Calviño Sánchez <danxuliu@gmail.com>
Eventually all signaling related code should be moved to a Signaling
class that abstracts the differences between the internal and external
signaling servers, including how messages are sent and listened to. In
the meantime a temporary SignalingMessageReceiver implementation is
added to CallActivity to be able to start using it.
Signed-off-by: Daniel Calviño Sánchez <danxuliu@gmail.com>
For now only WebRTC messages can be listened to, although it will be
extended with other kinds later.
This commit only introduces the base class, although it is not used yet
anywhere; a concrete implementation will be added in a following commit.
The test class is named "SignalingMessageReceiverWebRtcTest" rather than
just "SignalingMessageReceiverTest" to have smaller, more manageable
test classes for each listener kind rather than one large test class for
all of them.
Signed-off-by: Daniel Calviño Sánchez <danxuliu@gmail.com>
Although the rest of the methods are no longer needed since the handling
of WebRTC messages was moved to PeerConnectionWrapper "setSessionId()"
was not needed even before that.
Signed-off-by: Daniel Calviño Sánchez <danxuliu@gmail.com>
"peerConnectionWrapper" needs to be defined to enter the if and execute
the switch, so just return before the switch if "peerConnectionWrapper"
is null.
Signed-off-by: Daniel Calviño Sánchez <danxuliu@gmail.com>
The message type is set for all signaling messages. On the other hand,
the payload type is only set for offers and answers (and, if the message
was sent by the Android app, also for candidates). However, in all those
cases the payload type just duplicates the message type, so the message
type can be assigned directly rather than falling back to it if there is
no payload type.
Signed-off-by: Daniel Calviño Sánchez <danxuliu@gmail.com>
When the HPB is used the signaling messages can be received even when
the local participant is not currently in the call (for example, when
starting the call timed out without other participant joining, or when
reconnecting due to the publisher connection failing). Therefore if the
local participant is not in the call it should not try to establish a
connection with the other participants and disconnect them instead.
Moreover, if the connection is tried to be established when not
in the call the HPB will prevent that, and the PeerConnectionWrapper
will stay in a limbo state waiting for an offer to be sent. If the local
participant then joins the call the PeerConnectionWrapper will already
exist for the other participants, so no new connections will be created,
but those previous connections will never be finally established.
Additionally, as the signaling messages can be received before the join
call response the participant list could be received while the call
state is "RECONNECTING" or "PUBLISHER_FAILED". In those cases, as long
as the local participant is already in the call, the participant list
should be processed as if the call state was already "JOINED" (otherwise
the connections were not established either).
For simplicity the participant list is now ignored only when the call
state is "LEAVING"; this means that the participant list would be also
processed in the "CONNECTION_TIMEOUT" state, but the signaling message
should not be received anyway in that case.
Signed-off-by: Daniel Calviño Sánchez <danxuliu@gmail.com>
If the call is hung up with a view shutdown (which finishes the
activity) there is no need to do any further processing on the
participant list.
Signed-off-by: Daniel Calviño Sánchez <danxuliu@gmail.com>
When a call is joined the call flags of the local participant change, so
this causes a signaling message to be sent by the server. When the HPB
is used the signaling message is sent through a WebSocket, which is
already connected before joining the call. Therefore, in some cases the
signaling message can be received through the WebSocket even before the
response to the HTTP "joinCall" request.
If there are other participants in the call the call state is changed to
"IN_CONVERSATION" when the signaling message is processed. However, in
the case described above the call state was then set to "JOINED", which
automatically traverses to "CONNECTION_TIMEOUT" if no other call state
was set in 45 seconds. Due to all this the call was joined and the
connections with the other participants were established, but they were
not visible in the UI (although they could be heard) and after 45
seconds the call was left.
To prevent that now the call state is changed to "JOINED" if it was not
already changed to "IN_CONVERSATION" in the meantime.
Signed-off-by: Daniel Calviño Sánchez <danxuliu@gmail.com>
did not test why this can happen. autocompleteUser.id must have been null..
Exception java.lang.NullPointerException:
at com.nextcloud.talk.controllers.ContactsController.processAutocompleteUserList (ContactsController.kt:498)
at com.nextcloud.talk.controllers.ContactsController.processAutocompleteUserList (ContactsController.kt:482)
at com.nextcloud.talk.controllers.ContactsController.access$processAutocompleteUserList (ContactsController.kt:90)
at com.nextcloud.talk.controllers.ContactsController$fetchData$1.onNext (ContactsController.kt:438)
at com.nextcloud.talk.controllers.ContactsController$fetchData$1.onNext (ContactsController.kt:432)
at io.reactivex.internal.operators.observable.ObservableRetryPredicate$RepeatObserver.onNext (ObservableRetryPredicate.java:69)
at io.reactivex.internal.operators.observable.ObservableObserveOn$ObserveOnObserver.drainNormal (ObservableObserveOn.java:201)
at io.reactivex.internal.operators.observable.ObservableObserveOn$ObserveOnObserver.run (ObservableObserveOn.java:255)
at io.reactivex.android.schedulers.HandlerScheduler$ScheduledRunnable.run (HandlerScheduler.java:124)
at android.os.Handler.handleCallback (Handler.java:883)
at android.os.Handler.dispatchMessage (Handler.java:100)
at android.os.Looper.loop (Looper.java:237)
at android.app.ActivityThread.main (ActivityThread.java:7948)
at java.lang.reflect.Method.invoke
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1075)
Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
The module class is not supposed to have things injected into it. @Provides-annotated methods
will have their parameters injected, instead.
Signed-off-by: Álvaro Brey <alvaro.brey@nextcloud.com>
Possible null pointer dereference in com.nextcloud.talk.utils.PushUtils.updatePushStateForUser(Map, User) due to return value of called method
Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
PRMC: In class com.nextcloud.talk.utils.ssl.MagicKeyManager
In class com.nextcloud.talk.utils.ssl.MagicKeyManager
In method com.nextcloud.talk.utils.ssl.MagicKeyManager.chooseClientAlias(String[], Principal[], Socket)
At MagicKeyManager.java:[line 68]
Value getCurrentUser()Lio/reactivex/Maybe;
Method com.nextcloud.talk.utils.ssl.MagicKeyManager.chooseClientAlias(String[], Principal[], Socket) appears to call the same method on the same object redundantly
Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
This is to work around a race condition where this class would asynchronously overwrite other user attributes
with old values after a user switch.
Co-authored-by: Marcel Hibbe <dev@mhibbe.de>
Signed-off-by: Álvaro Brey <alvaro.brey@nextcloud.com>
i was able to see that the loop was somehow interrupted during debugging which caused two users to have current =true
this should avoid the problem with the loop.
anyway, this doesn't seem to solve the issue completely as i was able to reproduce it again with the new solution. so maybe there are still more methods/scenarios which can cause this.
additionally, i managed to have all users to have current =false with this new query (while switching accounts very fast and often in ChooseAccountDialogFragment..)
Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
this should have no effect but it should ensure "current" is not falsely set to true if the method storeProfile will be used for more scenarios in the future.
Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
just refactoring for now. this doesn't solve the bug!
Problem that needs to be solved:
When adding a new Account (User), it is marked as "current", while for the other logged in users "current" must be unset (-> disabled).
The problem is, that for the old active user, "current" is not unset so there were multiple accounts marked as "current".
In the ChooseAccountDialogFragment, only one of the current accounts is shown at the top. Below the set status field, all accounts are listed that are not marked with "current". So as a result, there can be accounts hidden that were marked as "current".
Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
-> add callback methods to ConductorRemapping to execute after chat room was left. Whenever there is a ChatController on top, it's room is now left, before replacing the controller or pushing another one on top.
this avoids problems where entering a chat before the old one was left led to sessionId="0" for the new chat.
Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
when opening a chat notification, the old chat should not be kept in the backstack. so when clicking the back button when coming from a chat that was opened by a notification, now the ConversationList opens.
by the way, this also avoids to run into bug #2181 (but it's root cause is not solved yet)
Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
# reproduce:
1. click in chat where someone is mentioned.
2. click on the mention
3. go back to first chat
# result:
validSessionId() can be false in onDetach for the second chat when going back to first chat
-> leaveRoom is not executed
-> disposable is not disposed
-> getRoomInfo() continues to execute for old controller
-> e.g. appbar infos can be wrong (wrong avatar/title)
Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
set drawable of referenceThumbImage to null. this hopefully avoids that wrong images are loaded from recycler view to wrong messages
Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
there was a report that a message contained the link preview of a previous message. This was most likely because of a recycler view error. Hopefully setting empty values should avoid this now.
Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
for example when call is hangup on mobile and immediately after on web, the loop in "checkIfCallIsActive" is still active and might trigger to send the "missed call" notification. Because of this, there is now another check if the "ongoing call" notification is still visible. It makes only sense to show the missed call notification, when the ongoing call notification is still visible.
Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
this commit removes the logic to play the ringtone in CallNotificationActivity. Playing ringtone should only be controlled by the notification channel from OS!
furthermore the checks if a call is stopped or is still ongoing etc was removed from CallNotificationActivity. Instead the CallNotificationActivity now is completely dependent on the notification. If the notification is canceled, the Activity stops. If the Notification is ongoing and hangup of accept call is clicked, then the notification is canceled (including the ringtone).
Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
Fresco is replaced with Coil everywhere to make it possible to set 'minSdkVersion'
to 23. But Coil is not used directly to avoid splintering the dependency
everywhere in the code. Coil is wrapped by extension functions for 'ImageView'.
Some shared functionality is moved from 'DisplayUtils' into the
'ImageViewExtensions'.
The exisiting initialization of Coil has also be changed. The usage of the self
initialized OKHttp client is removed. If this one is added the
caching of the http client is used by Coil additionally to memory and
disk cache.
Resolves: #2227, #2376
Signed-off-by: Tim Krüger <t@timkrueger.me>
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.nextcloud.talk2, PID: 10874
java.lang.NullPointerException: uri param can not be null.
at android.media.MediaPlayer.setDataSource(MediaPlayer.java:1058)
at android.media.MediaPlayer.setDataSource(MediaPlayer.java:1021)
at com.nextcloud.talk.activities.CallActivity.playCallingSound(CallActivity.java:2643)
Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
When the web socket is abruptly closed it is connected again and the
call is rejoined. However, the call was rejoined in a background thread,
so an exception was thrown when trying to modify the views, which
prevented the call from being joined again.
Besides that the call state needs to be explicitly changed, as if the
web socket was connected again while in a call the state would be
already "JOINED" or "IN_CONVERSATION", which prevents the signaling
settings from being fetched again after the permissions check, and
therefore also prevented the call from being joined again.
Signed-off-by: Daniel Calviño Sánchez <danxuliu@gmail.com>
The call flags should be checked using bitwise operators; otherwise a
call with phone ("in call | with phone" / "in call + with phone", that
is, "0001 + 1000 = 1001" or "1 + 8 = 9") would be seen as a call with
video ("in call | with video" / "in call + with video",
"0001 + 0100 = 0101" or "1 + 4 = 5"), as "9 >= 5". On the other hand,
using bitwise operators (and only checking against "with video")
succeeds only when the call flags contain "with video" (in the previous
example, "1001 and 0100 = 0000", so it does not succeed).
The "IN_CALL" flag is no longer checked, as "WITH_VIDEO" will be set
only during calls, and therefore checking for "IN_CALL" is not needed.
Signed-off-by: Daniel Calviño Sánchez <danxuliu@gmail.com>
In further versions of the ImagePicker library the class 'File' is used
to reference an image. Using 'File' caused the permission problems mentioned
in #2511.
Resolves: #2511
See: d7e643b560
Signed-off-by: Tim Krüger <t@timkrueger.me>
The ParticipantDisplayItems were associated to both the session ID and
the video stream type ("video" or "screen"), but the code that gets them
was not updated to include the video stream type in the key.
Signed-off-by: Daniel Calviño Sánchez <danxuliu@gmail.com>
spreed usage can be restricted by admins to groups. For this case users should get a error message which also takes this into account. Else it could be frustrating for users to find out why talk is not working.
Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
ParticipantDisplayItems are not associated to a full participant but to
each of the single connections that the participant may have (video and
screen). However, when they are added to the map only the session ID is
used as key. Due to this when a participant starts a screen share the
ParticipantDisplayItem for the screen share overwrites the item for the
video, and once the screen share is stopped the old item is not
restored. Moreover, if a participant is already sharing a screen when
the local participant joins whether the video or the screen share is
shown is undefined and depends on which connection is established first.
To solve that the ParticipantDisplayItems are now associated with both
the session ID and the video stream type ("video" or "screen"). Due to
this both the video and the screen share of the remote participant are
shown in the grid view; in the future it might be better to only show
the screen share, or allow switching between screen share and video, or
show the screen share in full screen and hide the grid... but for now,
as a quick fix, this is good enough :-)
Signed-off-by: Daniel Calviño Sánchez <danxuliu@gmail.com>
this didn't make sense because time between firebase and devices is not synchronized, so the results were useless.
Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
- add missed call notifications in NotificationWorker and CallNotificationActivity
- introduce refactoring of Notification handling (isolate firebase stuff from other logic). All "UI-notification" logic from ChatAndCallMessagingService was moved to NotificationWorker. ChatAndCallMessagingService was renamed to NCFirebaseMessagingService because it is now only responsible for firebase stuff. This separation should make it easier for alternative push services to dock with the app (if they are incorporated in the future).
- for DEBUG mode: show delivery delay time in notifications (time between sending from firebase to receive on device).
Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
When pulling signaling messages failed the source observable was
immediately subscribed again, which immediately triggered another pull.
Rather than hammering the server or a flaky network with new requests
again and again now further requests are performed with an incremental
delay (up to 16 seconds).
The delay is increased only when several requests fail in a row, and it
is reset as soon as a request succeeds.
Signed-off-by: Daniel Calviño Sánchez <danxuliu@gmail.com>
When the internal signaling server is used the observable to pull
signaling messages is subscribed again after each completion, but in
case of an error it was retried only 3 times. Those 3 times are not in a
row, though, but in total for the whole observable, no matter how many
times it was subscribed again.
Due to the limitation on retries in a long call with a flaky connection
pulling the signaling messages could fail more than 3 times, which
caused the observable to finish with an error and therefore stop further
pullings. In this situation the Android app would not notice if other
participants joined or left the call, and thus it would not establish a
connection with them or stop it. To prevent that now the number of
retries is unlimited (although the retry is still stopped if the local
participant is no longer in the call).
Signed-off-by: Daniel Calviño Sánchez <danxuliu@gmail.com>
The URL for the avatar depends on whether the call participant is a user
or a guest and, if it is a guest, on its nick. Although the user id of a
participant does not change if the participant is a guest the nick may
be changed during a call, so the avatar URL needs to be updated as well.
As the avatar URL is fully derived from other properties it is now
calculated internally in the ParticipantDisplayItem and calculated when
any of the properties it depends on changes (also for the user id for
completeness, as technically the item could be reused for a different
participant with a different user id, even if it is not currently done).
Signed-off-by: Daniel Calviño Sánchez <danxuliu@gmail.com>