瀏覽代碼

Merge pull request #2656 from nextcloud/chore/noid/spotbugsImprovements

Spotbugs improvements
Andy Scherzinger 2 年之前
父節點
當前提交
34cd485d4b
共有 32 個文件被更改,包括 418 次插入318 次删除
  1. 5 8
      app/src/main/java/com/nextcloud/talk/activities/CallActivity.java
  2. 1 1
      app/src/main/java/com/nextcloud/talk/activities/CallBaseActivity.java
  3. 3 3
      app/src/main/java/com/nextcloud/talk/adapters/items/AdvancedUserItem.java
  4. 7 1
      app/src/main/java/com/nextcloud/talk/adapters/items/ContactItem.java
  5. 20 1
      app/src/main/java/com/nextcloud/talk/adapters/items/NotificationSoundItem.java
  6. 55 51
      app/src/main/java/com/nextcloud/talk/adapters/items/ParticipantItem.java
  7. 5 5
      app/src/main/java/com/nextcloud/talk/callbacks/MentionAutocompleteCallback.java
  8. 1 2
      app/src/main/java/com/nextcloud/talk/components/filebrowser/models/DavResponse.java
  9. 14 30
      app/src/main/java/com/nextcloud/talk/components/filebrowser/webdav/DavUtils.java
  10. 1 1
      app/src/main/java/com/nextcloud/talk/components/filebrowser/webdav/ReadFilesystemOperation.java
  11. 1 3
      app/src/main/java/com/nextcloud/talk/dagger/modules/RestModule.java
  12. 1 2
      app/src/main/java/com/nextcloud/talk/events/EventStatus.java
  13. 1 2
      app/src/main/java/com/nextcloud/talk/events/MoreMenuClickEvent.java
  14. 1 2
      app/src/main/java/com/nextcloud/talk/events/NetworkEvent.java
  15. 1 2
      app/src/main/java/com/nextcloud/talk/events/UserMentionClickEvent.java
  16. 1 2
      app/src/main/java/com/nextcloud/talk/events/WebSocketCommunicationEvent.java
  17. 1 2
      app/src/main/java/com/nextcloud/talk/models/ImportAccount.java
  18. 2 1
      app/src/main/java/com/nextcloud/talk/presenters/MentionAutocompletePresenter.java
  19. 21 7
      app/src/main/java/com/nextcloud/talk/ui/StatusDrawable.java
  20. 0 1
      app/src/main/java/com/nextcloud/talk/ui/dialog/SortingOrderDialogFragment.java
  21. 3 6
      app/src/main/java/com/nextcloud/talk/utils/AuthenticatorService.java
  22. 8 5
      app/src/main/java/com/nextcloud/talk/utils/DeviceUtils.java
  23. 7 7
      app/src/main/java/com/nextcloud/talk/utils/DisplayUtils.java
  24. 30 16
      app/src/main/java/com/nextcloud/talk/utils/MagicCharPolicy.java
  25. 7 7
      app/src/main/java/com/nextcloud/talk/utils/preferences/preferencestorage/DatabaseStorageModule.java
  26. 1 3
      app/src/main/java/com/nextcloud/talk/utils/text/Spans.java
  27. 1 0
      app/src/main/java/com/nextcloud/talk/webrtc/Globals.java
  28. 199 132
      app/src/main/java/com/nextcloud/talk/webrtc/MagicWebSocketInstance.java
  29. 5 4
      app/src/main/java/com/nextcloud/talk/webrtc/PeerConnectionWrapper.java
  30. 1 1
      app/src/main/java/com/nextcloud/talk/webrtc/WebRtcAudioManager.java
  31. 8 6
      app/src/main/java/com/nextcloud/talk/webrtc/WebRtcBluetoothManager.java
  32. 6 4
      app/src/main/java/com/nextcloud/talk/webrtc/WebSocketConnectionHelper.java

+ 5 - 8
app/src/main/java/com/nextcloud/talk/activities/CallActivity.java

@@ -90,7 +90,6 @@ import com.nextcloud.talk.utils.NotificationUtils;
 import com.nextcloud.talk.utils.animations.PulseAnimation;
 import com.nextcloud.talk.utils.permissions.PlatformPermissionUtil;
 import com.nextcloud.talk.utils.power.PowerManagerUtils;
-import com.nextcloud.talk.utils.preferences.AppPreferences;
 import com.nextcloud.talk.utils.singletons.ApplicationWideCurrentRoomHolder;
 import com.nextcloud.talk.webrtc.MagicWebRTCUtils;
 import com.nextcloud.talk.webrtc.MagicWebSocketInstance;
@@ -100,7 +99,6 @@ import com.nextcloud.talk.webrtc.WebSocketConnectionHelper;
 import com.wooplr.spotlight.SpotlightView;
 
 import org.apache.commons.lang3.StringEscapeUtils;
-import org.greenrobot.eventbus.EventBus;
 import org.greenrobot.eventbus.Subscribe;
 import org.greenrobot.eventbus.ThreadMode;
 import org.webrtc.AudioSource;
