RtpSessionActivity.java

  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}