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