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