blob: c68aa4a27619ae138170fdfdb448c8398d891fdd [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>
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
Hadrien De Sousaccc947d2017-04-12 14:26:52 -040023import java.util.concurrent.TimeUnit;
24
25import javax.inject.Inject;
Adrien Béraudace37a22018-06-22 14:58:36 -040026import javax.inject.Named;
Hadrien De Sousaccc947d2017-04-12 14:26:52 -040027
Hadrien De Sousaccc947d2017-04-12 14:26:52 -040028import cx.ring.model.SipCall;
Hadrien De Sousaccc947d2017-04-12 14:26:52 -040029import cx.ring.mvp.RootPresenter;
30import cx.ring.services.AccountService;
31import cx.ring.services.CallService;
Hadrien De Sousaccc947d2017-04-12 14:26:52 -040032import cx.ring.services.HardwareService;
Hadrien De Sousaccc947d2017-04-12 14:26:52 -040033import cx.ring.services.NotificationService;
Hadrien De Sousaccc947d2017-04-12 14:26:52 -040034import cx.ring.utils.Log;
Hadrien De Sousac165e9b2017-11-24 12:33:32 -050035import cx.ring.utils.StringUtils;
Adrien Béraudace37a22018-06-22 14:58:36 -040036import io.reactivex.Scheduler;
Adrien Béraud3aeed5d2018-07-26 11:26:13 -040037import io.reactivex.disposables.Disposable;
Hadrien De Sousaccc947d2017-04-12 14:26:52 -040038
Adrien Béraudace37a22018-06-22 14:58:36 -040039public class CallPresenter extends RootPresenter<CallView> {
Hadrien De Sousaccc947d2017-04-12 14:26:52 -040040
41 public final static String TAG = CallPresenter.class.getSimpleName();
42
Adrien Béraudace37a22018-06-22 14:58:36 -040043 private AccountService mAccountService;
44 private NotificationService mNotificationService;
45 private HardwareService mHardwareService;
46 private CallService mCallService;
Hadrien De Sousaccc947d2017-04-12 14:26:52 -040047
48 private SipCall mSipCall;
49 private boolean mOnGoingCall = false;
Hadrien De Sousa203164c2017-12-05 17:41:42 -050050 private boolean mAudioOnly = true;
Hadrien De Sousaccc947d2017-04-12 14:26:52 -040051
52 private int videoWidth = -1;
53 private int videoHeight = -1;
54 private int previewWidth = -1;
55 private int previewHeight = -1;
56
Adrien Béraud3aeed5d2018-07-26 11:26:13 -040057 private Disposable timeUpdateTask = null;
Hadrien De Sousaccc947d2017-04-12 14:26:52 -040058
59 @Inject
Adrien Béraudace37a22018-06-22 14:58:36 -040060 @Named("UiScheduler")
61 protected Scheduler mUiScheduler;
62
63 @Inject
Hadrien De Sousaccc947d2017-04-12 14:26:52 -040064 public CallPresenter(AccountService accountService,
Hadrien De Sousaccc947d2017-04-12 14:26:52 -040065 NotificationService notificationService,
Hadrien De Sousaccc947d2017-04-12 14:26:52 -040066 HardwareService hardwareService,
Adrien Béraud2c378b32018-07-11 15:24:02 -040067 CallService callService) {
Adrien Béraudace37a22018-06-22 14:58:36 -040068 mAccountService = accountService;
69 mNotificationService = notificationService;
70 mHardwareService = hardwareService;
71 mCallService = callService;
Hadrien De Sousaccc947d2017-04-12 14:26:52 -040072 }
73
74 @Override
Hadrien De Sousaccc947d2017-04-12 14:26:52 -040075 public void unbindView() {
Hadrien De Sousa203164c2017-12-05 17:41:42 -050076 if (!mAudioOnly) {
Adrien Béraud30532a52018-09-11 15:10:05 -040077 mHardwareService.endCapture();
Hadrien De Sousaccc947d2017-04-12 14:26:52 -040078 }
Adrien Béraud99fd4202018-12-13 13:02:21 -050079 super.unbindView();
Hadrien De Sousaccc947d2017-04-12 14:26:52 -040080 }
81
82 @Override
83 public void bindView(CallView view) {
84 super.bindView(view);
Adrien Béraudace37a22018-06-22 14:58:36 -040085 mCompositeDisposable.add(mAccountService.getRegisteredNames()
86 .observeOn(mUiScheduler)
87 .subscribe(r -> {
88 if (mSipCall != null && mSipCall.getContact() != null) {
89 getView().updateContactBubble(mSipCall.getContact());
90 }
91 }));
92 mCompositeDisposable.add(mHardwareService.getVideoEvents()
93 .observeOn(mUiScheduler)
94 .subscribe(this::onVideoEvent));
Adrien Béraud057e9a32018-09-30 17:48:55 -040095 mCompositeDisposable.add(mHardwareService
96 .getBluetoothEvents()
97 .subscribe(event -> {
98 if (!event.connected && mSipCall != null) {
99 hangupCall();
100 }
101 }));
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400102 }
103
Hadrien De Sousa203164c2017-12-05 17:41:42 -0500104 public void initOutGoing(String accountId, String contactRingId, boolean audioOnly) {
Adrien Béraud85683c22018-05-23 11:18:43 -0400105 if (accountId == null || contactRingId == null) {
106 Log.e(TAG, "initOutGoing: null account or contact");
107 getView().finish();
108 return;
109 }
Hadrien De Sousafcf2a992017-12-15 11:08:56 -0500110 if (mHardwareService.getCameraCount() == 0) {
Adrien Béraudd3bf4e42018-02-14 14:44:41 +0100111 audioOnly = true;
Hadrien De Sousafcf2a992017-12-15 11:08:56 -0500112 }
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400113 getView().blockScreenRotation();
Adrien Béraudace37a22018-06-22 14:58:36 -0400114
115 mCompositeDisposable.add(mCallService
116 .placeCallObservable(accountId, StringUtils.toNumber(contactRingId), audioOnly)
117 .observeOn(mUiScheduler)
118 .subscribe(call -> {
Adrien Béraud2c378b32018-07-11 15:24:02 -0400119 contactUpdate(call);
Adrien Béraud99fd4202018-12-13 13:02:21 -0500120 confUpdate(call);
Adrien Béraud2c378b32018-07-11 15:24:02 -0400121 }));
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400122 }
123
124 public void initIncoming(String confId) {
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400125 getView().blockScreenRotation();
Adrien Béraudace37a22018-06-22 14:58:36 -0400126 mCompositeDisposable.add(mCallService.getCallUpdates(confId)
127 .observeOn(mUiScheduler)
128 .subscribe(call -> {
Adrien Béraud2c378b32018-07-11 15:24:02 -0400129 contactUpdate(call);
Adrien Béraud99fd4202018-12-13 13:02:21 -0500130 confUpdate(call);
131 }, e -> finish()));
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400132 }
133
134 public void prepareOptionMenu() {
135 boolean isSpeakerOn = mHardwareService.isSpeakerPhoneOn();
136 boolean hasContact = mSipCall != null && null != mSipCall.getContact() && mSipCall.getContact().isUnknown();
137 boolean canDial = mOnGoingCall && mSipCall != null && !mSipCall.isIncoming();
Hadrien De Sousa203164c2017-12-05 17:41:42 -0500138 boolean hasMultipleCamera = mHardwareService.getCameraCount() > 1 && mOnGoingCall && !mAudioOnly;
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400139 getView().initMenu(isSpeakerOn, hasContact, hasMultipleCamera, canDial, mOnGoingCall);
140 }
141
142 public void chatClick() {
143 if (mSipCall == null
144 || mSipCall.getContact() == null
145 || mSipCall.getContact().getIds() == null
146 || mSipCall.getContact().getIds().isEmpty()) {
147 return;
148 }
Adrien Béraudace37a22018-06-22 14:58:36 -0400149 getView().goToConversation(mSipCall.getAccount(), mSipCall.getContact().getIds().get(0));
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400150 }
151
Adrien Béraudbc0898e2018-08-23 14:31:11 -0400152 public void speakerClick(boolean checked) {
153 mHardwareService.toggleSpeakerphone(checked);
154 }
155
156 public void muteMicrophoneToggled(boolean checked) {
157 mCallService.setMuted(checked);
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400158 }
159
160 public void switchVideoInputClick() {
161 mHardwareService.switchInput(mSipCall.getCallId());
162 getView().switchCameraIcon(mHardwareService.isPreviewFromFrontCamera());
163 }
164
165 public void screenRotationClick() {
166 getView().changeScreenRotation();
167 }
168
169 public void configurationChanged() {
Adrien Béraud68bdcf42018-04-02 01:55:15 -0400170 if (mSipCall != null) {
171 mHardwareService.restartCamera(mSipCall.getCallId());
172 }
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400173 }
174
175 public void dialpadClick() {
176 getView().displayDialPadKeyboard();
177 }
178
179 public void acceptCall() {
180 if (mSipCall == null) {
181 return;
182 }
183 mCallService.accept(mSipCall.getCallId());
184 }
185
186 public void hangupCall() {
187 if (mSipCall != null) {
188 mCallService.hangUp(mSipCall.getCallId());
189 }
190 finish();
191 }
192
193 public void refuseCall() {
Adrien Béraudace37a22018-06-22 14:58:36 -0400194 final SipCall call = mSipCall;
195 if (call != null) {
196 mCallService.refuse(call.getCallId());
197 mNotificationService.cancelCallNotification(call.getCallId().hashCode());
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400198 }
199 finish();
200 }
201
202 public void videoSurfaceCreated(Object holder) {
Alexandre Lisione4d40262016-12-12 14:30:09 -0500203 if (mSipCall == null) {
204 return;
205 }
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400206 mHardwareService.addVideoSurface(mSipCall.getCallId(), holder);
207 getView().displayContactBubble(false);
208 }
209
210 public void previewVideoSurfaceCreated(Object holder) {
211 mHardwareService.addPreviewVideoSurface(holder);
Adrien Béraud30532a52018-09-11 15:10:05 -0400212 //mHardwareService.startCapture(null);
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400213 }
214
215 public void videoSurfaceDestroyed() {
216 if (mSipCall == null) {
217 return;
218 }
219 mHardwareService.removeVideoSurface(mSipCall.getCallId());
220 }
221
222 public void previewVideoSurfaceDestroyed() {
223 mHardwareService.removePreviewVideoSurface();
Adrien Béraud30532a52018-09-11 15:10:05 -0400224 mHardwareService.endCapture();
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400225 }
226
227 public void displayChanged() {
228 mHardwareService.switchInput(mSipCall.getCallId());
229 }
230
231 public void layoutChanged() {
232 getView().resetVideoSize(videoWidth, videoHeight, previewWidth, previewHeight);
233 }
234
235 public void uiVisibilityChanged(boolean displayed) {
Adrien Béraud665faa22018-07-26 16:51:20 -0400236 CallView view = getView();
237 if (view != null)
238 view.displayHangupButton(mOnGoingCall && displayed);
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400239 }
240
241 private void finish() {
Adrien Béraud3aeed5d2018-07-26 11:26:13 -0400242 if (timeUpdateTask != null && !timeUpdateTask.isDisposed()) {
243 timeUpdateTask.dispose();
244 timeUpdateTask = null;
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400245 }
246 mSipCall = null;
Adrien Béraud8d6baf32018-08-03 23:37:31 -0400247 CallView view = getView();
248 if (view != null)
249 view.finish();
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400250 }
251
Adrien Béraud2c378b32018-07-11 15:24:02 -0400252 private void contactUpdate(final SipCall call) {
253 if (mSipCall != call) {
254 mSipCall = call;
255 mCompositeDisposable.add(call.getContact()
256 .getUpdates()
257 .observeOn(mUiScheduler)
258 .subscribe(c -> getView().updateContactBubble(c)));
259 }
260 }
261
Adrien Béraud99fd4202018-12-13 13:02:21 -0500262 private void confUpdate(SipCall call) {
263 mAudioOnly = call.isAudioOnly();
Adrien Béraudace37a22018-06-22 14:58:36 -0400264 CallView view = getView();
Adrien Béraud99fd4202018-12-13 13:02:21 -0500265 if (view == null)
266 return;
267 view.updateMenu();
268 if (call.isOnGoing()) {
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400269 mOnGoingCall = true;
Adrien Béraudbc0898e2018-08-23 14:31:11 -0400270 view.initNormalStateDisplay(mAudioOnly, mHardwareService.isSpeakerPhoneOn());
Adrien Béraudace37a22018-06-22 14:58:36 -0400271 view.updateMenu();
Hadrien De Sousa203164c2017-12-05 17:41:42 -0500272 if (!mAudioOnly) {
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400273 mHardwareService.setPreviewSettings();
Adrien Béraudace37a22018-06-22 14:58:36 -0400274 view.displayVideoSurface(true);
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400275 }
Adrien Béraud3aeed5d2018-07-26 11:26:13 -0400276 if (timeUpdateTask != null)
277 timeUpdateTask.dispose();
Adrien Béraud99fd4202018-12-13 13:02:21 -0500278 timeUpdateTask = mUiScheduler.schedulePeriodicallyDirect(this::updateTime, 0, 1, TimeUnit.SECONDS);
279 } else if (call.isRinging()) {
280 if (call.isIncoming()) {
281 if (mAccountService.getAccount(call.getAccount()).isAutoanswerEnabled()) {
282 mCallService.accept(call.getCallId());
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400283 } else {
Adrien Béraudace37a22018-06-22 14:58:36 -0400284 view.initIncomingCallDisplay();
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400285 }
286 } else {
287 mOnGoingCall = false;
Adrien Béraud99fd4202018-12-13 13:02:21 -0500288 view.updateCallStatus(call.getCallState());
Adrien Béraudace37a22018-06-22 14:58:36 -0400289 view.initOutGoingCallDisplay();
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400290 }
291 } else {
292 finish();
293 }
294 }
295
296 private void updateTime() {
Adrien Béraud6325fad2018-08-15 15:34:11 -0400297 CallView view = getView();
298 if (view != null && mSipCall != null) {
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400299 long duration = System.currentTimeMillis() - mSipCall.getTimestampStart();
300 duration = duration / 1000;
301 if (mSipCall.isOnGoing()) {
Adrien Béraud6325fad2018-08-15 15:34:11 -0400302 view.updateTime(duration);
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400303 }
304 }
305 }
306
Adrien Béraudace37a22018-06-22 14:58:36 -0400307 private void onVideoEvent(HardwareService.VideoEvent event) {
308 Log.d(TAG, "VIDEO_EVENT: " + event.start + " " + event.callId);
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400309
Adrien Béraudace37a22018-06-22 14:58:36 -0400310 if (event.start) {
311 getView().displayVideoSurface(true);
312 } else if (mSipCall != null && mSipCall.getCallId().equals(event.callId)) {
313 getView().displayVideoSurface(event.started);
314 if (event.started) {
315 videoWidth = event.w;
316 videoHeight = event.h;
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400317 }
Adrien Béraud30532a52018-09-11 15:10:05 -0400318 } else if (event.callId == null) {
319 if (event.started) {
320 previewWidth = event.w;
321 previewHeight = event.h;
322 }
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400323 }
Adrien Béraudace37a22018-06-22 14:58:36 -0400324 getView().resetVideoSize(videoWidth, videoHeight, previewWidth, previewHeight);
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400325 }
Pierre Duchemin8b2c1b52017-12-29 17:17:13 -0500326
327 public void positiveButtonClicked() {
328 if (mSipCall.isRinging() && mSipCall.isIncoming()) {
329 acceptCall();
330 } else {
331 hangupCall();
332 }
333 }
334
335 public void negativeButtonClicked() {
336 if (mSipCall.isRinging() && mSipCall.isIncoming()) {
337 refuseCall();
338 } else {
339 hangupCall();
340 }
341 }
342
343 public void toggleButtonClicked() {
344 if (!(mSipCall.isRinging() && mSipCall.isIncoming())) {
345 hangupCall();
346 }
347 }
Adrien Béraud79808d02018-02-20 00:16:28 +0100348
349 public void requestPipMode() {
Adrien Béraud6325fad2018-08-15 15:34:11 -0400350 if (mSipCall != null && mSipCall.isOnGoing() && !mSipCall.isAudioOnly()) {
Adrien Béraud79808d02018-02-20 00:16:28 +0100351 getView().enterPipMode(mSipCall);
352 }
353 }
354
355 public void pipModeChanged(boolean pip) {
356 if (pip) {
357 getView().displayHangupButton(false);
358 getView().displayPreviewSurface(false);
359 } else {
360 getView().displayPreviewSurface(true);
361 }
362 }
Adrien Béraudbc0898e2018-08-23 14:31:11 -0400363
364 public boolean isSpeakerphoneOn() {
365 return mHardwareService.isSpeakerPhoneOn();
366 }
Adrien Béraudbcbb4d42018-12-08 16:06:39 -0500367
368 public void sendDtmf(CharSequence s) {
369 mCallService.playDtmf(s.toString());
370 }
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400371}