From b98ff4c56a67aeaa9060614df782c12a5eff9da6 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Mon, 10 May 2021 17:12:00 +0200 Subject: [PATCH 1/8] Fix joining a public chat as a guest Signed-off-by: Joas Schilling --- .../bottomsheet/OperationsMenuController.java | 511 ++++++++++-------- 1 file changed, 271 insertions(+), 240 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/OperationsMenuController.java b/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/OperationsMenuController.java index a7190a404..22a19f51a 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/OperationsMenuController.java +++ b/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/OperationsMenuController.java @@ -20,6 +20,7 @@ package com.nextcloud.talk.controllers.bottomsheet; +import android.annotation.SuppressLint; import android.app.Activity; import android.content.Intent; import android.net.Uri; @@ -48,6 +49,7 @@ import com.nextcloud.talk.api.NcApi; import com.nextcloud.talk.application.NextcloudTalkApplication; import com.nextcloud.talk.controllers.base.BaseController; import com.nextcloud.talk.events.BottomSheetLockEvent; +import com.nextcloud.talk.events.EventStatus; import com.nextcloud.talk.models.RetrofitBucket; import com.nextcloud.talk.models.database.UserEntity; import com.nextcloud.talk.models.json.capabilities.Capabilities; @@ -58,6 +60,7 @@ import com.nextcloud.talk.models.json.participants.AddParticipantOverall; import com.nextcloud.talk.utils.ApiUtils; import com.nextcloud.talk.utils.ConductorRemapping; import com.nextcloud.talk.utils.DisplayUtils; +import com.nextcloud.talk.utils.NoSupportedApiException; import com.nextcloud.talk.utils.bundle.BundleKeys; import com.nextcloud.talk.utils.database.user.UserUtils; import com.nextcloud.talk.utils.singletons.ApplicationWideMessageHolder; @@ -81,6 +84,8 @@ import retrofit2.HttpException; @AutoInjector(NextcloudTalkApplication.class) public class OperationsMenuController extends BaseController { + private static final String TAG = "OperationsMenuController"; + @BindView(R.id.progress_bar) ProgressBar progressBar; @@ -165,13 +170,7 @@ public class OperationsMenuController extends BaseController { super.onViewBound(view); NextcloudTalkApplication.Companion.getSharedApplication().getComponentApplication().inject(this); - processOperation(); - } - - private void processOperation() { currentUser = userUtils.getCurrentUser(); - OperationsObserver operationsObserver = new OperationsObserver(); - if (!TextUtils.isEmpty(callUrl) && callUrl.contains("/call")) { conversationToken = callUrl.substring(callUrl.lastIndexOf("/") + 1); if (callUrl.contains("/index.php")) { @@ -181,201 +180,280 @@ public class OperationsMenuController extends BaseController { } } - if (currentUser != null) { - credentials = ApiUtils.getCredentials(currentUser.getUsername(), currentUser.getToken()); - - int apiVersion; - if (!TextUtils.isEmpty(baseUrl) && !baseUrl.equals(currentUser.getBaseUrl())) { - credentials = null; - // FIXME joining a public link we need to check other capabilities - apiVersion = 1; - } else { - apiVersion = ApiUtils.getConversationApiVersion(currentUser, new int[] {ApiUtils.APIv4, 1}); - } + if (!TextUtils.isEmpty(baseUrl) && !baseUrl.equals(currentUser.getBaseUrl())) { + fetchCapabilitiesForGuest(); + } else { + processOperation(); + } + } - switch (operationCode) { - case 2: - ncApi.renameRoom(credentials, ApiUtils.getUrlForRoom(apiVersion, currentUser.getBaseUrl(), - conversation.getToken()), - conversation.getName()) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .retry(1) - .subscribe(operationsObserver); - break; - case 3: - ncApi.makeRoomPublic(credentials, ApiUtils.getUrlForRoomPublic(apiVersion, currentUser.getBaseUrl(), - conversation.getToken())) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .retry(1) - .subscribe(operationsObserver); - break; - case 4: - case 5: - case 6: - String pass = ""; - if (conversation.getPassword() != null) { - pass = conversation.getPassword(); + private void fetchCapabilitiesForGuest() { + ncApi.getCapabilities(null, ApiUtils.getUrlForCapabilities(baseUrl)) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(new Observer() { + @Override + public void onSubscribe(Disposable d) { } - ncApi.setPassword(credentials, ApiUtils.getUrlForRoomPassword(apiVersion, currentUser.getBaseUrl(), - conversation.getToken()), pass) + + @SuppressLint("LongLogTag") + @Override + public void onNext(CapabilitiesOverall capabilitiesOverall) { + currentUser = new UserEntity(); + currentUser.setBaseUrl(baseUrl); + currentUser.setUserId("?"); + try { + currentUser.setCapabilities(LoganSquare.serialize(capabilitiesOverall.getOcs().getData().getCapabilities())); + } catch (IOException e) { + Log.e("OperationsMenu", "Failed to serialize capabilities"); + } + + try { + checkCapabilities(currentUser); + processOperation(); + } catch (NoSupportedApiException e) { + showResultImage(false, false); + Log.d(TAG, "No supported server version found", e); + } + } + + @SuppressLint("LongLogTag") + @Override + public void onError(Throwable e) { + showResultImage(false, false); + Log.e(TAG, "Error fetching capabilities for guest", e); + } + + @Override + public void onComplete() { + + } + }); + } + + @SuppressLint("LongLogTag") + private void processOperation() { + OperationsObserver operationsObserver = new OperationsObserver(); + + if (currentUser == null) { + showResultImage(false, true); + Log.e(TAG, "Ended up in processOperation without a valid currentUser"); + return; + } + + credentials = ApiUtils.getCredentials(currentUser.getUsername(), currentUser.getToken()); + int apiVersion = ApiUtils.getConversationApiVersion(currentUser, new int[] {ApiUtils.APIv4, 1}); + + switch (operationCode) { + case 2: + ncApi.renameRoom(credentials, ApiUtils.getUrlForRoom(apiVersion, currentUser.getBaseUrl(), + conversation.getToken()), + conversation.getName()) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .retry(1) + .subscribe(operationsObserver); + break; + case 3: + ncApi.makeRoomPublic(credentials, ApiUtils.getUrlForRoomPublic(apiVersion, currentUser.getBaseUrl(), + conversation.getToken())) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .retry(1) + .subscribe(operationsObserver); + break; + case 4: + case 5: + case 6: + String pass = ""; + if (conversation.getPassword() != null) { + pass = conversation.getPassword(); + } + ncApi.setPassword(credentials, ApiUtils.getUrlForRoomPassword(apiVersion, currentUser.getBaseUrl(), + conversation.getToken()), pass) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .retry(1) + .subscribe(operationsObserver); + break; + case 7: + // Operation 7 is sharing, so we handle this differently + break; + case 8: + ncApi.makeRoomPrivate(credentials, ApiUtils.getUrlForRoomPublic(apiVersion, + currentUser.getBaseUrl(), + conversation.getToken())) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .retry(1) + .subscribe(operationsObserver); + break; + case 10: + ncApi.getRoom(credentials, ApiUtils.getUrlForRoom(apiVersion, baseUrl, conversationToken)) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .retry(1) + .subscribe(new Observer() { + @Override + public void onSubscribe(Disposable d) { + disposable = d; + } + + @Override + public void onNext(RoomOverall roomOverall) { + conversation = roomOverall.getOcs().getData(); + if (conversation.isHasPassword() && conversation.isGuest()) { + eventBus.post(new BottomSheetLockEvent(true, 0, + true, false)); + Bundle bundle = new Bundle(); + bundle.putParcelable(BundleKeys.INSTANCE.getKEY_ROOM(), Parcels.wrap(conversation)); + bundle.putString(BundleKeys.INSTANCE.getKEY_CALL_URL(), callUrl); + try { + bundle.putParcelable(BundleKeys.INSTANCE.getKEY_SERVER_CAPABILITIES(), + Parcels.wrap(LoganSquare.parse(currentUser.getCapabilities(), + Capabilities.class))); + } catch (IOException e) { + Log.e(TAG, "Failed to parse capabilities for guest"); + showResultImage(false, false); + } + bundle.putInt(BundleKeys.INSTANCE.getKEY_OPERATION_CODE(), 99); + getRouter().pushController(RouterTransaction.with(new EntryMenuController(bundle)) + .pushChangeHandler(new HorizontalChangeHandler()) + .popChangeHandler(new HorizontalChangeHandler())); + } else { + try { + initiateConversation( + false, + LoganSquare.parse(currentUser.getCapabilities(), Capabilities.class) + ); + } catch (IOException e) { + Log.e(TAG, "Failed to parse capabilities for guest"); + showResultImage(false, false); + } + } + } + + @Override + public void onError(Throwable e) { + showResultImage(false, false); + dispose(); + } + + @Override + public void onComplete() { + dispose(); + } + }); + break; + case 11: + RetrofitBucket retrofitBucket; + String invite = null; + + if (invitedGroups.size() > 0) { + invite = invitedGroups.get(0); + } + + if (conversationType.equals(Conversation.ConversationType.ROOM_PUBLIC_CALL)) { + retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(apiVersion, currentUser.getBaseUrl(), + "3", invite, conversationName); + } else { + retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(apiVersion, currentUser.getBaseUrl(), + "2", invite, conversationName); + } + + ncApi.createRoom(credentials, retrofitBucket.getUrl(), retrofitBucket.getQueryMap()) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .retry(1) + .subscribe(new Observer() { + @Override + public void onSubscribe(Disposable d) { + + } + + @Override + public void onNext(RoomOverall roomOverall) { + conversation = roomOverall.getOcs().getData(); + + ncApi.getRoom(credentials, + ApiUtils.getUrlForRoom(apiVersion, currentUser.getBaseUrl(), + conversation.getToken())) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(new Observer() { + @Override + public void onSubscribe(Disposable d) { + + } + + @Override + public void onNext(RoomOverall roomOverall) { + conversation = roomOverall.getOcs().getData(); + inviteUsersToAConversation(); + } + + @Override + public void onError(Throwable e) { + showResultImage(false, false); + dispose(); + } + + @Override + public void onComplete() { + + } + }); + + } + + @Override + public void onError(Throwable e) { + showResultImage(false, false); + dispose(); + } + + @Override + public void onComplete() { + dispose(); + } + }); + + break; + case 97: + case 98: + if (operationCode == 97) { + ncApi.removeConversationFromFavorites(credentials, + ApiUtils.getUrlForRoomFavorite(apiVersion, + currentUser.getBaseUrl(), + conversation.getToken())) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .retry(1) .subscribe(operationsObserver); - break; - case 7: - // Operation 7 is sharing, so we handle this differently - break; - case 8: - ncApi.makeRoomPrivate(credentials, ApiUtils.getUrlForRoomPublic(apiVersion, + } else { + ncApi.addConversationToFavorites(credentials, + ApiUtils.getUrlForRoomFavorite(apiVersion, currentUser.getBaseUrl(), conversation.getToken())) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .retry(1) .subscribe(operationsObserver); - break; - case 10: - ncApi.getRoom(credentials, ApiUtils.getUrlForRoom(apiVersion, baseUrl, conversationToken)) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .retry(1) - .subscribe(new Observer() { - @Override - public void onSubscribe(Disposable d) { - disposable = d; - } - - @Override - public void onNext(RoomOverall roomOverall) { - conversation = roomOverall.getOcs().getData(); - fetchCapabilities(credentials); - } - - @Override - public void onError(Throwable e) { - showResultImage(false, false); - dispose(); - } - - @Override - public void onComplete() { - dispose(); - } - }); - break; - case 11: - RetrofitBucket retrofitBucket; - String invite = null; - - if (invitedGroups.size() > 0) { - invite = invitedGroups.get(0); - } - - if (conversationType.equals(Conversation.ConversationType.ROOM_PUBLIC_CALL)) { - retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(apiVersion, currentUser.getBaseUrl(), - "3", invite, conversationName); - } else { - retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(apiVersion, currentUser.getBaseUrl(), - "2", invite, conversationName); - } - - ncApi.createRoom(credentials, retrofitBucket.getUrl(), retrofitBucket.getQueryMap()) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .retry(1) - .subscribe(new Observer() { - @Override - public void onSubscribe(Disposable d) { - - } - - @Override - public void onNext(RoomOverall roomOverall) { - conversation = roomOverall.getOcs().getData(); - - ncApi.getRoom(credentials, - ApiUtils.getUrlForRoom(apiVersion, currentUser.getBaseUrl(), - conversation.getToken())) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(new Observer() { - @Override - public void onSubscribe(Disposable d) { - - } - - @Override - public void onNext(RoomOverall roomOverall) { - conversation = roomOverall.getOcs().getData(); - inviteUsersToAConversation(); - } - - @Override - public void onError(Throwable e) { - showResultImage(false, false); - dispose(); - } - - @Override - public void onComplete() { - - } - }); - - } - - @Override - public void onError(Throwable e) { - showResultImage(false, false); - dispose(); - } - - @Override - public void onComplete() { - dispose(); - } - }); - - break; - case 97: - case 98: - if (operationCode == 97) { - ncApi.removeConversationFromFavorites(credentials, - ApiUtils.getUrlForRoomFavorite(apiVersion, - currentUser.getBaseUrl(), - conversation.getToken())) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .retry(1) - .subscribe(operationsObserver); - } else { - ncApi.addConversationToFavorites(credentials, - ApiUtils.getUrlForRoomFavorite(apiVersion, - currentUser.getBaseUrl(), - conversation.getToken())) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .retry(1) - .subscribe(operationsObserver); - } - break; - case 99: - ncApi.joinRoom(credentials, ApiUtils.getUrlForParticipantsActive(apiVersion, - baseUrl, - conversationToken), - callPassword) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .retry(1) - .subscribe(operationsObserver); - break; - default: - break; - } + } + break; + case 99: + ncApi.joinRoom(credentials, ApiUtils.getUrlForParticipantsActive(apiVersion, + baseUrl, + conversationToken), + callPassword) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .retry(1) + .subscribe(operationsObserver); + break; + default: + break; } } @@ -436,58 +514,11 @@ public class OperationsMenuController extends BaseController { dispose(); } - private void fetchCapabilities(String credentials) { - ncApi.getCapabilities(credentials, ApiUtils.getUrlForCapabilities(baseUrl)) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(new Observer() { - @Override - public void onSubscribe(Disposable d) { - - } - - @Override - public void onNext(CapabilitiesOverall capabilitiesOverall) { - // Guest checking the capabilities so there is no global server EOL warning until here. - // FIXME check the serverEOL capabilities instead - if (capabilitiesOverall.getOcs().getData() - .getCapabilities().getSpreedCapability() != null && - capabilitiesOverall.getOcs().getData() - .getCapabilities().getSpreedCapability() - .getFeatures() != null && capabilitiesOverall.getOcs().getData() - .getCapabilities().getSpreedCapability() - .getFeatures().contains("chat-v2")) { - if (conversation.isHasPassword() && conversation.isGuest()) { - eventBus.post(new BottomSheetLockEvent(true, 0, - true, false)); - Bundle bundle = new Bundle(); - bundle.putParcelable(BundleKeys.INSTANCE.getKEY_ROOM(), Parcels.wrap(conversation)); - bundle.putString(BundleKeys.INSTANCE.getKEY_CALL_URL(), callUrl); - bundle.putParcelable(BundleKeys.INSTANCE.getKEY_SERVER_CAPABILITIES(), - Parcels.wrap(capabilitiesOverall.getOcs().getData().getCapabilities())); - bundle.putInt(BundleKeys.INSTANCE.getKEY_OPERATION_CODE(), 99); - getRouter().pushController(RouterTransaction.with(new EntryMenuController(bundle)) - .pushChangeHandler(new HorizontalChangeHandler()) - .popChangeHandler(new HorizontalChangeHandler())); - } else { - initiateConversation(false, capabilitiesOverall.getOcs().getData() - .getCapabilities()); - } - } else { - showResultImage(false, true); - } - } - - @Override - public void onError(Throwable e) { - showResultImage(false, false); - } - - @Override - public void onComplete() { - - } - }); + private void checkCapabilities(UserEntity currentUser) throws NoSupportedApiException { + ApiUtils.getConversationApiVersion(currentUser, new int[] {ApiUtils.APIv4, 1}); + ApiUtils.getCallApiVersion(currentUser, new int[] {ApiUtils.APIv4, 1}); + ApiUtils.getChatApiVersion(currentUser, new int[] {1}); + ApiUtils.getSignalingApiVersion(currentUser, new int[] {2, 1}); } private void inviteUsersToAConversation() { From a4cc7f2816e5d89643dd9bf8d6b4b10c3c337450 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Tue, 11 May 2021 10:50:49 +0200 Subject: [PATCH 2/8] Remove non reachable "initiateCall()" Signed-off-by: Joas Schilling --- .../bottomsheet/OperationsMenuController.java | 95 +++++-------------- 1 file changed, 26 insertions(+), 69 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/OperationsMenuController.java b/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/OperationsMenuController.java index 22a19f51a..e58c0b796 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/OperationsMenuController.java +++ b/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/OperationsMenuController.java @@ -21,17 +21,14 @@ package com.nextcloud.talk.controllers.bottomsheet; import android.annotation.SuppressLint; -import android.app.Activity; import android.content.Intent; import android.net.Uri; import android.os.Bundle; -import android.os.Handler; import android.text.TextUtils; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.view.inputmethod.InputMethodManager; import android.widget.Button; import android.widget.ImageView; import android.widget.ProgressBar; @@ -49,7 +46,6 @@ import com.nextcloud.talk.api.NcApi; import com.nextcloud.talk.application.NextcloudTalkApplication; import com.nextcloud.talk.controllers.base.BaseController; import com.nextcloud.talk.events.BottomSheetLockEvent; -import com.nextcloud.talk.events.EventStatus; import com.nextcloud.talk.models.RetrofitBucket; import com.nextcloud.talk.models.database.UserEntity; import com.nextcloud.talk.models.json.capabilities.Capabilities; @@ -622,81 +618,42 @@ public class OperationsMenuController extends BaseController { private void initiateConversation(boolean dismissView, @Nullable Capabilities capabilities) { Bundle bundle = new Bundle(); boolean isGuestUser = false; - boolean hasChatCapability; if (baseUrl != null && !baseUrl.equals(currentUser.getBaseUrl())) { isGuestUser = true; - // Guest checking the capabilities so there is no global server EOL warning until here. - // FIXME check the serverEOL capabilities instead - hasChatCapability = capabilities != null && capabilities.getSpreedCapability() != null && capabilities.getSpreedCapability().getFeatures() != null && capabilities.getSpreedCapability().getFeatures().contains("chat-v2"); - } else { - hasChatCapability = currentUser.hasSpreedFeatureCapability("chat-v2"); } + eventBus.post(new BottomSheetLockEvent(true, 0, + true, true, dismissView)); - if (hasChatCapability) { - eventBus.post(new BottomSheetLockEvent(true, 0, - true, true, dismissView)); - - Intent conversationIntent = new Intent(getActivity(), MagicCallActivity.class); - bundle.putString(BundleKeys.INSTANCE.getKEY_ROOM_TOKEN(), conversation.getToken()); - bundle.putString(BundleKeys.INSTANCE.getKEY_ROOM_ID(), conversation.getRoomId()); - bundle.putString(BundleKeys.INSTANCE.getKEY_CONVERSATION_NAME(), conversation.getDisplayName()); - UserEntity conversationUser; - if (isGuestUser) { - conversationUser = new UserEntity(); - conversationUser.setBaseUrl(baseUrl); - conversationUser.setUserId("?"); - try { - conversationUser.setCapabilities(LoganSquare.serialize(capabilities)); - } catch (IOException e) { - Log.e("OperationsMenu", "Failed to serialize capabilities"); - } - } else { - conversationUser = currentUser; - } - - bundle.putParcelable(BundleKeys.INSTANCE.getKEY_USER_ENTITY(), conversationUser); - bundle.putParcelable(BundleKeys.INSTANCE.getKEY_ACTIVE_CONVERSATION(), Parcels.wrap(conversation)); - bundle.putString(BundleKeys.INSTANCE.getKEY_CONVERSATION_PASSWORD(), callPassword); - - conversationIntent.putExtras(bundle); - - if (getParentController() != null) { - ConductorRemapping.INSTANCE.remapChatController(getParentController().getRouter(), conversationUser.getId(), - conversation.getToken(), bundle, true); - } - } else { - initiateCall(); - } - } - - - private void initiateCall() { - eventBus.post(new BottomSheetLockEvent(true, 0, true, true)); - Bundle bundle = new Bundle(); + Intent conversationIntent = new Intent(getActivity(), MagicCallActivity.class); bundle.putString(BundleKeys.INSTANCE.getKEY_ROOM_TOKEN(), conversation.getToken()); - bundle.putParcelable(BundleKeys.INSTANCE.getKEY_USER_ENTITY(), currentUser); - if (baseUrl != null && !baseUrl.equals(currentUser.getBaseUrl())) { - bundle.putString(BundleKeys.INSTANCE.getKEY_MODIFIED_BASE_URL(), baseUrl); - } - bundle.putParcelable(BundleKeys.INSTANCE.getKEY_ACTIVE_CONVERSATION(), Parcels.wrap(conversation)); - - if (getActivity() != null) { - - Intent callIntent = new Intent(getActivity(), MagicCallActivity.class); - callIntent.putExtras(bundle); - - InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Activity.INPUT_METHOD_SERVICE); - if (imm != null) { - imm.toggleSoftInput(InputMethodManager.HIDE_IMPLICIT_ONLY, 0); + bundle.putString(BundleKeys.INSTANCE.getKEY_ROOM_ID(), conversation.getRoomId()); + bundle.putString(BundleKeys.INSTANCE.getKEY_CONVERSATION_NAME(), conversation.getDisplayName()); + UserEntity conversationUser; + if (isGuestUser) { + conversationUser = new UserEntity(); + conversationUser.setBaseUrl(baseUrl); + conversationUser.setUserId("?"); + try { + conversationUser.setCapabilities(LoganSquare.serialize(capabilities)); + } catch (IOException e) { + Log.e("OperationsMenu", "Failed to serialize capabilities"); } - - new Handler().postDelayed(() -> getParentController().getRouter().popCurrentController(), 100); - startActivity(callIntent); - + } else { + conversationUser = currentUser; } + bundle.putParcelable(BundleKeys.INSTANCE.getKEY_USER_ENTITY(), conversationUser); + bundle.putParcelable(BundleKeys.INSTANCE.getKEY_ACTIVE_CONVERSATION(), Parcels.wrap(conversation)); + bundle.putString(BundleKeys.INSTANCE.getKEY_CONVERSATION_PASSWORD(), callPassword); + + conversationIntent.putExtras(bundle); + + if (getParentController() != null) { + ConductorRemapping.INSTANCE.remapChatController(getParentController().getRouter(), conversationUser.getId(), + conversation.getToken(), bundle, true); + } } private class OperationsObserver implements Observer { From 2fd093338327adb9169f567161dcfe0564e09665 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Tue, 11 May 2021 11:21:43 +0200 Subject: [PATCH 3/8] Remove unused capability checking Signed-off-by: Joas Schilling --- .../bottomsheet/OperationsMenuController.java | 47 ++++--------------- 1 file changed, 9 insertions(+), 38 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/OperationsMenuController.java b/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/OperationsMenuController.java index e58c0b796..49baead20 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/OperationsMenuController.java +++ b/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/OperationsMenuController.java @@ -35,7 +35,6 @@ import android.widget.ProgressBar; import android.widget.TextView; import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import com.bluelinelabs.conductor.RouterTransaction; import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler; @@ -318,15 +317,7 @@ public class OperationsMenuController extends BaseController { .pushChangeHandler(new HorizontalChangeHandler()) .popChangeHandler(new HorizontalChangeHandler())); } else { - try { - initiateConversation( - false, - LoganSquare.parse(currentUser.getCapabilities(), Capabilities.class) - ); - } catch (IOException e) { - Log.e(TAG, "Failed to parse capabilities for guest"); - showResultImage(false, false); - } + initiateConversation(false); } } @@ -562,7 +553,7 @@ public class OperationsMenuController extends BaseController { } if (localInvitedGroups.size() == 0 && localInvitedUsers.size() == 0) { - initiateConversation(true, null); + initiateConversation(true); } dispose(); } @@ -604,54 +595,34 @@ public class OperationsMenuController extends BaseController { } if (localInvitedGroups.size() == 0 && localInvitedUsers.size() == 0) { - initiateConversation(true, null); + initiateConversation(true); } dispose(); } }); } } else { - initiateConversation(true, null); + initiateConversation(true); } } - private void initiateConversation(boolean dismissView, @Nullable Capabilities capabilities) { - Bundle bundle = new Bundle(); - boolean isGuestUser = false; - - if (baseUrl != null && !baseUrl.equals(currentUser.getBaseUrl())) { - isGuestUser = true; - } - + private void initiateConversation(boolean dismissView) { eventBus.post(new BottomSheetLockEvent(true, 0, true, true, dismissView)); Intent conversationIntent = new Intent(getActivity(), MagicCallActivity.class); + Bundle bundle = new Bundle(); bundle.putString(BundleKeys.INSTANCE.getKEY_ROOM_TOKEN(), conversation.getToken()); bundle.putString(BundleKeys.INSTANCE.getKEY_ROOM_ID(), conversation.getRoomId()); bundle.putString(BundleKeys.INSTANCE.getKEY_CONVERSATION_NAME(), conversation.getDisplayName()); - UserEntity conversationUser; - if (isGuestUser) { - conversationUser = new UserEntity(); - conversationUser.setBaseUrl(baseUrl); - conversationUser.setUserId("?"); - try { - conversationUser.setCapabilities(LoganSquare.serialize(capabilities)); - } catch (IOException e) { - Log.e("OperationsMenu", "Failed to serialize capabilities"); - } - } else { - conversationUser = currentUser; - } - - bundle.putParcelable(BundleKeys.INSTANCE.getKEY_USER_ENTITY(), conversationUser); + bundle.putParcelable(BundleKeys.INSTANCE.getKEY_USER_ENTITY(), currentUser); bundle.putParcelable(BundleKeys.INSTANCE.getKEY_ACTIVE_CONVERSATION(), Parcels.wrap(conversation)); bundle.putString(BundleKeys.INSTANCE.getKEY_CONVERSATION_PASSWORD(), callPassword); conversationIntent.putExtras(bundle); if (getParentController() != null) { - ConductorRemapping.INSTANCE.remapChatController(getParentController().getRouter(), conversationUser.getId(), + ConductorRemapping.INSTANCE.remapChatController(getParentController().getRouter(), currentUser.getId(), conversation.getToken(), bundle, true); } } @@ -670,7 +641,7 @@ public class OperationsMenuController extends BaseController { } else { RoomOverall roomOverall = (RoomOverall) o; conversation = roomOverall.getOcs().getData(); - initiateConversation(true, serverCapabilities); + initiateConversation(true); } } From 01813d41658c6a021e266b599ed931f23a6e91cf Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Tue, 11 May 2021 11:27:08 +0200 Subject: [PATCH 4/8] Use injected capabilities Signed-off-by: Joas Schilling --- .../bottomsheet/OperationsMenuController.java | 33 ++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/OperationsMenuController.java b/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/OperationsMenuController.java index 49baead20..d2a978a32 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/OperationsMenuController.java +++ b/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/OperationsMenuController.java @@ -176,13 +176,44 @@ public class OperationsMenuController extends BaseController { } if (!TextUtils.isEmpty(baseUrl) && !baseUrl.equals(currentUser.getBaseUrl())) { - fetchCapabilitiesForGuest(); + if (serverCapabilities != null) { + try { + useBundledCapabilitiesForGuest(); + } catch (IOException e) { + // Fall back to fetching capabilities again + fetchCapabilitiesForGuest(); + } + } else { + fetchCapabilitiesForGuest(); + } } else { processOperation(); } } + @SuppressLint("LongLogTag") + private void useBundledCapabilitiesForGuest() throws IOException { + currentUser = new UserEntity(); + currentUser.setBaseUrl(baseUrl); + currentUser.setUserId("?"); + try { + currentUser.setCapabilities(LoganSquare.serialize(serverCapabilities)); + } catch (IOException e) { + Log.e("OperationsMenu", "Failed to serialize capabilities"); + throw e; + } + + try { + checkCapabilities(currentUser); + processOperation(); + } catch (NoSupportedApiException e) { + showResultImage(false, false); + Log.d(TAG, "No supported server version found", e); + } + } + + @SuppressLint("LongLogTag") private void fetchCapabilitiesForGuest() { ncApi.getCapabilities(null, ApiUtils.getUrlForCapabilities(baseUrl)) .subscribeOn(Schedulers.io()) From 932afc2b58ef530c0d87861131052cde9fa156fd Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Tue, 11 May 2021 12:01:30 +0200 Subject: [PATCH 5/8] Remove duplicate call Signed-off-by: Joas Schilling --- .../nextcloud/talk/controllers/CallNotificationController.java | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/java/com/nextcloud/talk/controllers/CallNotificationController.java b/app/src/main/java/com/nextcloud/talk/controllers/CallNotificationController.java index 1095ff051..939b04eef 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/CallNotificationController.java +++ b/app/src/main/java/com/nextcloud/talk/controllers/CallNotificationController.java @@ -201,7 +201,6 @@ public class CallNotificationController extends BaseController { private void proceedToCall() { originalBundle.putString(BundleKeys.INSTANCE.getKEY_ROOM_TOKEN(), currentConversation.getToken()); originalBundle.putString(BundleKeys.INSTANCE.getKEY_CONVERSATION_NAME(), currentConversation.getDisplayName()); - originalBundle.putString(BundleKeys.INSTANCE.getKEY_CONVERSATION_NAME(), currentConversation.getDisplayName()); getRouter().replaceTopController(RouterTransaction.with(new CallController(originalBundle)) .popChangeHandler(new HorizontalChangeHandler()) From c335a69413bc66225641a49854262337a4e6ddd6 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Tue, 11 May 2021 19:14:44 +0200 Subject: [PATCH 6/8] Connect the buttons with the actions also for guests Signed-off-by: Joas Schilling --- .../main/java/com/nextcloud/talk/controllers/ChatController.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt b/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt index 6a735d9e6..edd46c7c7 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt +++ b/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt @@ -1450,6 +1450,8 @@ class ChatController(args: Bundle) : inflater.inflate(R.menu.menu_conversation, menu) if (conversationUser?.userId == "?") { menu.removeItem(R.id.conversation_info) + conversationVoiceCallMenuItem = menu.findItem(R.id.conversation_voice_call) + conversationVideoMenuItem = menu.findItem(R.id.conversation_video_call) } else { conversationInfoMenuItem = menu.findItem(R.id.conversation_info) conversationVoiceCallMenuItem = menu.findItem(R.id.conversation_voice_call) From 97117eb90cc913bbce7e46e2242a89ea82e5143c Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Tue, 11 May 2021 19:15:06 +0200 Subject: [PATCH 7/8] Use the correct signaling information and user+ticket for "Join via link" as well Signed-off-by: Joas Schilling --- .../com/nextcloud/talk/controllers/CallController.java | 9 ++++++++- .../nextcloud/talk/webrtc/MagicWebSocketInstance.java | 10 ++++++---- .../talk/webrtc/WebSocketConnectionHelper.java | 4 +++- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/controllers/CallController.java b/app/src/main/java/com/nextcloud/talk/controllers/CallController.java index db683cc2e..17477e987 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/CallController.java +++ b/app/src/main/java/com/nextcloud/talk/controllers/CallController.java @@ -1132,7 +1132,13 @@ public class CallController extends BaseController { .subscribeOn(Schedulers.io()) .subscribe(); } catch (IOException exception) { - Log.e(TAG, "Failed to serialize external signaling server"); + Log.e(TAG, "Failed to serialize external signaling server", exception); + } + } else { + try { + conversationUser.setExternalSignalingServer(LoganSquare.serialize(externalSignalingServer)); + } catch (IOException exception) { + Log.e(TAG, "Failed to serialize external signaling server", exception); } } @@ -1172,6 +1178,7 @@ public class CallController extends BaseController { @Override public void onError(Throwable e) { + Log.e(TAG, e.getMessage(), e); } @Override diff --git a/app/src/main/java/com/nextcloud/talk/webrtc/MagicWebSocketInstance.java b/app/src/main/java/com/nextcloud/talk/webrtc/MagicWebSocketInstance.java index 70756bc87..3dd20eddd 100644 --- a/app/src/main/java/com/nextcloud/talk/webrtc/MagicWebSocketInstance.java +++ b/app/src/main/java/com/nextcloud/talk/webrtc/MagicWebSocketInstance.java @@ -152,9 +152,10 @@ public class MagicWebSocketInstance extends WebSocketListener { public void restartWebSocket() { reconnecting = true; - Request request = new Request.Builder().url(connectionUrl).build(); - okHttpClient.newWebSocket(request, this); - restartCount++; + Log.d(TAG, "restartWebSocket: " + connectionUrl); + Request request = new Request.Builder().url(connectionUrl).build(); + okHttpClient.newWebSocket(request, this); + restartCount++; } @Override @@ -196,6 +197,7 @@ public class MagicWebSocketInstance extends WebSocketListener { 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())) { LoggingUtils.INSTANCE.writeLogEntryToFile(context, @@ -351,7 +353,7 @@ public class MagicWebSocketInstance extends WebSocketListener { } } } catch (IOException e) { - e.printStackTrace(); + Log.e(TAG, e.getMessage(), e); } } diff --git a/app/src/main/java/com/nextcloud/talk/webrtc/WebSocketConnectionHelper.java b/app/src/main/java/com/nextcloud/talk/webrtc/WebSocketConnectionHelper.java index 8882e3b77..8a11a8ee4 100644 --- a/app/src/main/java/com/nextcloud/talk/webrtc/WebSocketConnectionHelper.java +++ b/app/src/main/java/com/nextcloud/talk/webrtc/WebSocketConnectionHelper.java @@ -98,7 +98,9 @@ public class WebSocketConnectionHelper { authWebSocketMessage.setUrl(ApiUtils.getUrlForSignalingBackend(apiVersion, userEntity.getBaseUrl())); AuthParametersWebSocketMessage authParametersWebSocketMessage = new AuthParametersWebSocketMessage(); authParametersWebSocketMessage.setTicket(ticket); - authParametersWebSocketMessage.setUserid(userEntity.getUserId()); + if (!userEntity.getUserId().equals("?")) { + authParametersWebSocketMessage.setUserid(userEntity.getUserId()); + } authWebSocketMessage.setAuthParametersWebSocketMessage(authParametersWebSocketMessage); helloWebSocketMessage.setAuthWebSocketMessage(authWebSocketMessage); helloOverallWebSocketMessage.setHelloWebSocketMessage(helloWebSocketMessage); From 213d11358addc6a0fecd46efd99a6729060783cb Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Wed, 12 May 2021 16:20:44 +0200 Subject: [PATCH 8/8] Fix "own message" detection with guests Signed-off-by: Joas Schilling --- .../talk/controllers/ChatController.kt | 31 ++++++------------- .../bottomsheet/OperationsMenuController.java | 31 +++++++++++++++++++ .../talk/models/json/chat/ChatMessage.java | 8 +++-- .../json/conversations/Conversation.java | 30 ++++++++++++++++++ 4 files changed, 77 insertions(+), 23 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt b/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt index edd46c7c7..c759f64f9 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt +++ b/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt @@ -454,8 +454,17 @@ class ChatController(args: Bundle) : R.layout.item_date_header, this ) + var senderId = "" + if (!conversationUser?.userId.equals("?")) { + senderId = "users/" + conversationUser?.userId + } else { + senderId = currentConversation?.getActorType() + "/" + currentConversation?.getActorId() + } + + Log.d(TAG, "Initialize TalkMessagesListAdapter with senderId: " + senderId) + adapter = TalkMessagesListAdapter( - conversationUser?.userId, + senderId, messageHolders, ImageLoader { imageView, url, payload -> val draweeController = Fresco.newDraweeControllerBuilder() @@ -1050,18 +1059,6 @@ class ChatController(args: Bundle) : }) } - private fun setSenderId() { - try { - val senderId = adapter?.javaClass?.getDeclaredField("senderId") - senderId?.isAccessible = true - senderId?.set(adapter, conversationUser?.userId) - } catch (e: NoSuchFieldException) { - Log.w(TAG, "Failed to set sender id") - } catch (e: IllegalAccessException) { - Log.w(TAG, "Failed to access and set field") - } - } - private fun submitMessage() { if (messageInput != null) { val editable = messageInput!!.editableText @@ -1351,14 +1348,6 @@ class ChatController(args: Bundle) : chatMessage.activeUser = conversationUser chatMessage.isLinkPreviewAllowed = isLinkPreviewAllowed - // if credentials are empty, we're acting as a guest - if (TextUtils.isEmpty(credentials) && myFirstMessage != null && !TextUtils.isEmpty(myFirstMessage?.toString())) { - if (chatMessage.actorType == "guests") { - conversationUser?.userId = chatMessage.actorId - setSenderId() - } - } - val shouldScroll = !isThereANewNotice && !shouldAddNewMessagesNotice && layoutManager?.findFirstVisibleItemPosition() == 0 || adapter != null && adapter?.itemCount == 0 diff --git a/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/OperationsMenuController.java b/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/OperationsMenuController.java index d2a978a32..eae132ecd 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/OperationsMenuController.java +++ b/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/OperationsMenuController.java @@ -347,6 +347,35 @@ public class OperationsMenuController extends BaseController { getRouter().pushController(RouterTransaction.with(new EntryMenuController(bundle)) .pushChangeHandler(new HorizontalChangeHandler()) .popChangeHandler(new HorizontalChangeHandler())); + } else if (conversation.isGuest()) { + ncApi.joinRoom(credentials, ApiUtils.getUrlForParticipantsActive(apiVersion, + baseUrl, + conversationToken), null) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(new Observer() { + @Override + public void onSubscribe(Disposable d) { + + } + + @Override + public void onNext(RoomOverall roomOverall) { + conversation = roomOverall.getOcs().getData(); + initiateConversation(false); + } + + @Override + public void onError(Throwable e) { + showResultImage(false, false); + dispose(); + } + + @Override + public void onComplete() { + + } + }); } else { initiateConversation(false); } @@ -668,6 +697,8 @@ public class OperationsMenuController extends BaseController { @Override public void onNext(Object o) { if (operationCode != 99) { + RoomOverall roomOverall = (RoomOverall) o; + conversation = roomOverall.getOcs().getData(); showResultImage(true, false); } else { RoomOverall roomOverall = (RoomOverall) o; diff --git a/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatMessage.java b/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatMessage.java index 8e4dba56d..b13ff44cd 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatMessage.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatMessage.java @@ -214,12 +214,16 @@ public class ChatMessage implements IMessage, MessageContentType, MessageContent return new IUser() { @Override public String getId() { - return actorId; + return actorType + "/" + actorId; } @Override public String getName() { - return actorDisplayName; + if (!TextUtils.isEmpty(actorDisplayName)) { + return actorDisplayName; + } + + return NextcloudTalkApplication.Companion.getSharedApplication().getString(R.string.nc_guest); } @Override diff --git a/app/src/main/java/com/nextcloud/talk/models/json/conversations/Conversation.java b/app/src/main/java/com/nextcloud/talk/models/json/conversations/Conversation.java index bc81ef087..d068c2750 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/conversations/Conversation.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/conversations/Conversation.java @@ -61,6 +61,10 @@ public class Conversation { public boolean hasPassword; @JsonField(name = "sessionId") public String sessionId; + @JsonField(name = "actorId") + public String actorId; + @JsonField(name = "actorType") + public String actorType; public String password; @JsonField(name = "isFavorite") public boolean isFavorite; @@ -180,6 +184,14 @@ public class Conversation { return this.participantType; } + public String getActorId() { + return actorId; + } + + public String getActorType() { + return actorType; + } + public boolean isHasPassword() { return this.hasPassword; } @@ -277,6 +289,14 @@ public class Conversation { this.participantType = participantType; } + public void setActorId(String actorId) { + this.actorId = actorId; + } + + public void setActorType(String actorType) { + this.actorType = actorType; + } + public void setHasPassword(boolean hasPassword) { this.hasPassword = hasPassword; } @@ -399,6 +419,12 @@ public class Conversation { if (sessionId != null ? !sessionId.equals(that.sessionId) : that.sessionId != null) { return false; } + if (actorId != null ? !actorId.equals(that.actorId) : that.actorId != null) { + return false; + } + if (actorType != null ? !actorType.equals(that.actorType) : that.actorType != null) { + return false; + } if (password != null ? !password.equals(that.password) : that.password != null) { return false; } @@ -441,6 +467,8 @@ public class Conversation { result = 31 * result + (int) (lastPing ^ (lastPing >>> 32)); result = 31 * result + (participants != null ? participants.hashCode() : 0); result = 31 * result + (participantType != null ? participantType.hashCode() : 0); + result = 31 * result + (actorId != null ? actorId.hashCode() : 0); + result = 31 * result + (actorType != null ? actorType.hashCode() : 0); result = 31 * result + (hasPassword ? 1 : 0); result = 31 * result + (sessionId != null ? sessionId.hashCode() : 0); result = 31 * result + (password != null ? password.hashCode() : 0); @@ -473,6 +501,8 @@ public class Conversation { ", lastPing=" + lastPing + ", participants=" + participants + ", participantType=" + participantType + + ", actorId=" + actorId + + ", actorType=" + actorType + ", hasPassword=" + hasPassword + ", sessionId='" + sessionId + '\'' + ", password='" + password + '\'' +