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;
 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}