1package eu.siacs.conversations.ui;
2
3import android.Manifest;
4import android.annotation.SuppressLint;
5import android.content.Context;
6import android.content.Intent;
7import android.databinding.DataBindingUtil;
8import android.os.Build;
9import android.os.Bundle;
10import android.os.PowerManager;
11import android.support.annotation.NonNull;
12import android.support.annotation.StringRes;
13import android.util.Log;
14import android.view.View;
15import android.view.WindowManager;
16import android.widget.Toast;
17
18import com.google.common.base.Optional;
19import com.google.common.collect.ImmutableList;
20import com.google.common.collect.ImmutableSet;
21
22import org.webrtc.RendererCommon;
23import org.webrtc.SurfaceViewRenderer;
24import org.webrtc.VideoTrack;
25
26import java.lang.ref.WeakReference;
27import java.util.Arrays;
28import java.util.Set;
29
30import eu.siacs.conversations.Config;
31import eu.siacs.conversations.R;
32import eu.siacs.conversations.databinding.ActivityRtpSessionBinding;
33import eu.siacs.conversations.entities.Account;
34import eu.siacs.conversations.entities.Contact;
35import eu.siacs.conversations.services.AppRTCAudioManager;
36import eu.siacs.conversations.services.XmppConnectionService;
37import eu.siacs.conversations.utils.PermissionUtils;
38import eu.siacs.conversations.xmpp.jingle.AbstractJingleConnection;
39import eu.siacs.conversations.xmpp.jingle.JingleRtpConnection;
40import eu.siacs.conversations.xmpp.jingle.Media;
41import eu.siacs.conversations.xmpp.jingle.RtpEndUserState;
42import rocks.xmpp.addr.Jid;
43
44import static eu.siacs.conversations.utils.PermissionUtils.getFirstDenied;
45import static java.util.Arrays.asList;
46
47public class RtpSessionActivity extends XmppActivity implements XmppConnectionService.OnJingleRtpConnectionUpdate {
48
49 private static final String PROXIMITY_WAKE_LOCK_TAG = "conversations:in-rtp-session";
50
51 private static final int REQUEST_ACCEPT_CALL = 0x1111;
52
53 public static final String EXTRA_WITH = "with";
54 public static final String EXTRA_SESSION_ID = "session_id";
55 public static final String EXTRA_LAST_REPORTED_STATE = "last_reported_state";
56
57 public static final String ACTION_ACCEPT_CALL = "action_accept_call";
58 public static final String ACTION_MAKE_VOICE_CALL = "action_make_voice_call";
59 public static final String ACTION_MAKE_VIDEO_CALL = "action_make_video_call";
60
61
62 private WeakReference<JingleRtpConnection> rtpConnectionReference;
63
64 private ActivityRtpSessionBinding binding;
65 private PowerManager.WakeLock mProximityWakeLock;
66
67 @Override
68 public void onCreate(Bundle savedInstanceState) {
69 super.onCreate(savedInstanceState);
70 getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
71 | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
72 | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
73 | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
74 Log.d(Config.LOGTAG, "RtpSessionActivity.onCreate()");
75 this.binding = DataBindingUtil.setContentView(this, R.layout.activity_rtp_session);
76 setSupportActionBar(binding.toolbar);
77 }
78
79 @Override
80 public void onStart() {
81 super.onStart();
82 Log.d(Config.LOGTAG, "RtpSessionActivity.onStart()");
83 }
84
85 private void endCall(View view) {
86 endCall();
87 }
88
89 private void endCall() {
90 if (this.rtpConnectionReference == null) {
91 final Intent intent = getIntent();
92 final Account account = extractAccount(intent);
93 final Jid with = Jid.of(intent.getStringExtra(EXTRA_WITH));
94 xmppConnectionService.getJingleConnectionManager().retractSessionProposal(account, with.asBareJid());
95 finish();
96 } else {
97 requireRtpConnection().endCall();
98 }
99 }
100
101 private void rejectCall(View view) {
102 requireRtpConnection().rejectCall();
103 finish();
104 }
105
106 private void acceptCall(View view) {
107 requestPermissionsAndAcceptCall();
108 }
109
110 private void requestPermissionsAndAcceptCall() {
111 if (PermissionUtils.hasPermission(this, ImmutableList.of(Manifest.permission.RECORD_AUDIO), REQUEST_ACCEPT_CALL)) {
112 //TODO like wise the propose; we might just wait here for the audio manager to come up
113 putScreenInCallMode();
114 requireRtpConnection().acceptCall();
115 }
116 }
117
118 @SuppressLint("WakelockTimeout")
119 private void putScreenInCallMode() {
120 getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
121 final JingleRtpConnection rtpConnection = rtpConnectionReference != null ? rtpConnectionReference.get() : null;
122 final AppRTCAudioManager audioManager = rtpConnection == null ? null : rtpConnection.getAudioManager();
123 if (audioManager == null || audioManager.getSelectedAudioDevice() == AppRTCAudioManager.AudioDevice.EARPIECE) {
124 acquireProximityWakeLock();
125 }
126 }
127
128 private void acquireProximityWakeLock() {
129 final PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
130 if (powerManager == null) {
131 Log.e(Config.LOGTAG, "power manager not available");
132 return;
133 }
134 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
135 if (this.mProximityWakeLock == null) {
136 this.mProximityWakeLock = powerManager.newWakeLock(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK, PROXIMITY_WAKE_LOCK_TAG);
137 }
138 if (!this.mProximityWakeLock.isHeld()) {
139 Log.d(Config.LOGTAG, "acquiring proximity wake lock");
140 this.mProximityWakeLock.acquire();
141 }
142 }
143 }
144
145 private void releaseProximityWakeLock() {
146 if (this.mProximityWakeLock != null && mProximityWakeLock.isHeld()) {
147 Log.d(Config.LOGTAG, "releasing proximity wake lock");
148 this.mProximityWakeLock.release();
149 this.mProximityWakeLock = null;
150 }
151 }
152
153 private void putProximityWakeLockInProperState() {
154 if (requireRtpConnection().getAudioManager().getSelectedAudioDevice() == AppRTCAudioManager.AudioDevice.EARPIECE) {
155 acquireProximityWakeLock();
156 } else {
157 releaseProximityWakeLock();
158 }
159 }
160
161 @Override
162 protected void refreshUiReal() {
163
164 }
165
166 @Override
167 public void onNewIntent(final Intent intent) {
168 super.onNewIntent(intent);
169 setIntent(intent);
170 if (xmppConnectionService == null) {
171 Log.d(Config.LOGTAG, "RtpSessionActivity: background service wasn't bound in onNewIntent()");
172 return;
173 }
174 final Account account = extractAccount(intent);
175 final Jid with = Jid.of(intent.getStringExtra(EXTRA_WITH));
176 final String sessionId = intent.getStringExtra(EXTRA_SESSION_ID);
177 if (sessionId != null) {
178 Log.d(Config.LOGTAG, "reinitializing from onNewIntent()");
179 initializeActivityWithRunningRtpSession(account, with, sessionId);
180 if (ACTION_ACCEPT_CALL.equals(intent.getAction())) {
181 Log.d(Config.LOGTAG, "accepting call from onNewIntent()");
182 requestPermissionsAndAcceptCall();
183 resetIntent(intent.getExtras());
184 }
185 } else {
186 throw new IllegalStateException("received onNewIntent without sessionId");
187 }
188 }
189
190 @Override
191 void onBackendConnected() {
192 final Intent intent = getIntent();
193 final String action = intent.getAction();
194 final Account account = extractAccount(intent);
195 final Jid with = Jid.of(intent.getStringExtra(EXTRA_WITH));
196 final String sessionId = intent.getStringExtra(EXTRA_SESSION_ID);
197 if (sessionId != null) {
198 initializeActivityWithRunningRtpSession(account, with, sessionId);
199 if (ACTION_ACCEPT_CALL.equals(intent.getAction())) {
200 Log.d(Config.LOGTAG, "intent action was accept");
201 requestPermissionsAndAcceptCall();
202 resetIntent(intent.getExtras());
203 }
204 } else if (asList(ACTION_MAKE_VIDEO_CALL, ACTION_MAKE_VOICE_CALL).contains(action)) {
205 final Set<Media> media;
206 if (ACTION_MAKE_VIDEO_CALL.equals(action)) {
207 media = ImmutableSet.of(Media.AUDIO, Media.VIDEO);
208 } else {
209 media = ImmutableSet.of(Media.AUDIO);
210 }
211 proposeJingleRtpSession(account, with, media);
212 binding.with.setText(account.getRoster().getContact(with).getDisplayName());
213 } else if (Intent.ACTION_VIEW.equals(action)) {
214 final String extraLastState = intent.getStringExtra(EXTRA_LAST_REPORTED_STATE);
215 if (extraLastState != null) {
216 Log.d(Config.LOGTAG, "restored last state from intent extra");
217 RtpEndUserState state = RtpEndUserState.valueOf(extraLastState);
218 updateButtonConfiguration(state);
219 updateStateDisplay(state);
220 }
221 binding.with.setText(account.getRoster().getContact(with).getDisplayName());
222 }
223 }
224
225 private void proposeJingleRtpSession(final Account account, final Jid with, final Set<Media> media) {
226 xmppConnectionService.getJingleConnectionManager().proposeJingleRtpSession(account, with, media);
227 //TODO maybe we don’t want to acquire a wake lock just yet and wait for audio manager to discover what speaker we are using
228 putScreenInCallMode();
229 }
230
231 @Override
232 public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
233 super.onRequestPermissionsResult(requestCode, permissions, grantResults);
234 if (PermissionUtils.allGranted(grantResults)) {
235 if (requestCode == REQUEST_ACCEPT_CALL) {
236 requireRtpConnection().acceptCall();
237 }
238 } else {
239 @StringRes int res;
240 final String firstDenied = getFirstDenied(grantResults, permissions);
241 if (Manifest.permission.RECORD_AUDIO.equals(firstDenied)) {
242 res = R.string.no_microphone_permission;
243 } else if (Manifest.permission.CAMERA.equals(firstDenied)) {
244 res = R.string.no_camera_permission;
245 } else {
246 throw new IllegalStateException("Invalid permission result request");
247 }
248 Toast.makeText(this, res, Toast.LENGTH_SHORT).show();
249 }
250 }
251
252 @Override
253 public void onStop() {
254 binding.remoteVideo.release();
255 binding.localVideo.release();
256 releaseProximityWakeLock();
257 //TODO maybe we want to finish if call had ended
258 super.onStop();
259 }
260
261 @Override
262 public void onBackPressed() {
263 endCall();
264 super.onBackPressed();
265 }
266
267
268 private void initializeActivityWithRunningRtpSession(final Account account, Jid with, String sessionId) {
269 final WeakReference<JingleRtpConnection> reference = xmppConnectionService.getJingleConnectionManager()
270 .findJingleRtpConnection(account, with, sessionId);
271 if (reference == null || reference.get() == null) {
272 finish();
273 return;
274 }
275 this.rtpConnectionReference = reference;
276 final RtpEndUserState currentState = requireRtpConnection().getEndUserState();
277 if (currentState == RtpEndUserState.ENDED) {
278 finish();
279 return;
280 }
281 if (currentState == RtpEndUserState.INCOMING_CALL) {
282 getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
283 }
284 if (JingleRtpConnection.STATES_SHOWING_ONGOING_CALL.contains(requireRtpConnection().getState())) {
285 putScreenInCallMode();
286 }
287 binding.with.setText(getWith().getDisplayName());
288 updateStateDisplay(currentState);
289 updateButtonConfiguration(currentState);
290 }
291
292 private void reInitializeActivityWithRunningRapSession(final Account account, Jid with, String sessionId) {
293 runOnUiThread(() -> {
294 initializeActivityWithRunningRtpSession(account, with, sessionId);
295 });
296 final Intent intent = new Intent(Intent.ACTION_VIEW);
297 intent.putExtra(EXTRA_ACCOUNT, account.getJid().toEscapedString());
298 intent.putExtra(EXTRA_WITH, with.toEscapedString());
299 intent.putExtra(EXTRA_SESSION_ID, sessionId);
300 setIntent(intent);
301 }
302
303 private void updateVideoViews() {
304 final Optional<VideoTrack> localVideoTrack = requireRtpConnection().geLocalVideoTrack();
305 if (localVideoTrack.isPresent()) {
306 ensureSurfaceViewRendererIsSetup(binding.localVideo);
307 //paint local view over remote view
308 binding.localVideo.setZOrderMediaOverlay(true);
309 binding.localVideo.setMirror(true);
310 localVideoTrack.get().addSink(binding.localVideo);
311 } else {
312 binding.localVideo.setVisibility(View.GONE);
313 }
314 final Optional<VideoTrack> remoteVideoTrack = requireRtpConnection().getRemoteVideoTrack();
315 if (remoteVideoTrack.isPresent()) {
316 ensureSurfaceViewRendererIsSetup(binding.remoteVideo);
317 remoteVideoTrack.get().addSink(binding.remoteVideo);
318 } else {
319 binding.remoteVideo.setVisibility(View.GONE);
320 }
321 }
322
323 private void ensureSurfaceViewRendererIsSetup(final SurfaceViewRenderer surfaceViewRenderer) {
324 surfaceViewRenderer.setVisibility(View.VISIBLE);
325 try {
326 surfaceViewRenderer.init(requireRtpConnection().getEglBaseContext(), null);
327 } catch (IllegalStateException e) {
328 Log.d(Config.LOGTAG, "SurfaceViewRenderer was already initialized");
329 }
330 surfaceViewRenderer.setEnableHardwareScaler(true);
331 }
332
333 private void updateStateDisplay(final RtpEndUserState state) {
334 switch (state) {
335 case INCOMING_CALL:
336 if (getMedia().contains(Media.VIDEO)) {
337 setTitle(R.string.rtp_state_incoming_video_call);
338 } else {
339 setTitle(R.string.rtp_state_incoming_call);
340 }
341 break;
342 case CONNECTING:
343 setTitle(R.string.rtp_state_connecting);
344 break;
345 case CONNECTED:
346 setTitle(R.string.rtp_state_connected);
347 break;
348 case ACCEPTING_CALL:
349 setTitle(R.string.rtp_state_accepting_call);
350 break;
351 case ENDING_CALL:
352 setTitle(R.string.rtp_state_ending_call);
353 break;
354 case FINDING_DEVICE:
355 setTitle(R.string.rtp_state_finding_device);
356 break;
357 case RINGING:
358 setTitle(R.string.rtp_state_ringing);
359 break;
360 case DECLINED_OR_BUSY:
361 setTitle(R.string.rtp_state_declined_or_busy);
362 break;
363 case CONNECTIVITY_ERROR:
364 setTitle(R.string.rtp_state_connectivity_error);
365 break;
366 case APPLICATION_ERROR:
367 setTitle(R.string.rtp_state_application_failure);
368 break;
369 case ENDED:
370 throw new IllegalStateException("Activity should have called finishAndReleaseWakeLock();");
371 default:
372 throw new IllegalStateException(String.format("State %s has not been handled in UI", state));
373 }
374 }
375
376 private Set<Media> getMedia() {
377 return requireRtpConnection().getMedia();
378 }
379
380 @SuppressLint("RestrictedApi")
381 private void updateButtonConfiguration(final RtpEndUserState state) {
382 if (state == RtpEndUserState.INCOMING_CALL) {
383 this.binding.rejectCall.setOnClickListener(this::rejectCall);
384 this.binding.rejectCall.setImageResource(R.drawable.ic_call_end_white_48dp);
385 this.binding.rejectCall.setVisibility(View.VISIBLE);
386 this.binding.endCall.setVisibility(View.INVISIBLE);
387 this.binding.acceptCall.setOnClickListener(this::acceptCall);
388 this.binding.acceptCall.setImageResource(R.drawable.ic_call_white_48dp);
389 this.binding.acceptCall.setVisibility(View.VISIBLE);
390 } else if (state == RtpEndUserState.ENDING_CALL) {
391 this.binding.rejectCall.setVisibility(View.INVISIBLE);
392 this.binding.endCall.setVisibility(View.INVISIBLE);
393 this.binding.acceptCall.setVisibility(View.INVISIBLE);
394 } else if (state == RtpEndUserState.DECLINED_OR_BUSY) {
395 this.binding.rejectCall.setVisibility(View.INVISIBLE);
396 this.binding.endCall.setOnClickListener(this::exit);
397 this.binding.endCall.setImageResource(R.drawable.ic_clear_white_48dp);
398 this.binding.endCall.setVisibility(View.VISIBLE);
399 this.binding.acceptCall.setVisibility(View.INVISIBLE);
400 } else if (state == RtpEndUserState.CONNECTIVITY_ERROR || state == RtpEndUserState.APPLICATION_ERROR) {
401 this.binding.rejectCall.setOnClickListener(this::exit);
402 this.binding.rejectCall.setImageResource(R.drawable.ic_clear_white_48dp);
403 this.binding.rejectCall.setVisibility(View.VISIBLE);
404 this.binding.endCall.setVisibility(View.INVISIBLE);
405 this.binding.acceptCall.setOnClickListener(this::retry);
406 this.binding.acceptCall.setImageResource(R.drawable.ic_replay_white_48dp);
407 this.binding.acceptCall.setVisibility(View.VISIBLE);
408 } else {
409 this.binding.rejectCall.setVisibility(View.INVISIBLE);
410 this.binding.endCall.setOnClickListener(this::endCall);
411 this.binding.endCall.setImageResource(R.drawable.ic_call_end_white_48dp);
412 this.binding.endCall.setVisibility(View.VISIBLE);
413 this.binding.acceptCall.setVisibility(View.INVISIBLE);
414 }
415 updateInCallButtonConfiguration(state);
416 }
417
418 private void updateInCallButtonConfiguration() {
419 updateInCallButtonConfiguration(requireRtpConnection().getEndUserState());
420 }
421
422 @SuppressLint("RestrictedApi")
423 private void updateInCallButtonConfiguration(final RtpEndUserState state) {
424 if (state == RtpEndUserState.CONNECTED) {
425 final AppRTCAudioManager audioManager = requireRtpConnection().getAudioManager();
426 updateInCallButtonConfiguration(
427 audioManager.getSelectedAudioDevice(),
428 audioManager.getAudioDevices().size(),
429 requireRtpConnection().isMicrophoneEnabled()
430 );
431 } else {
432 this.binding.inCallActionLeft.setVisibility(View.GONE);
433 this.binding.inCallActionRight.setVisibility(View.GONE);
434 }
435 }
436
437 @SuppressLint("RestrictedApi")
438 private void updateInCallButtonConfiguration(final AppRTCAudioManager.AudioDevice selectedAudioDevice, final int numberOfChoices, final boolean microphoneEnabled) {
439 switch (selectedAudioDevice) {
440 case EARPIECE:
441 this.binding.inCallActionLeft.setImageResource(R.drawable.ic_volume_off_black_24dp);
442 if (numberOfChoices >= 2) {
443 this.binding.inCallActionLeft.setOnClickListener(this::switchToSpeaker);
444 } else {
445 this.binding.inCallActionLeft.setOnClickListener(null);
446 this.binding.inCallActionLeft.setClickable(false);
447 }
448 break;
449 case WIRED_HEADSET:
450 this.binding.inCallActionLeft.setImageResource(R.drawable.ic_headset_black_24dp);
451 this.binding.inCallActionLeft.setOnClickListener(null);
452 this.binding.inCallActionLeft.setClickable(false);
453 break;
454 case SPEAKER_PHONE:
455 this.binding.inCallActionLeft.setImageResource(R.drawable.ic_volume_up_black_24dp);
456 if (numberOfChoices >= 2) {
457 this.binding.inCallActionLeft.setOnClickListener(this::switchToEarpiece);
458 } else {
459 this.binding.inCallActionLeft.setOnClickListener(null);
460 this.binding.inCallActionLeft.setClickable(false);
461 }
462 break;
463 case BLUETOOTH:
464 this.binding.inCallActionLeft.setImageResource(R.drawable.ic_bluetooth_audio_black_24dp);
465 this.binding.inCallActionLeft.setOnClickListener(null);
466 this.binding.inCallActionLeft.setClickable(false);
467 break;
468 }
469 this.binding.inCallActionLeft.setVisibility(View.VISIBLE);
470 if (microphoneEnabled) {
471 this.binding.inCallActionRight.setImageResource(R.drawable.ic_mic_black_24dp);
472 this.binding.inCallActionRight.setOnClickListener(this::disableMicrophone);
473 } else {
474 this.binding.inCallActionRight.setImageResource(R.drawable.ic_mic_off_black_24dp);
475 this.binding.inCallActionRight.setOnClickListener(this::enableMicrophone);
476 }
477 this.binding.inCallActionRight.setVisibility(View.VISIBLE);
478 }
479
480 private void disableMicrophone(View view) {
481 JingleRtpConnection rtpConnection = requireRtpConnection();
482 rtpConnection.setMicrophoneEnabled(false);
483 updateInCallButtonConfiguration();
484 }
485
486 private void enableMicrophone(View view) {
487 JingleRtpConnection rtpConnection = requireRtpConnection();
488 rtpConnection.setMicrophoneEnabled(true);
489 updateInCallButtonConfiguration();
490 }
491
492 private void switchToEarpiece(View view) {
493 requireRtpConnection().getAudioManager().setDefaultAudioDevice(AppRTCAudioManager.AudioDevice.EARPIECE);
494 acquireProximityWakeLock();
495 }
496
497 private void switchToSpeaker(View view) {
498 requireRtpConnection().getAudioManager().setDefaultAudioDevice(AppRTCAudioManager.AudioDevice.SPEAKER_PHONE);
499 releaseProximityWakeLock();
500 }
501
502 private void retry(View view) {
503 Log.d(Config.LOGTAG, "attempting retry");
504 final Intent intent = getIntent();
505 final Account account = extractAccount(intent);
506 final Jid with = Jid.of(intent.getStringExtra(EXTRA_WITH));
507 this.rtpConnectionReference = null;
508 proposeJingleRtpSession(account, with, ImmutableSet.of(Media.AUDIO, Media.VIDEO));
509 }
510
511 private void exit(View view) {
512 finish();
513 }
514
515 private Contact getWith() {
516 final AbstractJingleConnection.Id id = requireRtpConnection().getId();
517 final Account account = id.account;
518 return account.getRoster().getContact(id.with);
519 }
520
521 private JingleRtpConnection requireRtpConnection() {
522 final JingleRtpConnection connection = this.rtpConnectionReference != null ? this.rtpConnectionReference.get() : null;
523 if (connection == null) {
524 throw new IllegalStateException("No RTP connection found");
525 }
526 return connection;
527 }
528
529 @Override
530 public void onJingleRtpConnectionUpdate(Account account, Jid with, final String sessionId, RtpEndUserState state) {
531 if (Arrays.asList(RtpEndUserState.APPLICATION_ERROR, RtpEndUserState.DECLINED_OR_BUSY, RtpEndUserState.DECLINED_OR_BUSY).contains(state)) {
532 releaseProximityWakeLock();
533 }
534 Log.d(Config.LOGTAG, "onJingleRtpConnectionUpdate(" + state + ")");
535 if (with.isBareJid()) {
536 updateRtpSessionProposalState(account, with, state);
537 return;
538 }
539 if (this.rtpConnectionReference == null) {
540 //this happens when going from proposed session to actual session
541 reInitializeActivityWithRunningRapSession(account, with, sessionId);
542 return;
543 }
544 final AbstractJingleConnection.Id id = requireRtpConnection().getId();
545 if (account == id.account && id.with.equals(with) && id.sessionId.equals(sessionId)) {
546 if (state == RtpEndUserState.ENDED) {
547 finish();
548 return;
549 } else if (asList(RtpEndUserState.APPLICATION_ERROR, RtpEndUserState.DECLINED_OR_BUSY, RtpEndUserState.CONNECTIVITY_ERROR).contains(state)) {
550 resetIntent(account, with, state);
551 }
552 runOnUiThread(() -> {
553 updateStateDisplay(state);
554 updateButtonConfiguration(state);
555 updateVideoViews();
556 });
557 } else {
558 Log.d(Config.LOGTAG, "received update for other rtp session");
559 //TODO if we only ever have one; we might just switch over? Maybe!
560 }
561 }
562
563 @Override
564 public void onAudioDeviceChanged(AppRTCAudioManager.AudioDevice selectedAudioDevice, Set<AppRTCAudioManager.AudioDevice> availableAudioDevices) {
565 Log.d(Config.LOGTAG, "onAudioDeviceChanged in activity: selected:" + selectedAudioDevice + ", available:" + availableAudioDevices);
566 try {
567 if (requireRtpConnection().getEndUserState() == RtpEndUserState.CONNECTED) {
568 final AppRTCAudioManager audioManager = requireRtpConnection().getAudioManager();
569 updateInCallButtonConfiguration(
570 audioManager.getSelectedAudioDevice(),
571 audioManager.getAudioDevices().size(),
572 requireRtpConnection().isMicrophoneEnabled()
573 );
574 }
575 putProximityWakeLockInProperState();
576 } catch (IllegalStateException e) {
577 Log.d(Config.LOGTAG, "RTP connection was not available when audio device changed");
578 }
579 }
580
581 private void updateRtpSessionProposalState(final Account account, final Jid with, final RtpEndUserState state) {
582 final Intent currentIntent = getIntent();
583 final String withExtra = currentIntent == null ? null : currentIntent.getStringExtra(EXTRA_WITH);
584 if (withExtra == null) {
585 return;
586 }
587 if (Jid.ofEscaped(withExtra).asBareJid().equals(with)) {
588 runOnUiThread(() -> {
589 updateStateDisplay(state);
590 updateButtonConfiguration(state);
591 });
592 resetIntent(account, with, state);
593 }
594 }
595
596 private void resetIntent(final Bundle extras) {
597 final Intent intent = new Intent(Intent.ACTION_VIEW);
598 intent.putExtras(extras);
599 setIntent(intent);
600 }
601
602 private void resetIntent(final Account account, Jid with, final RtpEndUserState state) {
603 final Intent intent = new Intent(Intent.ACTION_VIEW);
604 intent.putExtra(EXTRA_WITH, with.asBareJid().toEscapedString());
605 intent.putExtra(EXTRA_ACCOUNT, account.getJid().toEscapedString());
606 intent.putExtra(EXTRA_LAST_REPORTED_STATE, state.toString());
607 setIntent(intent);
608 }
609}