@@ -178,14 +176,13 @@ public class CallActivity extends CallBaseActivity {
 
     @Inject
     NcApi ncApi;
-    @Inject
-    EventBus eventBus;
+
     @Inject
     UserManager userManager;
-    @Inject
-    AppPreferences appPreferences;
+
     @Inject
     Cache cache;
+
     @Inject
     PlatformPermissionUtil permissionUtil;
 
@@ -367,7 +364,7 @@ public class CallActivity extends CallBaseActivity {
 
         powerManagerUtils = new PowerManagerUtils();
 
-        if (extras.getString("state", "").equalsIgnoreCase("resume")) {
+        if ("resume".equalsIgnoreCase(extras.getString("state", ""))) {
             setCallState(CallStatus.IN_CONVERSATION);
         } else {
             setCallState(CallStatus.CONNECTING);
@@ -592,7 +589,7 @@ public class CallActivity extends CallBaseActivity {
     private void handleFromNotification() {
         int apiVersion = ApiUtils.getConversationApiVersion(conversationUser, new int[]{ApiUtils.APIv4, 1});
 
-        ncApi.getRooms(credentials, ApiUtils.getUrlForRooms(apiVersion, baseUrl), false)
+        ncApi.getRooms(credentials, ApiUtils.getUrlForRooms(apiVersion, baseUrl), Boolean.FALSE)
             .retry(3)
             .subscribeOn(Schedulers.io())
             .observeOn(AndroidSchedulers.mainThread())

+ 1 - 1
app/src/main/java/com/nextcloud/talk/activities/CallBaseActivity.java

@@ -21,7 +21,7 @@ public abstract class CallBaseActivity extends BaseActivity {
     public static final String TAG = "CallBaseActivity";
 
     public PictureInPictureParams.Builder mPictureInPictureParamsBuilder;
-    public Boolean isInPipMode = false;
+    public Boolean isInPipMode = Boolean.FALSE;
     long onCreateTime;
 
     @SuppressLint("ClickableViewAccessibility")

+ 3 - 3
app/src/main/java/com/nextcloud/talk/adapters/items/AdvancedUserItem.java

@@ -125,9 +125,9 @@ public class AdvancedUserItem extends AbstractFlexibleItem<AdvancedUserItem.User
             }
         }
 
-        if (user != null && user.getBaseUrl() != null &&
-                user.getBaseUrl().startsWith("http://") ||
-                user.getBaseUrl().startsWith("https://")) {
+        if (user != null &&
+            user.getBaseUrl() != null &&
+            (user.getBaseUrl().startsWith("http://") || user.getBaseUrl().startsWith("https://"))) {
             ImageViewExtensionsKt.loadAvatar(holder.binding.userIcon, user, participant.getCalculatedActorId(), true);
         }
     }

+ 7 - 1
app/src/main/java/com/nextcloud/talk/adapters/items/ContactItem.java

@@ -38,6 +38,7 @@ import com.nextcloud.talk.models.json.participants.Participant;
 import com.nextcloud.talk.ui.theme.ViewThemeUtils;
 
 import java.util.List;
+import java.util.Objects;
 import java.util.regex.Pattern;
 
 import androidx.core.content.res.ResourcesCompat;
@@ -171,10 +172,15 @@ public class ContactItem extends AbstractFlexibleItem<ContactItem.ContactItemVie
             if (!TextUtils.isEmpty(participant.getDisplayName())) {
                 displayName = participant.getDisplayName();
             } else {
-                displayName = NextcloudTalkApplication.Companion.getSharedApplication()
+                displayName = Objects.requireNonNull(NextcloudTalkApplication.Companion.getSharedApplication())
                     .getResources().getString(R.string.nc_guest);
             }
 
+            // absolute fallback to prevent NPE deference
+            if (displayName == null) {
+                displayName = "Guest";
+            }
+
             ImageViewExtensionsKt.loadAvatar(holder.binding.avatarView, user, displayName, true);
         } else if (participant.getCalculatedActorType() == Participant.ActorType.USERS ||
             PARTICIPANT_SOURCE_USERS.equals(participant.getSource())) {

+ 20 - 1
app/src/main/java/com/nextcloud/talk/adapters/items/NotificationSoundItem.java

@@ -28,6 +28,7 @@ import com.nextcloud.talk.R;
 import com.nextcloud.talk.databinding.RvItemNotificationSoundBinding;
 
 import java.util.List;
+import java.util.Objects;
 
 import eu.davidea.flexibleadapter.FlexibleAdapter;
 import eu.davidea.flexibleadapter.items.AbstractFlexibleItem;
@@ -54,7 +55,25 @@ public class NotificationSoundItem extends AbstractFlexibleItem<NotificationSoun
 
     @Override
     public boolean equals(Object o) {
-        return false;
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        NotificationSoundItem that = (NotificationSoundItem) o;
+
+        if (!Objects.equals(notificationSoundName, that.notificationSoundName)) {
+            return false;
+        }
+        return Objects.equals(notificationSoundUri, that.notificationSoundUri);
+    }
+
+    @Override
+    public int hashCode() {
+        int result = notificationSoundName != null ? notificationSoundName.hashCode() : 0;
+        return 31 * result + (notificationSoundUri != null ? notificationSoundUri.hashCode() : 0);
     }
 
     @Override

+ 55 - 51
app/src/main/java/com/nextcloud/talk/adapters/items/ParticipantItem.java

@@ -154,8 +154,11 @@ public class ParticipantItem extends AbstractFlexibleItem<ParticipantItem.Partic
             participant.getType() == Participant.ParticipantType.GUEST ||
             participant.getType() == Participant.ParticipantType.GUEST_MODERATOR) {
 
-            String displayName = NextcloudTalkApplication.Companion.getSharedApplication()
-                .getResources().getString(R.string.nc_guest);
+            String displayName = NextcloudTalkApplication
+                .Companion
+                .getSharedApplication()
+                .getResources()
+                .getString(R.string.nc_guest);
 
             if (!TextUtils.isEmpty(participant.getDisplayName())) {
                 displayName = participant.getDisplayName();
@@ -164,8 +167,11 @@ public class ParticipantItem extends AbstractFlexibleItem<ParticipantItem.Partic
             ImageViewExtensionsKt.loadGuestAvatar(holder.binding.avatarView, user, displayName, false);
 
         } else if (participant.getCalculatedActorType() == Participant.ActorType.USERS ||
-            participant.getSource().equals("users")) {
-            ImageViewExtensionsKt.loadAvatar(holder.binding.avatarView, user, participant.getCalculatedActorId(), true);
+            "users".equals(participant.getSource())) {
+            ImageViewExtensionsKt.loadAvatar(holder.binding.avatarView,
+                                             user,
+                                             participant.getCalculatedActorId(),
+                                             true);
         }
 
         Resources resources = NextcloudTalkApplication.Companion.getSharedApplication().getResources();
@@ -190,63 +196,61 @@ public class ParticipantItem extends AbstractFlexibleItem<ParticipantItem.Partic
             holder.binding.videoCallIcon.setVisibility(View.GONE);
         }
 
-        if (holder.binding.secondaryText != null) {
-            String userType = "";
-
-            switch (new EnumParticipantTypeConverter().convertToInt(participant.getType())) {
-                case 1:
-                    //userType = NextcloudTalkApplication.Companion.getSharedApplication().getString(R.string.nc_owner);
-                    //break;
-                case 2:
-                case 6: // Guest moderator
+        String userType = "";
+
+        switch (new EnumParticipantTypeConverter().convertToInt(participant.getType())) {
+            case 1:
+                //userType = NextcloudTalkApplication.Companion.getSharedApplication().getString(R.string.nc_owner);
+                //break;
+            case 2:
+            case 6: // Guest moderator
+                userType = NextcloudTalkApplication
+                    .Companion
+                    .getSharedApplication()
+                    .getString(R.string.nc_moderator);
+                break;
+            case 3:
+                userType = NextcloudTalkApplication
+                    .Companion
+                    .getSharedApplication()
+                    .getString(R.string.nc_user);
+                if (participant.getCalculatedActorType() == Participant.ActorType.GROUPS) {
                     userType = NextcloudTalkApplication
                         .Companion
                         .getSharedApplication()
-                        .getString(R.string.nc_moderator);
-                    break;
-                case 3:
+                        .getString(R.string.nc_group);
+                }
+                if (participant.getCalculatedActorType() == Participant.ActorType.CIRCLES) {
                     userType = NextcloudTalkApplication
                         .Companion
                         .getSharedApplication()
-                        .getString(R.string.nc_user);
-                    if (participant.getCalculatedActorType() == Participant.ActorType.GROUPS) {
-                        userType = NextcloudTalkApplication
-                            .Companion
-                            .getSharedApplication()
-                            .getString(R.string.nc_group);
-                    }
-                    if (participant.getCalculatedActorType() == Participant.ActorType.CIRCLES) {
-                        userType = NextcloudTalkApplication
-                            .Companion
-                            .getSharedApplication()
-                            .getString(R.string.nc_circle);
-                    }
-                    break;
-                case 4:
-                    userType = NextcloudTalkApplication.Companion.getSharedApplication().getString(R.string.nc_guest);
-                    if (participant.getCalculatedActorType() == Participant.ActorType.EMAILS) {
-                        userType = NextcloudTalkApplication
-                            .Companion
-                            .getSharedApplication()
-                            .getString(R.string.nc_email);
-                    }
-                    break;
-                case 5:
+                        .getString(R.string.nc_circle);
+                }
+                break;
+            case 4:
+                userType = NextcloudTalkApplication.Companion.getSharedApplication().getString(R.string.nc_guest);
+                if (participant.getCalculatedActorType() == Participant.ActorType.EMAILS) {
                     userType = NextcloudTalkApplication
                         .Companion
                         .getSharedApplication()
-                        .getString(R.string.nc_following_link);
-                    break;
-                default:
-                    break;
-            }
+                        .getString(R.string.nc_email);
+                }
+                break;
+            case 5:
+                userType = NextcloudTalkApplication
+                    .Companion
+                    .getSharedApplication()
+                    .getString(R.string.nc_following_link);
+                break;
+            default:
+                break;
+        }
 
-            if (!userType.equals(NextcloudTalkApplication
-                                     .Companion
-                                     .getSharedApplication()
-                                     .getString(R.string.nc_user))) {
-                holder.binding.secondaryText.setText("(" + userType + ")");
-            }
+        if (!userType.equals(NextcloudTalkApplication
+                                 .Companion
+                                 .getSharedApplication()
+                                 .getString(R.string.nc_user))) {
+            holder.binding.secondaryText.setText("(" + userType + ")");
         }
     }
 

+ 5 - 5
app/src/main/java/com/nextcloud/talk/callbacks/MentionAutocompleteCallback.java

@@ -60,12 +60,10 @@ public class MentionAutocompleteCallback implements AutocompleteCallback<Mention
     @OptIn(markerClass = kotlin.ExperimentalStdlibApi.class)
     @Override
     public boolean onPopupItemClicked(Editable editable, Mention item) {
-        int[] range = MagicCharPolicy.getQueryRange(editable);
+        MagicCharPolicy.TextSpan range = MagicCharPolicy.getQueryRange(editable);
         if (range == null) {
             return false;
         }
-        int start = range[0];
-        int end = range[1];
         String replacement = item.getLabel();
 
         StringBuilder replacementStringBuilder = new StringBuilder(item.getLabel());
@@ -73,7 +71,7 @@ public class MentionAutocompleteCallback implements AutocompleteCallback<Mention
             replacementStringBuilder.delete(emojiRange.range.getStart(), emojiRange.range.getEndInclusive());
         }
 
-        editable.replace(start, end, replacementStringBuilder.toString() + " ");
+        editable.replace(range.getStart(), range.getEnd(), replacementStringBuilder + " ");
         Spans.MentionChipSpan mentionChipSpan =
             new Spans.MentionChipSpan(DisplayUtils.getDrawableForMentionChipSpan(context,
                                                                                  item.getId(),
@@ -85,7 +83,9 @@ public class MentionAutocompleteCallback implements AutocompleteCallback<Mention
                                                                                  viewThemeUtils),
                                       BetterImageSpan.ALIGN_CENTER,
                                       item.getId(), item.getLabel());
-        editable.setSpan(mentionChipSpan, start, start + replacementStringBuilder.toString().length(),
+        editable.setSpan(mentionChipSpan,
+                         range.getStart(),
+                         range.getStart() + replacementStringBuilder.length(),
                          Spanned.SPAN_INCLUSIVE_INCLUSIVE);
 
 

+ 1 - 2
app/src/main/java/com/nextcloud/talk/components/filebrowser/models/DavResponse.java

@@ -74,8 +74,7 @@ public class DavResponse {
         final Object $response = this.getResponse();
         result = result * PRIME + ($response == null ? 43 : $response.hashCode());
         final Object $data = this.getData();
-        result = result * PRIME + ($data == null ? 43 : $data.hashCode());
-        return result;
+        return result * PRIME + ($data == null ? 43 : $data.hashCode());
     }
 
     public String toString() {

+ 14 - 30
app/src/main/java/com/nextcloud/talk/components/filebrowser/webdav/DavUtils.java

@@ -22,8 +22,6 @@
 
 package com.nextcloud.talk.components.filebrowser.webdav;
 
-import android.util.Log;
-
 import com.nextcloud.talk.components.filebrowser.models.properties.NCEncrypted;
 import com.nextcloud.talk.components.filebrowser.models.properties.NCPermission;
 import com.nextcloud.talk.components.filebrowser.models.properties.NCPreview;
@@ -31,14 +29,10 @@ import com.nextcloud.talk.components.filebrowser.models.properties.OCFavorite;
 import com.nextcloud.talk.components.filebrowser.models.properties.OCId;
 import com.nextcloud.talk.components.filebrowser.models.properties.OCSize;
 
-import java.lang.reflect.Field;
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 
 import at.bitfire.dav4jvm.Property;
-import at.bitfire.dav4jvm.PropertyFactory;
 import at.bitfire.dav4jvm.PropertyRegistry;
 import at.bitfire.dav4jvm.property.CreationDate;
 import at.bitfire.dav4jvm.property.DisplayName;
@@ -49,7 +43,6 @@ import at.bitfire.dav4jvm.property.GetLastModified;
 import at.bitfire.dav4jvm.property.ResourceType;
 
 public class DavUtils {
-    private static final String TAG = "DavUtils";
 
     public static final String OC_NAMESPACE = "http://owncloud.org/ns";
     public static final String NC_NAMESPACE = "http://nextcloud.org/ns";
@@ -66,15 +59,16 @@ public class DavUtils {
     public static final String EXTENDED_PROPERTY_UNREAD_COMMENTS = "comments-unread";
     public static final String EXTENDED_PROPERTY_HAS_PREVIEW = "has-preview";
     public static final String EXTENDED_PROPERTY_NOTE = "note";
-    public static final String TRASHBIN_FILENAME = "trashbin-filename";
-    public static final String TRASHBIN_ORIGINAL_LOCATION = "trashbin-original-location";
-    public static final String TRASHBIN_DELETION_TIME = "trashbin-deletion-time";
 
-    public static final String PROPERTY_QUOTA_USED_BYTES = "quota-used-bytes";
-    public static final String PROPERTY_QUOTA_AVAILABLE_BYTES = "quota-available-bytes";
+    // public static final String TRASHBIN_FILENAME = "trashbin-filename";
+    // public static final String TRASHBIN_ORIGINAL_LOCATION = "trashbin-original-location";
+    // public static final String TRASHBIN_DELETION_TIME = "trashbin-deletion-time";
+
+    // public static final String PROPERTY_QUOTA_USED_BYTES = "quota-used-bytes";
+    // public static final String PROPERTY_QUOTA_AVAILABLE_BYTES = "quota-available-bytes";
 
     static Property.Name[] getAllPropSet() {
-        List<Property.Name> props = new ArrayList<>();
+        List<Property.Name> props = new ArrayList<>(20);
 
         props.add(DisplayName.NAME);
         props.add(GetContentType.NAME);
@@ -104,22 +98,12 @@ public class DavUtils {
 
     public static void registerCustomFactories() {
         PropertyRegistry propertyRegistry = PropertyRegistry.INSTANCE;
-        try {
-            Field factories = propertyRegistry.getClass().getDeclaredField("factories");
-            factories.setAccessible(true);
-            Map<Property.Name, PropertyFactory> reflectionMap = (HashMap<Property.Name,
-                    PropertyFactory>) factories.get(propertyRegistry);
-
-            reflectionMap.put(OCId.NAME, new OCId.Factory());
-            reflectionMap.put(NCPreview.NAME, new NCPreview.Factory());
-            reflectionMap.put(NCEncrypted.NAME, new NCEncrypted.Factory());
-            reflectionMap.put(OCFavorite.NAME, new OCFavorite.Factory());
-            reflectionMap.put(OCSize.NAME, new OCSize.Factory());
-            reflectionMap.put(NCPermission.NAME, new NCPermission.Factory());
-
-            factories.set(propertyRegistry, reflectionMap);
-        } catch (NoSuchFieldException | IllegalAccessException e) {
-            Log.w(TAG, "Error registering custom factories", e);
-        }
+
+        propertyRegistry.register(new OCId.Factory());
+        propertyRegistry.register(new NCPreview.Factory());
+        propertyRegistry.register(new NCEncrypted.Factory());
+        propertyRegistry.register(new OCFavorite.Factory());
+        propertyRegistry.register(new OCSize.Factory());
+        propertyRegistry.register(new NCPermission.Factory());
     }
 }

+ 1 - 1
app/src/main/java/com/nextcloud/talk/components/filebrowser/webdav/ReadFilesystemOperation.java

@@ -69,7 +69,6 @@ public class ReadFilesystemOperation {
         DavResponse davResponse = new DavResponse();
         final List<Response> memberElements = new ArrayList<>();
         final Response[] rootElement = new Response[1];
-        final List<BrowserFile> remoteFiles = new ArrayList<>();
 
         try {
             new DavResource(okHttpClient, HttpUrl.parse(url)).propfind(depth, DavUtils.getAllPropSet(),
@@ -94,6 +93,7 @@ public class ReadFilesystemOperation {
             Log.w(TAG, "Error reading remote path");
         }
 
+        final List<BrowserFile> remoteFiles = new ArrayList<>(1 + memberElements.size());
         remoteFiles.add(BrowserFile.Companion.getModelFromResponse(rootElement[0],
                 rootElement[0].getHref().toString().substring(basePath.length())));
         for (Response memberElement : memberElements) {

+ 1 - 3
app/src/main/java/com/nextcloud/talk/dagger/modules/RestModule.java

@@ -249,9 +249,7 @@ public class RestModule {
                     .method(original.method(), original.body())
                     .build();
 
-            Response response = chain.proceed(request);
-
-            return response;
+            return chain.proceed(request);
         }
     }
 

+ 1 - 2
app/src/main/java/com/nextcloud/talk/events/EventStatus.java

@@ -89,8 +89,7 @@ public class EventStatus {
         result = result * PRIME + (int) ($userId >>> 32 ^ $userId);
         final Object $eventType = this.getEventType();
         result = result * PRIME + ($eventType == null ? 43 : $eventType.hashCode());
-        result = result * PRIME + (this.isAllGood() ? 79 : 97);
-        return result;
+        return result * PRIME + (this.isAllGood() ? 79 : 97);
     }
 
     public String toString() {

+ 1 - 2
app/src/main/java/com/nextcloud/talk/events/MoreMenuClickEvent.java

@@ -58,8 +58,7 @@ public class MoreMenuClickEvent {
         final int PRIME = 59;
         int result = 1;
         final Object $conversation = this.getConversation();
-        result = result * PRIME + ($conversation == null ? 43 : $conversation.hashCode());
-        return result;
+        return result * PRIME + ($conversation == null ? 43 : $conversation.hashCode());
     }
 
     public String toString() {

+ 1 - 2
app/src/main/java/com/nextcloud/talk/events/NetworkEvent.java

@@ -56,8 +56,7 @@ public class NetworkEvent {
         final int PRIME = 59;
         int result = 1;
         final Object $networkConnectionEvent = this.getNetworkConnectionEvent();
-        result = result * PRIME + ($networkConnectionEvent == null ? 43 : $networkConnectionEvent.hashCode());
-        return result;
+        return result * PRIME + ($networkConnectionEvent == null ? 43 : $networkConnectionEvent.hashCode());
     }
 
     public String toString() {

+ 1 - 2
app/src/main/java/com/nextcloud/talk/events/UserMentionClickEvent.java

@@ -56,8 +56,7 @@ public class UserMentionClickEvent {
         final int PRIME = 59;
         int result = 1;
         final Object $userId = this.getUserId();
-        result = result * PRIME + ($userId == null ? 43 : $userId.hashCode());
-        return result;
+        return result * PRIME + ($userId == null ? 43 : $userId.hashCode());
     }
 
     public String toString() {

+ 1 - 2
app/src/main/java/com/nextcloud/talk/events/WebSocketCommunicationEvent.java

@@ -75,8 +75,7 @@ public class WebSocketCommunicationEvent {
         final Object $type = this.getType();
         result = result * PRIME + ($type == null ? 43 : $type.hashCode());
         final Object $hashMap = this.getHashMap();
-        result = result * PRIME + ($hashMap == null ? 43 : $hashMap.hashCode());
-        return result;
+        return result * PRIME + ($hashMap == null ? 43 : $hashMap.hashCode());
     }
 
     public String toString() {

+ 1 - 2
app/src/main/java/com/nextcloud/talk/models/ImportAccount.java

@@ -98,8 +98,7 @@ public class ImportAccount {
         final Object $token = this.getToken();
         result = result * PRIME + ($token == null ? 43 : $token.hashCode());
         final Object $baseUrl = this.getBaseUrl();
-        result = result * PRIME + ($baseUrl == null ? 43 : $baseUrl.hashCode());
-        return result;
+        return result * PRIME + ($baseUrl == null ? 43 : $baseUrl.hashCode());
     }
 
     public String toString() {

+ 2 - 1
app/src/main/java/com/nextcloud/talk/presenters/MentionAutocompletePresenter.java

@@ -147,7 +147,8 @@ public class MentionAutocompletePresenter extends RecyclerViewPresenter<Mention>
                         if (mentionsList.size() == 0) {
                             adapter.clear();
                         } else {
-                            List<AbstractFlexibleItem> internalAbstractFlexibleItemList = new ArrayList<>();
+                            List<AbstractFlexibleItem> internalAbstractFlexibleItemList =
+                                new ArrayList<>(mentionsList.size());
                             for (Mention mention : mentionsList) {
                                 internalAbstractFlexibleItemList.add(
                                         new MentionAutocompleteItem(

+ 21 - 7
app/src/main/java/com/nextcloud/talk/ui/StatusDrawable.java

@@ -42,9 +42,9 @@ import androidx.core.content.res.ResourcesCompat;
  */
 public class StatusDrawable extends Drawable {
     private String text;
-    private @DrawableRes int icon = -1;
+    private StatusDrawableType icon = StatusDrawableType.UNDEFINED;
     private Paint textPaint;
-    private int backgroundColor;
+    private final int backgroundColor;
     private final float radius;
     private Context context;
 
@@ -54,17 +54,17 @@ public class StatusDrawable extends Drawable {
 
 
         if ("dnd".equals(status)) {
-            icon = R.drawable.ic_user_status_dnd;
+            icon = StatusDrawableType.DND;
             this.context = context;
         } else if (TextUtils.isEmpty(statusIcon) && status != null) {
             switch (status) {
                 case "online":
-                    icon = R.drawable.online_status;
+                    icon = StatusDrawableType.ONLINE;
                     this.context = context;
                     break;
 
                 case "away":
-                    icon = R.drawable.ic_user_status_away;
+                    icon = StatusDrawableType.AWAY;
                     this.context = context;
                     break;
 
@@ -95,7 +95,7 @@ public class StatusDrawable extends Drawable {
             canvas.drawText(text, radius, radius - ((textPaint.descent() + textPaint.ascent()) / 2), textPaint);
         }
 
-        if (icon != -1) {
+        if (icon != StatusDrawableType.UNDEFINED) {
 
             Paint backgroundPaint = new Paint();
             backgroundPaint.setStyle(Paint.Style.FILL);
@@ -104,7 +104,7 @@ public class StatusDrawable extends Drawable {
 
             canvas.drawCircle(radius, radius, radius, backgroundPaint);
 
-            Drawable drawable = ResourcesCompat.getDrawable(context.getResources(), icon, null);
+            Drawable drawable = ResourcesCompat.getDrawable(context.getResources(), icon.drawableId, null);
 
             if (drawable != null) {
                 drawable.setBounds(0,
@@ -130,4 +130,18 @@ public class StatusDrawable extends Drawable {
     public int getOpacity() {
         return PixelFormat.TRANSLUCENT;
     }
+
+    private enum StatusDrawableType {
+        DND(R.drawable.ic_user_status_dnd),
+        ONLINE(R.drawable.online_status),
+        AWAY(R.drawable.ic_user_status_away),
+        UNDEFINED(-1);
+
+        @DrawableRes
+        private final int drawableId;
+
+        StatusDrawableType(int drawableId) {
+            this.drawableId = drawableId;
+        }
+    }
 }

+ 0 - 1
app/src/main/java/com/nextcloud/talk/ui/dialog/SortingOrderDialogFragment.java

@@ -177,7 +177,6 @@ public class SortingOrderDialogFragment extends DialogFragment implements View.O
         binding.cancel.setOnClickListener(view -> dismiss());
 
         for (View view : taggedViews) {
-            Log.i("SortOrder", "view="+view.getTag().toString());
             view.setOnClickListener(this);
         }
     }

+ 3 - 6
app/src/main/java/com/nextcloud/talk/utils/AuthenticatorService.java

@@ -62,8 +62,7 @@ public class AuthenticatorService extends Service {
         }
 
         @Override
-        public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options)
-                throws NetworkErrorException {
+        public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {
             return null;
         }
 
@@ -73,8 +72,7 @@ public class AuthenticatorService extends Service {
         }
 
         @Override
-        public Bundle hasFeatures(AccountAuthenticatorResponse response,
-                                  Account account, String[] features) {
+        public Bundle hasFeatures(AccountAuthenticatorResponse response, Account account, String[] features) {
             return null;
         }
 
@@ -82,7 +80,6 @@ public class AuthenticatorService extends Service {
         public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) {
             return null;
         }
-
     }
 
     protected Authenticator getAuthenticator() {
@@ -95,7 +92,7 @@ public class AuthenticatorService extends Service {
 
     @Override
     public IBinder onBind(Intent intent) {
-        if (intent.getAction().equals(AccountManager.ACTION_AUTHENTICATOR_INTENT)) {
+        if (AccountManager.ACTION_AUTHENTICATOR_INTENT.equals(intent.getAction())) {
             return getAuthenticator().getIBinder();
         } else {
             return null;

+ 8 - 5
app/src/main/java/com/nextcloud/talk/utils/DeviceUtils.java

@@ -35,14 +35,17 @@ public class DeviceUtils {
     private static final String TAG = "DeviceUtils";
 
     public static void ignoreSpecialBatteryFeatures() {
-        if (Build.MANUFACTURER.equalsIgnoreCase("xiaomi") || Build.MANUFACTURER.equalsIgnoreCase("meizu")) {
+        if ("xiaomi".equalsIgnoreCase(Build.MANUFACTURER) || "meizu".equalsIgnoreCase(Build.MANUFACTURER)) {
             try {
                 @SuppressLint("PrivateApi") Class<?> appOpsUtilsClass = Class.forName("android.miui.AppOpsUtils");
                 if (appOpsUtilsClass != null) {
                     Method setApplicationAutoStartMethod = appOpsUtilsClass.getMethod("setApplicationAutoStart", Context
                             .class, String.class, Boolean.TYPE);
                     if (setApplicationAutoStartMethod != null) {
-                        Context applicationContext = NextcloudTalkApplication.Companion.getSharedApplication().getApplicationContext();
+                        Context applicationContext = NextcloudTalkApplication
+                            .Companion
+                            .getSharedApplication()
+                            .getApplicationContext();
                         setApplicationAutoStartMethod.invoke(appOpsUtilsClass, applicationContext, applicationContext
                                 .getPackageName(), Boolean.TRUE);
                     }
@@ -56,10 +59,10 @@ public class DeviceUtils {
             } catch (InvocationTargetException e) {
                 Log.e(TAG, "InvocationTargetException");
             }
-        } else if (Build.MANUFACTURER.equalsIgnoreCase("huawei")) {
+        } else if ("huawei".equalsIgnoreCase(Build.MANUFACTURER)) {
             try {
-                @SuppressLint("PrivateApi") Class<?> protectAppControlClass = Class.forName("com.huawei.systemmanager.optimize.process" +
-                        ".ProtectAppControl");
+                @SuppressLint("PrivateApi") Class<?> protectAppControlClass = Class.forName(
+                    "com.huawei.systemmanager.optimize.process.ProtectAppControl");
                 if (protectAppControlClass != null) {
                     Context applicationContext = NextcloudTalkApplication.Companion.getSharedApplication().getApplicationContext();
 

+ 7 - 7
app/src/main/java/com/nextcloud/talk/utils/DisplayUtils.java

@@ -369,8 +369,6 @@ public class DisplayUtils {
                 decor.setSystemUiVisibility(0);
             }
             window.setStatusBarColor(color);
-        } else if (isLightTheme) {
-            window.setStatusBarColor(Color.BLACK);
         }
     }
 
@@ -380,7 +378,7 @@ public class DisplayUtils {
      * @param color the color
      * @return true if primaryColor is lighter than MAX_LIGHTNESS
      */
-    @SuppressWarnings("correctness")
+    @SuppressWarnings("CLI_CONSTANT_LIST_INDEX")
     public static boolean lightTheme(int color) {
         float[] hsl = colorToHSL(color);
 
@@ -455,10 +453,12 @@ public class DisplayUtils {
             avatarId = user.getUsername();
         }
 
-        if (deleteCache) {
-            ImageViewExtensionsKt.replaceAvatar(avatarImageView, user, avatarId, true);
-        } else {
-            ImageViewExtensionsKt.loadAvatar(avatarImageView, user, avatarId, true);
+        if (avatarId != null) {
+            if (deleteCache) {
+                ImageViewExtensionsKt.replaceAvatar(avatarImageView, user, avatarId, true);
+            } else {
+                ImageViewExtensionsKt.loadAvatar(avatarImageView, user, avatarId, true);
+            }
         }
     }
 

+ 30 - 16
app/src/main/java/com/nextcloud/talk/utils/MagicCharPolicy.java

@@ -37,43 +37,57 @@ public class MagicCharPolicy implements AutocompletePolicy {
     }
 
     @Nullable
-    public static int[] getQueryRange(Spannable text) {
+    public static TextSpan getQueryRange(Spannable text) {
         QuerySpan[] span = text.getSpans(0, text.length(), QuerySpan.class);
-        if (span == null || span.length == 0) return null;
-        if (span.length > 1) {
-            // Do absolutely nothing
+        if (span == null || span.length == 0) {
+            return null;
+        } else  {
+            QuerySpan sp = span[0];
+            return new TextSpan(text.getSpanStart(sp), text.getSpanEnd(sp));
         }
-        QuerySpan sp = span[0];
-        return new int[]{text.getSpanStart(sp), text.getSpanEnd(sp)};
     }
 
-    private int[] checkText(Spannable text, int cursorPos) {
+    private TextSpan checkText(Spannable text, int cursorPos) {
         if (text.length() == 0) {
             return null;
         }
 
-        int[] span = new int[2];
         Pattern pattern = Pattern.compile("@+\\S*", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);
         Matcher matcher = pattern.matcher(text);
 
         while (matcher.find()) {
-            if (cursorPos >= matcher.start() && cursorPos <= matcher.end()) {
-                span[0] = matcher.start();
-                span[1] = matcher.end();
-                if (text.subSequence(matcher.start(), matcher.end()).charAt(0) == character) {
-                    return span;
-                }
+            if (cursorPos >= matcher.start() && cursorPos <= matcher.end() &&
+                text.subSequence(matcher.start(), matcher.end()).charAt(0) == character) {
+                return new TextSpan(matcher.start(), matcher.end());
             }
         }
 
         return null;
     }
 
+    public static class TextSpan {
+        int start;
+        int end;
+
+        public TextSpan(int start, int end) {
+            this.start = start;
+            this.end = end;
+        }
+
+        public int getStart() {
+            return start;
+        }
+
+        public int getEnd() {
+            return end;
+        }
+    }
+
     @Override
     public boolean shouldShowPopup(Spannable text, int cursorPos) {
-        int[] show = checkText(text, cursorPos);
+        TextSpan show = checkText(text, cursorPos);
         if (show != null) {
-            text.setSpan(new QuerySpan(), show[0], show[1], Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+            text.setSpan(new QuerySpan(), show.getStart(), show.getEnd(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
             return true;
         }
         return false;

+ 7 - 7
app/src/main/java/com/nextcloud/talk/utils/preferences/preferencestorage/DatabaseStorageModule.java

@@ -78,7 +78,7 @@ public class DatabaseStorageModule implements StorageModule {
 
     @Override
     public void saveBoolean(String key, boolean value) {
-        if (key.equals("call_notifications")) {
+        if ("call_notifications".equals(key)) {
             int apiVersion = ApiUtils.getConversationApiVersion(conversationUser, new int[]{4});
             ncApi.notificationCalls(ApiUtils.getCredentials(conversationUser.getUsername(),
                                                             conversationUser.getToken()),
@@ -112,7 +112,7 @@ public class DatabaseStorageModule implements StorageModule {
                           );
         }
 
-        if (!key.equals("conversation_lobby")) {
+        if (!"conversation_lobby".equals(key)) {
             arbitraryStorageManager.storeStorageSetting(accountIdentifier,
                                                         key,
                                                         Boolean.toString(value),
@@ -124,7 +124,7 @@ public class DatabaseStorageModule implements StorageModule {
 
     @Override
     public void saveString(String key, String value) {
-        if (key.equals("message_expire_key")) {
+        if ("message_expire_key".equals(key)) {
             int apiVersion = ApiUtils.getConversationApiVersion(conversationUser, new int[]{4});
 
             String trimmedValue = value.replace("expire_", "");
@@ -163,7 +163,7 @@ public class DatabaseStorageModule implements StorageModule {
                     }
                 });
 
-        } else if (key.equals("message_notification_level")) {
+        } else if ("message_notification_level".equals(key)) {
             if (CapabilitiesUtilNew.hasSpreedFeatureCapability(conversationUser, "notification-levels")) {
                 if (!TextUtils.isEmpty(messageNotificationLevel) && !messageNotificationLevel.equals(value)) {
                     int intValue;
@@ -232,7 +232,7 @@ public class DatabaseStorageModule implements StorageModule {
 
     @Override
     public boolean getBoolean(String key, boolean defaultVal) {
-        if (key.equals("conversation_lobby")) {
+        if ("conversation_lobby".equals(key)) {
             return lobbyValue;
         } else {
             return arbitraryStorageManager
@@ -244,7 +244,7 @@ public class DatabaseStorageModule implements StorageModule {
 
     @Override
     public String getString(String key, String defaultVal) {
-        if (key.equals("message_expire_key")) {
+        if ("message_expire_key".equals(key)) {
             switch (messageExpiration) {
                 case 2419200:
                     return "expire_2419200";
@@ -259,7 +259,7 @@ public class DatabaseStorageModule implements StorageModule {
                 default:
                     return "expire_0";
             }
-        } else if (key.equals("message_notification_level")) {
+        } else if ("message_notification_level".equals(key)) {
             return messageNotificationLevel;
         } else {
             return arbitraryStorageManager

+ 1 - 3
app/src/main/java/com/nextcloud/talk/utils/text/Spans.java

@@ -87,13 +87,11 @@ public class Spans {
             final Object $id = this.getId();
             result = result * PRIME + ($id == null ? 43 : $id.hashCode());
             final Object $label = this.getLabel();
-            result = result * PRIME + ($label == null ? 43 : $label.hashCode());
-            return result;
+            return result * PRIME + ($label == null ? 43 : $label.hashCode());
         }
 
         public String toString() {
             return "Spans.MentionChipSpan(id=" + this.getId() + ", label=" + this.getLabel() + ")";
         }
     }
-
 }

+ 1 - 0
app/src/main/java/com/nextcloud/talk/webrtc/Globals.java

@@ -4,4 +4,5 @@ public class Globals {
     public static final String ROOM_TOKEN = "roomToken";
 
     public static final String TARGET_PARTICIPANTS = "participants";
+    public static final String TARGET_ROOM = "room";
 }

+ 199 - 132
app/src/main/java/com/nextcloud/talk/webrtc/MagicWebSocketInstance.java

@@ -35,6 +35,7 @@ import com.nextcloud.talk.models.json.websocket.BaseWebSocketMessage;
 import com.nextcloud.talk.models.json.websocket.ByeWebSocketMessage;
 import com.nextcloud.talk.models.json.websocket.CallOverallWebSocketMessage;
 import com.nextcloud.talk.models.json.websocket.ErrorOverallWebSocketMessage;
+import com.nextcloud.talk.models.json.websocket.ErrorWebSocketMessage;
 import com.nextcloud.talk.models.json.websocket.EventOverallWebSocketMessage;
 import com.nextcloud.talk.models.json.websocket.HelloResponseOverallWebSocketMessage;
 import com.nextcloud.talk.models.json.websocket.JoinedRoomOverallWebSocketMessage;
@@ -54,6 +55,7 @@ import java.util.Map;
 
 import javax.inject.Inject;
 
+import androidx.annotation.NonNull;
 import autodagger.AutoInjector;
 import okhttp3.OkHttpClient;
 import okhttp3.Request;
@@ -66,12 +68,12 @@ import static com.nextcloud.talk.models.json.participants.Participant.ActorType.
 import static com.nextcloud.talk.models.json.participants.Participant.ActorType.USERS;
 import static com.nextcloud.talk.webrtc.Globals.ROOM_TOKEN;
 import static com.nextcloud.talk.webrtc.Globals.TARGET_PARTICIPANTS;
+import static com.nextcloud.talk.webrtc.Globals.TARGET_ROOM;
 
 @AutoInjector(NextcloudTalkApplication.class)
 public class MagicWebSocketInstance extends WebSocketListener {
     private static final String TAG = "MagicWebSocketInstance";
 
-
     @Inject
     OkHttpClient okHttpClient;
 
@@ -81,18 +83,17 @@ public class MagicWebSocketInstance extends WebSocketListener {
     @Inject
     Context context;
 
-    private User conversationUser;
-    private String webSocketTicket;
+    private final User conversationUser;
+    private final String webSocketTicket;
     private String resumeId;
     private String sessionId;
     private boolean hasMCU;
     private boolean connected;
-    private WebSocketConnectionHelper webSocketConnectionHelper;
+    private final WebSocketConnectionHelper webSocketConnectionHelper;
     private WebSocket internalWebSocket;
-    private String connectionUrl;
+    private final String connectionUrl;
 
     private String currentRoomToken;
-    private int restartCount = 0;
     private boolean reconnecting = false;
 
     private HashMap<String, Participant> usersHashMap;
@@ -121,9 +122,13 @@ public class MagicWebSocketInstance extends WebSocketListener {
     private void sendHello() {
         try {
             if (TextUtils.isEmpty(resumeId)) {
-                internalWebSocket.send(LoganSquare.serialize(webSocketConnectionHelper.getAssembledHelloModel(conversationUser, webSocketTicket)));
+                internalWebSocket.send(
+                    LoganSquare.serialize(webSocketConnectionHelper
+                                              .getAssembledHelloModel(conversationUser, webSocketTicket)));
             } else {
-                internalWebSocket.send(LoganSquare.serialize(webSocketConnectionHelper.getAssembledHelloModelForResume(resumeId)));
+                internalWebSocket.send(
+                    LoganSquare.serialize(webSocketConnectionHelper
+                                              .getAssembledHelloModelForResume(resumeId)));
             }
         } catch (IOException e) {
             Log.e(TAG, "Failed to serialize hello model");
@@ -152,149 +157,208 @@ public class MagicWebSocketInstance extends WebSocketListener {
         resumeId = "";
     }
 
-    public void restartWebSocket() {
+    public final void restartWebSocket() {
         reconnecting = true;
 
         // TODO when improving logging, keep in mind this issue: https://github.com/nextcloud/talk-android/issues/1013
         Log.d(TAG, "restartWebSocket: " + connectionUrl);
         Request request = new Request.Builder().url(connectionUrl).build();
         okHttpClient.newWebSocket(request, this);
-        restartCount++;
     }
 
     @Override
-    public void onMessage(WebSocket webSocket, String text) {
+    public void onMessage(@NonNull WebSocket webSocket, @NonNull String text) {
         if (webSocket == internalWebSocket) {
-            Log.d(TAG, "Receiving : " + webSocket.toString() + " " + text);
+            Log.d(TAG, "Receiving : " + webSocket + " " + text);
 
             try {
                 BaseWebSocketMessage baseWebSocketMessage = LoganSquare.parse(text, BaseWebSocketMessage.class);
                 String messageType = baseWebSocketMessage.getType();
-                switch (messageType) {
-                    case "hello":
-                        connected = true;
-                        reconnecting = false;
-                        restartCount = 0;
-                        String oldResumeId = resumeId;
-                        HelloResponseOverallWebSocketMessage helloResponseWebSocketMessage = LoganSquare.parse(text, HelloResponseOverallWebSocketMessage.class);
-                        resumeId = helloResponseWebSocketMessage.getHelloResponseWebSocketMessage().getResumeId();
-                        sessionId = helloResponseWebSocketMessage.getHelloResponseWebSocketMessage().getSessionId();
-                        hasMCU = helloResponseWebSocketMessage.getHelloResponseWebSocketMessage().serverHasMCUSupport();
-
-                        for (int i = 0; i < messagesQueue.size(); i++) {
-                            webSocket.send(messagesQueue.get(i));
-                        }
+                if (messageType != null) {
+                    switch (messageType) {
+                        case "hello":
+                            processHelloMessage(webSocket, text);
+                            break;
+                        case "error":
+                            processErrorMessage(webSocket, text);
+                            break;
+                        case "room":
+                            processJoinedRoomMessage(text);
+                            break;
+                        case "event":
+                            processEventMessage(text);
+                            break;
+                        case "message":
+                            processMessage(text);
+                            break;
+                        case "bye":
+                            connected = false;
+                            resumeId = "";
+                            break;
+                        default:
+                            break;
+                    }
+                } else {
+                    Log.e(TAG, "Received message with type: null");
+                }
+            } catch (IOException e) {
+                Log.e(TAG, "Failed to recognize WebSocket message", e);
+            }
+        }
+    }
 
-                        messagesQueue = new ArrayList<>();
-                        HashMap<String, String> helloHasHap = new HashMap<>();
-                        if (!TextUtils.isEmpty(oldResumeId)) {
-                            helloHasHap.put("oldResumeId", oldResumeId);
-                        } else {
-                            currentRoomToken = "";
-                        }
+    private void processMessage(String text) throws IOException {
+        CallOverallWebSocketMessage callOverallWebSocketMessage =
+            LoganSquare.parse(text, CallOverallWebSocketMessage.class);
+
+        if (callOverallWebSocketMessage.getCallWebSocketMessage() != null) {
+            NCSignalingMessage ncSignalingMessage = callOverallWebSocketMessage
+                .getCallWebSocketMessage()
+                .getNcSignalingMessage();
+            if (ncSignalingMessage != null && TextUtils.isEmpty(ncSignalingMessage.getFrom()) &&
+                callOverallWebSocketMessage.getCallWebSocketMessage().getSenderWebSocketMessage() != null) {
+                ncSignalingMessage.setFrom(
+                    callOverallWebSocketMessage.getCallWebSocketMessage().getSenderWebSocketMessage().getSessionId());
+            }
 
-                        if (!TextUtils.isEmpty(currentRoomToken)) {
-                            helloHasHap.put(ROOM_TOKEN, currentRoomToken);
-                        }
-                        eventBus.post(new WebSocketCommunicationEvent("hello", helloHasHap));
-                        break;
-                    case "error":
-                        Log.e(TAG, "Received error: " + text);
-                        ErrorOverallWebSocketMessage errorOverallWebSocketMessage = LoganSquare.parse(text, ErrorOverallWebSocketMessage.class);
-                        if (("no_such_session").equals(errorOverallWebSocketMessage.getErrorWebSocketMessage().getCode())) {
-                            Log.d(TAG, "WebSocket " + webSocket.hashCode() + " resumeID " + resumeId + " expired");
-                            resumeId = "";
-                            currentRoomToken = "";
-                            restartWebSocket();
-                        } else if (("hello_expected").equals(errorOverallWebSocketMessage.getErrorWebSocketMessage().getCode())) {
-                            restartWebSocket();
-                        }
+            signalingMessageReceiver.process(ncSignalingMessage);
+        }
+    }
 
-                        break;
-                    case "room":
-                        JoinedRoomOverallWebSocketMessage joinedRoomOverallWebSocketMessage = LoganSquare.parse(text, JoinedRoomOverallWebSocketMessage.class);
-                        currentRoomToken = joinedRoomOverallWebSocketMessage.getRoomWebSocketMessage().getRoomId();
-                        if (joinedRoomOverallWebSocketMessage.getRoomWebSocketMessage().getRoomPropertiesWebSocketMessage() != null && !TextUtils.isEmpty(currentRoomToken)) {
-                            sendRoomJoinedEvent();
+    private void processEventMessage(String text) throws IOException {
+        EventOverallWebSocketMessage eventOverallWebSocketMessage =
+            LoganSquare.parse(text, EventOverallWebSocketMessage.class);
+        if (eventOverallWebSocketMessage.getEventMap() != null) {
+            String target = (String) eventOverallWebSocketMessage.getEventMap().get("target");
+            if (target != null) {
+                switch (target) {
+                    case TARGET_ROOM:
+                        if ("message".equals(eventOverallWebSocketMessage.getEventMap().get("type"))) {
+                            processRoomMessageMessage(eventOverallWebSocketMessage);
+                        } else if ("join".equals(eventOverallWebSocketMessage.getEventMap().get("type"))) {
+                            processRoomJoinMessage(eventOverallWebSocketMessage);
                         }
                         break;
-                    case "event":
-                        EventOverallWebSocketMessage eventOverallWebSocketMessage = LoganSquare.parse(text, EventOverallWebSocketMessage.class);
-                        if (eventOverallWebSocketMessage.getEventMap() != null) {
-                            String target = (String) eventOverallWebSocketMessage.getEventMap().get("target");
-                            switch (target) {
-                                case "room":
-                                    if (eventOverallWebSocketMessage.getEventMap().get("type").equals("message")) {
-                                        Map<String, Object> messageHashMap =
-                                            (Map<String, Object>) eventOverallWebSocketMessage.getEventMap().get("message");
-                                        if (messageHashMap.containsKey("data")) {
-                                            Map<String, Object> dataHashMap = (Map<String, Object>) messageHashMap.get(
-                                                "data");
-                                            if (dataHashMap.containsKey("chat")) {
-                                                boolean shouldRefreshChat;
-                                                Map<String, Object> chatMap = (Map<String, Object>) dataHashMap.get("chat");
-                                                if (chatMap.containsKey("refresh")) {
-                                                    shouldRefreshChat = (boolean) chatMap.get("refresh");
-                                                    if (shouldRefreshChat) {
-                                                        HashMap<String, String> refreshChatHashMap = new HashMap<>();
-                                                        refreshChatHashMap.put(BundleKeys.KEY_ROOM_TOKEN, (String) messageHashMap.get("roomid"));
-                                                        refreshChatHashMap.put(BundleKeys.KEY_INTERNAL_USER_ID, Long.toString(conversationUser.getId()));
-                                                        eventBus.post(new WebSocketCommunicationEvent("refreshChat", refreshChatHashMap));
-                                                    }
-                                                }
-                                            }
-                                        }
-                                    } else if (eventOverallWebSocketMessage.getEventMap().get("type").equals("join")) {
-                                        List<HashMap<String, Object>> joinEventList = (List<HashMap<String, Object>>) eventOverallWebSocketMessage.getEventMap().get("join");
-                                        HashMap<String, Object> internalHashMap;
-                                        Participant participant;
-                                        for (int i = 0; i < joinEventList.size(); i++) {
-                                            internalHashMap = joinEventList.get(i);
-                                            HashMap<String, Object> userMap = (HashMap<String, Object>) internalHashMap.get("user");
-                                            participant = new Participant();
-                                            String userId = (String) internalHashMap.get("userid");
-                                            if (userId != null) {
-                                                participant.setActorType(USERS);
-                                                participant.setActorId(userId);
-                                            } else {
-                                                participant.setActorType(GUESTS);
-                                                // FIXME seems to be not given by the HPB: participant.setActorId();
-                                            }
-                                            if (userMap != null) {
-                                                // There is no "user" attribute for guest participants.
-                                                participant.setDisplayName((String) userMap.get("displayname"));
-                                            }
-                                            usersHashMap.put((String) internalHashMap.get("sessionid"), participant);
-                                        }
-                                    }
-                                    break;
-                                case TARGET_PARTICIPANTS:
-                                    signalingMessageReceiver.process(eventOverallWebSocketMessage.getEventMap());
-                                    break;
-                            }
-                        }
+                    case TARGET_PARTICIPANTS:
+                        signalingMessageReceiver.process(eventOverallWebSocketMessage.getEventMap());
                         break;
-                    case "message":
-                        CallOverallWebSocketMessage callOverallWebSocketMessage = LoganSquare.parse(text, CallOverallWebSocketMessage.class);
-                        NCSignalingMessage ncSignalingMessage = callOverallWebSocketMessage.getCallWebSocketMessage().getNcSignalingMessage();
-                        if (TextUtils.isEmpty(ncSignalingMessage.getFrom()) && callOverallWebSocketMessage.getCallWebSocketMessage().getSenderWebSocketMessage() != null) {
-                            ncSignalingMessage.setFrom(callOverallWebSocketMessage.getCallWebSocketMessage().getSenderWebSocketMessage().getSessionId());
-                        }
-
-                        signalingMessageReceiver.process(ncSignalingMessage);
-                        break;
-                    case "bye":
-                        connected = false;
-                        resumeId = "";
                     default:
+                        Log.i(TAG, "Received unknown/ignored event target: " + target);
                         break;
                 }
-            } catch (IOException e) {
-                Log.e(TAG, "Failed to recognize WebSocket message", e);
+            } else {
+                Log.w(TAG, "Received message with event target: null");
+            }
+        }
+    }
+
+    private void processRoomMessageMessage(EventOverallWebSocketMessage eventOverallWebSocketMessage) {
+        Map<String, Object> messageHashMap = (Map<String, Object>) eventOverallWebSocketMessage
+            .getEventMap()
+            .get("message");
+        if (messageHashMap != null && messageHashMap.containsKey("data")) {
+            Map<String, Object> dataHashMap = (Map<String, Object>) messageHashMap.get("data");
+            if (dataHashMap != null && dataHashMap.containsKey("chat")) {
+                Map<String, Object> chatMap = (Map<String, Object>) dataHashMap.get("chat");
+                if (chatMap != null && chatMap.containsKey("refresh") && (boolean) chatMap.get("refresh")) {
+                    HashMap<String, String> refreshChatHashMap = new HashMap<>();
+                    refreshChatHashMap.put(BundleKeys.KEY_ROOM_TOKEN, (String) messageHashMap.get("roomid"));
+                    refreshChatHashMap.put(BundleKeys.KEY_INTERNAL_USER_ID, Long.toString(conversationUser.getId()));
+                    eventBus.post(new WebSocketCommunicationEvent("refreshChat", refreshChatHashMap));
+                }
+            }
+        }
+    }
+
+    private void processRoomJoinMessage(EventOverallWebSocketMessage eventOverallWebSocketMessage) {
+        List<HashMap<String, Object>> joinEventList = (List<HashMap<String, Object>>) eventOverallWebSocketMessage
+            .getEventMap()
+            .get("join");
+        HashMap<String, Object> internalHashMap;
+        Participant participant;
+        for (int i = 0; i < joinEventList.size(); i++) {
+            internalHashMap = joinEventList.get(i);
+            HashMap<String, Object> userMap = (HashMap<String, Object>) internalHashMap.get("user");
+            participant = new Participant();
+            String userId = (String) internalHashMap.get("userid");
+            if (userId != null) {
+                participant.setActorType(USERS);
+                participant.setActorId(userId);
+            } else {
+                participant.setActorType(GUESTS);
+                // FIXME seems to be not given by the HPB: participant.setActorId();
+            }
+            if (userMap != null) {
+                // There is no "user" attribute for guest participants.
+                participant.setDisplayName((String) userMap.get("displayname"));
+            }
+            usersHashMap.put((String) internalHashMap.get("sessionid"), participant);
+        }
+    }
+
+    private void processJoinedRoomMessage(String text) throws IOException {
+        JoinedRoomOverallWebSocketMessage joinedRoomOverallWebSocketMessage =
+            LoganSquare.parse(text, JoinedRoomOverallWebSocketMessage.class);
+        if (joinedRoomOverallWebSocketMessage.getRoomWebSocketMessage() != null) {
+            currentRoomToken = joinedRoomOverallWebSocketMessage.getRoomWebSocketMessage().getRoomId();
+
+            if (joinedRoomOverallWebSocketMessage
+                .getRoomWebSocketMessage()
+                .getRoomPropertiesWebSocketMessage() != null &&
+                !TextUtils.isEmpty(currentRoomToken)) {
+                sendRoomJoinedEvent();
             }
         }
     }
 
+    private void processErrorMessage(WebSocket webSocket, String text) throws IOException {
+        Log.e(TAG, "Received error: " + text);
+        ErrorOverallWebSocketMessage errorOverallWebSocketMessage =
+            LoganSquare.parse(text, ErrorOverallWebSocketMessage.class);
+        ErrorWebSocketMessage message = errorOverallWebSocketMessage.getErrorWebSocketMessage();
+
+        if(message != null) {
+            if ("no_such_session".equals(message.getCode())) {
+                Log.d(TAG, "WebSocket " + webSocket.hashCode() + " resumeID " + resumeId + " expired");
+                resumeId = "";
+                currentRoomToken = "";
+                restartWebSocket();
+            } else if ("hello_expected".equals(message.getCode())) {
+                restartWebSocket();
+            }
+        }
+    }
+
+    private void processHelloMessage(WebSocket webSocket, String text) throws IOException {
+        connected = true;
+        reconnecting = false;
+        String oldResumeId = resumeId;
+        HelloResponseOverallWebSocketMessage helloResponseWebSocketMessage =
+            LoganSquare.parse(text, HelloResponseOverallWebSocketMessage.class);
+        if (helloResponseWebSocketMessage.getHelloResponseWebSocketMessage() != null) {
+            resumeId = helloResponseWebSocketMessage.getHelloResponseWebSocketMessage().getResumeId();
+            sessionId = helloResponseWebSocketMessage.getHelloResponseWebSocketMessage().getSessionId();
+            hasMCU = helloResponseWebSocketMessage.getHelloResponseWebSocketMessage().serverHasMCUSupport();
+        }
+
+        for (int i = 0; i < messagesQueue.size(); i++) {
+            webSocket.send(messagesQueue.get(i));
+        }
+
+        messagesQueue = new ArrayList<>();
+        HashMap<String, String> helloHasHap = new HashMap<>();
+        if (!TextUtils.isEmpty(oldResumeId)) {
+            helloHasHap.put("oldResumeId", oldResumeId);
+        } else {
+            currentRoomToken = "";
+        }
+
+        if (!TextUtils.isEmpty(currentRoomToken)) {
+            helloHasHap.put(ROOM_TOKEN, currentRoomToken);
+        }
+        eventBus.post(new WebSocketCommunicationEvent("hello", helloHasHap));
+    }
+
     private void sendRoomJoinedEvent() {
         HashMap<String, String> joinRoomHashMap = new HashMap<>();
         joinRoomHashMap.put(ROOM_TOKEN, currentRoomToken);
@@ -302,12 +366,12 @@ public class MagicWebSocketInstance extends WebSocketListener {
     }
 
     @Override
-    public void onMessage(WebSocket webSocket, ByteString bytes) {
+    public void onMessage(@NonNull WebSocket webSocket, ByteString bytes) {
         Log.d(TAG, "Receiving bytes : " + bytes.hex());
     }
 
     @Override
-    public void onClosing(WebSocket webSocket, int code, String reason) {
+    public void onClosing(@NonNull WebSocket webSocket, int code, @NonNull String reason) {
         Log.d(TAG, "Closing : " + code + " / " + reason);
     }
 
@@ -330,7 +394,8 @@ public class MagicWebSocketInstance extends WebSocketListener {
         Log.d(TAG, "   roomToken: " + roomToken);
         Log.d(TAG, "   session: " + normalBackendSession);
         try {
-            String message = LoganSquare.serialize(webSocketConnectionHelper.getAssembledJoinOrLeaveRoomModel(roomToken, normalBackendSession));
+            String message = LoganSquare.serialize(
+                webSocketConnectionHelper.getAssembledJoinOrLeaveRoomModel(roomToken, normalBackendSession));
             if (!connected || reconnecting) {
                 messagesQueue.add(message);
             } else {
@@ -347,7 +412,8 @@ public class MagicWebSocketInstance extends WebSocketListener {
 
     private void sendCallMessage(NCSignalingMessage ncSignalingMessage) {
         try {
-            String message = LoganSquare.serialize(webSocketConnectionHelper.getAssembledCallMessageModel(ncSignalingMessage));
+            String message = LoganSquare.serialize(
+                webSocketConnectionHelper.getAssembledCallMessageModel(ncSignalingMessage));
             if (!connected || reconnecting) {
                 messagesQueue.add(message);
             } else {
@@ -388,7 +454,8 @@ public class MagicWebSocketInstance extends WebSocketListener {
 
     @Subscribe(threadMode = ThreadMode.BACKGROUND)
     public void onMessageEvent(NetworkEvent networkEvent) {
-        if (networkEvent.getNetworkConnectionEvent() == NetworkEvent.NetworkConnectionEvent.NETWORK_CONNECTED && !isConnected()) {
+        if (networkEvent.getNetworkConnectionEvent() == NetworkEvent.NetworkConnectionEvent.NETWORK_CONNECTED &&
+            !isConnected()) {
             restartWebSocket();
         }
     }
@@ -404,9 +471,9 @@ public class MagicWebSocketInstance extends WebSocketListener {
     /**
      * Temporary implementation of SignalingMessageReceiver until signaling related code is extracted to a Signaling
      * class.
-     *
-     * All listeners are called in the WebSocket reader thread. This thread should be the same as long as the
-     * WebSocket stays connected, but it may change whenever it is connected again.
+     * <p>
+     * All listeners are called in the WebSocket reader thread. This thread should be the same as long as the WebSocket
+     * stays connected, but it may change whenever it is connected again.
      */
     private static class ExternalSignalingMessageReceiver extends SignalingMessageReceiver {
         public void process(Map<String, Object> eventMap) {

+ 5 - 4
app/src/main/java/com/nextcloud/talk/webrtc/PeerConnectionWrapper.java

@@ -171,7 +171,7 @@ public class PeerConnectionWrapper {
                 dataChannel.registerObserver(new MagicDataChannelObserver());
                 if (isMCUPublisher) {
                     peerConnection.createOffer(magicSdpObserver, mediaConstraints);
-                } else if (hasMCU && this.videoStreamType.equals("video")) {
+                } else if (hasMCU && "video".equals(this.videoStreamType)) {
                     // If the connection type is "screen" the client sharing the screen will send an
                     // offer; offers should be requested only for videos.
                     // "to" property is not actually needed in the "requestoffer" signaling message, but it is used to
@@ -360,8 +360,9 @@ public class PeerConnectionWrapper {
 
         @Override
         public void onStateChange() {
-            if (dataChannel != null && dataChannel.state() == DataChannel.State.OPEN &&
-                    dataChannel.label().equals("status")) {
+            if (dataChannel != null &&
+                dataChannel.state() == DataChannel.State.OPEN &&
+                "status".equals(dataChannel.label())) {
                 sendInitialMediaStatus();
             }
         }
@@ -493,7 +494,7 @@ public class PeerConnectionWrapper {
 
         @Override
         public void onDataChannel(DataChannel dataChannel) {
-            if (dataChannel.label().equals("status") || dataChannel.label().equals("JanusDataChannel")) {
+            if ("status".equals(dataChannel.label()) || "JanusDataChannel".equals(dataChannel.label())) {
                 PeerConnectionWrapper.this.dataChannel = dataChannel;
                 PeerConnectionWrapper.this.dataChannel.registerObserver(new MagicDataChannelObserver());
             }

+ 1 - 1
app/src/main/java/com/nextcloud/talk/webrtc/WebRtcAudioManager.java

@@ -399,7 +399,7 @@ public class WebRtcAudioManager {
         return false;
     }
 
-    public void updateAudioDeviceState() {
+    public final void updateAudioDeviceState() {
         ThreadUtils.checkIsOnMainThread();
         Log.d(TAG, "--- updateAudioDeviceState: "
             + "wired headset=" + hasWiredHeadset + ", "

+ 8 - 6
app/src/main/java/com/nextcloud/talk/webrtc/WebRtcBluetoothManager.java

@@ -109,8 +109,6 @@ public class WebRtcBluetoothManager {
         return bluetoothState;
     }
 
-    ;
-
     /**
      * Activates components required to detect Bluetooth devices and to enable
      * BT SCO (audio is routed via BT SCO) for the headset profile. The end
@@ -297,7 +295,7 @@ public class WebRtcBluetoothManager {
     /**
      * Stubs for test mocks.
      */
-    protected AudioManager getAudioManager(Context context) {
+    protected final AudioManager getAudioManager(Context context) {
         return (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
     }
 
@@ -502,8 +500,10 @@ public class WebRtcBluetoothManager {
             Log.d(TAG, "onServiceConnected done: BT state=" + bluetoothState);
         }
 
+        /**
+         * Notifies the client when the proxy object has been disconnected from the service.
+         */
         @Override
-        /** Notifies the client when the proxy object has been disconnected from the service. */
         public void onServiceDisconnected(int profile) {
             if (profile != BluetoothProfile.HEADSET || bluetoothState == State.UNINITIALIZED) {
                 return;
@@ -531,7 +531,7 @@ public class WebRtcBluetoothManager {
             // change does not tell us anything about whether we're streaming
             // audio to BT over SCO. Typically received when user turns on a BT
             // headset while audio is active using another audio device.
-            if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) {
+            if (BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED.equals(action)) {
                 final int state =
                         intent.getIntExtra(BluetoothHeadset.EXTRA_STATE, BluetoothHeadset.STATE_DISCONNECTED);
                 Log.d(TAG, "BluetoothHeadsetBroadcastReceiver.onReceive: "
@@ -543,8 +543,10 @@ public class WebRtcBluetoothManager {
                     scoConnectionAttempts = 0;
                     updateAudioDeviceState();
                 } else if (state == BluetoothHeadset.STATE_CONNECTING) {
+                    Log.d(TAG, "+++ Bluetooth is connecting...");
                     // No action needed.
                 } else if (state == BluetoothHeadset.STATE_DISCONNECTING) {
+                    Log.d(TAG, "+++ Bluetooth is disconnecting...");
                     // No action needed.
                 } else if (state == BluetoothHeadset.STATE_DISCONNECTED) {
                     // Bluetooth is probably powered off during the call.
@@ -553,7 +555,7 @@ public class WebRtcBluetoothManager {
                 }
                 // Change in the audio (SCO) connection state of the Headset profile.
                 // Typically received after call to startScoAudio() has finalized.
-            } else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) {
+            } else if (BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED.equals(action)) {
                 final int state = intent.getIntExtra(
                         BluetoothHeadset.EXTRA_STATE, BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
                 Log.d(TAG, "BluetoothHeadsetBroadcastReceiver.onReceive: "

+ 6 - 4
app/src/main/java/com/nextcloud/talk/webrtc/WebSocketConnectionHelper.java

@@ -60,11 +60,13 @@ public class WebSocketConnectionHelper {
 
     @SuppressLint("LongLogTag")
     public static synchronized MagicWebSocketInstance getMagicWebSocketInstanceForUserId(long userId) {
-        if (userId != -1 && magicWebSocketInstanceMap.containsKey(userId)) {
-            return magicWebSocketInstanceMap.get(userId);
+        MagicWebSocketInstance webSocketInstance = magicWebSocketInstanceMap.get(userId);
+
+        if (webSocketInstance == null) {
+            Log.d(TAG, "No magicWebSocketInstance found for user " + userId);
         }
-        Log.d(TAG, "no magicWebSocketInstance found");
-        return null;
+
+        return webSocketInstance;
     }
 
     public static synchronized MagicWebSocketInstance getExternalSignalingInstanceForServer(String url,