blob: cef1f72085c3510049261fa2e8ddf90508e43dcb [file] [log] [blame]
Hadrien De Sousaccc947d2017-04-12 14:26:52 -04001/*
Sébastien Blinab53ada2019-01-02 17:23:00 -05002 * Copyright (C) 2004-2019 Savoir-faire Linux Inc.
Hadrien De Sousaccc947d2017-04-12 14:26:52 -04003 *
4 * Author: Hadrien De Sousa <hadrien.desousa@savoirfairelinux.com>
Adrien Béraudace37a22018-06-22 14:58:36 -04005 * Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
Hadrien De Sousaccc947d2017-04-12 14:26:52 -04006 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21package cx.ring.call;
22
Adrien Béraude4f901a2019-10-25 17:26:30 -040023import java.util.ArrayList;
24import java.util.List;
25import java.util.Objects;
Hadrien De Sousaccc947d2017-04-12 14:26:52 -040026import java.util.concurrent.TimeUnit;
27
28import javax.inject.Inject;
Adrien Béraudace37a22018-06-22 14:58:36 -040029import javax.inject.Named;
Hadrien De Sousaccc947d2017-04-12 14:26:52 -040030
Adrien Béraude4f901a2019-10-25 17:26:30 -040031import cx.ring.facades.ConversationFacade;
32import cx.ring.model.Conference;
33import cx.ring.model.Conversation;
Hadrien De Sousaccc947d2017-04-12 14:26:52 -040034import cx.ring.model.SipCall;
Adrien Béraude4f901a2019-10-25 17:26:30 -040035import cx.ring.model.Uri;
Hadrien De Sousaccc947d2017-04-12 14:26:52 -040036import cx.ring.mvp.RootPresenter;
37import cx.ring.services.AccountService;
38import cx.ring.services.CallService;
Adrien Béraud8d710752019-02-20 13:53:59 -050039import cx.ring.services.ContactService;
Rayan Osseiran38206c62019-05-15 14:49:03 -040040import cx.ring.services.DeviceRuntimeService;
Hadrien De Sousaccc947d2017-04-12 14:26:52 -040041import cx.ring.services.HardwareService;
Hadrien De Sousaccc947d2017-04-12 14:26:52 -040042import cx.ring.utils.Log;
Hadrien De Sousac165e9b2017-11-24 12:33:32 -050043import cx.ring.utils.StringUtils;
Adrien Béraude4f901a2019-10-25 17:26:30 -040044import io.reactivex.Maybe;
Rayan Osseiraned9feeb2019-07-05 16:04:30 -040045import io.reactivex.Observable;
Adrien Béraude4f901a2019-10-25 17:26:30 -040046import io.reactivex.Observer;
Adrien Béraudace37a22018-06-22 14:58:36 -040047import io.reactivex.Scheduler;
Adrien Béraud3aeed5d2018-07-26 11:26:13 -040048import io.reactivex.disposables.Disposable;
Adrien Béraude4f901a2019-10-25 17:26:30 -040049import io.reactivex.subjects.BehaviorSubject;
50import io.reactivex.subjects.Subject;
Hadrien De Sousaccc947d2017-04-12 14:26:52 -040051
AGS5ef482ce2020-06-12 13:28:51 -040052import static cx.ring.daemon.Ringservice.listCallMediaHandlers;
53import static cx.ring.daemon.Ringservice.toggleCallMediaHandler;
54
Adrien Béraudace37a22018-06-22 14:58:36 -040055public class CallPresenter extends RootPresenter<CallView> {
Hadrien De Sousaccc947d2017-04-12 14:26:52 -040056
57 public final static String TAG = CallPresenter.class.getSimpleName();
58
Adrien Béraudace37a22018-06-22 14:58:36 -040059 private AccountService mAccountService;
Adrien Béraud8d710752019-02-20 13:53:59 -050060 private ContactService mContactService;
Adrien Béraudace37a22018-06-22 14:58:36 -040061 private HardwareService mHardwareService;
62 private CallService mCallService;
Rayan Osseiran38206c62019-05-15 14:49:03 -040063 private DeviceRuntimeService mDeviceRuntimeService;
Adrien Béraude4f901a2019-10-25 17:26:30 -040064 private ConversationFacade mConversationFacade;
Hadrien De Sousaccc947d2017-04-12 14:26:52 -040065
Adrien Béraudac3de5a2019-11-06 11:36:01 -050066 private Conference mConference;
Adrien Béraude4f901a2019-10-25 17:26:30 -040067 private final List<SipCall> mPendingCalls = new ArrayList<>();
68 private final Subject<List<SipCall>> mPendingSubject = BehaviorSubject.createDefault(mPendingCalls);
69
Hadrien De Sousaccc947d2017-04-12 14:26:52 -040070 private boolean mOnGoingCall = false;
Hadrien De Sousa203164c2017-12-05 17:41:42 -050071 private boolean mAudioOnly = true;
Adrien Béraud335d32e2019-05-31 09:31:40 -040072 private boolean permissionChanged = false;
Rayan Osseiranc1532d42019-06-05 13:17:53 -040073 private boolean pipIsActive = false;
Rayan Osseiran3dd04662019-06-28 16:47:48 -040074 private boolean incomingIsFullIntent = true;
Rayan Osseiraned9feeb2019-07-05 16:04:30 -040075 private boolean callInitialized = false;
Hadrien De Sousaccc947d2017-04-12 14:26:52 -040076
77 private int videoWidth = -1;
78 private int videoHeight = -1;
79 private int previewWidth = -1;
80 private int previewHeight = -1;
Adrien Béraude4f901a2019-10-25 17:26:30 -040081 private String currentSurfaceId = null;
AGS5ef482ce2020-06-12 13:28:51 -040082 private String currentPluginSurfaceId = null;
Hadrien De Sousaccc947d2017-04-12 14:26:52 -040083
Adrien Béraud3aeed5d2018-07-26 11:26:13 -040084 private Disposable timeUpdateTask = null;
Hadrien De Sousaccc947d2017-04-12 14:26:52 -040085
86 @Inject
Adrien Béraudace37a22018-06-22 14:58:36 -040087 @Named("UiScheduler")
88 protected Scheduler mUiScheduler;
89
90 @Inject
Hadrien De Sousaccc947d2017-04-12 14:26:52 -040091 public CallPresenter(AccountService accountService,
Adrien Béraud8d710752019-02-20 13:53:59 -050092 ContactService contactService,
Hadrien De Sousaccc947d2017-04-12 14:26:52 -040093 HardwareService hardwareService,
Adrien Béraude4f901a2019-10-25 17:26:30 -040094 CallService callService,
95 DeviceRuntimeService deviceRuntimeService,
96 ConversationFacade conversationFacade) {
Adrien Béraudace37a22018-06-22 14:58:36 -040097 mAccountService = accountService;
Adrien Béraud8d710752019-02-20 13:53:59 -050098 mContactService = contactService;
Adrien Béraudace37a22018-06-22 14:58:36 -040099 mHardwareService = hardwareService;
100 mCallService = callService;
Rayan Osseiran38206c62019-05-15 14:49:03 -0400101 mDeviceRuntimeService = deviceRuntimeService;
Adrien Béraude4f901a2019-10-25 17:26:30 -0400102 mConversationFacade = conversationFacade;
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400103 }
104
Rayan Osseiran38206c62019-05-15 14:49:03 -0400105 public void cameraPermissionChanged(boolean isGranted) {
106 if (isGranted && mHardwareService.isVideoAvailable()) {
Adrien Béraud99931dd2019-08-27 16:52:01 -0400107 mHardwareService.initVideo().blockingAwait();
Adrien Béraud335d32e2019-05-31 09:31:40 -0400108 permissionChanged = true;
Rayan Osseiran38206c62019-05-15 14:49:03 -0400109 }
110 }
111
112 public void audioPermissionChanged(boolean isGranted) {
Rayan Osseirandb4667e2019-05-31 09:41:54 -0400113 if (isGranted && mHardwareService.hasMicrophone()) {
Adrien Béraud22ec8b82019-07-27 21:53:50 -0400114 mCallService.restartAudioLayer();
Rayan Osseiran38206c62019-05-15 14:49:03 -0400115 }
116 }
117
118
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400119 @Override
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400120 public void unbindView() {
Hadrien De Sousa203164c2017-12-05 17:41:42 -0500121 if (!mAudioOnly) {
Adrien Béraud30532a52018-09-11 15:10:05 -0400122 mHardwareService.endCapture();
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400123 }
Adrien Béraud99fd4202018-12-13 13:02:21 -0500124 super.unbindView();
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400125 }
126
127 @Override
128 public void bindView(CallView view) {
129 super.bindView(view);
Adrien Béraude4f901a2019-10-25 17:26:30 -0400130 /*mCompositeDisposable.add(mAccountService.getRegisteredNames()
Adrien Béraudace37a22018-06-22 14:58:36 -0400131 .observeOn(mUiScheduler)
132 .subscribe(r -> {
133 if (mSipCall != null && mSipCall.getContact() != null) {
134 getView().updateContactBubble(mSipCall.getContact());
135 }
Adrien Béraude4f901a2019-10-25 17:26:30 -0400136 }));*/
Adrien Béraudace37a22018-06-22 14:58:36 -0400137 mCompositeDisposable.add(mHardwareService.getVideoEvents()
138 .observeOn(mUiScheduler)
139 .subscribe(this::onVideoEvent));
Adrien Béraud34a6fa22019-07-28 16:02:59 -0400140 mCompositeDisposable.add(mHardwareService.getAudioState()
141 .observeOn(mUiScheduler)
142 .subscribe(state -> getView().updateAudioState(state)));
143
Adrien Béraud22ec8b82019-07-27 21:53:50 -0400144 /*mCompositeDisposable.add(mHardwareService
Adrien Béraud057e9a32018-09-30 17:48:55 -0400145 .getBluetoothEvents()
146 .subscribe(event -> {
Rayan Osseiran7f77c8a2019-07-17 16:40:50 -0400147 if (!event.connected && mSipCall == null) {
Adrien Béraud057e9a32018-09-30 17:48:55 -0400148 hangupCall();
149 }
Adrien Béraud22ec8b82019-07-27 21:53:50 -0400150 }));*/
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400151 }
152
Hadrien De Sousa203164c2017-12-05 17:41:42 -0500153 public void initOutGoing(String accountId, String contactRingId, boolean audioOnly) {
Adrien Béraud85683c22018-05-23 11:18:43 -0400154 if (accountId == null || contactRingId == null) {
155 Log.e(TAG, "initOutGoing: null account or contact");
Rayan Osseiran106a4742019-07-18 15:03:55 -0400156 hangupCall();
Adrien Béraud85683c22018-05-23 11:18:43 -0400157 return;
158 }
Adrien Béraud7403f352019-11-22 16:30:55 -0500159 if (!mHardwareService.hasCamera()) {
Adrien Béraudd3bf4e42018-02-14 14:44:41 +0100160 audioOnly = true;
Hadrien De Sousafcf2a992017-12-15 11:08:56 -0500161 }
Adrien Béraudeb4bb002019-03-14 15:52:48 -0400162 //getView().blockScreenRotation();
Adrien Béraudace37a22018-06-22 14:58:36 -0400163
Adrien Béraud34915fb2020-08-24 17:12:03 -0400164 Observable<Conference> callObservable = mCallService
Adrien Béraude4f901a2019-10-25 17:26:30 -0400165 .placeCall(accountId, StringUtils.toNumber(contactRingId), audioOnly)
166 //.map(mCallService::getConference)
167 .flatMapObservable(call -> mCallService.getConfUpdates(call))
Adrien Béraud34915fb2020-08-24 17:12:03 -0400168 .share();
169
170 mCompositeDisposable.add(callObservable
Adrien Béraudace37a22018-06-22 14:58:36 -0400171 .observeOn(mUiScheduler)
Adrien Béraude4f901a2019-10-25 17:26:30 -0400172 .subscribe(conference -> {
173 contactUpdate(conference);
174 confUpdate(conference);
Rayan Osseirandb4667e2019-05-31 09:41:54 -0400175 }, e -> {
176 hangupCall();
177 Log.e(TAG, "Error with initOutgoing: " + e.getMessage());
178 }));
Adrien Béraud34915fb2020-08-24 17:12:03 -0400179
180 showConference(callObservable);
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400181 }
182
Rayan Osseiraned9feeb2019-07-05 16:04:30 -0400183 /**
184 * Returns to or starts an incoming call
185 *
186 * @param confId the call id
187 * @param actionViewOnly true if only returning to call or if using full screen intent
188 */
189 public void initIncomingCall(String confId, boolean actionViewOnly) {
190 //getView().blockScreenRotation();
191
192 // if the call is incoming through a full intent, this allows the incoming call to display
193 incomingIsFullIntent = actionViewOnly;
194
Adrien Béraude4f901a2019-10-25 17:26:30 -0400195 Observable<Conference> callObservable = mCallService.getConfUpdates(confId)
196 .observeOn(mUiScheduler)
197 .share();
Rayan Osseiraned9feeb2019-07-05 16:04:30 -0400198
199 // Handles the case where the call has been accepted, emits a single so as to only check for permissions and start the call once
Adrien Béraude4f901a2019-10-25 17:26:30 -0400200 mCompositeDisposable.add(callObservable
201 .firstOrError()
202 .subscribe(call -> {
203 if (!actionViewOnly) {
204 contactUpdate(call);
205 confUpdate(call);
206 callInitialized = true;
207 getView().prepareCall(true);
208 }
209 }, e -> {
210 hangupCall();
211 Log.e(TAG, "Error with initIncoming, preparing call flow :" , e);
212 }));
Rayan Osseiraned9feeb2019-07-05 16:04:30 -0400213
214 // Handles retrieving call updates. Items emitted are only used if call is already in process or if user is returning to a call.
Adrien Béraude4f901a2019-10-25 17:26:30 -0400215 mCompositeDisposable.add(callObservable
216 .subscribe(call -> {
217 if (callInitialized || actionViewOnly) {
218 contactUpdate(call);
219 confUpdate(call);
220 }
221 }, e -> {
222 hangupCall();
223 Log.e(TAG, "Error with initIncoming, action view flow: ", e);
224 }));
Adrien Béraud34915fb2020-08-24 17:12:03 -0400225
226 showConference(callObservable);
227 }
228
229 private void showConference(Observable<Conference> conference) {
230 mCompositeDisposable.add(conference
231 .distinctUntilChanged()
232 .switchMap(Conference::getParticipantInfo)
233 .observeOn(mUiScheduler)
234 .subscribe(info -> getView().updateConfInfo(info),
235 e -> Log.e(TAG, "Error with initIncoming, action view flow: ", e)));
Rayan Osseiran3dd04662019-06-28 16:47:48 -0400236 }
237
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400238 public void prepareOptionMenu() {
239 boolean isSpeakerOn = mHardwareService.isSpeakerPhoneOn();
Adrien Béraude4f901a2019-10-25 17:26:30 -0400240 //boolean hasContact = mSipCall != null && null != mSipCall.getContact() && mSipCall.getContact().isUnknown();
Adrien Béraudac3de5a2019-11-06 11:36:01 -0500241 boolean canDial = mOnGoingCall && mConference != null && !mConference.isIncoming();
ayounesc6c2c012019-11-11 11:09:51 -0500242 // get the preferences
243 boolean displayPluginsButton = getView().displayPluginsButton();
244 boolean showPluginBtn = displayPluginsButton && mOnGoingCall && mConference != null;
Hadrien De Sousa203164c2017-12-05 17:41:42 -0500245 boolean hasMultipleCamera = mHardwareService.getCameraCount() > 1 && mOnGoingCall && !mAudioOnly;
ayounes47e93992019-10-28 12:09:53 -0400246 getView().initMenu(isSpeakerOn, hasMultipleCamera, canDial, showPluginBtn, mOnGoingCall);
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400247 }
248
249 public void chatClick() {
Adrien Béraudac3de5a2019-11-06 11:36:01 -0500250 if (mConference == null || mConference.getParticipants().isEmpty()) {
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400251 return;
252 }
Adrien Béraudac3de5a2019-11-06 11:36:01 -0500253 SipCall firstCall = mConference.getParticipants().get(0);
Adrien Béraude4f901a2019-10-25 17:26:30 -0400254 if (firstCall == null
255 || firstCall.getContact() == null
256 || firstCall.getContact().getIds() == null
257 || firstCall.getContact().getIds().isEmpty()) {
258 return;
259 }
260 getView().goToConversation(firstCall.getAccount(), firstCall.getContact().getIds().get(0));
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400261 }
262
Adrien Béraudbc0898e2018-08-23 14:31:11 -0400263 public void speakerClick(boolean checked) {
264 mHardwareService.toggleSpeakerphone(checked);
265 }
266
267 public void muteMicrophoneToggled(boolean checked) {
268 mCallService.setMuted(checked);
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400269 }
270
Rayan Osseiran38206c62019-05-15 14:49:03 -0400271
Adrien Béraudec19fad2018-12-30 16:51:05 -0500272 public boolean isMicrophoneMuted() {
273 return mCallService.isCaptureMuted();
274 }
275
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400276 public void switchVideoInputClick() {
Adrien Béraudac3de5a2019-11-06 11:36:01 -0500277 if(mConference == null)
Rayan Osseiranca0436c2019-07-16 10:11:06 -0400278 return;
Adrien Béraudac3de5a2019-11-06 11:36:01 -0500279 mHardwareService.switchInput(mConference.getId(), false);
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400280 getView().switchCameraIcon(mHardwareService.isPreviewFromFrontCamera());
281 }
282
Adrien Béraudeb4bb002019-03-14 15:52:48 -0400283 public void configurationChanged(int rotation) {
284 mHardwareService.setDeviceOrientation(rotation);
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400285 }
286
287 public void dialpadClick() {
288 getView().displayDialPadKeyboard();
289 }
290
291 public void acceptCall() {
Adrien Béraudac3de5a2019-11-06 11:36:01 -0500292 if (mConference == null) {
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400293 return;
294 }
Adrien Béraudac3de5a2019-11-06 11:36:01 -0500295 mCallService.accept(mConference.getId());
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400296 }
297
298 public void hangupCall() {
AGS5ef482ce2020-06-12 13:28:51 -0400299 List<String> callMediaHandlers = listCallMediaHandlers();
300
301 for (String callMediaHandler : callMediaHandlers)
302 {
303 toggleCallMediaHandler(callMediaHandler, false);
304 }
305
Adrien Béraudac3de5a2019-11-06 11:36:01 -0500306 if (mConference != null) {
307 if (mConference.isConference())
308 mCallService.hangUpConference(mConference.getId());
Adrien Béraude4f901a2019-10-25 17:26:30 -0400309 else
Adrien Béraudac3de5a2019-11-06 11:36:01 -0500310 mCallService.hangUp(mConference.getId());
Adrien Béraude4f901a2019-10-25 17:26:30 -0400311 }
312 for (SipCall call : mPendingCalls) {
313 mCallService.hangUp(call.getDaemonIdString());
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400314 }
315 finish();
316 }
317
318 public void refuseCall() {
Adrien Béraudac3de5a2019-11-06 11:36:01 -0500319 final Conference call = mConference;
Adrien Béraudace37a22018-06-22 14:58:36 -0400320 if (call != null) {
Adrien Béraude4f901a2019-10-25 17:26:30 -0400321 mCallService.refuse(call.getId());
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400322 }
323 finish();
324 }
325
326 public void videoSurfaceCreated(Object holder) {
Adrien Béraudac3de5a2019-11-06 11:36:01 -0500327 if (mConference == null) {
Alexandre Lisione4d40262016-12-12 14:30:09 -0500328 return;
329 }
Adrien Béraudac3de5a2019-11-06 11:36:01 -0500330 String newId = mConference.getId();
Adrien Béraude4f901a2019-10-25 17:26:30 -0400331 if (!newId.equals(currentSurfaceId)) {
332 mHardwareService.removeVideoSurface(currentSurfaceId);
333 currentSurfaceId = newId;
334 }
Adrien Béraudac3de5a2019-11-06 11:36:01 -0500335 mHardwareService.addVideoSurface(mConference.getId(), holder);
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400336 getView().displayContactBubble(false);
337 }
338
Adrien Béraude4f901a2019-10-25 17:26:30 -0400339 public void videoSurfaceUpdateId(String newId) {
340 if (!Objects.equals(newId, currentSurfaceId)) {
341 mHardwareService.updateVideoSurfaceId(currentSurfaceId, newId);
342 currentSurfaceId = newId;
343 }
344 }
345
AGS5ef482ce2020-06-12 13:28:51 -0400346 public void pluginSurfaceCreated(Object holder) {
347 if (mConference == null) {
348 return;
349 }
350 String newId = mConference.getPluginId();
351 if (!newId.equals(currentPluginSurfaceId)) {
352 mHardwareService.removeVideoSurface(currentPluginSurfaceId);
353 currentPluginSurfaceId = newId;
354 }
355 mHardwareService.addVideoSurface(mConference.getPluginId(), holder);
356 getView().displayContactBubble(false);
357 }
358
359 public void pluginSurfaceUpdateId(String newId) {
360 if (!Objects.equals(newId, currentPluginSurfaceId)) {
361 mHardwareService.updateVideoSurfaceId(currentPluginSurfaceId, newId);
362 currentPluginSurfaceId = newId;
363 }
364 }
365
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400366 public void previewVideoSurfaceCreated(Object holder) {
Adrien Béraudac3de5a2019-11-06 11:36:01 -0500367 mHardwareService.addPreviewVideoSurface(holder, mConference);
Adrien Béraud30532a52018-09-11 15:10:05 -0400368 //mHardwareService.startCapture(null);
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400369 }
370
371 public void videoSurfaceDestroyed() {
Adrien Béraude4f901a2019-10-25 17:26:30 -0400372 if (currentSurfaceId != null) {
373 mHardwareService.removeVideoSurface(currentSurfaceId);
374 currentSurfaceId = null;
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400375 }
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400376 }
AGS5ef482ce2020-06-12 13:28:51 -0400377 public void pluginSurfaceDestroyed() {
378 if (currentPluginSurfaceId != null) {
379 mHardwareService.removeVideoSurface(currentPluginSurfaceId);
380 currentPluginSurfaceId = null;
381 }
382 }
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400383 public void previewVideoSurfaceDestroyed() {
384 mHardwareService.removePreviewVideoSurface();
Adrien Béraud30532a52018-09-11 15:10:05 -0400385 mHardwareService.endCapture();
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400386 }
387
388 public void displayChanged() {
Adrien Béraudac3de5a2019-11-06 11:36:01 -0500389 mHardwareService.switchInput(mConference.getId(), false);
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400390 }
391
392 public void layoutChanged() {
Adrien Béraudde0d4522018-12-12 12:11:02 -0500393 //getView().resetVideoSize(videoWidth, videoHeight, previewWidth, previewHeight);
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400394 }
395
Rayan Osseiran38206c62019-05-15 14:49:03 -0400396
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400397 public void uiVisibilityChanged(boolean displayed) {
Adrien Béraud665faa22018-07-26 16:51:20 -0400398 CallView view = getView();
399 if (view != null)
400 view.displayHangupButton(mOnGoingCall && displayed);
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400401 }
402
403 private void finish() {
Adrien Béraud3aeed5d2018-07-26 11:26:13 -0400404 if (timeUpdateTask != null && !timeUpdateTask.isDisposed()) {
405 timeUpdateTask.dispose();
406 timeUpdateTask = null;
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400407 }
Adrien Béraudac3de5a2019-11-06 11:36:01 -0500408 mConference = null;
Adrien Béraud8d6baf32018-08-03 23:37:31 -0400409 CallView view = getView();
410 if (view != null)
411 view.finish();
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400412 }
413
Adrien Béraude4f901a2019-10-25 17:26:30 -0400414 private Disposable contactDisposable = null;
415
416 private void contactUpdate(final Conference conference) {
Adrien Béraudac3de5a2019-11-06 11:36:01 -0500417 if (mConference != conference) {
418 mConference = conference;
Adrien Béraude4f901a2019-10-25 17:26:30 -0400419 if (contactDisposable != null && !contactDisposable.isDisposed()) {
420 contactDisposable.dispose();
421 }
422 if (conference.getParticipants().isEmpty())
423 return;
424
425 // Updates of participant (and pending participant) list
426 Observable<List<SipCall>> callsObservable = mPendingSubject
427 .map(pendingList -> {
428 Log.w(TAG, "mPendingSubject onNext " + pendingList.size() + " " + conference.getParticipants().size());
429 if (pendingList.isEmpty())
430 return conference.getParticipants();
431 List<SipCall> newList = new ArrayList<>(conference.getParticipants().size() + pendingList.size());
432 newList.addAll(conference.getParticipants());
433 newList.addAll(pendingList);
434 return newList;
435 });
436
437 // Updates of individual contacts
438 Observable<List<Observable<SipCall>>> contactsObservable = callsObservable
439 .flatMapSingle(calls -> Observable
440 .fromIterable(calls)
441 .map(call -> mContactService.observeContact(call.getAccount(), call.getContact())
442 .map(contact -> call))
443 .toList(calls.size()));
444
445 // Combined updates of contacts as participant list updates
446 Observable<List<SipCall>> contactUpdates = contactsObservable
447 .switchMap(list -> Observable
448 .combineLatest(list, objects -> {
449 Log.w(TAG, "flatMapObservable " + objects.length);
450 ArrayList<SipCall> calls = new ArrayList<>(objects.length);
451 for (Object call : objects)
452 calls.add((SipCall)call);
453 return (List<SipCall>)calls;
454 }))
455 .filter(list -> !list.isEmpty());
456
457 contactDisposable = contactUpdates
Adrien Béraud2c378b32018-07-11 15:24:02 -0400458 .observeOn(mUiScheduler)
Adrien Béraude4f901a2019-10-25 17:26:30 -0400459 .subscribe(cs -> getView().updateContactBubble(cs), e -> Log.e(TAG, "Error updating contact data", e));
460 mCompositeDisposable.add(contactDisposable);
Adrien Béraud2c378b32018-07-11 15:24:02 -0400461 }
Adrien Béraud7c45b742019-11-15 14:30:11 -0500462 mPendingSubject.onNext(mPendingCalls);
Adrien Béraud2c378b32018-07-11 15:24:02 -0400463 }
464
Adrien Béraude4f901a2019-10-25 17:26:30 -0400465 private void confUpdate(Conference call) {
466 Log.w(TAG, "confUpdate " + call.getId());
467
Adrien Béraudac3de5a2019-11-06 11:36:01 -0500468 mConference = call;
Amirhossein063a5e22020-06-24 17:53:11 -0400469 SipCall.CallStatus status = mConference.getState();
470 if (status == SipCall.CallStatus.HOLD && mCallService.getConferenceList().size() == 1) {
471 mCallService.unhold(mConference.getId());
472 }
Adrien Béraude4f901a2019-10-25 17:26:30 -0400473 mAudioOnly = !call.hasVideo();
Adrien Béraudace37a22018-06-22 14:58:36 -0400474 CallView view = getView();
Adrien Béraud99fd4202018-12-13 13:02:21 -0500475 if (view == null)
476 return;
477 view.updateMenu();
478 if (call.isOnGoing()) {
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400479 mOnGoingCall = true;
Adrien Béraud34a6fa22019-07-28 16:02:59 -0400480 view.initNormalStateDisplay(mAudioOnly, isMicrophoneMuted());
Adrien Béraudace37a22018-06-22 14:58:36 -0400481 view.updateMenu();
Hadrien De Sousa203164c2017-12-05 17:41:42 -0500482 if (!mAudioOnly) {
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400483 mHardwareService.setPreviewSettings();
Adrien Béraud7403f352019-11-22 16:30:55 -0500484 mHardwareService.updatePreviewVideoSurface(mConference);
Adrien Béraude4f901a2019-10-25 17:26:30 -0400485 videoSurfaceUpdateId(call.getId());
AGS5ef482ce2020-06-12 13:28:51 -0400486 pluginSurfaceUpdateId(call.getPluginId());
Rayan Osseiranc1532d42019-06-05 13:17:53 -0400487 view.displayVideoSurface(true, mDeviceRuntimeService.hasVideoPermission());
Rayan Osseiran3dd04662019-06-28 16:47:48 -0400488 if (permissionChanged) {
Adrien Béraudac3de5a2019-11-06 11:36:01 -0500489 mHardwareService.switchInput(mConference.getId(), permissionChanged);
Adrien Béraud335d32e2019-05-31 09:31:40 -0400490 permissionChanged = false;
Rayan Osseiran38206c62019-05-15 14:49:03 -0400491 }
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400492 }
Adrien Béraud3aeed5d2018-07-26 11:26:13 -0400493 if (timeUpdateTask != null)
494 timeUpdateTask.dispose();
Adrien Béraud99fd4202018-12-13 13:02:21 -0500495 timeUpdateTask = mUiScheduler.schedulePeriodicallyDirect(this::updateTime, 0, 1, TimeUnit.SECONDS);
496 } else if (call.isRinging()) {
Adrien Béraude4f901a2019-10-25 17:26:30 -0400497 SipCall scall = call.getCall();
498
Rayan Osseirande9ac142019-07-02 13:01:32 -0400499 view.handleCallWakelock(mAudioOnly);
Adrien Béraude4f901a2019-10-25 17:26:30 -0400500 if (scall.isIncoming()) {
501 if (mAccountService.getAccount(scall.getAccount()).isAutoanswerEnabled()) {
502 mCallService.accept(scall.getDaemonIdString());
Rayan Osseiran3dd04662019-06-28 16:47:48 -0400503 // only display the incoming call screen if the notification is a full screen intent
504 } else if (incomingIsFullIntent) {
Adrien Béraudace37a22018-06-22 14:58:36 -0400505 view.initIncomingCallDisplay();
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400506 }
507 } else {
508 mOnGoingCall = false;
Adrien Béraude4f901a2019-10-25 17:26:30 -0400509 view.updateCallStatus(scall.getCallStatus());
Adrien Béraudace37a22018-06-22 14:58:36 -0400510 view.initOutGoingCallDisplay();
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400511 }
512 } else {
513 finish();
514 }
515 }
516
517 private void updateTime() {
Adrien Béraud6325fad2018-08-15 15:34:11 -0400518 CallView view = getView();
Adrien Béraudac3de5a2019-11-06 11:36:01 -0500519 if (view != null && mConference != null) {
520 if (mConference.isOnGoing()) {
521 long start = mConference.getTimestampStart();
Adrien Béraude4f901a2019-10-25 17:26:30 -0400522 if (start != Long.MAX_VALUE) {
523 view.updateTime((System.currentTimeMillis() - start) / 1000);
524 } else {
525 view.updateTime(-1);
526 }
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400527 }
528 }
529 }
530
Adrien Béraudace37a22018-06-22 14:58:36 -0400531 private void onVideoEvent(HardwareService.VideoEvent event) {
Adrien Béraudeb4bb002019-03-14 15:52:48 -0400532 Log.d(TAG, "VIDEO_EVENT: " + event.start + " " + event.callId + " " + event.w + "x" + event.h);
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400533
Adrien Béraudace37a22018-06-22 14:58:36 -0400534 if (event.start) {
Rayan Osseiranc1532d42019-06-05 13:17:53 -0400535 getView().displayVideoSurface(true, !isPipMode() && mDeviceRuntimeService.hasVideoPermission());
Adrien Béraudac3de5a2019-11-06 11:36:01 -0500536 } else if (mConference != null && mConference.getId().equals(event.callId)) {
Rayan Osseiranc1532d42019-06-05 13:17:53 -0400537 getView().displayVideoSurface(event.started, event.started && !isPipMode() && mDeviceRuntimeService.hasVideoPermission());
Adrien Béraudace37a22018-06-22 14:58:36 -0400538 if (event.started) {
539 videoWidth = event.w;
540 videoHeight = event.h;
Adrien Béraudeb4bb002019-03-14 15:52:48 -0400541 getView().resetVideoSize(videoWidth, videoHeight);
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400542 }
Adrien Béraud30532a52018-09-11 15:10:05 -0400543 } else if (event.callId == null) {
544 if (event.started) {
545 previewWidth = event.w;
546 previewHeight = event.h;
Adrien Béraudeb4bb002019-03-14 15:52:48 -0400547 getView().resetPreviewVideoSize(previewWidth, previewHeight, event.rot);
Adrien Béraud30532a52018-09-11 15:10:05 -0400548 }
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400549 }
AGS5ef482ce2020-06-12 13:28:51 -0400550 if (mConference != null && mConference.getPluginId().equals(event.callId)) {
551 if (event.started) {
552 previewWidth = event.w;
553 previewHeight = event.h;
554 getView().resetPluginPreviewVideoSize(previewWidth, previewHeight, event.rot);
555 }
556 }
Adrien Béraudeb4bb002019-03-14 15:52:48 -0400557 /*if (event.started || event.start) {
Adrien Béraudde0d4522018-12-12 12:11:02 -0500558 getView().resetVideoSize(videoWidth, videoHeight, previewWidth, previewHeight);
Adrien Béraudeb4bb002019-03-14 15:52:48 -0400559 }*/
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400560 }
Pierre Duchemin8b2c1b52017-12-29 17:17:13 -0500561
562 public void positiveButtonClicked() {
Adrien Béraudac3de5a2019-11-06 11:36:01 -0500563 if (mConference.isRinging() && mConference.isIncoming()) {
Pierre Duchemin8b2c1b52017-12-29 17:17:13 -0500564 acceptCall();
565 } else {
566 hangupCall();
567 }
568 }
569
570 public void negativeButtonClicked() {
Adrien Béraudac3de5a2019-11-06 11:36:01 -0500571 if (mConference.isRinging() && mConference.isIncoming()) {
Pierre Duchemin8b2c1b52017-12-29 17:17:13 -0500572 refuseCall();
573 } else {
574 hangupCall();
575 }
576 }
577
578 public void toggleButtonClicked() {
Amirhossein1cbc8df2020-08-14 11:48:30 -0400579 if (mConference != null && !(mConference.isRinging() && mConference.isIncoming())) {
Pierre Duchemin8b2c1b52017-12-29 17:17:13 -0500580 hangupCall();
581 }
582 }
Adrien Béraud79808d02018-02-20 00:16:28 +0100583
Rayan Osseiran38206c62019-05-15 14:49:03 -0400584 public boolean isAudioOnly() {
585 return mAudioOnly;
586 }
587
Adrien Béraud79808d02018-02-20 00:16:28 +0100588 public void requestPipMode() {
Adrien Béraudac3de5a2019-11-06 11:36:01 -0500589 if (mConference != null && mConference.isOnGoing() && mConference.hasVideo()) {
590 getView().enterPipMode(mConference.getId());
Adrien Béraud79808d02018-02-20 00:16:28 +0100591 }
592 }
593
Rayan Osseiranc1532d42019-06-05 13:17:53 -0400594 public boolean isPipMode() {
595 return pipIsActive;
596 }
597
Adrien Béraud79808d02018-02-20 00:16:28 +0100598 public void pipModeChanged(boolean pip) {
Rayan Osseiranc1532d42019-06-05 13:17:53 -0400599 pipIsActive = pip;
Adrien Béraud79808d02018-02-20 00:16:28 +0100600 if (pip) {
601 getView().displayHangupButton(false);
602 getView().displayPreviewSurface(false);
Rayan Osseiranc1532d42019-06-05 13:17:53 -0400603 getView().displayVideoSurface(true, false);
Adrien Béraud79808d02018-02-20 00:16:28 +0100604 } else {
605 getView().displayPreviewSurface(true);
Rayan Osseiranc1532d42019-06-05 13:17:53 -0400606 getView().displayVideoSurface(true, mDeviceRuntimeService.hasVideoPermission());
Adrien Béraud79808d02018-02-20 00:16:28 +0100607 }
608 }
Adrien Béraudbc0898e2018-08-23 14:31:11 -0400609
610 public boolean isSpeakerphoneOn() {
611 return mHardwareService.isSpeakerPhoneOn();
612 }
Adrien Béraudbcbb4d42018-12-08 16:06:39 -0500613
614 public void sendDtmf(CharSequence s) {
615 mCallService.playDtmf(s.toString());
616 }
Adrien Béraude4f901a2019-10-25 17:26:30 -0400617
618 public void addConferenceParticipant(String accountId, String contactId) {
Adrien Béraude4f901a2019-10-25 17:26:30 -0400619 mCompositeDisposable.add(mConversationFacade.startConversation(accountId, new Uri(contactId))
620 .map(Conversation::getCurrentCalls)
Adrien Béraud94150a72019-11-18 16:41:28 -0500621 .subscribe(confs -> {
622 if (confs.isEmpty()) {
Adrien Béraud7c45b742019-11-15 14:30:11 -0500623 final Observer<SipCall> pendingObserver = new Observer<SipCall>() {
Adrien Béraude4f901a2019-10-25 17:26:30 -0400624 private SipCall call = null;
625 @Override
626 public void onSubscribe(Disposable d) {}
627
628 @Override
629 public void onNext(SipCall sipCall) {
Adrien Béraude4f901a2019-10-25 17:26:30 -0400630 if (call == null) {
631 call = sipCall;
632 mPendingCalls.add(sipCall);
633 }
634 mPendingSubject.onNext(mPendingCalls);
635 }
636
637 @Override
638 public void onError(Throwable e) {}
639
640 @Override
641 public void onComplete() {
Adrien Béraude4f901a2019-10-25 17:26:30 -0400642 if (call != null) {
643 mPendingCalls.remove(call);
644 mPendingSubject.onNext(mPendingCalls);
645 call = null;
646 }
647 }
648 };
649
650 // Place new call, join to conference when answered
651 Maybe<SipCall> newCall = mCallService.placeCallObservable(accountId, contactId, mAudioOnly)
652 .doOnEach(pendingObserver)
653 .filter(SipCall::isOnGoing)
654 .firstElement()
655 .delay(1, TimeUnit.SECONDS)
656 .doOnEvent((v, e) -> pendingObserver.onComplete());
Adrien Béraud7c45b742019-11-15 14:30:11 -0500657 mCompositeDisposable.add(newCall.subscribe(call -> {
658 String id = mConference.getId();
Adrien Béraud94150a72019-11-18 16:41:28 -0500659 if (mConference.isConference()) {
Adrien Béraud7c45b742019-11-15 14:30:11 -0500660 mCallService.addParticipant(call.getDaemonIdString(), id);
661 } else {
Adrien Béraud94150a72019-11-18 16:41:28 -0500662 mCallService.joinParticipant(id, call.getDaemonIdString()).subscribe();
Adrien Béraud7c45b742019-11-15 14:30:11 -0500663 }
664 }));
Adrien Béraude4f901a2019-10-25 17:26:30 -0400665 } else {
666 // Selected contact already in call or conference, join it to current conference
Adrien Béraud94150a72019-11-18 16:41:28 -0500667 Conference selectedConf = confs.get(0);
668 if (selectedConf != mConference) {
669 if (mConference.isConference()) {
670 if (selectedConf.isConference())
671 mCallService.joinConference(mConference.getId(), selectedConf.getId());
672 else
673 mCallService.addParticipant(selectedConf.getId(), mConference.getId());
Adrien Béraude4f901a2019-10-25 17:26:30 -0400674 } else {
Adrien Béraud94150a72019-11-18 16:41:28 -0500675 if (selectedConf.isConference())
676 mCallService.addParticipant(mConference.getId(), selectedConf.getId());
677 else
678 mCallService.joinParticipant(mConference.getId(), selectedConf.getId()).subscribe();
Adrien Béraude4f901a2019-10-25 17:26:30 -0400679 }
680 }
681 }
682 }));
683 }
684
685 public void startAddParticipant() {
Adrien Béraudac3de5a2019-11-06 11:36:01 -0500686 getView().startAddParticipant(mConference.getId());
Adrien Béraude4f901a2019-10-25 17:26:30 -0400687 }
688
689 public void hangupParticipant(SipCall call) {
690 mCallService.hangUp(call.getDaemonIdString());
691 }
692
693 public void openParticipantContact(SipCall call) {
694 getView().goToContact(call.getAccount(), call.getContact());
695 }
Adrien Béraudade9bee2019-12-02 17:13:26 -0500696
697 public void stopCapture() {
698 mHardwareService.stopCapture();
699 }
700
Adrien Béraud20bd7232020-03-16 11:33:23 -0400701 public boolean startScreenShare(Object mediaProjection) {
702 return mHardwareService.startScreenShare(mediaProjection);
Adrien Béraudade9bee2019-12-02 17:13:26 -0500703 }
704
705 public void stopScreenShare() {
706 mHardwareService.stopScreenShare();
707 }
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400708}