| /* |
| * Copyright (C) 2004-2020 Savoir-faire Linux Inc. |
| * |
| * Author: Hadrien De Sousa <hadrien.desousa@savoirfairelinux.com> |
| * Author: Adrien BĂ©raud <adrien.beraud@savoirfairelinux.com> |
| * |
| * 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, write to the Free Software |
| * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
| */ |
| package cx.ring.call; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.Objects; |
| import java.util.concurrent.TimeUnit; |
| |
| import javax.inject.Inject; |
| import javax.inject.Named; |
| |
| import cx.ring.facades.ConversationFacade; |
| import cx.ring.model.Conference; |
| import cx.ring.model.Conversation; |
| import cx.ring.model.SipCall; |
| import cx.ring.model.Uri; |
| import cx.ring.mvp.RootPresenter; |
| import cx.ring.services.AccountService; |
| import cx.ring.services.CallService; |
| import cx.ring.services.ContactService; |
| import cx.ring.services.DeviceRuntimeService; |
| import cx.ring.services.HardwareService; |
| import cx.ring.services.PreferencesService; |
| import cx.ring.utils.Log; |
| import cx.ring.utils.StringUtils; |
| import io.reactivex.Maybe; |
| import io.reactivex.Observable; |
| import io.reactivex.Observer; |
| import io.reactivex.Scheduler; |
| import io.reactivex.disposables.Disposable; |
| import io.reactivex.subjects.BehaviorSubject; |
| import io.reactivex.subjects.Subject; |
| |
| import static cx.ring.daemon.Ringservice.listCallMediaHandlers; |
| |
| public class CallPresenter extends RootPresenter<CallView> { |
| |
| public final static String TAG = CallPresenter.class.getSimpleName(); |
| |
| private AccountService mAccountService; |
| private ContactService mContactService; |
| private HardwareService mHardwareService; |
| private CallService mCallService; |
| private DeviceRuntimeService mDeviceRuntimeService; |
| private ConversationFacade mConversationFacade; |
| |
| private Conference mConference; |
| private final List<SipCall> mPendingCalls = new ArrayList<>(); |
| private final Subject<List<SipCall>> mPendingSubject = BehaviorSubject.createDefault(mPendingCalls); |
| private final PreferencesService mPreferencesService; |
| |
| private boolean mOnGoingCall = false; |
| private boolean mAudioOnly = true; |
| private boolean permissionChanged = false; |
| private boolean pipIsActive = false; |
| private boolean incomingIsFullIntent = true; |
| private boolean callInitialized = false; |
| |
| private int videoWidth = -1; |
| private int videoHeight = -1; |
| private int previewWidth = -1; |
| private int previewHeight = -1; |
| private String currentSurfaceId = null; |
| private String currentPluginSurfaceId = null; |
| |
| private Disposable timeUpdateTask = null; |
| |
| @Inject |
| @Named("UiScheduler") |
| protected Scheduler mUiScheduler; |
| |
| @Inject |
| public CallPresenter(AccountService accountService, |
| ContactService contactService, |
| HardwareService hardwareService, |
| CallService callService, |
| DeviceRuntimeService deviceRuntimeService, |
| ConversationFacade conversationFacade, |
| PreferencesService preferencesService) { |
| mAccountService = accountService; |
| mContactService = contactService; |
| mHardwareService = hardwareService; |
| mCallService = callService; |
| mDeviceRuntimeService = deviceRuntimeService; |
| mConversationFacade = conversationFacade; |
| mPreferencesService = preferencesService; |
| } |
| |
| public void cameraPermissionChanged(boolean isGranted) { |
| if (isGranted && mHardwareService.isVideoAvailable()) { |
| mHardwareService.initVideo() |
| .onErrorComplete() |
| .blockingAwait(); |
| permissionChanged = true; |
| } |
| } |
| |
| public void audioPermissionChanged(boolean isGranted) { |
| if (isGranted && mHardwareService.hasMicrophone()) { |
| mCallService.restartAudioLayer(); |
| } |
| } |
| |
| |
| @Override |
| public void unbindView() { |
| if (!mAudioOnly) { |
| mHardwareService.endCapture(); |
| } |
| super.unbindView(); |
| } |
| |
| @Override |
| public void bindView(CallView view) { |
| super.bindView(view); |
| /*mCompositeDisposable.add(mAccountService.getRegisteredNames() |
| .observeOn(mUiScheduler) |
| .subscribe(r -> { |
| if (mSipCall != null && mSipCall.getContact() != null) { |
| getView().updateContactBubble(mSipCall.getContact()); |
| } |
| }));*/ |
| mCompositeDisposable.add(mHardwareService.getVideoEvents() |
| .observeOn(mUiScheduler) |
| .subscribe(this::onVideoEvent)); |
| mCompositeDisposable.add(mHardwareService.getAudioState() |
| .observeOn(mUiScheduler) |
| .subscribe(state -> getView().updateAudioState(state))); |
| |
| /*mCompositeDisposable.add(mHardwareService |
| .getBluetoothEvents() |
| .subscribe(event -> { |
| if (!event.connected && mSipCall == null) { |
| hangupCall(); |
| } |
| }));*/ |
| } |
| |
| public void initOutGoing(String accountId, String contactRingId, boolean audioOnly) { |
| if (accountId == null || contactRingId == null) { |
| Log.e(TAG, "initOutGoing: null account or contact"); |
| hangupCall(); |
| return; |
| } |
| if (!mHardwareService.hasCamera()) { |
| audioOnly = true; |
| } |
| //getView().blockScreenRotation(); |
| |
| Observable<Conference> callObservable = mCallService |
| .placeCall(accountId, StringUtils.toNumber(contactRingId), audioOnly) |
| //.map(mCallService::getConference) |
| .flatMapObservable(call -> mCallService.getConfUpdates(call)) |
| .share(); |
| |
| mCompositeDisposable.add(callObservable |
| .observeOn(mUiScheduler) |
| .subscribe(conference -> { |
| contactUpdate(conference); |
| confUpdate(conference); |
| }, e -> { |
| hangupCall(); |
| Log.e(TAG, "Error with initOutgoing: " + e.getMessage()); |
| })); |
| |
| showConference(callObservable); |
| } |
| |
| /** |
| * Returns to or starts an incoming call |
| * |
| * @param confId the call id |
| * @param actionViewOnly true if only returning to call or if using full screen intent |
| */ |
| public void initIncomingCall(String confId, boolean actionViewOnly) { |
| //getView().blockScreenRotation(); |
| |
| // if the call is incoming through a full intent, this allows the incoming call to display |
| incomingIsFullIntent = actionViewOnly; |
| |
| Observable<Conference> callObservable = mCallService.getConfUpdates(confId) |
| .observeOn(mUiScheduler) |
| .share(); |
| |
| // Handles the case where the call has been accepted, emits a single so as to only check for permissions and start the call once |
| mCompositeDisposable.add(callObservable |
| .firstOrError() |
| .subscribe(call -> { |
| if (!actionViewOnly) { |
| contactUpdate(call); |
| confUpdate(call); |
| callInitialized = true; |
| getView().prepareCall(true); |
| } |
| }, e -> { |
| hangupCall(); |
| Log.e(TAG, "Error with initIncoming, preparing call flow :" , e); |
| })); |
| |
| // Handles retrieving call updates. Items emitted are only used if call is already in process or if user is returning to a call. |
| mCompositeDisposable.add(callObservable |
| .subscribe(call -> { |
| if (callInitialized || actionViewOnly) { |
| contactUpdate(call); |
| confUpdate(call); |
| } |
| }, e -> { |
| hangupCall(); |
| Log.e(TAG, "Error with initIncoming, action view flow: ", e); |
| })); |
| |
| showConference(callObservable); |
| } |
| |
| private void showConference(Observable<Conference> conference) { |
| mCompositeDisposable.add(conference |
| .distinctUntilChanged() |
| .switchMap(Conference::getParticipantInfo) |
| .observeOn(mUiScheduler) |
| .subscribe(info -> getView().updateConfInfo(info), |
| e -> Log.e(TAG, "Error with initIncoming, action view flow: ", e))); |
| } |
| |
| public void prepareOptionMenu() { |
| boolean isSpeakerOn = mHardwareService.isSpeakerPhoneOn(); |
| //boolean hasContact = mSipCall != null && null != mSipCall.getContact() && mSipCall.getContact().isUnknown(); |
| boolean canDial = mOnGoingCall && mConference != null; |
| // get the preferences |
| boolean displayPluginsButton = getView().displayPluginsButton(); |
| boolean showPluginBtn = displayPluginsButton && mOnGoingCall && mConference != null; |
| boolean hasMultipleCamera = mHardwareService.getCameraCount() > 1 && mOnGoingCall && !mAudioOnly; |
| getView().initMenu(isSpeakerOn, hasMultipleCamera, canDial, showPluginBtn, mOnGoingCall); |
| } |
| |
| public void chatClick() { |
| if (mConference == null || mConference.getParticipants().isEmpty()) { |
| return; |
| } |
| SipCall firstCall = mConference.getParticipants().get(0); |
| if (firstCall == null |
| || firstCall.getContact() == null |
| || firstCall.getContact().getIds() == null |
| || firstCall.getContact().getIds().isEmpty()) { |
| return; |
| } |
| getView().goToConversation(firstCall.getAccount(), firstCall.getContact().getIds().get(0)); |
| } |
| |
| public void speakerClick(boolean checked) { |
| mHardwareService.toggleSpeakerphone(checked); |
| } |
| |
| public void muteMicrophoneToggled(boolean checked) { |
| mCallService.setMuted(checked); |
| } |
| |
| |
| public boolean isMicrophoneMuted() { |
| return mCallService.isCaptureMuted(); |
| } |
| |
| public void switchVideoInputClick() { |
| if(mConference == null) |
| return; |
| mHardwareService.switchInput(mConference.getId(), false); |
| getView().switchCameraIcon(mHardwareService.isPreviewFromFrontCamera()); |
| } |
| |
| public void configurationChanged(int rotation) { |
| mHardwareService.setDeviceOrientation(rotation); |
| } |
| |
| public void dialpadClick() { |
| getView().displayDialPadKeyboard(); |
| } |
| |
| public void acceptCall() { |
| if (mConference == null) { |
| return; |
| } |
| mCallService.accept(mConference.getId()); |
| } |
| |
| public void hangupCall() { |
| List<String> callMediaHandlers = listCallMediaHandlers(); |
| |
| for (String callMediaHandler : callMediaHandlers) |
| { |
| toggleCallMediaHandler(callMediaHandler, false); |
| } |
| |
| if (mConference != null) { |
| if (mConference.isConference()) |
| mCallService.hangUpConference(mConference.getId()); |
| else |
| mCallService.hangUp(mConference.getId()); |
| } |
| for (SipCall call : mPendingCalls) { |
| mCallService.hangUp(call.getDaemonIdString()); |
| } |
| finish(); |
| } |
| |
| public void refuseCall() { |
| final Conference call = mConference; |
| if (call != null) { |
| mCallService.refuse(call.getId()); |
| } |
| finish(); |
| } |
| |
| public void videoSurfaceCreated(Object holder) { |
| if (mConference == null) { |
| return; |
| } |
| String newId = mConference.getId(); |
| if (!newId.equals(currentSurfaceId)) { |
| mHardwareService.removeVideoSurface(currentSurfaceId); |
| currentSurfaceId = newId; |
| } |
| mHardwareService.addVideoSurface(mConference.getId(), holder); |
| getView().displayContactBubble(false); |
| } |
| |
| public void videoSurfaceUpdateId(String newId) { |
| if (!Objects.equals(newId, currentSurfaceId)) { |
| mHardwareService.updateVideoSurfaceId(currentSurfaceId, newId); |
| currentSurfaceId = newId; |
| } |
| } |
| |
| public void pluginSurfaceCreated(Object holder) { |
| if (mConference == null) { |
| return; |
| } |
| String newId = mConference.getPluginId(); |
| if (!newId.equals(currentPluginSurfaceId)) { |
| mHardwareService.removeVideoSurface(currentPluginSurfaceId); |
| currentPluginSurfaceId = newId; |
| } |
| mHardwareService.addVideoSurface(mConference.getPluginId(), holder); |
| getView().displayContactBubble(false); |
| } |
| |
| public void pluginSurfaceUpdateId(String newId) { |
| if (!Objects.equals(newId, currentPluginSurfaceId)) { |
| mHardwareService.updateVideoSurfaceId(currentPluginSurfaceId, newId); |
| currentPluginSurfaceId = newId; |
| } |
| } |
| |
| public void previewVideoSurfaceCreated(Object holder) { |
| mHardwareService.addPreviewVideoSurface(holder, mConference); |
| //mHardwareService.startCapture(null); |
| } |
| |
| public void videoSurfaceDestroyed() { |
| if (currentSurfaceId != null) { |
| mHardwareService.removeVideoSurface(currentSurfaceId); |
| currentSurfaceId = null; |
| } |
| } |
| public void pluginSurfaceDestroyed() { |
| if (currentPluginSurfaceId != null) { |
| mHardwareService.removeVideoSurface(currentPluginSurfaceId); |
| currentPluginSurfaceId = null; |
| } |
| } |
| public void previewVideoSurfaceDestroyed() { |
| mHardwareService.removePreviewVideoSurface(); |
| mHardwareService.endCapture(); |
| } |
| |
| public void displayChanged() { |
| mHardwareService.switchInput(mConference.getId(), false); |
| } |
| |
| public void layoutChanged() { |
| //getView().resetVideoSize(videoWidth, videoHeight, previewWidth, previewHeight); |
| } |
| |
| |
| public void uiVisibilityChanged(boolean displayed) { |
| CallView view = getView(); |
| if (view != null) |
| view.displayHangupButton(mOnGoingCall && displayed); |
| } |
| |
| private void finish() { |
| if (timeUpdateTask != null && !timeUpdateTask.isDisposed()) { |
| timeUpdateTask.dispose(); |
| timeUpdateTask = null; |
| } |
| mConference = null; |
| CallView view = getView(); |
| if (view != null) |
| view.finish(); |
| } |
| |
| private Disposable contactDisposable = null; |
| |
| private void contactUpdate(final Conference conference) { |
| if (mConference != conference) { |
| mConference = conference; |
| if (contactDisposable != null && !contactDisposable.isDisposed()) { |
| contactDisposable.dispose(); |
| } |
| if (conference.getParticipants().isEmpty()) |
| return; |
| |
| // Updates of participant (and pending participant) list |
| Observable<List<SipCall>> callsObservable = mPendingSubject |
| .map(pendingList -> { |
| Log.w(TAG, "mPendingSubject onNext " + pendingList.size() + " " + conference.getParticipants().size()); |
| if (pendingList.isEmpty()) |
| return conference.getParticipants(); |
| List<SipCall> newList = new ArrayList<>(conference.getParticipants().size() + pendingList.size()); |
| newList.addAll(conference.getParticipants()); |
| newList.addAll(pendingList); |
| return newList; |
| }); |
| |
| // Updates of individual contacts |
| Observable<List<Observable<SipCall>>> contactsObservable = callsObservable |
| .flatMapSingle(calls -> Observable |
| .fromIterable(calls) |
| .map(call -> mContactService.observeContact(call.getAccount(), call.getContact(), false) |
| .map(contact -> call)) |
| .toList(calls.size())); |
| |
| // Combined updates of contacts as participant list updates |
| Observable<List<SipCall>> contactUpdates = contactsObservable |
| .switchMap(list -> Observable |
| .combineLatest(list, objects -> { |
| Log.w(TAG, "flatMapObservable " + objects.length); |
| ArrayList<SipCall> calls = new ArrayList<>(objects.length); |
| for (Object call : objects) |
| calls.add((SipCall)call); |
| return (List<SipCall>)calls; |
| })) |
| .filter(list -> !list.isEmpty()); |
| |
| contactDisposable = contactUpdates |
| .observeOn(mUiScheduler) |
| .subscribe(cs -> getView().updateContactBubble(cs), e -> Log.e(TAG, "Error updating contact data", e)); |
| mCompositeDisposable.add(contactDisposable); |
| } |
| mPendingSubject.onNext(mPendingCalls); |
| } |
| |
| private void confUpdate(Conference call) { |
| Log.w(TAG, "confUpdate " + call.getId()); |
| |
| mConference = call; |
| SipCall.CallStatus status = mConference.getState(); |
| if (status == SipCall.CallStatus.HOLD && mCallService.getConferenceList().size() == 1) { |
| mCallService.unhold(mConference.getId()); |
| } |
| mAudioOnly = !call.hasVideo(); |
| CallView view = getView(); |
| if (view == null) |
| return; |
| view.updateMenu(); |
| if (call.isOnGoing()) { |
| mOnGoingCall = true; |
| view.initNormalStateDisplay(mAudioOnly, isMicrophoneMuted()); |
| view.updateMenu(); |
| if (!mAudioOnly) { |
| mHardwareService.setPreviewSettings(); |
| mHardwareService.updatePreviewVideoSurface(mConference); |
| videoSurfaceUpdateId(call.getId()); |
| pluginSurfaceUpdateId(call.getPluginId()); |
| view.displayVideoSurface(true, mDeviceRuntimeService.hasVideoPermission()); |
| if (permissionChanged) { |
| mHardwareService.switchInput(mConference.getId(), permissionChanged); |
| permissionChanged = false; |
| } |
| } |
| if (timeUpdateTask != null) |
| timeUpdateTask.dispose(); |
| timeUpdateTask = mUiScheduler.schedulePeriodicallyDirect(this::updateTime, 0, 1, TimeUnit.SECONDS); |
| } else if (call.isRinging()) { |
| SipCall scall = call.getCall(); |
| |
| view.handleCallWakelock(mAudioOnly); |
| if (scall.isIncoming()) { |
| if (mAccountService.getAccount(scall.getAccount()).isAutoanswerEnabled()) { |
| mCallService.accept(scall.getDaemonIdString()); |
| // only display the incoming call screen if the notification is a full screen intent |
| } else if (incomingIsFullIntent) { |
| view.initIncomingCallDisplay(); |
| } |
| } else { |
| mOnGoingCall = false; |
| view.updateCallStatus(scall.getCallStatus()); |
| view.initOutGoingCallDisplay(); |
| } |
| } else { |
| finish(); |
| } |
| } |
| |
| public void maximizeParticipant(SipCall call) { |
| if (mConference.getMaximizedCall() == call) |
| call = null; |
| mConference.setMaximizedCall(call); |
| if (call != null) { |
| mCallService.setConfMaximizedParticipant(mConference.getConfId(), call.getDaemonIdString()); |
| } else { |
| mCallService.setConfGridLayout(mConference.getConfId()); |
| } |
| } |
| |
| private void updateTime() { |
| CallView view = getView(); |
| if (view != null && mConference != null) { |
| if (mConference.isOnGoing()) { |
| long start = mConference.getTimestampStart(); |
| if (start != Long.MAX_VALUE) { |
| view.updateTime((System.currentTimeMillis() - start) / 1000); |
| } else { |
| view.updateTime(-1); |
| } |
| } |
| } |
| } |
| |
| private void onVideoEvent(HardwareService.VideoEvent event) { |
| Log.d(TAG, "VIDEO_EVENT: " + event.start + " " + event.callId + " " + event.w + "x" + event.h); |
| |
| if (event.start) { |
| getView().displayVideoSurface(true, !isPipMode() && mDeviceRuntimeService.hasVideoPermission()); |
| } else if (mConference != null && mConference.getId().equals(event.callId)) { |
| getView().displayVideoSurface(event.started, event.started && !isPipMode() && mDeviceRuntimeService.hasVideoPermission()); |
| if (event.started) { |
| videoWidth = event.w; |
| videoHeight = event.h; |
| getView().resetVideoSize(videoWidth, videoHeight); |
| } |
| } else if (event.callId == null) { |
| if (event.started) { |
| previewWidth = event.w; |
| previewHeight = event.h; |
| getView().resetPreviewVideoSize(previewWidth, previewHeight, event.rot); |
| } |
| } |
| if (mConference != null && mConference.getPluginId().equals(event.callId)) { |
| if (event.started) { |
| previewWidth = event.w; |
| previewHeight = event.h; |
| getView().resetPluginPreviewVideoSize(previewWidth, previewHeight, event.rot); |
| } |
| } |
| /*if (event.started || event.start) { |
| getView().resetVideoSize(videoWidth, videoHeight, previewWidth, previewHeight); |
| }*/ |
| } |
| |
| public void positiveButtonClicked() { |
| if (mConference.isRinging() && mConference.isIncoming()) { |
| acceptCall(); |
| } else { |
| hangupCall(); |
| } |
| } |
| |
| public void negativeButtonClicked() { |
| if (mConference.isRinging() && mConference.isIncoming()) { |
| refuseCall(); |
| } else { |
| hangupCall(); |
| } |
| } |
| |
| public void toggleButtonClicked() { |
| if (mConference != null && !(mConference.isRinging() && mConference.isIncoming())) { |
| hangupCall(); |
| } |
| } |
| |
| public boolean isAudioOnly() { |
| return mAudioOnly; |
| } |
| |
| public void requestPipMode() { |
| if (mConference != null && mConference.isOnGoing() && mConference.hasVideo()) { |
| getView().enterPipMode(mConference.getId()); |
| } |
| } |
| |
| public boolean isPipMode() { |
| return pipIsActive; |
| } |
| |
| public void pipModeChanged(boolean pip) { |
| pipIsActive = pip; |
| if (pip) { |
| getView().displayHangupButton(false); |
| getView().displayPreviewSurface(false); |
| getView().displayVideoSurface(true, false); |
| } else { |
| getView().displayPreviewSurface(true); |
| getView().displayVideoSurface(true, mDeviceRuntimeService.hasVideoPermission()); |
| } |
| } |
| |
| public void toggleCallMediaHandler(String id, boolean toggle) |
| { |
| if (mConference != null && mConference.isOnGoing() && mConference.hasVideo()) { |
| getView().toggleCallMediaHandler(id, mConference.getId(), toggle); |
| } |
| } |
| |
| public boolean isSpeakerphoneOn() { |
| return mHardwareService.isSpeakerPhoneOn(); |
| } |
| |
| public void sendDtmf(CharSequence s) { |
| mCallService.playDtmf(s.toString()); |
| } |
| |
| public void addConferenceParticipant(String accountId, String contactId) { |
| mCompositeDisposable.add(mConversationFacade.startConversation(accountId, new Uri(contactId)) |
| .map(Conversation::getCurrentCalls) |
| .subscribe(confs -> { |
| if (confs.isEmpty()) { |
| final Observer<SipCall> pendingObserver = new Observer<SipCall>() { |
| private SipCall call = null; |
| @Override |
| public void onSubscribe(Disposable d) {} |
| |
| @Override |
| public void onNext(SipCall sipCall) { |
| if (call == null) { |
| call = sipCall; |
| mPendingCalls.add(sipCall); |
| } |
| mPendingSubject.onNext(mPendingCalls); |
| } |
| |
| @Override |
| public void onError(Throwable e) {} |
| |
| @Override |
| public void onComplete() { |
| if (call != null) { |
| mPendingCalls.remove(call); |
| mPendingSubject.onNext(mPendingCalls); |
| call = null; |
| } |
| } |
| }; |
| |
| // Place new call, join to conference when answered |
| Maybe<SipCall> newCall = mCallService.placeCallObservable(accountId, contactId, mAudioOnly) |
| .doOnEach(pendingObserver) |
| .filter(SipCall::isOnGoing) |
| .firstElement() |
| .delay(1, TimeUnit.SECONDS) |
| .doOnEvent((v, e) -> pendingObserver.onComplete()); |
| mCompositeDisposable.add(newCall.subscribe(call -> { |
| String id = mConference.getId(); |
| if (mConference.isConference()) { |
| mCallService.addParticipant(call.getDaemonIdString(), id); |
| } else { |
| mCallService.joinParticipant(id, call.getDaemonIdString()).subscribe(); |
| } |
| })); |
| } else { |
| // Selected contact already in call or conference, join it to current conference |
| Conference selectedConf = confs.get(0); |
| if (selectedConf != mConference) { |
| if (mConference.isConference()) { |
| if (selectedConf.isConference()) |
| mCallService.joinConference(mConference.getId(), selectedConf.getId()); |
| else |
| mCallService.addParticipant(selectedConf.getId(), mConference.getId()); |
| } else { |
| if (selectedConf.isConference()) |
| mCallService.addParticipant(mConference.getId(), selectedConf.getId()); |
| else |
| mCallService.joinParticipant(mConference.getId(), selectedConf.getId()).subscribe(); |
| } |
| } |
| } |
| })); |
| } |
| |
| public void startAddParticipant() { |
| getView().startAddParticipant(mConference.getId()); |
| } |
| |
| public void hangupParticipant(SipCall call) { |
| mCallService.hangUp(call.getDaemonIdString()); |
| } |
| |
| public void openParticipantContact(SipCall call) { |
| getView().goToContact(call.getAccount(), call.getContact()); |
| } |
| |
| public void stopCapture() { |
| mHardwareService.stopCapture(); |
| } |
| |
| public boolean startScreenShare(Object mediaProjection) { |
| return mHardwareService.startScreenShare(mediaProjection); |
| } |
| |
| public void stopScreenShare() { |
| mHardwareService.stopScreenShare(); |
| } |
| |
| public boolean isMaximized(SipCall call) { |
| return mConference.getMaximizedCall() == call; |
| } |
| |
| public void startPlugin(String mediaHandlerId) { |
| mHardwareService.startMediaHandler(mediaHandlerId); |
| if(mConference == null) |
| return; |
| mHardwareService.switchInput(mConference.getId(), mHardwareService.isPreviewFromFrontCamera()); |
| } |
| |
| public void stopPlugin() { |
| mHardwareService.stopMediaHandler(); |
| if(mConference == null) |
| return; |
| mHardwareService.switchInput(mConference.getId(), mHardwareService.isPreviewFromFrontCamera()); |
| } |
| |
| public boolean setBlockRecordStatus(){ |
| return mPreferencesService.getSettings().isRecordingBlocked(); |
| } |
| |
| } |