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 Account account = extractAccount(intent);
194        final Jid with = Jid.of(intent.getStringExtra(EXTRA_WITH));
195        final String sessionId = intent.getStringExtra(EXTRA_SESSION_ID);
196        if (sessionId != null) {
197            initializeActivityWithRunningRtpSession(account, with, sessionId);
198            if (ACTION_ACCEPT_CALL.equals(intent.getAction())) {
199                Log.d(Config.LOGTAG, "intent action was accept");
200                requestPermissionsAndAcceptCall();
201                resetIntent(intent.getExtras());
202            }
203        } else if (asList(ACTION_MAKE_VIDEO_CALL, ACTION_MAKE_VOICE_CALL).contains(intent.getAction())) {
204            proposeJingleRtpSession(account, with, ImmutableSet.of(Media.AUDIO));
205            binding.with.setText(account.getRoster().getContact(with).getDisplayName());
206        } else if (Intent.ACTION_VIEW.equals(intent.getAction())) {
207            final String extraLastState = intent.getStringExtra(EXTRA_LAST_REPORTED_STATE);
208            if (extraLastState != null) {
209                Log.d(Config.LOGTAG, "restored last state from intent extra");
210                RtpEndUserState state = RtpEndUserState.valueOf(extraLastState);
211                updateButtonConfiguration(state);
212                updateStateDisplay(state);
213            }
214            binding.with.setText(account.getRoster().getContact(with).getDisplayName());
215        }
216    }
217
218    private void proposeJingleRtpSession(final Account account, final Jid with, final Set<Media> media) {
219        xmppConnectionService.getJingleConnectionManager().proposeJingleRtpSession(account, with, media);
220        //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
221        putScreenInCallMode();
222    }
223
224    @Override
225    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
226        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
227        if (PermissionUtils.allGranted(grantResults)) {
228            if (requestCode == REQUEST_ACCEPT_CALL) {
229                requireRtpConnection().acceptCall();
230            }
231        } else {
232            @StringRes int res;
233            final String firstDenied = getFirstDenied(grantResults, permissions);
234            if (Manifest.permission.RECORD_AUDIO.equals(firstDenied)) {
235                res = R.string.no_microphone_permission;
236            } else if (Manifest.permission.CAMERA.equals(firstDenied)) {
237                res = R.string.no_camera_permission;
238            } else {
239                throw new IllegalStateException("Invalid permission result request");
240            }
241            Toast.makeText(this, res, Toast.LENGTH_SHORT).show();
242        }
243    }
244
245    @Override
246    public void onStop() {
247        binding.remoteVideo.release();
248        binding.localVideo.release();
249        releaseProximityWakeLock();
250        //TODO maybe we want to finish if call had ended
251        super.onStop();
252    }
253
254    @Override
255    public void onBackPressed() {
256        endCall();
257        super.onBackPressed();
258    }
259
260
261    private void initializeActivityWithRunningRtpSession(final Account account, Jid with, String sessionId) {
262        final WeakReference<JingleRtpConnection> reference = xmppConnectionService.getJingleConnectionManager()
263                .findJingleRtpConnection(account, with, sessionId);
264        if (reference == null || reference.get() == null) {
265            finish();
266            return;
267        }
268        this.rtpConnectionReference = reference;
269        final RtpEndUserState currentState = requireRtpConnection().getEndUserState();
270        if (currentState == RtpEndUserState.ENDED) {
271            finish();
272            return;
273        }
274        if (currentState == RtpEndUserState.INCOMING_CALL) {
275            getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
276        }
277        if (JingleRtpConnection.STATES_SHOWING_ONGOING_CALL.contains(requireRtpConnection().getState())) {
278            putScreenInCallMode();
279        }
280        binding.with.setText(getWith().getDisplayName());
281        updateStateDisplay(currentState);
282        updateButtonConfiguration(currentState);
283    }
284
285    private void reInitializeActivityWithRunningRapSession(final Account account, Jid with, String sessionId) {
286        runOnUiThread(() -> {
287            initializeActivityWithRunningRtpSession(account, with, sessionId);
288        });
289        final Intent intent = new Intent(Intent.ACTION_VIEW);
290        intent.putExtra(EXTRA_ACCOUNT, account.getJid().toEscapedString());
291        intent.putExtra(EXTRA_WITH, with.toEscapedString());
292        intent.putExtra(EXTRA_SESSION_ID, sessionId);
293        setIntent(intent);
294    }
295
296    private void updateVideoViews() {
297        final Optional<VideoTrack> localVideoTrack = requireRtpConnection().geLocalVideoTrack();
298        if (localVideoTrack.isPresent()) {
299            try {
300                binding.localVideo.init(requireRtpConnection().getEglBaseContext(), null);
301            } catch (IllegalStateException e) {
302                Log.d(Config.LOGTAG,"ignoring already init for now",e);
303            }
304            binding.localVideo.setEnableHardwareScaler(true);
305            binding.localVideo.setMirror(true);
306            localVideoTrack.get().addSink(binding.localVideo);
307        }
308        final Optional<VideoTrack> remoteVideoTrack = requireRtpConnection().getRemoteVideoTrack();
309        if (remoteVideoTrack.isPresent()) {
310            try {
311                binding.remoteVideo.init(requireRtpConnection().getEglBaseContext(), null);
312            } catch (IllegalStateException e) {
313                Log.d(Config.LOGTAG,"ignoring already init for now",e);
314            }
315            binding.remoteVideo.setEnableHardwareScaler(true);
316            remoteVideoTrack.get().addSink(binding.remoteVideo);
317        }
318    }
319
320    private void updateStateDisplay(final RtpEndUserState state) {
321        switch (state) {
322            case INCOMING_CALL:
323                setTitle(R.string.rtp_state_incoming_call);
324                break;
325            case CONNECTING:
326                setTitle(R.string.rtp_state_connecting);
327                break;
328            case CONNECTED:
329                setTitle(R.string.rtp_state_connected);
330                break;
331            case ACCEPTING_CALL:
332                setTitle(R.string.rtp_state_accepting_call);
333                break;
334            case ENDING_CALL:
335                setTitle(R.string.rtp_state_ending_call);
336                break;
337            case FINDING_DEVICE:
338                setTitle(R.string.rtp_state_finding_device);
339                break;
340            case RINGING:
341                setTitle(R.string.rtp_state_ringing);
342                break;
343            case DECLINED_OR_BUSY:
344                setTitle(R.string.rtp_state_declined_or_busy);
345                break;
346            case CONNECTIVITY_ERROR:
347                setTitle(R.string.rtp_state_connectivity_error);
348                break;
349            case APPLICATION_ERROR:
350                setTitle(R.string.rtp_state_application_failure);
351                break;
352            case ENDED:
353                throw new IllegalStateException("Activity should have called finishAndReleaseWakeLock();");
354            default:
355                throw new IllegalStateException(String.format("State %s has not been handled in UI", state));
356        }
357    }
358
359    @SuppressLint("RestrictedApi")
360    private void updateButtonConfiguration(final RtpEndUserState state) {
361        if (state == RtpEndUserState.INCOMING_CALL) {
362            this.binding.rejectCall.setOnClickListener(this::rejectCall);
363            this.binding.rejectCall.setImageResource(R.drawable.ic_call_end_white_48dp);
364            this.binding.rejectCall.setVisibility(View.VISIBLE);
365            this.binding.endCall.setVisibility(View.INVISIBLE);
366            this.binding.acceptCall.setOnClickListener(this::acceptCall);
367            this.binding.acceptCall.setImageResource(R.drawable.ic_call_white_48dp);
368            this.binding.acceptCall.setVisibility(View.VISIBLE);
369        } else if (state == RtpEndUserState.ENDING_CALL) {
370            this.binding.rejectCall.setVisibility(View.INVISIBLE);
371            this.binding.endCall.setVisibility(View.INVISIBLE);
372            this.binding.acceptCall.setVisibility(View.INVISIBLE);
373        } else if (state == RtpEndUserState.DECLINED_OR_BUSY) {
374            this.binding.rejectCall.setVisibility(View.INVISIBLE);
375            this.binding.endCall.setOnClickListener(this::exit);
376            this.binding.endCall.setImageResource(R.drawable.ic_clear_white_48dp);
377            this.binding.endCall.setVisibility(View.VISIBLE);
378            this.binding.acceptCall.setVisibility(View.INVISIBLE);
379        } else if (state == RtpEndUserState.CONNECTIVITY_ERROR || state == RtpEndUserState.APPLICATION_ERROR) {
380            this.binding.rejectCall.setOnClickListener(this::exit);
381            this.binding.rejectCall.setImageResource(R.drawable.ic_clear_white_48dp);
382            this.binding.rejectCall.setVisibility(View.VISIBLE);
383            this.binding.endCall.setVisibility(View.INVISIBLE);
384            this.binding.acceptCall.setOnClickListener(this::retry);
385            this.binding.acceptCall.setImageResource(R.drawable.ic_replay_white_48dp);
386            this.binding.acceptCall.setVisibility(View.VISIBLE);
387        } else {
388            this.binding.rejectCall.setVisibility(View.INVISIBLE);
389            this.binding.endCall.setOnClickListener(this::endCall);
390            this.binding.endCall.setImageResource(R.drawable.ic_call_end_white_48dp);
391            this.binding.endCall.setVisibility(View.VISIBLE);
392            this.binding.acceptCall.setVisibility(View.INVISIBLE);
393        }
394        updateInCallButtonConfiguration(state);
395    }
396
397    private void updateInCallButtonConfiguration() {
398        updateInCallButtonConfiguration(requireRtpConnection().getEndUserState());
399    }
400
401    @SuppressLint("RestrictedApi")
402    private void updateInCallButtonConfiguration(final RtpEndUserState state) {
403        if (state == RtpEndUserState.CONNECTED) {
404            final AppRTCAudioManager audioManager = requireRtpConnection().getAudioManager();
405            updateInCallButtonConfiguration(
406                    audioManager.getSelectedAudioDevice(),
407                    audioManager.getAudioDevices().size(),
408                    requireRtpConnection().isMicrophoneEnabled()
409            );
410        } else {
411            this.binding.inCallActionLeft.setVisibility(View.GONE);
412            this.binding.inCallActionRight.setVisibility(View.GONE);
413        }
414    }
415
416    @SuppressLint("RestrictedApi")
417    private void updateInCallButtonConfiguration(final AppRTCAudioManager.AudioDevice selectedAudioDevice, final int numberOfChoices, final boolean microphoneEnabled) {
418        switch (selectedAudioDevice) {
419            case EARPIECE:
420                this.binding.inCallActionLeft.setImageResource(R.drawable.ic_volume_off_black_24dp);
421                if (numberOfChoices >= 2) {
422                    this.binding.inCallActionLeft.setOnClickListener(this::switchToSpeaker);
423                } else {
424                    this.binding.inCallActionLeft.setOnClickListener(null);
425                    this.binding.inCallActionLeft.setClickable(false);
426                }
427                break;
428            case WIRED_HEADSET:
429                this.binding.inCallActionLeft.setImageResource(R.drawable.ic_headset_black_24dp);
430                this.binding.inCallActionLeft.setOnClickListener(null);
431                this.binding.inCallActionLeft.setClickable(false);
432                break;
433            case SPEAKER_PHONE:
434                this.binding.inCallActionLeft.setImageResource(R.drawable.ic_volume_up_black_24dp);
435                if (numberOfChoices >= 2) {
436                    this.binding.inCallActionLeft.setOnClickListener(this::switchToEarpiece);
437                } else {
438                    this.binding.inCallActionLeft.setOnClickListener(null);
439                    this.binding.inCallActionLeft.setClickable(false);
440                }
441                break;
442            case BLUETOOTH:
443                this.binding.inCallActionLeft.setImageResource(R.drawable.ic_bluetooth_audio_black_24dp);
444                this.binding.inCallActionLeft.setOnClickListener(null);
445                this.binding.inCallActionLeft.setClickable(false);
446                break;
447        }
448        this.binding.inCallActionLeft.setVisibility(View.VISIBLE);
449        if (microphoneEnabled) {
450            this.binding.inCallActionRight.setImageResource(R.drawable.ic_mic_black_24dp);
451            this.binding.inCallActionRight.setOnClickListener(this::disableMicrophone);
452        } else {
453            this.binding.inCallActionRight.setImageResource(R.drawable.ic_mic_off_black_24dp);
454            this.binding.inCallActionRight.setOnClickListener(this::enableMicrophone);
455        }
456        this.binding.inCallActionRight.setVisibility(View.VISIBLE);
457    }
458
459    private void disableMicrophone(View view) {
460        JingleRtpConnection rtpConnection = requireRtpConnection();
461        rtpConnection.setMicrophoneEnabled(false);
462        updateInCallButtonConfiguration();
463    }
464
465    private void enableMicrophone(View view) {
466        JingleRtpConnection rtpConnection = requireRtpConnection();
467        rtpConnection.setMicrophoneEnabled(true);
468        updateInCallButtonConfiguration();
469    }
470
471    private void switchToEarpiece(View view) {
472        requireRtpConnection().getAudioManager().setDefaultAudioDevice(AppRTCAudioManager.AudioDevice.EARPIECE);
473        acquireProximityWakeLock();
474    }
475
476    private void switchToSpeaker(View view) {
477        requireRtpConnection().getAudioManager().setDefaultAudioDevice(AppRTCAudioManager.AudioDevice.SPEAKER_PHONE);
478        releaseProximityWakeLock();
479    }
480
481    private void retry(View view) {
482        Log.d(Config.LOGTAG, "attempting retry");
483        final Intent intent = getIntent();
484        final Account account = extractAccount(intent);
485        final Jid with = Jid.of(intent.getStringExtra(EXTRA_WITH));
486        this.rtpConnectionReference = null;
487        proposeJingleRtpSession(account, with, ImmutableSet.of(Media.AUDIO));
488    }
489
490    private void exit(View view) {
491        finish();
492    }
493
494    private Contact getWith() {
495        final AbstractJingleConnection.Id id = requireRtpConnection().getId();
496        final Account account = id.account;
497        return account.getRoster().getContact(id.with);
498    }
499
500    private JingleRtpConnection requireRtpConnection() {
501        final JingleRtpConnection connection = this.rtpConnectionReference != null ? this.rtpConnectionReference.get() : null;
502        if (connection == null) {
503            throw new IllegalStateException("No RTP connection found");
504        }
505        return connection;
506    }
507
508    @Override
509    public void onJingleRtpConnectionUpdate(Account account, Jid with, final String sessionId, RtpEndUserState state) {
510        if (Arrays.asList(RtpEndUserState.APPLICATION_ERROR, RtpEndUserState.DECLINED_OR_BUSY, RtpEndUserState.DECLINED_OR_BUSY).contains(state)) {
511            releaseProximityWakeLock();
512        }
513        Log.d(Config.LOGTAG, "onJingleRtpConnectionUpdate(" + state + ")");
514        if (with.isBareJid()) {
515            updateRtpSessionProposalState(account, with, state);
516            return;
517        }
518        if (this.rtpConnectionReference == null) {
519            //this happens when going from proposed session to actual session
520            reInitializeActivityWithRunningRapSession(account, with, sessionId);
521            return;
522        }
523        final AbstractJingleConnection.Id id = requireRtpConnection().getId();
524        if (account == id.account && id.with.equals(with) && id.sessionId.equals(sessionId)) {
525            if (state == RtpEndUserState.ENDED) {
526                finish();
527                return;
528            } else if (asList(RtpEndUserState.APPLICATION_ERROR, RtpEndUserState.DECLINED_OR_BUSY, RtpEndUserState.CONNECTIVITY_ERROR).contains(state)) {
529                resetIntent(account, with, state);
530            }
531            runOnUiThread(() -> {
532                updateStateDisplay(state);
533                updateButtonConfiguration(state);
534                updateVideoViews();
535            });
536        } else {
537            Log.d(Config.LOGTAG, "received update for other rtp session");
538            //TODO if we only ever have one; we might just switch over? Maybe!
539        }
540    }
541
542    @Override
543    public void onAudioDeviceChanged(AppRTCAudioManager.AudioDevice selectedAudioDevice, Set<AppRTCAudioManager.AudioDevice> availableAudioDevices) {
544        Log.d(Config.LOGTAG, "onAudioDeviceChanged in activity: selected:" + selectedAudioDevice + ", available:" + availableAudioDevices);
545        try {
546            if (requireRtpConnection().getEndUserState() == RtpEndUserState.CONNECTED) {
547                final AppRTCAudioManager audioManager = requireRtpConnection().getAudioManager();
548                updateInCallButtonConfiguration(
549                        audioManager.getSelectedAudioDevice(),
550                        audioManager.getAudioDevices().size(),
551                        requireRtpConnection().isMicrophoneEnabled()
552                );
553            }
554            putProximityWakeLockInProperState();
555        } catch (IllegalStateException e) {
556            Log.d(Config.LOGTAG, "RTP connection was not available when audio device changed");
557        }
558    }
559
560    private void updateRtpSessionProposalState(final Account account, final Jid with, final RtpEndUserState state) {
561        final Intent currentIntent = getIntent();
562        final String withExtra = currentIntent == null ? null : currentIntent.getStringExtra(EXTRA_WITH);
563        if (withExtra == null) {
564            return;
565        }
566        if (Jid.ofEscaped(withExtra).asBareJid().equals(with)) {
567            runOnUiThread(() -> {
568                updateStateDisplay(state);
569                updateButtonConfiguration(state);
570            });
571            resetIntent(account, with, state);
572        }
573    }
574
575    private void resetIntent(final Bundle extras) {
576        final Intent intent = new Intent(Intent.ACTION_VIEW);
577        intent.putExtras(extras);
578        setIntent(intent);
579    }
580
581    private void resetIntent(final Account account, Jid with, final RtpEndUserState state) {
582        final Intent intent = new Intent(Intent.ACTION_VIEW);
583        intent.putExtra(EXTRA_WITH, with.asBareJid().toEscapedString());
584        intent.putExtra(EXTRA_ACCOUNT, account.getJid().toEscapedString());
585        intent.putExtra(EXTRA_LAST_REPORTED_STATE, state.toString());
586        setIntent(intent);
587    }
588}