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