blob: 692f49fd7e11b50d3c78dfb0a5cca12d1fddec5a [file] [log] [blame]
Hadrien De Sousaccc947d2017-04-12 14:26:52 -04001/*
Pierre Duchemin2802bfd2018-02-21 16:41:32 -05002 * Copyright (C) 2004-2018 Savoir-faire Linux Inc.
Hadrien De Sousaccc947d2017-04-12 14:26:52 -04003 *
4 * Author: Hadrien De Sousa <hadrien.desousa@savoirfairelinux.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20package cx.ring.call;
21
Hadrien De Sousaccc947d2017-04-12 14:26:52 -040022import java.util.concurrent.Executors;
23import java.util.concurrent.ScheduledExecutorService;
24import java.util.concurrent.TimeUnit;
25
26import javax.inject.Inject;
27
Adrien Béraud87fe69c2018-03-19 22:05:07 -040028import cx.ring.facades.ConversationFacade;
Hadrien De Sousaccc947d2017-04-12 14:26:52 -040029import cx.ring.model.CallContact;
30import cx.ring.model.Conference;
31import cx.ring.model.ServiceEvent;
32import cx.ring.model.SipCall;
Hadrien De Sousaccc947d2017-04-12 14:26:52 -040033import cx.ring.mvp.RootPresenter;
34import cx.ring.services.AccountService;
35import cx.ring.services.CallService;
36import cx.ring.services.ContactService;
Hadrien De Sousaccc947d2017-04-12 14:26:52 -040037import cx.ring.services.HardwareService;
38import cx.ring.services.HistoryService;
39import cx.ring.services.NotificationService;
Hadrien De Sousaccc947d2017-04-12 14:26:52 -040040import cx.ring.utils.Log;
41import cx.ring.utils.Observable;
42import cx.ring.utils.Observer;
Hadrien De Sousac165e9b2017-11-24 12:33:32 -050043import cx.ring.utils.StringUtils;
Hadrien De Sousaccc947d2017-04-12 14:26:52 -040044
45public class CallPresenter extends RootPresenter<CallView> implements Observer<ServiceEvent> {
46
47 public final static String TAG = CallPresenter.class.getSimpleName();
48
49 protected AccountService mAccountService;
Hadrien De Sousaccc947d2017-04-12 14:26:52 -040050 protected NotificationService mNotificationService;
Hadrien De Sousaccc947d2017-04-12 14:26:52 -040051 protected HardwareService mHardwareService;
52 protected CallService mCallService;
53 protected ContactService mContactService;
54 protected HistoryService mHistoryService;
Adrien Béraud87fe69c2018-03-19 22:05:07 -040055 protected ConversationFacade mConversationFacade;
Hadrien De Sousaccc947d2017-04-12 14:26:52 -040056
57 private SipCall mSipCall;
58 private boolean mOnGoingCall = false;
Hadrien De Sousa203164c2017-12-05 17:41:42 -050059 private boolean mAudioOnly = true;
Hadrien De Sousaccc947d2017-04-12 14:26:52 -040060
61 private int videoWidth = -1;
62 private int videoHeight = -1;
63 private int previewWidth = -1;
64 private int previewHeight = -1;
65
Hadrien De Sousaccc947d2017-04-12 14:26:52 -040066 private ScheduledExecutorService executor;
Pierre Ducheminc3fafbf2018-02-15 10:15:07 -050067 private Runnable timeRunnable = this::updateTime;
Hadrien De Sousaccc947d2017-04-12 14:26:52 -040068
69 @Inject
70 public CallPresenter(AccountService accountService,
Hadrien De Sousaccc947d2017-04-12 14:26:52 -040071 NotificationService notificationService,
Hadrien De Sousaccc947d2017-04-12 14:26:52 -040072 HardwareService hardwareService,
73 CallService callService,
74 ContactService contactService,
Adrien Béraud87fe69c2018-03-19 22:05:07 -040075 HistoryService historyService,
76 ConversationFacade conversationFacade) {
Hadrien De Sousaccc947d2017-04-12 14:26:52 -040077 this.mAccountService = accountService;
Hadrien De Sousaccc947d2017-04-12 14:26:52 -040078 this.mNotificationService = notificationService;
Hadrien De Sousaccc947d2017-04-12 14:26:52 -040079 this.mHardwareService = hardwareService;
80 this.mCallService = callService;
81 this.mContactService = contactService;
Adrien Béraudd3bf4e42018-02-14 14:44:41 +010082 this.mHistoryService = historyService;
Adrien Béraud87fe69c2018-03-19 22:05:07 -040083 this.mConversationFacade = conversationFacade;
Hadrien De Sousaccc947d2017-04-12 14:26:52 -040084 }
85
86 @Override
Hadrien De Sousaccc947d2017-04-12 14:26:52 -040087 public void unbindView() {
88 super.unbindView();
89 mAccountService.removeObserver(this);
90 mCallService.removeObserver(this);
91 mHardwareService.removeObserver(this);
92
Hadrien De Sousa203164c2017-12-05 17:41:42 -050093 if (!mAudioOnly) {
Hadrien De Sousaccc947d2017-04-12 14:26:52 -040094 mHardwareService.stopCapture();
95 }
Hadrien De Sousaccc947d2017-04-12 14:26:52 -040096 }
97
98 @Override
99 public void bindView(CallView view) {
100 super.bindView(view);
101 mAccountService.addObserver(this);
102 mCallService.addObserver(this);
103 mHardwareService.addObserver(this);
104 }
105
Hadrien De Sousa203164c2017-12-05 17:41:42 -0500106 public void initOutGoing(String accountId, String contactRingId, boolean audioOnly) {
Adrien Béraud85683c22018-05-23 11:18:43 -0400107 if (accountId == null || contactRingId == null) {
108 Log.e(TAG, "initOutGoing: null account or contact");
109 getView().finish();
110 return;
111 }
Hadrien De Sousafcf2a992017-12-15 11:08:56 -0500112 if (mHardwareService.getCameraCount() == 0) {
Adrien Béraudd3bf4e42018-02-14 14:44:41 +0100113 audioOnly = true;
Hadrien De Sousafcf2a992017-12-15 11:08:56 -0500114 }
115
Adrien Béraud87fe69c2018-03-19 22:05:07 -0400116 mSipCall = mConversationFacade.placeCall(accountId, StringUtils.toNumber(contactRingId), audioOnly);
Adrien Beraud8e6e2ff2017-06-14 14:40:27 +0200117 if (mSipCall == null) {
Adrien Béraudd3bf4e42018-02-14 14:44:41 +0100118 Log.w(TAG, "initOutGoing: null Call");
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400119 finish();
120 return;
121 }
Adrien Béraudd3bf4e42018-02-14 14:44:41 +0100122
123 mAudioOnly = mSipCall.isAudioOnly();
124
125 getView().updateMenu();
126
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400127 confUpdate();
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400128 getContactDetails();
129 getView().blockScreenRotation();
130 }
131
132 public void initIncoming(String confId) {
133 mSipCall = mCallService.getCurrentCallForId(confId);
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400134 if (mSipCall == null) {
Adrien Beraud8e6e2ff2017-06-14 14:40:27 +0200135 Log.w(TAG, "initIncoming: null Call");
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400136 finish();
137 return;
138 }
Hadrien De Sousa203164c2017-12-05 17:41:42 -0500139 mAudioOnly = mSipCall.isAudioOnly();
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400140 confUpdate();
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400141 getContactDetails();
142 getView().blockScreenRotation();
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400143 }
144
145 public void prepareOptionMenu() {
146 boolean isSpeakerOn = mHardwareService.isSpeakerPhoneOn();
147 boolean hasContact = mSipCall != null && null != mSipCall.getContact() && mSipCall.getContact().isUnknown();
148 boolean canDial = mOnGoingCall && mSipCall != null && !mSipCall.isIncoming();
Hadrien De Sousa203164c2017-12-05 17:41:42 -0500149 boolean hasMultipleCamera = mHardwareService.getCameraCount() > 1 && mOnGoingCall && !mAudioOnly;
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400150 getView().initMenu(isSpeakerOn, hasContact, hasMultipleCamera, canDial, mOnGoingCall);
151 }
152
153 public void chatClick() {
154 if (mSipCall == null
155 || mSipCall.getContact() == null
156 || mSipCall.getContact().getIds() == null
157 || mSipCall.getContact().getIds().isEmpty()) {
158 return;
159 }
Hadrien De Sousa0dc83162017-11-10 16:15:18 -0500160 getView().goToConversation(mAccountService.getCurrentAccount().getAccountID(), mSipCall.getContact().getIds().get(0));
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400161 }
162
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400163 public void speakerClick() {
Adrien Béraud4a7473f2018-02-18 14:24:01 +0100164 mHardwareService.toggleSpeakerphone();
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400165 }
166
167 public void switchVideoInputClick() {
168 mHardwareService.switchInput(mSipCall.getCallId());
169 getView().switchCameraIcon(mHardwareService.isPreviewFromFrontCamera());
170 }
171
172 public void screenRotationClick() {
173 getView().changeScreenRotation();
174 }
175
176 public void configurationChanged() {
Adrien Béraud68bdcf42018-04-02 01:55:15 -0400177 if (mSipCall != null) {
178 mHardwareService.restartCamera(mSipCall.getCallId());
179 }
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400180 }
181
182 public void dialpadClick() {
183 getView().displayDialPadKeyboard();
184 }
185
186 public void acceptCall() {
187 if (mSipCall == null) {
188 return;
189 }
190 mCallService.accept(mSipCall.getCallId());
191 }
192
193 public void hangupCall() {
194 if (mSipCall != null) {
195 mCallService.hangUp(mSipCall.getCallId());
196 }
197 finish();
198 }
199
200 public void refuseCall() {
201 if (mSipCall != null) {
202 mCallService.refuse(mSipCall.getCallId());
203 mNotificationService.cancelCallNotification(mSipCall.getCallId().hashCode());
204 }
205 finish();
206 }
207
208 public void videoSurfaceCreated(Object holder) {
Alexandre Lisione4d40262016-12-12 14:30:09 -0500209 if (mSipCall == null) {
210 return;
211 }
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400212 mHardwareService.addVideoSurface(mSipCall.getCallId(), holder);
213 getView().displayContactBubble(false);
214 }
215
216 public void previewVideoSurfaceCreated(Object holder) {
217 mHardwareService.addPreviewVideoSurface(holder);
218 mHardwareService.startCapture(null);
219 }
220
221 public void videoSurfaceDestroyed() {
222 if (mSipCall == null) {
223 return;
224 }
225 mHardwareService.removeVideoSurface(mSipCall.getCallId());
226 }
227
228 public void previewVideoSurfaceDestroyed() {
229 mHardwareService.removePreviewVideoSurface();
230 mHardwareService.stopCapture();
231 }
232
233 public void displayChanged() {
234 mHardwareService.switchInput(mSipCall.getCallId());
235 }
236
237 public void layoutChanged() {
238 getView().resetVideoSize(videoWidth, videoHeight, previewWidth, previewHeight);
239 }
240
241 public void uiVisibilityChanged(boolean displayed) {
242 getView().displayHangupButton(mOnGoingCall && displayed);
243 }
244
245 private void finish() {
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400246 if (executor != null && !executor.isShutdown()) {
247 executor.shutdown();
248 }
249 mSipCall = null;
250 getView().finish();
251 }
252
253 private void confUpdate() {
254 if (mSipCall == null) {
255 return;
256 }
Adrien Béraudd3bf4e42018-02-14 14:44:41 +0100257 mAudioOnly = mSipCall.isAudioOnly();
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400258 if (mSipCall.isOnGoing()) {
259 mOnGoingCall = true;
Hadrien De Sousa203164c2017-12-05 17:41:42 -0500260 getView().initNormalStateDisplay(mAudioOnly);
Adrien Beraud8e6e2ff2017-06-14 14:40:27 +0200261 getView().updateContactBubble(mSipCall.getContact());
Adrien Béraudd3bf4e42018-02-14 14:44:41 +0100262 getView().updateMenu();
Hadrien De Sousa203164c2017-12-05 17:41:42 -0500263 if (!mAudioOnly) {
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400264 mHardwareService.setPreviewSettings();
265 getView().displayVideoSurface(true);
266 }
Adrien Beraud683b9322017-07-08 22:53:41 -0400267 if (executor == null || executor.isShutdown()) {
268 executor = Executors.newSingleThreadScheduledExecutor();
269 executor.scheduleAtFixedRate(timeRunnable, 0, 1, TimeUnit.SECONDS);
270 }
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400271 } else if (mSipCall.isRinging()) {
272 if (mSipCall.isIncoming()) {
273 if (mAccountService.getAccount(mSipCall.getAccount()).isAutoanswerEnabled()) {
274 mCallService.accept(mSipCall.getCallId());
275 } else {
276 getView().initIncomingCallDisplay();
Adrien Beraud8e6e2ff2017-06-14 14:40:27 +0200277 getView().updateContactBubble(mSipCall.getContact());
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400278 }
279 } else {
280 mOnGoingCall = false;
281 getView().updateCallStatus(mSipCall.getCallState());
282 getView().initOutGoingCallDisplay();
Adrien Beraud8e6e2ff2017-06-14 14:40:27 +0200283 getView().updateContactBubble(mSipCall.getContact());
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400284 }
285 } else {
286 finish();
287 }
288 }
289
290 private void updateTime() {
291 if (mSipCall != null) {
292 long duration = System.currentTimeMillis() - mSipCall.getTimestampStart();
293 duration = duration / 1000;
294 if (mSipCall.isOnGoing()) {
295 getView().updateTime(duration);
296 }
297 }
298 }
299
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400300 private void getContactDetails() {
Pierre Duchemin2697ee92018-03-08 14:59:37 -0500301 if (mSipCall == null) {
302 Log.e(TAG, "Not able to get SIP call");
303 return;
304 }
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400305 CallContact callContact = mSipCall.getContact();
Pierre Duchemin2697ee92018-03-08 14:59:37 -0500306 if (mContactService == null) {
307 Log.e(TAG, "Not able to get contact service");
308 return;
309 }
Adrien Beraud8e6e2ff2017-06-14 14:40:27 +0200310 mContactService.loadContactData(callContact);
Pierre Duchemin2697ee92018-03-08 14:59:37 -0500311
Adrien Beraud8e6e2ff2017-06-14 14:40:27 +0200312 getView().updateContactBubble(callContact);
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400313 }
314
Adrien Beraud683b9322017-07-08 22:53:41 -0400315 private void parseCall(SipCall call, int callState) {
316 if (mSipCall == null || mSipCall != call) {
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400317 return;
318 }
319
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400320 if (callState == SipCall.State.HUNGUP
321 || callState == SipCall.State.BUSY
322 || callState == SipCall.State.FAILURE
323 || callState == SipCall.State.OVER) {
324 finish();
325 } else if (callState != SipCall.State.INACTIVE) {
326 mNotificationService.showCallNotification(new Conference(mSipCall));
327 }
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400328 }
329
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400330 @Override
331 public void update(Observable observable, ServiceEvent event) {
332 if (event == null) {
333 return;
334 }
335
336 if (observable instanceof CallService) {
337 switch (event.getEventType()) {
338 case CALL_STATE_CHANGED:
Adrien Beraud683b9322017-07-08 22:53:41 -0400339 SipCall call = event.getEventInput(ServiceEvent.EventInput.CALL, SipCall.class);
340 int state = call.getCallState();
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400341
Adrien Beraud683b9322017-07-08 22:53:41 -0400342 Log.d(TAG, "CALL_STATE_CHANGED: " + call.getCallId() + " " + state);
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400343
Adrien Beraud683b9322017-07-08 22:53:41 -0400344 parseCall(call, state);
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400345 confUpdate();
346 break;
347 }
348 } else if (observable instanceof AccountService) {
349 switch (event.getEventType()) {
350 case REGISTERED_NAME_FOUND:
Adrien Beraud8e6e2ff2017-06-14 14:40:27 +0200351 if (mSipCall != null && mSipCall.getContact() != null) {
352 getView().updateContactBubble(mSipCall.getContact());
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400353 }
354 break;
355 }
356 } else if (observable instanceof HardwareService) {
357 switch (event.getEventType()) {
358 case VIDEO_EVENT:
359 boolean videoStart = event.getEventInput(ServiceEvent.EventInput.VIDEO_START, Boolean.class, false);
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400360 String callId = event.getEventInput(ServiceEvent.EventInput.VIDEO_CALL, String.class);
361
Loïc Siretc61e5442017-08-17 12:07:55 -0400362 Log.d(TAG, "VIDEO_EVENT: " + videoStart + " " + callId);
Loïc Siret9d5aea12017-08-28 15:17:25 -0400363 previewHeight = event.getEventInput(ServiceEvent.EventInput.VIDEO_WIDTH, Integer.class, 0);
364 previewWidth = event.getEventInput(ServiceEvent.EventInput.VIDEO_HEIGHT, Integer.class, 0);
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400365
366 if (videoStart) {
367 getView().displayVideoSurface(true);
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400368 } else if (mSipCall != null && callId != null && mSipCall.getCallId().equals(callId)) {
369 boolean videoStarted = event.getEventInput(ServiceEvent.EventInput.VIDEO_STARTED, Boolean.class, false);
370 getView().displayVideoSurface(videoStarted);
371 if (videoStarted) {
372 videoWidth = event.getEventInput(ServiceEvent.EventInput.VIDEO_WIDTH, Integer.class, 0);
373 videoHeight = event.getEventInput(ServiceEvent.EventInput.VIDEO_HEIGHT, Integer.class, 0);
374 }
375 }
376 getView().resetVideoSize(videoWidth, videoHeight, previewWidth, previewHeight);
377 break;
378 }
379 }
380 }
Pierre Duchemin8b2c1b52017-12-29 17:17:13 -0500381
382 public void positiveButtonClicked() {
383 if (mSipCall.isRinging() && mSipCall.isIncoming()) {
384 acceptCall();
385 } else {
386 hangupCall();
387 }
388 }
389
390 public void negativeButtonClicked() {
391 if (mSipCall.isRinging() && mSipCall.isIncoming()) {
392 refuseCall();
393 } else {
394 hangupCall();
395 }
396 }
397
398 public void toggleButtonClicked() {
399 if (!(mSipCall.isRinging() && mSipCall.isIncoming())) {
400 hangupCall();
401 }
402 }
Adrien Béraud79808d02018-02-20 00:16:28 +0100403
404 public void requestPipMode() {
405 if (mSipCall.isOnGoing() && !mSipCall.isAudioOnly()) {
406 getView().enterPipMode(mSipCall);
407 }
408 }
409
410 public void pipModeChanged(boolean pip) {
411 if (pip) {
412 getView().displayHangupButton(false);
413 getView().displayPreviewSurface(false);
414 } else {
415 getView().displayPreviewSurface(true);
416 }
417 }
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400418}