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