blob: 42d6c106866d741e8a1e521328c461446bb594ba [file] [log] [blame]
Adrien Béraud04d822c2015-04-02 17:44:36 -04001/*
Pierre Duchemin2802bfd2018-02-21 16:41:32 -05002 * Copyright (C) 2004-2018 Savoir-faire Linux Inc.
Adrien Béraud04d822c2015-04-02 17:44:36 -04003 *
Hadrien De Sousaccc947d2017-04-12 14:26:52 -04004 * Author: Hadrien De Sousa <hadrien.desousa@savoirfairelinux.com>
Adrien Béraudbc0898e2018-08-23 14:31:11 -04005 * Adrien Béraud <adrien.beraud@savoirfairelinux.com>
Adrien Béraud04d822c2015-04-02 17:44:36 -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
Adrien Béraudda712a42015-11-22 23:45:26 -050019 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
Adrien Béraud04d822c2015-04-02 17:44:36 -040020 */
Adrien Béraud04d822c2015-04-02 17:44:36 -040021package cx.ring.fragments;
22
Adrien Béraud79808d02018-02-20 00:16:28 +010023import android.app.PendingIntent;
24import android.app.PictureInPictureParams;
25import android.app.RemoteAction;
Adrien Béraud162427f2016-04-12 18:04:08 -040026import android.content.Context;
27import android.content.Intent;
Kateryna Kostiukcea71192017-02-17 15:54:47 -050028import android.content.pm.ActivityInfo;
Adrien Béraudfb6341f2016-03-07 16:18:54 -050029import android.content.res.Configuration;
30import android.graphics.PixelFormat;
Adrien Béraud79808d02018-02-20 00:16:28 +010031import android.graphics.Rect;
32import android.graphics.drawable.Icon;
Adrien Béraud8d6a4832016-04-12 18:06:11 -040033import android.os.Build;
Adrien Béraud04d822c2015-04-02 17:44:36 -040034import android.os.Bundle;
35import android.os.PowerManager;
Adrien Béraud8bc95102018-08-25 22:34:51 -040036import androidx.annotation.NonNull;
37import androidx.annotation.Nullable;
Adrien Béraudfb6341f2016-03-07 16:18:54 -050038import android.util.DisplayMetrics;
Adrien Béraud79808d02018-02-20 00:16:28 +010039import android.util.Rational;
Adrien Béraud162427f2016-04-12 18:04:08 -040040import android.view.Menu;
41import android.view.MenuInflater;
42import android.view.MenuItem;
43import android.view.SurfaceHolder;
44import android.view.SurfaceView;
45import android.view.View;
Adrien Béraud162427f2016-04-12 18:04:08 -040046import android.view.ViewGroup;
Romain Bertozziad8fd842016-05-11 14:30:35 -040047import android.view.inputmethod.InputMethodManager;
Romain Bertozziad8fd842016-05-11 14:30:35 -040048import android.widget.EditText;
Adrien Béraudbc0898e2018-08-23 14:31:11 -040049import android.widget.ImageButton;
Adrien Béraud162427f2016-04-12 18:04:08 -040050import android.widget.ImageView;
51import android.widget.RelativeLayout;
52import android.widget.TextView;
Adrien Béraud04d822c2015-04-02 17:44:36 -040053
Pierre Ducheminbb833672018-03-09 16:18:33 -050054import com.rodolfonavalon.shaperipplelibrary.ShapeRipple;
55import com.rodolfonavalon.shaperipplelibrary.model.Circle;
Thibault Wittembergb70d5b02016-07-08 16:50:14 -040056
Adrien Béraud79808d02018-02-20 00:16:28 +010057import java.util.ArrayList;
Romain Bertozzi64a66782016-08-02 12:10:38 -040058import java.util.Locale;
Adrien Béraud79808d02018-02-20 00:16:28 +010059import java.util.Random;
Adrien Béraud04d822c2015-04-02 17:44:36 -040060
Adrien Béraud8bc95102018-08-25 22:34:51 -040061import androidx.appcompat.app.ActionBar;
62import androidx.appcompat.app.AppCompatActivity;
Thibault Wittembergb70d5b02016-07-08 16:50:14 -040063import butterknife.BindView;
Thibault Wittembergb70d5b02016-07-08 16:50:14 -040064import butterknife.OnClick;
Adrien Béraud162427f2016-04-12 18:04:08 -040065import cx.ring.R;
Hadrien De Sousaccc947d2017-04-12 14:26:52 -040066import cx.ring.call.CallPresenter;
67import cx.ring.call.CallView;
Adrien Béraudbc0898e2018-08-23 14:31:11 -040068import cx.ring.client.CallActivity;
Adrien Béraud162427f2016-04-12 18:04:08 -040069import cx.ring.client.ConversationActivity;
Thibault Wittemberg4f33d072016-10-06 17:11:41 -040070import cx.ring.client.HomeActivity;
Adrien Béraud5f8c3f72018-03-09 14:49:39 -050071import cx.ring.contacts.AvatarFactory;
Hadrien De Sousa73d6dd72017-09-07 10:54:51 -040072import cx.ring.dependencyinjection.RingInjectionComponent;
Adrien Béraud04d822c2015-04-02 17:44:36 -040073import cx.ring.model.CallContact;
Adrien Béraud04d822c2015-04-02 17:44:36 -040074import cx.ring.model.SipCall;
Hadrien De Sousaccc947d2017-04-12 14:26:52 -040075import cx.ring.mvp.BaseFragment;
Alexandre Lision5da46fa2017-08-09 10:25:50 -040076import cx.ring.service.DRingService;
Hadrien De Sousa9c968fc2017-08-30 17:46:03 -040077import cx.ring.services.HardwareServiceImpl;
Adrien Béraud79808d02018-02-20 00:16:28 +010078import cx.ring.services.NotificationService;
Alexandre Lision4865f7d2016-11-16 18:03:35 -050079import cx.ring.utils.ActionHelper;
Pierre Duchemin42788c32018-03-08 16:21:15 -050080import cx.ring.utils.DeviceUtils;
Romain Bertozziad8fd842016-05-11 14:30:35 -040081import cx.ring.utils.KeyboardVisibilityManager;
Adrien Béraud5f8c3f72018-03-09 14:49:39 -050082import cx.ring.utils.Log;
Pierre Duchemin8b2c1b52017-12-29 17:17:13 -050083import cx.ring.utils.MediaButtonsHelper;
Adrien Béraudbc0898e2018-08-23 14:31:11 -040084import cx.ring.views.CheckableImageButton;
Adrien Béraud2c378b32018-07-11 15:24:02 -040085import io.reactivex.disposables.CompositeDisposable;
Adrien Béraud04d822c2015-04-02 17:44:36 -040086
Pierre Duchemin8b2c1b52017-12-29 17:17:13 -050087public class CallFragment extends BaseFragment<CallPresenter> implements CallView, MediaButtonsHelper.MediaButtonsHelperCallback {
Adrien Béraud04d822c2015-04-02 17:44:36 -040088
Hadrien De Sousaccc947d2017-04-12 14:26:52 -040089 public static final String TAG = CallFragment.class.getSimpleName();
Adrien Béraud04d822c2015-04-02 17:44:36 -040090
Hadrien De Sousaccc947d2017-04-12 14:26:52 -040091 public static final String ACTION_PLACE_CALL = "PLACE_CALL";
92 public static final String ACTION_GET_CALL = "GET_CALL";
Adrien Béraud04d822c2015-04-02 17:44:36 -040093
Hadrien De Sousaccc947d2017-04-12 14:26:52 -040094 public static final String KEY_ACTION = "action";
95 public static final String KEY_ACCOUNT_ID = "accountId";
96 public static final String KEY_CONF_ID = "confId";
Hadrien De Sousa203164c2017-12-05 17:41:42 -050097 public static final String KEY_AUDIO_ONLY = "AUDIO_ONLY";
Thibault Wittembergb70d5b02016-07-08 16:50:14 -040098
99 @BindView(R.id.contact_bubble_layout)
Adrien Béraudbc0898e2018-08-23 14:31:11 -0400100 protected ViewGroup contactBubbleLayout;
Thibault Wittembergb70d5b02016-07-08 16:50:14 -0400101
102 @BindView(R.id.contact_bubble)
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400103 protected ImageView contactBubbleView;
Thibault Wittembergb70d5b02016-07-08 16:50:14 -0400104
105 @BindView(R.id.contact_bubble_txt)
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400106 protected TextView contactBubbleTxt;
Thibault Wittembergb70d5b02016-07-08 16:50:14 -0400107
108 @BindView(R.id.contact_bubble_num_txt)
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400109 protected TextView contactBubbleNumTxt;
Thibault Wittembergb70d5b02016-07-08 16:50:14 -0400110
111 @BindView(R.id.call_accept_btn)
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400112 protected View acceptButton;
Thibault Wittembergb70d5b02016-07-08 16:50:14 -0400113
114 @BindView(R.id.call_refuse_btn)
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400115 protected View refuseButton;
Thibault Wittembergb70d5b02016-07-08 16:50:14 -0400116
117 @BindView(R.id.call_hangup_btn)
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400118 protected View hangupButton;
Thibault Wittembergb70d5b02016-07-08 16:50:14 -0400119
Adrien Béraudbc0898e2018-08-23 14:31:11 -0400120 @BindView(R.id.call_speaker_btn)
121 protected CheckableImageButton speakerButton;
122
123 @BindView(R.id.call_mic_btn)
124 protected CheckableImageButton micButton;
125
126 @BindView(R.id.call_camera_flip_btn)
127 protected ImageButton flipCameraBtn = null;
128
Thibault Wittembergb70d5b02016-07-08 16:50:14 -0400129 @BindView(R.id.call_status_txt)
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400130 protected TextView mCallStatusTxt;
Thibault Wittembergb70d5b02016-07-08 16:50:14 -0400131
132 @BindView(R.id.dialpad_edit_text)
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400133 protected EditText mNumeralDialEditText;
Thibault Wittembergb70d5b02016-07-08 16:50:14 -0400134
Pierre Ducheminbb833672018-03-09 16:18:33 -0500135 @BindView(R.id.shape_ripple)
136 protected ShapeRipple shapeRipple = null;
Thibault Wittembergb70d5b02016-07-08 16:50:14 -0400137
138 @BindView(R.id.video_preview_surface)
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400139 protected SurfaceView mVideoSurface = null;
140
141 @BindView(R.id.camera_preview_surface)
142 protected SurfaceView mVideoPreview = null;
Thibault Wittembergb70d5b02016-07-08 16:50:14 -0400143
Adrien Béraudbc0898e2018-08-23 14:31:11 -0400144 @BindView(R.id.call_control_group)
145 protected ViewGroup controlLayout;
146
Romain Bertozziad8fd842016-05-11 14:30:35 -0400147 private MenuItem dialPadBtn = null;
Kateryna Kostiukcea71192017-02-17 15:54:47 -0500148 private MenuItem changeScreenOrientationBtn = null;
Adrien Béraudbc0898e2018-08-23 14:31:11 -0400149 private boolean restartVideo = false;
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400150 private PowerManager.WakeLock mScreenWakeLock;
Adrien Béraud79808d02018-02-20 00:16:28 +0100151 private int mCurrentOrientation = Configuration.ORIENTATION_UNDEFINED;
Adrien Béraud8d6a4832016-04-12 18:06:11 -0400152
Adrien Béraud2c378b32018-07-11 15:24:02 -0400153 private final CompositeDisposable mCompositeDisposable = new CompositeDisposable();
154
Hadrien De Sousa203164c2017-12-05 17:41:42 -0500155 public static CallFragment newInstance(@NonNull String action, @Nullable String accountID, @Nullable String contactRingId, boolean audioOnly) {
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400156 Bundle bundle = new Bundle();
157 bundle.putString(KEY_ACTION, action);
158 bundle.putString(KEY_ACCOUNT_ID, accountID);
Hadrien De Sousa0dc83162017-11-10 16:15:18 -0500159 bundle.putString(ConversationFragment.KEY_CONTACT_RING_ID, contactRingId);
Hadrien De Sousa203164c2017-12-05 17:41:42 -0500160 bundle.putBoolean(KEY_AUDIO_ONLY, audioOnly);
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400161 CallFragment countDownFragment = new CallFragment();
162 countDownFragment.setArguments(bundle);
163 return countDownFragment;
164 }
Adrien Béraudfb6341f2016-03-07 16:18:54 -0500165
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400166 public static CallFragment newInstance(@NonNull String action, @Nullable String confId) {
167 Bundle bundle = new Bundle();
168 bundle.putString(KEY_ACTION, action);
169 bundle.putString(KEY_CONF_ID, confId);
170 CallFragment countDownFragment = new CallFragment();
171 countDownFragment.setArguments(bundle);
172 return countDownFragment;
173 }
174
Adrien Bérauda3f0b4b2018-08-09 11:41:53 -0400175 public static int callStateToHumanState(final SipCall.State state) {
Loïc Siret0202fbf2017-09-28 16:34:28 -0400176 switch (state) {
Adrien Bérauda3f0b4b2018-08-09 11:41:53 -0400177 case SEARCHING:
Loïc Siret0202fbf2017-09-28 16:34:28 -0400178 return R.string.call_human_state_searching;
Adrien Bérauda3f0b4b2018-08-09 11:41:53 -0400179 case CONNECTING:
Loïc Siret0202fbf2017-09-28 16:34:28 -0400180 return R.string.call_human_state_connecting;
Adrien Bérauda3f0b4b2018-08-09 11:41:53 -0400181 case RINGING:
Loïc Siret0202fbf2017-09-28 16:34:28 -0400182 return R.string.call_human_state_ringing;
Adrien Bérauda3f0b4b2018-08-09 11:41:53 -0400183 case CURRENT:
Loïc Siret0202fbf2017-09-28 16:34:28 -0400184 return R.string.call_human_state_current;
Adrien Bérauda3f0b4b2018-08-09 11:41:53 -0400185 case HUNGUP:
Loïc Siret0202fbf2017-09-28 16:34:28 -0400186 return R.string.call_human_state_hungup;
Adrien Bérauda3f0b4b2018-08-09 11:41:53 -0400187 case BUSY:
Loïc Siret0202fbf2017-09-28 16:34:28 -0400188 return R.string.call_human_state_busy;
Adrien Bérauda3f0b4b2018-08-09 11:41:53 -0400189 case FAILURE:
Loïc Siret0202fbf2017-09-28 16:34:28 -0400190 return R.string.call_human_state_failure;
Adrien Bérauda3f0b4b2018-08-09 11:41:53 -0400191 case HOLD:
Loïc Siret0202fbf2017-09-28 16:34:28 -0400192 return R.string.call_human_state_hold;
Adrien Bérauda3f0b4b2018-08-09 11:41:53 -0400193 case UNHOLD:
Loïc Siret0202fbf2017-09-28 16:34:28 -0400194 return R.string.call_human_state_unhold;
Adrien Bérauda3f0b4b2018-08-09 11:41:53 -0400195 case OVER:
Loïc Siret0202fbf2017-09-28 16:34:28 -0400196 return R.string.call_human_state_over;
Adrien Bérauda3f0b4b2018-08-09 11:41:53 -0400197 case NONE:
Loïc Siret0202fbf2017-09-28 16:34:28 -0400198 default:
Adrien Bérauda3f0b4b2018-08-09 11:41:53 -0400199 return 0;
Loïc Siret0202fbf2017-09-28 16:34:28 -0400200 }
201 }
202
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400203 @Override
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400204 protected void initPresenter(CallPresenter presenter) {
205 super.initPresenter(presenter);
206
207 String action = getArguments().getString(KEY_ACTION);
Pierre Duchemin45707ca2018-01-15 15:05:23 -0500208 if (action != null) {
209 if (action.equals(ACTION_PLACE_CALL)) {
210 presenter.initOutGoing(getArguments().getString(KEY_ACCOUNT_ID),
211 getArguments().getString(ConversationFragment.KEY_CONTACT_RING_ID),
212 getArguments().getBoolean(KEY_AUDIO_ONLY));
213 } else if (action.equals(ACTION_GET_CALL)) {
214 presenter.initIncoming(getArguments().getString(KEY_CONF_ID));
215 }
Adrien Béraudfb6341f2016-03-07 16:18:54 -0500216 }
Adrien Béraudfb6341f2016-03-07 16:18:54 -0500217 }
218
Adrien Béraud79808d02018-02-20 00:16:28 +0100219 public void onUserLeave() {
220 presenter.requestPipMode();
221 }
222
223 @Override
224 public void enterPipMode(SipCall sipCall) {
225 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
226 return;
227 }
228 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
229 PictureInPictureParams.Builder paramBuilder = new PictureInPictureParams.Builder();
230 if (mVideoSurface.getVisibility() == View.VISIBLE) {
231 int[] l = new int[2];
232 mVideoSurface.getLocationInWindow(l);
233 int x = l[0];
234 int y = l[1];
235 int w = mVideoSurface.getWidth();
236 int h = mVideoSurface.getHeight();
237 Rect videoBounds = new Rect(x, y, x + w, y + h);
238 paramBuilder.setAspectRatio(new Rational(w, h));
239 paramBuilder.setSourceRectHint(videoBounds);
240 }
241 ArrayList<RemoteAction> actions = new ArrayList<>(1);
242 actions.add(new RemoteAction(
243 Icon.createWithResource(getContext(), R.drawable.ic_call_end_white),
244 getString(R.string.action_call_hangup),
245 getString(R.string.action_call_hangup),
246 PendingIntent.getService(getContext(), new Random().nextInt(),
247 new Intent(DRingService.ACTION_CALL_END)
248 .setClass(getContext(), DRingService.class)
249 .putExtra(NotificationService.KEY_CALL_ID, sipCall.getCallId()), PendingIntent.FLAG_ONE_SHOT)));
250 paramBuilder.setActions(actions);
251 getActivity().enterPictureInPictureMode(paramBuilder.build());
Adrien Béraud8e05cf32018-03-15 11:58:38 -0400252 } else if (DeviceUtils.isTv(getContext())) {
Adrien Béraud79808d02018-02-20 00:16:28 +0100253 getActivity().enterPictureInPictureMode();
254 }
255 }
256
Adrien Béraudfb6341f2016-03-07 16:18:54 -0500257 @Override
Hadrien De Sousa73d6dd72017-09-07 10:54:51 -0400258 public int getLayout() {
259 return R.layout.frag_call;
260 }
261
262 @Override
263 public void injectFragment(RingInjectionComponent component) {
264 component.inject(this);
265 }
266
267 @Override
Adrien Béraudbc0898e2018-08-23 14:31:11 -0400268 public void onStart() {
269 super.onStart();
270 if (restartVideo) {
271 displayVideoSurface(true);
272 restartVideo = false;
273 }
274 }
275
276 @Override
277 public void onStop() {
278 super.onStop();
279 if (mVideoSurface.getVisibility() == View.VISIBLE) {
280 restartVideo = true;
281 }
282 }
283
284 @Override
Hadrien De Sousa73d6dd72017-09-07 10:54:51 -0400285 public void onViewCreated(View view, Bundle savedInstanceState) {
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400286 setHasOptionsMenu(true);
Hadrien De Sousa73d6dd72017-09-07 10:54:51 -0400287 super.onViewCreated(view, savedInstanceState);
Adrien Béraud79808d02018-02-20 00:16:28 +0100288 mCurrentOrientation = getResources().getConfiguration().orientation;
Adrien Béraud04d822c2015-04-02 17:44:36 -0400289 PowerManager powerManager = (PowerManager) getActivity().getSystemService(Context.POWER_SERVICE);
Adrien Béraud87810c72018-06-11 15:55:03 -0400290 mScreenWakeLock = powerManager.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP | PowerManager.ON_AFTER_RELEASE, "ring:callLock");
Adrien Béraud04d822c2015-04-02 17:44:36 -0400291 mScreenWakeLock.setReferenceCounted(false);
292
Adrien Béraud04d822c2015-04-02 17:44:36 -0400293 if (mScreenWakeLock != null && !mScreenWakeLock.isHeld()) {
294 mScreenWakeLock.acquire();
295 }
Adrien Béraudfb6341f2016-03-07 16:18:54 -0500296
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400297 mVideoSurface.getHolder().setFormat(PixelFormat.RGBA_8888);
298 mVideoSurface.getHolder().addCallback(new SurfaceHolder.Callback() {
299 @Override
300 public void surfaceCreated(SurfaceHolder holder) {
301 presenter.videoSurfaceCreated(holder);
302 }
303
304 @Override
305 public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
306
307 }
308
309 @Override
310 public void surfaceDestroyed(SurfaceHolder holder) {
311 presenter.videoSurfaceDestroyed();
312 }
313 });
Pierre Duchemin45707ca2018-01-15 15:05:23 -0500314 view.addOnLayoutChangeListener((parent, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> presenter.layoutChanged());
315 view.setOnSystemUiVisibilityChangeListener(visibility -> {
316 boolean ui = (visibility & (View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN)) == 0;
317 presenter.uiVisibilityChanged(ui);
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400318 });
319
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400320 mVideoPreview.getHolder().addCallback(new SurfaceHolder.Callback() {
321 @Override
322 public void surfaceCreated(SurfaceHolder holder) {
323 presenter.previewVideoSurfaceCreated(holder);
324 }
325
326 @Override
327 public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
328
329 }
330
331 @Override
332 public void surfaceDestroyed(SurfaceHolder holder) {
333 presenter.previewVideoSurfaceDestroyed();
334 }
335 });
336 mVideoPreview.setZOrderMediaOverlay(true);
Pierre Ducheminbb833672018-03-09 16:18:33 -0500337 shapeRipple.setRippleShape(new Circle());
Adrien Béraudbc0898e2018-08-23 14:31:11 -0400338 speakerButton.setChecked(presenter.isSpeakerphoneOn());
Adrien Béraudfb6341f2016-03-07 16:18:54 -0500339 }
340
341 @Override
Adrien Béraud2c378b32018-07-11 15:24:02 -0400342 public void onDestroyView() {
343 super.onDestroyView();
344 mCompositeDisposable.clear();
Adrien Béraudfb6341f2016-03-07 16:18:54 -0500345 if (mScreenWakeLock != null && mScreenWakeLock.isHeld()) {
346 mScreenWakeLock.release();
347 }
Adrien Béraud04d822c2015-04-02 17:44:36 -0400348 }
349
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400350 @Override
351 public void onConfigurationChanged(Configuration newConfig) {
352 super.onConfigurationChanged(newConfig);
Adrien Béraud79808d02018-02-20 00:16:28 +0100353 int newOrientation = newConfig.orientation;
354 if (newOrientation == mCurrentOrientation) {
355 return;
356 }
357 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && getActivity().isInPictureInPictureMode()) {
358 // avoid restarting the camera when entering PIP mode
359 return;
360 }
361 mCurrentOrientation = newOrientation;
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400362 presenter.configurationChanged();
Adrien Béraudb179bab2015-10-08 12:04:22 -0400363 }
364
Adrien Béraud04d822c2015-04-02 17:44:36 -0400365 @Override
366 public void onCreateOptionsMenu(Menu m, MenuInflater inf) {
Adrien Béraudda712a42015-11-22 23:45:26 -0500367 super.onCreateOptionsMenu(m, inf);
Adrien Béraudda2eb1a2016-01-11 13:15:12 -0500368 inf.inflate(R.menu.ac_call, m);
Romain Bertozziad8fd842016-05-11 14:30:35 -0400369 dialPadBtn = m.findItem(R.id.menuitem_dialpad);
Kateryna Kostiukcea71192017-02-17 15:54:47 -0500370 changeScreenOrientationBtn = m.findItem(R.id.menuitem_change_screen_orientation);
Adrien Béraudda712a42015-11-22 23:45:26 -0500371 }
372
373 @Override
374 public void onPrepareOptionsMenu(Menu menu) {
375 super.onPrepareOptionsMenu(menu);
Hadrien De Sousaf5774ee2017-05-17 14:04:17 -0400376 presenter.prepareOptionMenu();
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400377 }
378
379 @Override
380 public boolean onOptionsItemSelected(MenuItem item) {
381 super.onOptionsItemSelected(item);
382 switch (item.getItemId()) {
383 case android.R.id.home:
384 case R.id.menuitem_chat:
Hadrien De Sousaf5774ee2017-05-17 14:04:17 -0400385 presenter.chatClick();
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400386 break;
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400387 case R.id.menuitem_dialpad:
Hadrien De Sousaf5774ee2017-05-17 14:04:17 -0400388 presenter.dialpadClick();
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400389 break;
390 case R.id.menuitem_change_screen_orientation:
Hadrien De Sousaf5774ee2017-05-17 14:04:17 -0400391 presenter.screenRotationClick();
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400392 break;
393 }
394 return true;
395 }
396
Adrien Béraud79808d02018-02-20 00:16:28 +0100397
398 @Override
399 public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode, Configuration newConfig) {
Adrien Béraudbc0898e2018-08-23 14:31:11 -0400400 if (isInPictureInPictureMode)
401 ((CallActivity) getActivity()).getSupportActionBar().hide();
402 else
403 ((CallActivity) getActivity()).getSupportActionBar().show();
Adrien Béraud79808d02018-02-20 00:16:28 +0100404 presenter.pipModeChanged(isInPictureInPictureMode);
405 }
406
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400407 @Override
408 public void blockScreenRotation() {
Adrien Béraudace37a22018-06-22 14:58:36 -0400409 getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED);
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400410 }
411
412 @Override
413 public void displayContactBubble(final boolean display) {
Pierre Duchemin45707ca2018-01-15 15:05:23 -0500414 getActivity().runOnUiThread(() -> contactBubbleLayout.setVisibility(display ? View.VISIBLE : View.GONE));
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400415 }
416
417 @Override
418 public void displayVideoSurface(final boolean display) {
Adrien Béraudace37a22018-06-22 14:58:36 -0400419 mVideoSurface.setVisibility(display ? View.VISIBLE : View.GONE);
420 mVideoPreview.setVisibility(display ? View.VISIBLE : View.GONE);
421 updateMenu();
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400422 }
423
424 @Override
Adrien Béraud79808d02018-02-20 00:16:28 +0100425 public void displayPreviewSurface(final boolean display) {
426 if (display) {
427 mVideoPreview.setZOrderMediaOverlay(true);
428 mVideoSurface.setZOrderMediaOverlay(false);
429 } else {
430 mVideoPreview.setZOrderMediaOverlay(false);
431 mVideoSurface.setZOrderMediaOverlay(true);
432 }
433 }
434
435 @Override
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400436 public void displayHangupButton(boolean display) {
Adrien Béraudbc0898e2018-08-23 14:31:11 -0400437 controlLayout.setVisibility(display ? View.VISIBLE : View.GONE);
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400438 hangupButton.setVisibility(display ? View.VISIBLE : View.GONE);
439 }
440
441 @Override
442 public void displayDialPadKeyboard() {
443 KeyboardVisibilityManager.showKeyboard(getActivity(),
444 mNumeralDialEditText,
445 InputMethodManager.SHOW_IMPLICIT);
446 }
447
448 @Override
449 public void switchCameraIcon(boolean isFront) {
Adrien Béraudbc0898e2018-08-23 14:31:11 -0400450 flipCameraBtn.setImageResource(isFront ? R.drawable.ic_camera_front_white : R.drawable.ic_camera_rear_white);
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400451 }
452
453 @Override
Adrien Béraudd3bf4e42018-02-14 14:44:41 +0100454 public void updateMenu() {
455 getActivity().invalidateOptionsMenu();
456 }
457
458 @Override
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400459 public void changeScreenRotation() {
Adrien Béraud79808d02018-02-20 00:16:28 +0100460 if (mCurrentOrientation == Configuration.ORIENTATION_LANDSCAPE) {
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400461 getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
462 } else {
463 getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
464 }
465 }
466
467 @Override
468 public void updateTime(final long duration) {
Adrien Béraud3aeed5d2018-07-26 11:26:13 -0400469 if (mCallStatusTxt != null)
470 mCallStatusTxt.setText(String.format(Locale.getDefault(), "%d:%02d:%02d", duration / 3600, duration % 3600 / 60, duration % 60));
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400471 }
472
473 @Override
Adrien Beraud8e6e2ff2017-06-14 14:40:27 +0200474 public void updateContactBubble(@NonNull final CallContact contact) {
Adrien Béraud5f8c3f72018-03-09 14:49:39 -0500475 String username = contact.getRingUsername();
476 String displayName = contact.getDisplayName();
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400477
Adrien Béraud5f8c3f72018-03-09 14:49:39 -0500478 String ringId = contact.getIds().get(0);
Adrien Béraudafcdd962018-07-06 21:38:41 -0400479 Log.d(TAG, "updateContactBubble: contact=" + contact + " username=" + username + ", ringId=" + ringId);
Adrien Béraud5f8c3f72018-03-09 14:49:39 -0500480
Adrien Béraudafcdd962018-07-06 21:38:41 -0400481 boolean hasProfileName = displayName != null && !displayName.contentEquals(username);
Adrien Béraudbc0898e2018-08-23 14:31:11 -0400482
483 AppCompatActivity activity = (AppCompatActivity) getActivity();
484 if (activity != null) {
485 ActionBar ab = activity.getSupportActionBar();
486 if (ab != null) {
487 if (hasProfileName) {
488 ab.setTitle(displayName);
489 ab.setSubtitle(username);
490 } else {
491 ab.setTitle(username);
492 ab.setSubtitle(null);
493 }
494 ab.setDisplayShowTitleEnabled(true);
495 }
496 }
497
Adrien Béraudafcdd962018-07-06 21:38:41 -0400498 if (hasProfileName) {
499 contactBubbleNumTxt.setVisibility(View.VISIBLE);
500 contactBubbleTxt.setText(displayName);
501 contactBubbleNumTxt.setText(username);
502 } else {
503 contactBubbleNumTxt.setVisibility(View.GONE);
504 contactBubbleTxt.setText(username);
505 }
Adrien Béraud5f8c3f72018-03-09 14:49:39 -0500506
Adrien Béraud78ae7b92018-08-25 19:04:17 -0400507 AvatarFactory.loadGlideAvatar(contactBubbleView, contact);
508
509 /*mCompositeDisposable.add(Single.fromCallable(() -> Glide.with(getActivity())
Adrien Béraud2c378b32018-07-11 15:24:02 -0400510 .load(AvatarFactory.getAvatar(
511 getActivity(),
512 contact.getPhoto(),
513 username,
514 ringId, true))
Adrien Béraudafcdd962018-07-06 21:38:41 -0400515 .apply(AvatarFactory.getGlideOptions(true, false))
Adrien Béraud2c378b32018-07-11 15:24:02 -0400516 .submit()
517 .get())
518 .subscribeOn(Schedulers.computation())
519 .observeOn(AndroidSchedulers.mainThread())
Adrien Béraud78ae7b92018-08-25 19:04:17 -0400520 .subscribe(d -> contactBubbleView.setImageDrawable(d)));*/
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400521 }
522
523 @Override
Adrien Bérauda3f0b4b2018-08-09 11:41:53 -0400524 public void updateCallStatus(final SipCall.State callState) {
Pierre Duchemin45707ca2018-01-15 15:05:23 -0500525 getActivity().runOnUiThread(() -> {
526 switch (callState) {
Adrien Bérauda3f0b4b2018-08-09 11:41:53 -0400527 case NONE:
Pierre Duchemin45707ca2018-01-15 15:05:23 -0500528 mCallStatusTxt.setText("");
529 break;
530 default:
531 mCallStatusTxt.setText(callStateToHumanState(callState));
532 break;
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400533 }
534 });
535 }
536
537 @Override
538 public void initMenu(boolean isSpeakerOn, boolean hasContact, boolean displayFlip, boolean canDial, boolean onGoingCall) {
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400539 if (flipCameraBtn != null) {
Adrien Béraudbc0898e2018-08-23 14:31:11 -0400540 flipCameraBtn.setVisibility(displayFlip ? View.VISIBLE : View.GONE);
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400541 }
Romain Bertozziad8fd842016-05-11 14:30:35 -0400542 if (dialPadBtn != null) {
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400543 dialPadBtn.setVisible(canDial);
Romain Bertozziad8fd842016-05-11 14:30:35 -0400544 }
Kateryna Kostiukcea71192017-02-17 15:54:47 -0500545 if (changeScreenOrientationBtn != null) {
546 changeScreenOrientationBtn.setVisible(mVideoSurface.getVisibility() == View.VISIBLE);
547 }
Adrien Béraud04d822c2015-04-02 17:44:36 -0400548 }
549
550 @Override
Adrien Béraudbc0898e2018-08-23 14:31:11 -0400551 public void initNormalStateDisplay(final boolean audioOnly, boolean isSpeakerphoneOn) {
Adrien Béraudace37a22018-06-22 14:58:36 -0400552 shapeRipple.stopRipple();
Pierre Ducheminbb833672018-03-09 16:18:33 -0500553
Adrien Béraudace37a22018-06-22 14:58:36 -0400554 acceptButton.setVisibility(View.GONE);
555 refuseButton.setVisibility(View.GONE);
Adrien Béraudbc0898e2018-08-23 14:31:11 -0400556 controlLayout.setVisibility(View.VISIBLE);
Adrien Béraudace37a22018-06-22 14:58:36 -0400557 hangupButton.setVisibility(View.VISIBLE);
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400558
Adrien Béraudbc0898e2018-08-23 14:31:11 -0400559 contactBubbleLayout.setVisibility(audioOnly ? View.VISIBLE : View.GONE);
560 speakerButton.setChecked(isSpeakerphoneOn);
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400561
Adrien Béraudace37a22018-06-22 14:58:36 -0400562 getActivity().invalidateOptionsMenu();
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400563 }
564
565 @Override
566 public void initIncomingCallDisplay() {
Adrien Béraudace37a22018-06-22 14:58:36 -0400567 acceptButton.setVisibility(View.VISIBLE);
568 refuseButton.setVisibility(View.VISIBLE);
Adrien Béraudbc0898e2018-08-23 14:31:11 -0400569 controlLayout.setVisibility(View.GONE);
Adrien Béraudace37a22018-06-22 14:58:36 -0400570 hangupButton.setVisibility(View.GONE);
Hadrien De Sousa841d15a2017-06-02 11:52:10 -0400571
Adrien Béraudbc0898e2018-08-23 14:31:11 -0400572 contactBubbleLayout.setVisibility(View.VISIBLE);
Adrien Béraudace37a22018-06-22 14:58:36 -0400573 getActivity().invalidateOptionsMenu();
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400574 }
575
576 @Override
577 public void initOutGoingCallDisplay() {
Adrien Béraudace37a22018-06-22 14:58:36 -0400578 acceptButton.setVisibility(View.GONE);
579 refuseButton.setVisibility(View.VISIBLE);
Adrien Béraudbc0898e2018-08-23 14:31:11 -0400580 controlLayout.setVisibility(View.GONE);
Adrien Béraudace37a22018-06-22 14:58:36 -0400581 hangupButton.setVisibility(View.GONE);
Hadrien De Sousa841d15a2017-06-02 11:52:10 -0400582
Adrien Béraudbc0898e2018-08-23 14:31:11 -0400583 contactBubbleLayout.setVisibility(View.VISIBLE);
Adrien Béraudace37a22018-06-22 14:58:36 -0400584 getActivity().invalidateOptionsMenu();
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400585 }
586
587 @Override
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400588 public void resetVideoSize(final int videoWidth, final int videoHeight, final int previewWidth, final int previewHeight) {
Adrien Béraudace37a22018-06-22 14:58:36 -0400589 ViewGroup rootView = (ViewGroup) getView();
590 if (rootView == null)
591 return;
Adrien Béraud8d6a4832016-04-12 18:06:11 -0400592
Adrien Béraudace37a22018-06-22 14:58:36 -0400593 double videoRatio = videoWidth / (double) videoHeight;
594 double screenRatio = getView().getWidth() / (double) getView().getHeight();
Adrien Béraud8d6a4832016-04-12 18:06:11 -0400595
Adrien Béraudace37a22018-06-22 14:58:36 -0400596 RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) mVideoSurface.getLayoutParams();
597 int oldW = params.width;
598 int oldH = params.height;
599 if (videoRatio >= screenRatio) {
600 params.width = RelativeLayout.LayoutParams.MATCH_PARENT;
601 params.height = (int) (videoHeight * (double) rootView.getWidth() / (double) videoWidth);
602 } else {
603 params.height = RelativeLayout.LayoutParams.MATCH_PARENT;
604 params.width = (int) (videoWidth * (double) rootView.getHeight() / (double) videoHeight);
605 }
Thibault Wittembergb3280502016-12-16 17:34:53 -0500606
Adrien Béraudace37a22018-06-22 14:58:36 -0400607 if (oldW != params.width || oldH != params.height) {
608 mVideoSurface.setLayoutParams(params);
609 }
Adrien Béraud04d822c2015-04-02 17:44:36 -0400610
Adrien Béraud30532a52018-09-11 15:10:05 -0400611 if (previewWidth == -1 && previewHeight == -1)
612 return;
613 Log.w(TAG, "resetVideoSize preview: " + previewWidth + "x" + previewHeight);
614 ViewGroup.LayoutParams paramsPreview = mVideoPreview.getLayoutParams();
615 DisplayMetrics metrics = getResources().getDisplayMetrics();
616
617 oldW = paramsPreview.width;
618 oldH = paramsPreview.height;
619 double previewMaxDim = Math.max(previewWidth, previewHeight);
620 double previewRatio = metrics.density * 160. / previewMaxDim;
621 paramsPreview.width = (int) (previewWidth * previewRatio);
622 paramsPreview.height = (int) (previewHeight * previewRatio);
623
624 if (oldW != paramsPreview.width || oldH != paramsPreview.height) {
625 Log.w(TAG, "mVideoPreview.setLayoutParams: " + paramsPreview.width + "x" + paramsPreview.height + " was: " + oldW + "x"+oldH);
626 mVideoPreview.setLayoutParams(paramsPreview);
627 }
628
629 /*final int mPreviewWidth;
Adrien Béraudace37a22018-06-22 14:58:36 -0400630 final int mPreviewHeight;
Hadrien De Sousa9c968fc2017-08-30 17:46:03 -0400631
Adrien Béraudace37a22018-06-22 14:58:36 -0400632 if (mCurrentOrientation == Configuration.ORIENTATION_PORTRAIT) {
633 mPreviewWidth = HardwareServiceImpl.VIDEO_HEIGHT;
634 mPreviewHeight = HardwareServiceImpl.VIDEO_WIDTH;
635 } else {
636 mPreviewWidth = HardwareServiceImpl.VIDEO_WIDTH;
637 mPreviewHeight = HardwareServiceImpl.VIDEO_HEIGHT;
638 }
Hadrien De Sousa9c968fc2017-08-30 17:46:03 -0400639
Adrien Béraudace37a22018-06-22 14:58:36 -0400640 DisplayMetrics metrics = getResources().getDisplayMetrics();
641 RelativeLayout.LayoutParams paramsPreview = (RelativeLayout.LayoutParams) mVideoPreview.getLayoutParams();
642 oldW = paramsPreview.width;
643 oldH = paramsPreview.height;
644 double previewMaxDim = Math.max(mPreviewWidth, mPreviewHeight);
645 double previewRatio = metrics.density * 160. / previewMaxDim;
646 paramsPreview.width = (int) (mPreviewWidth * previewRatio);
647 paramsPreview.height = (int) (mPreviewHeight * previewRatio);
Hadrien De Sousa9c968fc2017-08-30 17:46:03 -0400648
Adrien Béraudace37a22018-06-22 14:58:36 -0400649 if (oldW != paramsPreview.width || oldH != paramsPreview.height) {
650 mVideoPreview.setLayoutParams(paramsPreview);
Adrien Béraud30532a52018-09-11 15:10:05 -0400651 }*/
Adrien Béraud04d822c2015-04-02 17:44:36 -0400652 }
653
654 @Override
Hadrien De Sousa0dc83162017-11-10 16:15:18 -0500655 public void goToConversation(String accountId, String conversationId) {
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400656 Intent intent = new Intent();
Pierre Duchemin42788c32018-03-08 16:21:15 -0500657 if (DeviceUtils.isTablet(getActivity())) {
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400658 intent.setClass(getActivity(), HomeActivity.class)
Alexandre Lision5da46fa2017-08-09 10:25:50 -0400659 .setAction(DRingService.ACTION_CONV_ACCEPT)
Hadrien De Sousa0dc83162017-11-10 16:15:18 -0500660 .putExtra(ConversationFragment.KEY_CONTACT_RING_ID, conversationId);
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400661 startActivity(intent);
Adrien Béraudda712a42015-11-22 23:45:26 -0500662 } else {
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400663 intent.setClass(getActivity(), ConversationActivity.class)
664 .setAction(Intent.ACTION_VIEW)
665 .setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP)
Hadrien De Sousa0dc83162017-11-10 16:15:18 -0500666 .putExtra(ConversationFragment.KEY_ACCOUNT_ID, accountId)
667 .putExtra(ConversationFragment.KEY_CONTACT_RING_ID, conversationId);
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400668 startActivityForResult(intent, HomeActivity.REQUEST_CODE_CONVERSATION);
Adrien Béraudda712a42015-11-22 23:45:26 -0500669 }
Adrien Béraud04d822c2015-04-02 17:44:36 -0400670 }
671
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400672 @Override
673 public void goToAddContact(CallContact callContact) {
674 startActivityForResult(ActionHelper.getAddNumberIntentForContact(callContact),
675 ConversationFragment.REQ_ADD_CONTACT);
676 }
677
678 @Override
679 public void finish() {
680 getActivity().finish();
Alexandre Lision1532d952016-11-17 09:41:00 -0500681 }
682
Adrien Béraudbc0898e2018-08-23 14:31:11 -0400683 @OnClick({R.id.call_speaker_btn})
684 public void speakerClicked() {
685 presenter.speakerClick(speakerButton.isChecked());
686 }
687
688 @OnClick({R.id.call_mic_btn})
689 public void micClicked() {
690 presenter.muteMicrophoneToggled(micButton.isChecked());
691 }
692
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400693 @OnClick({R.id.call_hangup_btn})
694 public void hangUpClicked() {
Hadrien De Sousaf5774ee2017-05-17 14:04:17 -0400695 presenter.hangupCall();
Alexandre Lision1532d952016-11-17 09:41:00 -0500696 }
697
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400698 @OnClick(R.id.call_refuse_btn)
699 public void refuseClicked() {
Hadrien De Sousaf5774ee2017-05-17 14:04:17 -0400700 presenter.refuseCall();
Thibault Wittembergb70d5b02016-07-08 16:50:14 -0400701 }
702
703 @OnClick(R.id.call_accept_btn)
704 public void acceptClicked() {
Hadrien De Sousaf5774ee2017-05-17 14:04:17 -0400705 presenter.acceptCall();
Thibault Wittembergb70d5b02016-07-08 16:50:14 -0400706 }
Pierre Duchemin8b2c1b52017-12-29 17:17:13 -0500707
Adrien Béraudbc0898e2018-08-23 14:31:11 -0400708 @OnClick(R.id.call_camera_flip_btn)
709 public void cameraFlip() {
710 presenter.switchVideoInputClick();
711 }
712
Pierre Duchemin8b2c1b52017-12-29 17:17:13 -0500713 @Override
Pierre Duchemin1fa61922018-01-18 10:09:02 -0500714 public void positiveMediaButtonClicked() {
Pierre Duchemin8b2c1b52017-12-29 17:17:13 -0500715 presenter.positiveButtonClicked();
716 }
717
718 @Override
Pierre Duchemin1fa61922018-01-18 10:09:02 -0500719 public void negativeMediaButtonClicked() {
Pierre Duchemin8b2c1b52017-12-29 17:17:13 -0500720 presenter.negativeButtonClicked();
721 }
722
723 @Override
Pierre Duchemin1fa61922018-01-18 10:09:02 -0500724 public void toggleMediaButtonClicked() {
Pierre Duchemin8b2c1b52017-12-29 17:17:13 -0500725 presenter.toggleButtonClicked();
726 }
Hadrien De Sousaccc947d2017-04-12 14:26:52 -0400727}