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 setTitle(R.string.rtp_state_incoming_call);
337 break;
338 case CONNECTING:
339 setTitle(R.string.rtp_state_connecting);
340 break;
341 case CONNECTED:
342 setTitle(R.string.rtp_state_connected);
343 break;
344 case ACCEPTING_CALL:
345 setTitle(R.string.rtp_state_accepting_call);
346 break;
347 case ENDING_CALL:
348 setTitle(R.string.rtp_state_ending_call);
349 break;
350 case FINDING_DEVICE:
351 setTitle(R.string.rtp_state_finding_device);
352 break;
353 case RINGING:
354 setTitle(R.string.rtp_state_ringing);
355 break;
356 case DECLINED_OR_BUSY:
357 setTitle(R.string.rtp_state_declined_or_busy);
358 break;
359 case CONNECTIVITY_ERROR:
360 setTitle(R.string.rtp_state_connectivity_error);
361 break;
362 case APPLICATION_ERROR:
363 setTitle(R.string.rtp_state_application_failure);
364 break;
365 case ENDED:
366 throw new IllegalStateException("Activity should have called finishAndReleaseWakeLock();");
367 default:
368 throw new IllegalStateException(String.format("State %s has not been handled in UI", state));
369 }
370 }
371
372 @SuppressLint("RestrictedApi")
373 private void updateButtonConfiguration(final RtpEndUserState state) {
374 if (state == RtpEndUserState.INCOMING_CALL) {
375 this.binding.rejectCall.setOnClickListener(this::rejectCall);
376 this.binding.rejectCall.setImageResource(R.drawable.ic_call_end_white_48dp);
377 this.binding.rejectCall.setVisibility(View.VISIBLE);
378 this.binding.endCall.setVisibility(View.INVISIBLE);
379 this.binding.acceptCall.setOnClickListener(this::acceptCall);
380 this.binding.acceptCall.setImageResource(R.drawable.ic_call_white_48dp);
381 this.binding.acceptCall.setVisibility(View.VISIBLE);
382 } else if (state == RtpEndUserState.ENDING_CALL) {
383 this.binding.rejectCall.setVisibility(View.INVISIBLE);
384 this.binding.endCall.setVisibility(View.INVISIBLE);
385 this.binding.acceptCall.setVisibility(View.INVISIBLE);
386 } else if (state == RtpEndUserState.DECLINED_OR_BUSY) {
387 this.binding.rejectCall.setVisibility(View.INVISIBLE);
388 this.binding.endCall.setOnClickListener(this::exit);
389 this.binding.endCall.setImageResource(R.drawable.ic_clear_white_48dp);
390 this.binding.endCall.setVisibility(View.VISIBLE);
391 this.binding.acceptCall.setVisibility(View.INVISIBLE);
392 } else if (state == RtpEndUserState.CONNECTIVITY_ERROR || state == RtpEndUserState.APPLICATION_ERROR) {
393 this.binding.rejectCall.setOnClickListener(this::exit);
394 this.binding.rejectCall.setImageResource(R.drawable.ic_clear_white_48dp);
395 this.binding.rejectCall.setVisibility(View.VISIBLE);
396 this.binding.endCall.setVisibility(View.INVISIBLE);
397 this.binding.acceptCall.setOnClickListener(this::retry);
398 this.binding.acceptCall.setImageResource(R.drawable.ic_replay_white_48dp);
399 this.binding.acceptCall.setVisibility(View.VISIBLE);
400 } else {
401 this.binding.rejectCall.setVisibility(View.INVISIBLE);
402 this.binding.endCall.setOnClickListener(this::endCall);
403 this.binding.endCall.setImageResource(R.drawable.ic_call_end_white_48dp);
404 this.binding.endCall.setVisibility(View.VISIBLE);
405 this.binding.acceptCall.setVisibility(View.INVISIBLE);
406 }
407 updateInCallButtonConfiguration(state);
408 }
409
410 private void updateInCallButtonConfiguration() {
411 updateInCallButtonConfiguration(requireRtpConnection().getEndUserState());
412 }
413
414 @SuppressLint("RestrictedApi")
415 private void updateInCallButtonConfiguration(final RtpEndUserState state) {
416 if (state == RtpEndUserState.CONNECTED) {
417 final AppRTCAudioManager audioManager = requireRtpConnection().getAudioManager();
418 updateInCallButtonConfiguration(
419 audioManager.getSelectedAudioDevice(),
420 audioManager.getAudioDevices().size(),
421 requireRtpConnection().isMicrophoneEnabled()
422 );
423 } else {
424 this.binding.inCallActionLeft.setVisibility(View.GONE);
425 this.binding.inCallActionRight.setVisibility(View.GONE);
426 }
427 }
428
429 @SuppressLint("RestrictedApi")
430 private void updateInCallButtonConfiguration(final AppRTCAudioManager.AudioDevice selectedAudioDevice, final int numberOfChoices, final boolean microphoneEnabled) {
431 switch (selectedAudioDevice) {
432 case EARPIECE:
433 this.binding.inCallActionLeft.setImageResource(R.drawable.ic_volume_off_black_24dp);
434 if (numberOfChoices >= 2) {
435 this.binding.inCallActionLeft.setOnClickListener(this::switchToSpeaker);
436 } else {
437 this.binding.inCallActionLeft.setOnClickListener(null);
438 this.binding.inCallActionLeft.setClickable(false);
439 }
440 break;
441 case WIRED_HEADSET:
442 this.binding.inCallActionLeft.setImageResource(R.drawable.ic_headset_black_24dp);
443 this.binding.inCallActionLeft.setOnClickListener(null);
444 this.binding.inCallActionLeft.setClickable(false);
445 break;
446 case SPEAKER_PHONE:
447 this.binding.inCallActionLeft.setImageResource(R.drawable.ic_volume_up_black_24dp);
448 if (numberOfChoices >= 2) {
449 this.binding.inCallActionLeft.setOnClickListener(this::switchToEarpiece);
450 } else {
451 this.binding.inCallActionLeft.setOnClickListener(null);
452 this.binding.inCallActionLeft.setClickable(false);
453 }
454 break;
455 case BLUETOOTH:
456 this.binding.inCallActionLeft.setImageResource(R.drawable.ic_bluetooth_audio_black_24dp);
457 this.binding.inCallActionLeft.setOnClickListener(null);
458 this.binding.inCallActionLeft.setClickable(false);
459 break;
460 }
461 this.binding.inCallActionLeft.setVisibility(View.VISIBLE);
462 if (microphoneEnabled) {
463 this.binding.inCallActionRight.setImageResource(R.drawable.ic_mic_black_24dp);
464 this.binding.inCallActionRight.setOnClickListener(this::disableMicrophone);
465 } else {
466 this.binding.inCallActionRight.setImageResource(R.drawable.ic_mic_off_black_24dp);
467 this.binding.inCallActionRight.setOnClickListener(this::enableMicrophone);
468 }
469 this.binding.inCallActionRight.setVisibility(View.VISIBLE);
470 }
471
472 private void disableMicrophone(View view) {
473 JingleRtpConnection rtpConnection = requireRtpConnection();
474 rtpConnection.setMicrophoneEnabled(false);
475 updateInCallButtonConfiguration();
476 }
477
478 private void enableMicrophone(View view) {
479 JingleRtpConnection rtpConnection = requireRtpConnection();
480 rtpConnection.setMicrophoneEnabled(true);
481 updateInCallButtonConfiguration();
482 }
483
484 private void switchToEarpiece(View view) {
485 requireRtpConnection().getAudioManager().setDefaultAudioDevice(AppRTCAudioManager.AudioDevice.EARPIECE);
486 acquireProximityWakeLock();
487 }
488
489 private void switchToSpeaker(View view) {
490 requireRtpConnection().getAudioManager().setDefaultAudioDevice(AppRTCAudioManager.AudioDevice.SPEAKER_PHONE);
491 releaseProximityWakeLock();
492 }
493
494 private void retry(View view) {
495 Log.d(Config.LOGTAG, "attempting retry");
496 final Intent intent = getIntent();
497 final Account account = extractAccount(intent);
498 final Jid with = Jid.of(intent.getStringExtra(EXTRA_WITH));
499 this.rtpConnectionReference = null;
500 proposeJingleRtpSession(account, with, ImmutableSet.of(Media.AUDIO, Media.VIDEO));
501 }
502
503 private void exit(View view) {
504 finish();
505 }
506
507 private Contact getWith() {
508 final AbstractJingleConnection.Id id = requireRtpConnection().getId();
509 final Account account = id.account;
510 return account.getRoster().getContact(id.with);
511 }
512
513 private JingleRtpConnection requireRtpConnection() {
514 final JingleRtpConnection connection = this.rtpConnectionReference != null ? this.rtpConnectionReference.get() : null;
515 if (connection == null) {
516 throw new IllegalStateException("No RTP connection found");
517 }
518 return connection;
519 }
520
521 @Override
522 public void onJingleRtpConnectionUpdate(Account account, Jid with, final String sessionId, RtpEndUserState state) {
523 if (Arrays.asList(RtpEndUserState.APPLICATION_ERROR, RtpEndUserState.DECLINED_OR_BUSY, RtpEndUserState.DECLINED_OR_BUSY).contains(state)) {
524 releaseProximityWakeLock();
525 }
526 Log.d(Config.LOGTAG, "onJingleRtpConnectionUpdate(" + state + ")");
527 if (with.isBareJid()) {
528 updateRtpSessionProposalState(account, with, state);
529 return;
530 }
531 if (this.rtpConnectionReference == null) {
532 //this happens when going from proposed session to actual session
533 reInitializeActivityWithRunningRapSession(account, with, sessionId);
534 return;
535 }
536 final AbstractJingleConnection.Id id = requireRtpConnection().getId();
537 if (account == id.account && id.with.equals(with) && id.sessionId.equals(sessionId)) {
538 if (state == RtpEndUserState.ENDED) {
539 finish();
540 return;
541 } else if (asList(RtpEndUserState.APPLICATION_ERROR, RtpEndUserState.DECLINED_OR_BUSY, RtpEndUserState.CONNECTIVITY_ERROR).contains(state)) {
542 resetIntent(account, with, state);
543 }
544 runOnUiThread(() -> {
545 updateStateDisplay(state);
546 updateButtonConfiguration(state);
547 updateVideoViews();
548 });
549 } else {
550 Log.d(Config.LOGTAG, "received update for other rtp session");
551 //TODO if we only ever have one; we might just switch over? Maybe!
552 }
553 }
554
555 @Override
556 public void onAudioDeviceChanged(AppRTCAudioManager.AudioDevice selectedAudioDevice, Set<AppRTCAudioManager.AudioDevice> availableAudioDevices) {
557 Log.d(Config.LOGTAG, "onAudioDeviceChanged in activity: selected:" + selectedAudioDevice + ", available:" + availableAudioDevices);
558 try {
559 if (requireRtpConnection().getEndUserState() == RtpEndUserState.CONNECTED) {
560 final AppRTCAudioManager audioManager = requireRtpConnection().getAudioManager();
561 updateInCallButtonConfiguration(
562 audioManager.getSelectedAudioDevice(),
563 audioManager.getAudioDevices().size(),
564 requireRtpConnection().isMicrophoneEnabled()
565 );
566 }
567 putProximityWakeLockInProperState();
568 } catch (IllegalStateException e) {
569 Log.d(Config.LOGTAG, "RTP connection was not available when audio device changed");
570 }
571 }
572
573 private void updateRtpSessionProposalState(final Account account, final Jid with, final RtpEndUserState state) {
574 final Intent currentIntent = getIntent();
575 final String withExtra = currentIntent == null ? null : currentIntent.getStringExtra(EXTRA_WITH);
576 if (withExtra == null) {
577 return;
578 }
579 if (Jid.ofEscaped(withExtra).asBareJid().equals(with)) {
580 runOnUiThread(() -> {
581 updateStateDisplay(state);
582 updateButtonConfiguration(state);
583 });
584 resetIntent(account, with, state);
585 }
586 }
587
588 private void resetIntent(final Bundle extras) {
589 final Intent intent = new Intent(Intent.ACTION_VIEW);
590 intent.putExtras(extras);
591 setIntent(intent);
592 }
593
594 private void resetIntent(final Account account, Jid with, final RtpEndUserState state) {
595 final Intent intent = new Intent(Intent.ACTION_VIEW);
596 intent.putExtra(EXTRA_WITH, with.asBareJid().toEscapedString());
597 intent.putExtra(EXTRA_ACCOUNT, account.getJid().toEscapedString());
598 intent.putExtra(EXTRA_LAST_REPORTED_STATE, state.toString());
599 setIntent(intent);
600 }
601}