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 deleted file mode 100644 index 03cdb528b..000000000 --- a/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/OperationsMenuController.java +++ /dev/null @@ -1,808 +0,0 @@ -/* - * Nextcloud Talk application - * - * @author Mario Danic - * Copyright (C) 2017 Mario Danic - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.nextcloud.talk.controllers.bottomsheet; - -import static com.nextcloud.talk.controllers.bottomsheet.ConversationOperationEnum.OPS_CODE_JOIN_ROOM; -import static com.nextcloud.talk.controllers.bottomsheet.ConversationOperationEnum.OPS_CODE_REMOVE_FAVORITE; - -import android.annotation.SuppressLint; -import android.content.Intent; -import android.net.Uri; -import android.os.Bundle; -import android.text.TextUtils; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.Button; -import android.widget.ImageView; -import android.widget.ProgressBar; -import android.widget.TextView; - -import com.bluelinelabs.conductor.RouterTransaction; -import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler; -import com.bluelinelabs.logansquare.LoganSquare; -import com.nextcloud.talk.R; -import com.nextcloud.talk.api.NcApi; -import com.nextcloud.talk.application.NextcloudTalkApplication; -import com.nextcloud.talk.controllers.base.BaseController; -import com.nextcloud.talk.events.ConversationsListFetchDataEvent; -import com.nextcloud.talk.events.OpenConversationEvent; -import com.nextcloud.talk.models.RetrofitBucket; -import com.nextcloud.talk.models.database.CapabilitiesUtil; -import com.nextcloud.talk.models.database.UserEntity; -import com.nextcloud.talk.models.json.capabilities.Capabilities; -import com.nextcloud.talk.models.json.capabilities.CapabilitiesOverall; -import com.nextcloud.talk.models.json.conversations.Conversation; -import com.nextcloud.talk.models.json.conversations.RoomOverall; -import com.nextcloud.talk.models.json.generic.GenericOverall; -import com.nextcloud.talk.models.json.participants.AddParticipantOverall; -import com.nextcloud.talk.utils.ApiUtils; -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; - -import org.greenrobot.eventbus.EventBus; -import org.parceler.Parcels; - -import java.io.IOException; -import java.util.ArrayList; - -import javax.inject.Inject; - -import androidx.annotation.NonNull; -import autodagger.AutoInjector; -import butterknife.BindView; -import io.reactivex.Observer; -import io.reactivex.android.schedulers.AndroidSchedulers; -import io.reactivex.disposables.Disposable; -import io.reactivex.schedulers.Schedulers; -import retrofit2.HttpException; -import retrofit2.Response; - -@AutoInjector(NextcloudTalkApplication.class) -public class OperationsMenuController extends BaseController { - - private static final String TAG = "OperationsMenuController"; - - @BindView(R.id.progress_bar) - ProgressBar progressBar; - - @BindView(R.id.result_image_view) - ImageView resultImageView; - - @BindView(R.id.result_text_view) - TextView resultsTextView; - - @BindView(R.id.ok_button) - Button okButton; - - @BindView(R.id.web_button) - Button webButton; - - @Inject - NcApi ncApi; - - @Inject - UserUtils userUtils; - - @Inject - EventBus eventBus; - - private ConversationOperationEnum operation; - private Conversation conversation; - - private UserEntity currentUser; - private String callPassword; - private String callUrl; - - private String baseUrl; - private String conversationToken; - - private Disposable disposable; - - private Conversation.ConversationType conversationType; - private ArrayList invitedUsers = new ArrayList<>(); - private ArrayList invitedGroups = new ArrayList<>(); - - private Capabilities serverCapabilities; - private String credentials; - private String conversationName; - - public OperationsMenuController(Bundle args) { - super(args); - this.operation = (ConversationOperationEnum) args.getSerializable(BundleKeys.INSTANCE.getKEY_OPERATION_CODE()); - if (args.containsKey(BundleKeys.INSTANCE.getKEY_ROOM())) { - this.conversation = Parcels.unwrap(args.getParcelable(BundleKeys.INSTANCE.getKEY_ROOM())); - } - - this.callPassword = args.getString(BundleKeys.INSTANCE.getKEY_CONVERSATION_PASSWORD(), ""); - this.callUrl = args.getString(BundleKeys.INSTANCE.getKEY_CALL_URL(), ""); - - if (args.containsKey(BundleKeys.INSTANCE.getKEY_INVITED_PARTICIPANTS())) { - this.invitedUsers = args.getStringArrayList(BundleKeys.INSTANCE.getKEY_INVITED_PARTICIPANTS()); - } - - if (args.containsKey(BundleKeys.INSTANCE.getKEY_INVITED_GROUP())) { - this.invitedGroups = args.getStringArrayList(BundleKeys.INSTANCE.getKEY_INVITED_GROUP()); - } - - if (args.containsKey(BundleKeys.INSTANCE.getKEY_CONVERSATION_TYPE())) { - this.conversationType = Parcels.unwrap(args.getParcelable(BundleKeys.INSTANCE.getKEY_CONVERSATION_TYPE())); - } - - if (args.containsKey(BundleKeys.INSTANCE.getKEY_SERVER_CAPABILITIES())) { - this.serverCapabilities = Parcels.unwrap(args.getParcelable(BundleKeys.INSTANCE.getKEY_SERVER_CAPABILITIES())); - } - - this.conversationName = args.getString(BundleKeys.INSTANCE.getKEY_CONVERSATION_NAME(), ""); - - } - - @NonNull - @Override - protected View inflateView(@NonNull LayoutInflater inflater, @NonNull ViewGroup container) { - return inflater.inflate(R.layout.controller_operations_menu, container, false); - } - - @Override - protected void onViewBound(@NonNull View view) { - super.onViewBound(view); - NextcloudTalkApplication.Companion.getSharedApplication().getComponentApplication().inject(this); - - currentUser = userUtils.getCurrentUser(); - if (!TextUtils.isEmpty(callUrl) && callUrl.contains("/call")) { - conversationToken = callUrl.substring(callUrl.lastIndexOf("/") + 1); - if (callUrl.contains("/index.php")) { - baseUrl = callUrl.substring(0, callUrl.indexOf("/index.php")); - } else { - baseUrl = callUrl.substring(0, callUrl.indexOf("/call")); - } - } - - if (!TextUtils.isEmpty(baseUrl) && !baseUrl.equals(currentUser.getBaseUrl())) { - 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()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(new Observer() { - @Override - public void onSubscribe(@io.reactivex.annotations.NonNull Disposable d) { - } - - @SuppressLint("LongLogTag") - @Override - public void onNext(@io.reactivex.annotations.NonNull 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(@io.reactivex.annotations.NonNull Throwable e) { - showResultImage(false, false); - Log.e(TAG, "Error fetching capabilities for guest", e); - } - - @Override - public void onComplete() { - - } - }); - } - - @SuppressLint("LongLogTag") - private void processOperation() { - RoomOperationsObserver roomOperationsObserver = new RoomOperationsObserver(); - GenericOperationsObserver genericOperationsObserver = new GenericOperationsObserver(); - - 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, ApiUtils.APIv1}); - int chatApiVersion = ApiUtils.getChatApiVersion(currentUser, new int[] {ApiUtils.APIv1}); - - switch (operation) { - case OPS_CODE_RENAME_ROOM: - ncApi.renameRoom(credentials, ApiUtils.getUrlForRoom(apiVersion, currentUser.getBaseUrl(), - conversation.getToken()), - conversation.getName()) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .retry(1) - .subscribe(genericOperationsObserver); - break; - case OPS_CODE_MAKE_PUBLIC: - ncApi.makeRoomPublic(credentials, ApiUtils.getUrlForRoomPublic(apiVersion, currentUser.getBaseUrl(), - conversation.getToken())) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .retry(1) - .subscribe(genericOperationsObserver); - break; - case OPS_CODE_CHANGE_PASSWORD: - case OPS_CODE_CLEAR_PASSWORD: - case OPS_CODE_SET_PASSWORD: - 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(genericOperationsObserver); - break; - case OPS_CODE_MAKE_PRIVATE: - ncApi.makeRoomPrivate(credentials, ApiUtils.getUrlForRoomPublic(apiVersion, - currentUser.getBaseUrl(), - conversation.getToken())) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .retry(1) - .subscribe(genericOperationsObserver); - break; - case OPS_CODE_GET_AND_JOIN_ROOM: - ncApi.getRoom(credentials, ApiUtils.getUrlForRoom(apiVersion, baseUrl, conversationToken)) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .retry(1) - .subscribe(new Observer() { - @Override - public void onSubscribe(@io.reactivex.annotations.NonNull Disposable d) { - disposable = d; - } - - @Override - public void onNext(@io.reactivex.annotations.NonNull RoomOverall roomOverall) { - conversation = roomOverall.getOcs().getData(); - if (conversation.isHasPassword() && conversation.isGuest()) { - eventBus.post(new ConversationsListFetchDataEvent()); - 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.putSerializable(BundleKeys.INSTANCE.getKEY_OPERATION_CODE(), OPS_CODE_JOIN_ROOM); - 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( - @io.reactivex.annotations.NonNull Disposable d - ) { - } - - @Override - public void onNext( - @io.reactivex.annotations.NonNull RoomOverall roomOverall - ) { - conversation = roomOverall.getOcs().getData(); - initiateConversation(); - } - - @Override - public void onError(@io.reactivex.annotations.NonNull Throwable e) { - showResultImage(false, false); - dispose(); - } - - @Override - public void onComplete() { - } - }); - } else { - initiateConversation(); - } - } - - @Override - public void onError(@io.reactivex.annotations.NonNull Throwable e) { - showResultImage(false, false); - dispose(); - } - - @Override - public void onComplete() { - dispose(); - } - }); - break; - case OPS_CODE_INVITE_USERS: - 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", null, invite, conversationName); - } else { - retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(apiVersion, currentUser.getBaseUrl(), - "2", null, invite, conversationName); - } - - ncApi.createRoom(credentials, retrofitBucket.getUrl(), retrofitBucket.getQueryMap()) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .retry(1) - .subscribe(new Observer() { - @Override - public void onSubscribe(@io.reactivex.annotations.NonNull Disposable d) { - - } - - @Override - public void onNext(@io.reactivex.annotations.NonNull 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(@io.reactivex.annotations.NonNull Disposable d) { - - } - - @Override - public void onNext( - @io.reactivex.annotations.NonNull RoomOverall roomOverall - ) { - conversation = roomOverall.getOcs().getData(); - inviteUsersToAConversation(); - } - - @Override - public void onError(@io.reactivex.annotations.NonNull Throwable e) { - showResultImage(false, false); - dispose(); - } - - @Override - public void onComplete() { - - } - }); - - } - - @Override - public void onError(@io.reactivex.annotations.NonNull Throwable e) { - showResultImage(false, false); - dispose(); - } - - @Override - public void onComplete() { - dispose(); - } - }); - - break; - case OPS_CODE_MARK_AS_READ: - ncApi.setChatReadMarker(credentials, - ApiUtils.getUrlForSetChatReadMarker(chatApiVersion, - currentUser.getBaseUrl(), - conversation.getToken()), - conversation.lastMessage.jsonMessageId) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .retry(1) - .subscribe(genericOperationsObserver); - break; - case OPS_CODE_REMOVE_FAVORITE: - case OPS_CODE_ADD_FAVORITE: - if (operation == OPS_CODE_REMOVE_FAVORITE) { - ncApi.removeConversationFromFavorites(credentials, - ApiUtils.getUrlForRoomFavorite(apiVersion, - currentUser.getBaseUrl(), - conversation.getToken())) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .retry(1) - .subscribe(genericOperationsObserver); - } else { - ncApi.addConversationToFavorites(credentials, - ApiUtils.getUrlForRoomFavorite(apiVersion, - currentUser.getBaseUrl(), - conversation.getToken())) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .retry(1) - .subscribe(genericOperationsObserver); - } - break; - case OPS_CODE_JOIN_ROOM: - ncApi.joinRoom(credentials, ApiUtils.getUrlForParticipantsActive(apiVersion, - baseUrl, - conversationToken), - callPassword) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .retry(1) - .subscribe(roomOperationsObserver); - break; - default: - break; - } - } - - private void showResultImage(boolean everythingOK, boolean isGuestSupportError) { - progressBar.setVisibility(View.GONE); - - if (getResources() != null) { - if (everythingOK) { - resultImageView.setImageDrawable(DisplayUtils.getTintedDrawable(getResources(), - R.drawable.ic_check_circle_black_24dp, - R.color.nc_darkGreen)); - } else { - resultImageView.setImageDrawable(DisplayUtils.getTintedDrawable(getResources(), - R.drawable.ic_cancel_black_24dp, - R.color.nc_darkRed)); - } - } - - resultImageView.setVisibility(View.VISIBLE); - - if (everythingOK) { - resultsTextView.setText(R.string.nc_all_ok_operation); - } else { - resultsTextView.setTextColor(getResources().getColor(R.color.nc_darkRed)); - if (!isGuestSupportError) { - resultsTextView.setText(R.string.nc_failed_to_perform_operation); - } else { - resultsTextView.setText(R.string.nc_failed_signaling_settings); - webButton.setOnClickListener(v -> { - Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(callUrl)); - startActivity(browserIntent); - }); - webButton.setVisibility(View.VISIBLE); - } - } - - resultsTextView.setVisibility(View.VISIBLE); - if (everythingOK) { - eventBus.post(new ConversationsListFetchDataEvent()); - } else { - resultImageView.setImageDrawable(DisplayUtils.getTintedDrawable(getResources(), R.drawable - .ic_cancel_black_24dp, R.color.nc_darkRed)); - okButton.setOnClickListener(v -> eventBus.post(new ConversationsListFetchDataEvent())); - okButton.setVisibility(View.VISIBLE); - } - } - - private void dispose() { - if (disposable != null && !disposable.isDisposed()) { - disposable.dispose(); - } - - disposable = null; - } - - @Override - public void onDestroy() { - super.onDestroy(); - dispose(); - } - - 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[] {ApiUtils.APIv3, 2, 1}); - } - - private void inviteUsersToAConversation() { - final ArrayList localInvitedUsers = invitedUsers; - final ArrayList localInvitedGroups = invitedGroups; - if (localInvitedGroups.size() > 0) { - localInvitedGroups.remove(0); - } - - int apiVersion = ApiUtils.getConversationApiVersion(currentUser, new int[] {4, 1}); - - if (localInvitedUsers.size() > 0 || (localInvitedGroups.size() > 0 && - CapabilitiesUtil.hasSpreedFeatureCapability(currentUser, "invite-groups-and-mails"))) { - addGroupsToConversation(localInvitedUsers, localInvitedGroups, apiVersion); - addUsersToConversation(localInvitedUsers, localInvitedGroups, apiVersion); - } else { - initiateConversation(); - } - } - - private void addUsersToConversation( - ArrayList localInvitedUsers, - ArrayList localInvitedGroups, - int apiVersion) - { - RetrofitBucket retrofitBucket; - for (int i = 0; i < localInvitedUsers.size(); i++) { - final String userId = invitedUsers.get(i); - retrofitBucket = ApiUtils.getRetrofitBucketForAddParticipant(apiVersion, - currentUser.getBaseUrl(), - conversation.getToken(), - userId); - - ncApi.addParticipant(credentials, retrofitBucket.getUrl(), retrofitBucket.getQueryMap()) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .retry(1) - .subscribe(new Observer() { - @Override - public void onSubscribe(@io.reactivex.annotations.NonNull Disposable d) { - - } - - @Override - public void onNext( - @io.reactivex.annotations.NonNull AddParticipantOverall addParticipantOverall - ) { - } - - @Override - public void onError(@io.reactivex.annotations.NonNull Throwable e) { - dispose(); - } - - @Override - public void onComplete() { - synchronized (localInvitedUsers) { - localInvitedUsers.remove(userId); - } - - if (localInvitedGroups.size() == 0 && localInvitedUsers.size() == 0) { - initiateConversation(); - } - dispose(); - } - }); - } - } - - private void addGroupsToConversation( - ArrayList localInvitedUsers, - ArrayList localInvitedGroups, - int apiVersion) - { - RetrofitBucket retrofitBucket; - if ((localInvitedGroups.size() > 0 && - CapabilitiesUtil.hasSpreedFeatureCapability(currentUser, "invite-groups-and-mails"))) { - for (int i = 0; i < localInvitedGroups.size(); i++) { - final String groupId = localInvitedGroups.get(i); - retrofitBucket = ApiUtils.getRetrofitBucketForAddParticipantWithSource( - apiVersion, - currentUser.getBaseUrl(), - conversation.getToken(), - "groups", - groupId - ); - - ncApi.addParticipant(credentials, retrofitBucket.getUrl(), retrofitBucket.getQueryMap()) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .retry(1) - .subscribe(new Observer() { - @Override - public void onSubscribe(@io.reactivex.annotations.NonNull Disposable d) { - - } - - @Override - public void onNext( - @io.reactivex.annotations.NonNull AddParticipantOverall addParticipantOverall - ) { - } - - @Override - public void onError(@io.reactivex.annotations.NonNull Throwable e) { - dispose(); - } - - @Override - public void onComplete() { - synchronized (localInvitedGroups) { - localInvitedGroups.remove(groupId); - } - - if (localInvitedGroups.size() == 0 && localInvitedUsers.size() == 0) { - initiateConversation(); - } - dispose(); - } - }); - - } - } - } - - private void initiateConversation() { - eventBus.post(new ConversationsListFetchDataEvent()); - - 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()); - 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); - - eventBus.post(new OpenConversationEvent(conversation, bundle)); - } - - private void handleObserverError(@io.reactivex.annotations.NonNull Throwable e) { - if (operation != OPS_CODE_JOIN_ROOM || !(e instanceof HttpException)) { - showResultImage(false, false); - } else { - Response response = ((HttpException) e).response(); - if (response != null && response.code() == 403) { - ApplicationWideMessageHolder.getInstance().setMessageType(ApplicationWideMessageHolder.MessageType.CALL_PASSWORD_WRONG); - getRouter().popCurrentController(); - } else { - showResultImage(false, false); - } - } - dispose(); - } - - private class GenericOperationsObserver implements Observer { - - @Override - public void onSubscribe(@io.reactivex.annotations.NonNull Disposable d) { - disposable = d; - } - - @Override - public void onNext(@io.reactivex.annotations.NonNull GenericOverall genericOverall) { - if (operation != OPS_CODE_JOIN_ROOM) { - showResultImage(true, false); - } else { - throw new IllegalArgumentException("Unsupported operation code observed!"); - } - } - - @Override - public void onError(@io.reactivex.annotations.NonNull Throwable e) { - handleObserverError(e); - } - - @Override - public void onComplete() { - dispose(); - } - } - - private class RoomOperationsObserver implements Observer { - - @Override - public void onSubscribe(@io.reactivex.annotations.NonNull Disposable d) { - disposable = d; - } - - @Override - public void onNext(@io.reactivex.annotations.NonNull RoomOverall roomOverall) { - conversation = roomOverall.getOcs().getData(); - if (operation != OPS_CODE_JOIN_ROOM) { - showResultImage(true, false); - } else { - conversation = roomOverall.getOcs().getData(); - initiateConversation(); - } - } - - @Override - public void onError(@io.reactivex.annotations.NonNull Throwable e) { - handleObserverError(e); - } - - @Override - public void onComplete() { - dispose(); - } - } - - @Override - public AppBarLayoutType getAppBarLayoutType() { - return AppBarLayoutType.SEARCH_BAR; - } -} diff --git a/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/OperationsMenuController.kt b/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/OperationsMenuController.kt new file mode 100644 index 000000000..5f0c587a3 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/controllers/bottomsheet/OperationsMenuController.kt @@ -0,0 +1,819 @@ +/* + * Nextcloud Talk application + * + * @author Mario Danic + * @author Andy Scherzinger + * Copyright (C) 2022 Andy Scherzinger + * Copyright (C) 2017 Mario Danic + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.nextcloud.talk.controllers.bottomsheet + +import android.content.Intent +import android.net.Uri +import android.os.Bundle +import android.text.TextUtils +import android.util.Log +import android.view.View +import autodagger.AutoInjector +import com.bluelinelabs.conductor.RouterTransaction +import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler +import com.bluelinelabs.logansquare.LoganSquare +import com.nextcloud.talk.R +import com.nextcloud.talk.api.NcApi +import com.nextcloud.talk.application.NextcloudTalkApplication +import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication +import com.nextcloud.talk.controllers.base.NewBaseController +import com.nextcloud.talk.controllers.util.viewBinding +import com.nextcloud.talk.databinding.ControllerOperationsMenuBinding +import com.nextcloud.talk.events.ConversationsListFetchDataEvent +import com.nextcloud.talk.events.OpenConversationEvent +import com.nextcloud.talk.models.RetrofitBucket +import com.nextcloud.talk.models.database.CapabilitiesUtil +import com.nextcloud.talk.models.database.UserEntity +import com.nextcloud.talk.models.json.capabilities.Capabilities +import com.nextcloud.talk.models.json.capabilities.CapabilitiesOverall +import com.nextcloud.talk.models.json.conversations.Conversation +import com.nextcloud.talk.models.json.conversations.Conversation.ConversationType +import com.nextcloud.talk.models.json.conversations.RoomOverall +import com.nextcloud.talk.models.json.generic.GenericOverall +import com.nextcloud.talk.models.json.participants.AddParticipantOverall +import com.nextcloud.talk.utils.ApiUtils +import com.nextcloud.talk.utils.DisplayUtils +import com.nextcloud.talk.utils.NoSupportedApiException +import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ACTIVE_CONVERSATION +import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_CALL_URL +import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_CONVERSATION_NAME +import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_CONVERSATION_PASSWORD +import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_CONVERSATION_TYPE +import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_INVITED_GROUP +import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_INVITED_PARTICIPANTS +import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_OPERATION_CODE +import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM +import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_ID +import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_TOKEN +import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_SERVER_CAPABILITIES +import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_USER_ENTITY +import com.nextcloud.talk.utils.database.user.UserUtils +import com.nextcloud.talk.utils.singletons.ApplicationWideMessageHolder +import io.reactivex.Observer +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.disposables.Disposable +import io.reactivex.schedulers.Schedulers +import org.greenrobot.eventbus.EventBus +import org.parceler.Parcels +import retrofit2.HttpException +import java.io.IOException +import java.util.Collections +import javax.inject.Inject + +@AutoInjector(NextcloudTalkApplication::class) +class OperationsMenuController(args: Bundle) : NewBaseController( + R.layout.controller_operations_menu, + args +) { + private val binding: ControllerOperationsMenuBinding by viewBinding(ControllerOperationsMenuBinding::bind) + + @Inject + lateinit var ncApi: NcApi + + @Inject + lateinit var userUtils: UserUtils + + @Inject + lateinit var eventBus: EventBus + + private val operation: ConversationOperationEnum? + private var conversation: Conversation? = null + private var currentUser: UserEntity? = null + private val callPassword: String + private val callUrl: String + private var baseUrl: String? = null + private var conversationToken: String? = null + private var disposable: Disposable? = null + private var conversationType: ConversationType? = null + private var invitedUsers: ArrayList? = ArrayList() + private var invitedGroups: ArrayList? = ArrayList() + private var serverCapabilities: Capabilities? = null + private var credentials: String? = null + private val conversationName: String + + override val appBarLayoutType: AppBarLayoutType + get() = AppBarLayoutType.SEARCH_BAR + + override fun onViewBound(view: View) { + super.onViewBound(view) + sharedApplication!!.componentApplication.inject(this) + currentUser = userUtils.currentUser + + if (!TextUtils.isEmpty(callUrl) && callUrl.contains("/call")) { + conversationToken = callUrl.substring(callUrl.lastIndexOf("/") + 1) + if (callUrl.contains("/index.php")) { + baseUrl = callUrl.substring(0, callUrl.indexOf("/index.php")) + } else { + baseUrl = callUrl.substring(0, callUrl.indexOf("/call")) + } + } + if (!TextUtils.isEmpty(baseUrl) && baseUrl != currentUser!!.baseUrl) { + if (serverCapabilities != null) { + try { + useBundledCapabilitiesForGuest() + } catch (e: IOException) { + // Fall back to fetching capabilities again + fetchCapabilitiesForGuest() + } + } else { + fetchCapabilitiesForGuest() + } + } else { + processOperation() + } + } + + @Throws(IOException::class) + private fun useBundledCapabilitiesForGuest() { + currentUser = UserEntity() + currentUser!!.baseUrl = baseUrl + currentUser!!.userId = "?" + try { + currentUser!!.capabilities = LoganSquare.serialize(serverCapabilities) + } catch (e: IOException) { + Log.e("OperationsMenu", "Failed to serialize capabilities") + throw e + } + try { + checkCapabilities(currentUser!!) + processOperation() + } catch (e: NoSupportedApiException) { + showResultImage(everythingOK = false, isGuestSupportError = false) + Log.d(TAG, "No supported server version found", e) + } + } + + private fun fetchCapabilitiesForGuest() { + ncApi.getCapabilities(null, ApiUtils.getUrlForCapabilities(baseUrl)) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } + + override fun onNext(capabilitiesOverall: CapabilitiesOverall) { + currentUser = UserEntity() + currentUser!!.baseUrl = baseUrl + currentUser!!.userId = "?" + try { + currentUser!!.capabilities = LoganSquare + .serialize(capabilitiesOverall.ocs!!.data!!.capabilities) + } catch (e: IOException) { + Log.e("OperationsMenu", "Failed to serialize capabilities") + } + try { + checkCapabilities(currentUser!!) + processOperation() + } catch (e: NoSupportedApiException) { + showResultImage(everythingOK = false, isGuestSupportError = false) + Log.d(TAG, "No supported server version found", e) + } + } + + override fun onError(e: Throwable) { + showResultImage(everythingOK = false, isGuestSupportError = false) + Log.e(TAG, "Error fetching capabilities for guest", e) + } + + override fun onComplete() { + // unused atm + } + }) + } + + @Suppress("Detekt.ComplexMethod") + private fun processOperation() { + if (currentUser == null) { + showResultImage(everythingOK = false, isGuestSupportError = true) + Log.e(TAG, "Ended up in processOperation without a valid currentUser") + return + } + credentials = ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token) + when (operation) { + ConversationOperationEnum.OPS_CODE_RENAME_ROOM -> operationRenameRoom() + ConversationOperationEnum.OPS_CODE_MAKE_PUBLIC -> operationMakePublic() + ConversationOperationEnum.OPS_CODE_CHANGE_PASSWORD, + ConversationOperationEnum.OPS_CODE_CLEAR_PASSWORD, + ConversationOperationEnum.OPS_CODE_SET_PASSWORD -> operationChangePassword() + ConversationOperationEnum.OPS_CODE_MAKE_PRIVATE -> operationMakePrivate() + ConversationOperationEnum.OPS_CODE_GET_AND_JOIN_ROOM -> operationGetAndJoinRoom() + ConversationOperationEnum.OPS_CODE_INVITE_USERS -> operationInviteUsers() + ConversationOperationEnum.OPS_CODE_MARK_AS_READ -> operationMarkAsRead() + ConversationOperationEnum.OPS_CODE_REMOVE_FAVORITE, + ConversationOperationEnum.OPS_CODE_ADD_FAVORITE -> operationToggleFavorite() + ConversationOperationEnum.OPS_CODE_JOIN_ROOM -> operationJoinRoom() + else -> { + } + } + } + + private fun apiVersion(): Int { + return ApiUtils.getConversationApiVersion(currentUser, intArrayOf(ApiUtils.APIv4, ApiUtils.APIv1)) + } + + private fun chatApiVersion(): Int { + return ApiUtils.getChatApiVersion(currentUser, intArrayOf(ApiUtils.APIv1)) + } + + private fun operationJoinRoom() { + ncApi.joinRoom( + credentials, + ApiUtils.getUrlForParticipantsActive( + apiVersion(), + baseUrl, + conversationToken + ), + callPassword + ) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .retry(1) + .subscribe(RoomOperationsObserver()) + } + + private fun operationMarkAsRead() { + ncApi.setChatReadMarker( + credentials, + ApiUtils.getUrlForSetChatReadMarker( + chatApiVersion(), + currentUser!!.baseUrl, + conversation!!.getToken() + ), + conversation!!.lastMessage.jsonMessageId + ) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .retry(1) + .subscribe(GenericOperationsObserver()) + } + + private fun operationMakePrivate() { + ncApi.makeRoomPrivate( + credentials, + ApiUtils.getUrlForRoomPublic( + apiVersion(), + currentUser!!.baseUrl, + conversation!!.getToken() + ) + ) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .retry(1) + .subscribe(GenericOperationsObserver()) + } + + private fun operationChangePassword() { + var pass: String? = "" + if (conversation!!.getPassword() != null) { + pass = conversation!!.getPassword() + } + ncApi.setPassword( + credentials, + ApiUtils.getUrlForRoomPassword( + apiVersion(), + currentUser!!.baseUrl, + conversation!!.getToken() + ), + pass + ) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .retry(1) + .subscribe(GenericOperationsObserver()) + } + + private fun operationMakePublic() { + ncApi.makeRoomPublic( + credentials, + ApiUtils.getUrlForRoomPublic( + apiVersion(), + currentUser!!.baseUrl, + conversation!!.getToken() + ) + ) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .retry(1) + .subscribe(GenericOperationsObserver()) + } + + private fun operationRenameRoom() { + ncApi.renameRoom( + credentials, + ApiUtils.getUrlForRoom( + apiVersion(), + currentUser!!.baseUrl, + conversation!!.getToken() + ), + conversation!!.getName() + ) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .retry(1) + .subscribe(GenericOperationsObserver()) + } + + private fun operationToggleFavorite() { + val genericOperationsObserver = GenericOperationsObserver() + val apiVersion = apiVersion() + if (operation === ConversationOperationEnum.OPS_CODE_REMOVE_FAVORITE) { + ncApi.removeConversationFromFavorites( + credentials, + ApiUtils.getUrlForRoomFavorite( + apiVersion, + currentUser!!.baseUrl, + conversation!!.getToken() + ) + ) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .retry(1) + .subscribe(genericOperationsObserver) + } else { + ncApi.addConversationToFavorites( + credentials, + ApiUtils.getUrlForRoomFavorite( + apiVersion, + currentUser!!.baseUrl, + conversation!!.getToken() + ) + ) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .retry(1) + .subscribe(genericOperationsObserver) + } + } + + private fun operationInviteUsers() { + val retrofitBucket: RetrofitBucket + val apiVersion = apiVersion() + var invite: String? = null + if (invitedGroups!!.size > 0) { + invite = invitedGroups!![0] + } + retrofitBucket = if (conversationType == ConversationType.ROOM_PUBLIC_CALL) { + ApiUtils.getRetrofitBucketForCreateRoom( + apiVersion, + currentUser!!.baseUrl, + "3", + null, + invite, + conversationName + ) + } else { + ApiUtils.getRetrofitBucketForCreateRoom( + apiVersion, + currentUser!!.baseUrl, + "2", + null, + invite, + conversationName + ) + } + ncApi.createRoom(credentials, retrofitBucket.getUrl(), retrofitBucket.getQueryMap()) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .retry(1) + .subscribe(object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } + + override fun onNext(roomOverall: RoomOverall) { + conversation = roomOverall.getOcs().getData() + ncApi.getRoom( + credentials, + ApiUtils.getUrlForRoom( + apiVersion, currentUser!!.baseUrl, + conversation!!.getToken() + ) + ) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } + + override fun onNext( + roomOverall: RoomOverall + ) { + conversation = roomOverall.getOcs().getData() + inviteUsersToAConversation() + } + + override fun onError(e: Throwable) { + showResultImage(everythingOK = false, isGuestSupportError = false) + dispose() + } + + override fun onComplete() { + // unused atm + } + }) + } + + override fun onError(e: Throwable) { + showResultImage(everythingOK = false, isGuestSupportError = false) + dispose() + } + + override fun onComplete() { + dispose() + } + }) + } + + private fun operationGetAndJoinRoom() { + val apiVersion = apiVersion() + ncApi.getRoom( + credentials, + ApiUtils.getUrlForRoom(apiVersion, baseUrl, conversationToken) + ) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .retry(1) + .subscribe(object : Observer { + override fun onSubscribe(d: Disposable) { + disposable = d + } + + override fun onNext(roomOverall: RoomOverall) { + conversation = roomOverall.getOcs().getData() + if (conversation!!.isHasPassword && conversation!!.isGuest) { + eventBus.post(ConversationsListFetchDataEvent()) + val bundle = Bundle() + bundle.putParcelable(KEY_ROOM, Parcels.wrap(conversation)) + bundle.putString(KEY_CALL_URL, callUrl) + try { + bundle.putParcelable( + KEY_SERVER_CAPABILITIES, + Parcels.wrap( + LoganSquare.parse( + currentUser!!.capabilities, + Capabilities::class.java + ) + ) + ) + } catch (e: IOException) { + Log.e(TAG, "Failed to parse capabilities for guest") + showResultImage(everythingOK = false, isGuestSupportError = false) + } + bundle.putSerializable(KEY_OPERATION_CODE, ConversationOperationEnum.OPS_CODE_JOIN_ROOM) + router.pushController( + RouterTransaction.with(EntryMenuController(bundle)) + .pushChangeHandler(HorizontalChangeHandler()) + .popChangeHandler(HorizontalChangeHandler()) + ) + } else if (conversation!!.isGuest) { + ncApi.joinRoom( + credentials, + ApiUtils.getUrlForParticipantsActive( + apiVersion, + baseUrl, + conversationToken + ), + null + ) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } + + override fun onNext(roomOverall: RoomOverall) { + conversation = roomOverall.getOcs().getData() + initiateConversation() + } + + override fun onError(e: Throwable) { + showResultImage(everythingOK = false, isGuestSupportError = false) + dispose() + } + + override fun onComplete() { + // unused atm + } + }) + } else { + initiateConversation() + } + } + + override fun onError(e: Throwable) { + showResultImage(everythingOK = false, isGuestSupportError = false) + dispose() + } + + override fun onComplete() { + dispose() + } + }) + } + + @Suppress("Detekt.TooGenericExceptionCaught") + private fun showResultImage(everythingOK: Boolean, isGuestSupportError: Boolean) { + try { + binding.progressBar.visibility = View.GONE + if (resources != null) { + if (everythingOK) { + binding.resultImageView.setImageDrawable( + DisplayUtils.getTintedDrawable( + resources, + R.drawable.ic_check_circle_black_24dp, + R.color.nc_darkGreen + ) + ) + } else { + binding.resultImageView.setImageDrawable( + DisplayUtils.getTintedDrawable( + resources, + R.drawable.ic_cancel_black_24dp, + R.color.nc_darkRed + ) + ) + } + } + binding.resultImageView.visibility = View.VISIBLE + if (everythingOK) { + binding.resultTextView.setText(R.string.nc_all_ok_operation) + } else { + binding.resultTextView.setTextColor(resources!!.getColor(R.color.nc_darkRed)) + if (!isGuestSupportError) { + binding.resultTextView.setText(R.string.nc_failed_to_perform_operation) + } else { + binding.resultTextView.setText(R.string.nc_failed_signaling_settings) + binding.webButton.setOnClickListener { + val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(callUrl)) + startActivity(browserIntent) + } + binding.webButton.visibility = View.VISIBLE + } + } + binding.resultTextView.visibility = View.VISIBLE + if (everythingOK) { + eventBus!!.post(ConversationsListFetchDataEvent()) + } else { + binding.resultImageView.setImageDrawable( + DisplayUtils.getTintedDrawable( + resources, + R.drawable.ic_cancel_black_24dp, + R.color.nc_darkRed + ) + ) + binding.okButton.setOnClickListener { v: View? -> eventBus!!.post(ConversationsListFetchDataEvent()) } + binding.okButton.visibility = View.VISIBLE + } + } catch (npe: NullPointerException) { + Log.i(TAG, "Controller already closed", npe) + } + } + + private fun dispose() { + if (disposable != null && !disposable!!.isDisposed) { + disposable!!.dispose() + } + disposable = null + } + + public override fun onDestroy() { + super.onDestroy() + dispose() + } + + @kotlin.Throws(NoSupportedApiException::class) + private fun checkCapabilities(currentUser: UserEntity) { + ApiUtils.getConversationApiVersion(currentUser, intArrayOf(ApiUtils.APIv4, 1)) + ApiUtils.getCallApiVersion(currentUser, intArrayOf(ApiUtils.APIv4, 1)) + ApiUtils.getChatApiVersion(currentUser, intArrayOf(1)) + ApiUtils.getSignalingApiVersion(currentUser, intArrayOf(ApiUtils.APIv3, 2, 1)) + } + + private fun inviteUsersToAConversation() { + val localInvitedUsers = invitedUsers + val localInvitedGroups = invitedGroups + if (localInvitedGroups!!.size > 0) { + localInvitedGroups.removeAt(0) + } + val apiVersion = ApiUtils.getConversationApiVersion(currentUser, API_CONVERSATION_VERSIONS) + if (localInvitedUsers!!.size > 0 || localInvitedGroups.size > 0 && + CapabilitiesUtil.hasSpreedFeatureCapability(currentUser, "invite-groups-and-mails") + ) { + addGroupsToConversation(localInvitedUsers, localInvitedGroups, apiVersion) + addUsersToConversation(localInvitedUsers, localInvitedGroups, apiVersion) + } else { + initiateConversation() + } + } + + private fun addUsersToConversation( + localInvitedUsers: ArrayList?, + localInvitedGroups: ArrayList?, + apiVersion: Int + ) { + var retrofitBucket: RetrofitBucket + for (i in localInvitedUsers!!.indices) { + val userId = invitedUsers!![i] + retrofitBucket = ApiUtils.getRetrofitBucketForAddParticipant( + apiVersion, + currentUser!!.baseUrl, + conversation!!.getToken(), + userId + ) + ncApi.addParticipant(credentials, retrofitBucket.getUrl(), retrofitBucket.getQueryMap()) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .retry(1) + .subscribe(object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } + override fun onNext(addParticipantOverall: AddParticipantOverall) { + // unused atm + } + + override fun onError(e: Throwable) { + dispose() + } + + override fun onComplete() { + Collections.synchronizedList(localInvitedUsers).remove(userId) + if (localInvitedGroups!!.size == 0 && localInvitedUsers.size == 0) { + initiateConversation() + } + dispose() + } + }) + } + } + + private fun addGroupsToConversation( + localInvitedUsers: ArrayList?, + localInvitedGroups: ArrayList?, + apiVersion: Int + ) { + var retrofitBucket: RetrofitBucket + if (localInvitedGroups!!.size > 0 && + CapabilitiesUtil.hasSpreedFeatureCapability(currentUser, "invite-groups-and-mails") + ) { + for (i in localInvitedGroups.indices) { + val groupId = localInvitedGroups[i] + retrofitBucket = ApiUtils.getRetrofitBucketForAddParticipantWithSource( + apiVersion, + currentUser!!.baseUrl, + conversation!!.getToken(), + "groups", + groupId + ) + ncApi.addParticipant(credentials, retrofitBucket.getUrl(), retrofitBucket.getQueryMap()) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .retry(1) + .subscribe(object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } + override fun onNext(addParticipantOverall: AddParticipantOverall) { + // unused atm + } + + override fun onError(e: Throwable) { + dispose() + } + + override fun onComplete() { + Collections.synchronizedList(localInvitedGroups).remove(groupId) + if (localInvitedGroups.size == 0 && localInvitedUsers!!.size == 0) { + initiateConversation() + } + dispose() + } + }) + } + } + } + + private fun initiateConversation() { + eventBus.post(ConversationsListFetchDataEvent()) + val bundle = Bundle() + bundle.putString(KEY_ROOM_TOKEN, conversation!!.getToken()) + bundle.putString(KEY_ROOM_ID, conversation!!.getRoomId()) + bundle.putString(KEY_CONVERSATION_NAME, conversation!!.getDisplayName()) + bundle.putParcelable(KEY_USER_ENTITY, currentUser) + bundle.putParcelable(KEY_ACTIVE_CONVERSATION, Parcels.wrap(conversation)) + bundle.putString(KEY_CONVERSATION_PASSWORD, callPassword) + eventBus.post(OpenConversationEvent(conversation, bundle)) + } + + private fun handleObserverError(e: Throwable) { + if (operation !== ConversationOperationEnum.OPS_CODE_JOIN_ROOM || e !is HttpException) { + showResultImage(everythingOK = false, isGuestSupportError = false) + } else { + val response = e.response() + if (response != null && response.code() == FORBIDDEN) { + ApplicationWideMessageHolder.getInstance() + .setMessageType(ApplicationWideMessageHolder.MessageType.CALL_PASSWORD_WRONG) + router.popCurrentController() + } else { + showResultImage(everythingOK = false, isGuestSupportError = false) + } + } + dispose() + } + + private inner class GenericOperationsObserver : Observer { + override fun onSubscribe(d: Disposable) { + disposable = d + } + + override fun onNext(genericOverall: GenericOverall) { + if (operation !== ConversationOperationEnum.OPS_CODE_JOIN_ROOM) { + showResultImage(everythingOK = true, isGuestSupportError = false) + } else { + throw IllegalArgumentException("Unsupported operation code observed!") + } + } + + override fun onError(e: Throwable) { + handleObserverError(e) + } + + override fun onComplete() { + dispose() + } + } + + private inner class RoomOperationsObserver : Observer { + override fun onSubscribe(d: Disposable) { + disposable = d + } + + override fun onNext(roomOverall: RoomOverall) { + conversation = roomOverall.getOcs().getData() + if (operation !== ConversationOperationEnum.OPS_CODE_JOIN_ROOM) { + showResultImage(everythingOK = true, isGuestSupportError = false) + } else { + conversation = roomOverall.getOcs().getData() + initiateConversation() + } + } + + override fun onError(e: Throwable) { + handleObserverError(e) + } + + override fun onComplete() { + dispose() + } + } + + companion object { + private const val TAG = "OperationsMenu" + private const val FORBIDDEN = 403 + private val API_CONVERSATION_VERSIONS = intArrayOf(4, 1) + } + + init { + operation = args.getSerializable(KEY_OPERATION_CODE) as ConversationOperationEnum? + if (args.containsKey(KEY_ROOM)) { + conversation = Parcels.unwrap(args.getParcelable(KEY_ROOM)) + } + callPassword = args.getString(KEY_CONVERSATION_PASSWORD, "") + callUrl = args.getString(KEY_CALL_URL, "") + if (args.containsKey(KEY_INVITED_PARTICIPANTS)) { + invitedUsers = args.getStringArrayList(KEY_INVITED_PARTICIPANTS) + } + if (args.containsKey(KEY_INVITED_GROUP)) { + invitedGroups = args.getStringArrayList(KEY_INVITED_GROUP) + } + if (args.containsKey(KEY_CONVERSATION_TYPE)) { + conversationType = Parcels.unwrap(args.getParcelable(KEY_CONVERSATION_TYPE)) + } + if (args.containsKey(KEY_SERVER_CAPABILITIES)) { + serverCapabilities = Parcels.unwrap(args.getParcelable(KEY_SERVER_CAPABILITIES)) + } + conversationName = args.getString(KEY_CONVERSATION_NAME, "") + } +} diff --git a/scripts/analysis/findbugs-results.txt b/scripts/analysis/findbugs-results.txt index 8e9088e93..7b89b2202 100644 --- a/scripts/analysis/findbugs-results.txt +++ b/scripts/analysis/findbugs-results.txt @@ -1 +1 @@ -411 \ No newline at end of file +406 \ No newline at end of file diff --git a/scripts/analysis/lint-results.txt b/scripts/analysis/lint-results.txt index 8c4822eac..b44244c6f 100644 --- a/scripts/analysis/lint-results.txt +++ b/scripts/analysis/lint-results.txt @@ -1,2 +1,2 @@ DO NOT TOUCH; GENERATED BY DRONE - Lint Report: 1 error and 149 warnings + Lint Report: 1 error and 144 warnings