RtpSessionActivity.java

  1package eu.siacs.conversations.ui;
  2
  3import android.content.Intent;
  4import android.content.res.ColorStateList;
  5import android.databinding.DataBindingUtil;
  6import android.os.Bundle;
  7import android.util.Log;
  8import android.view.View;
  9import android.view.WindowManager;
 10
 11import java.lang.ref.WeakReference;
 12
 13import eu.siacs.conversations.Config;
 14import eu.siacs.conversations.R;
 15import eu.siacs.conversations.databinding.ActivityRtpSessionBinding;
 16import eu.siacs.conversations.entities.Account;
 17import eu.siacs.conversations.entities.Contact;
 18import eu.siacs.conversations.services.XmppConnectionService;
 19import eu.siacs.conversations.xmpp.jingle.AbstractJingleConnection;
 20import eu.siacs.conversations.xmpp.jingle.JingleRtpConnection;
 21import eu.siacs.conversations.xmpp.jingle.RtpEndUserState;
 22import rocks.xmpp.addr.Jid;
 23
 24import static java.util.Arrays.asList;
 25
 26//TODO if last state was BUSY (or RETRY); we want to reset action to view or something so we don’t automatically call again on recreate
 27
 28public class RtpSessionActivity extends XmppActivity implements XmppConnectionService.OnJingleRtpConnectionUpdate {
 29
 30    public static final String EXTRA_WITH = "with";
 31    public static final String EXTRA_SESSION_ID = "session_id";
 32
 33    public static final String ACTION_ACCEPT_CALL = "action_accept_call";
 34    public static final String ACTION_MAKE_VOICE_CALL = "action_make_voice_call";
 35    public static final String ACTION_MAKE_VIDEO_CALL = "action_make_video_call";
 36
 37    private WeakReference<JingleRtpConnection> rtpConnectionReference;
 38
 39    private ActivityRtpSessionBinding binding;
 40
 41    @Override
 42    public void onCreate(Bundle savedInstanceState) {
 43        super.onCreate(savedInstanceState);
 44        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
 45                | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
 46                | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
 47                | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON)
 48        ;
 49        Log.d(Config.LOGTAG, "RtpSessionActivity.onCreate()");
 50        this.binding = DataBindingUtil.setContentView(this, R.layout.activity_rtp_session);
 51    }
 52
 53    @Override
 54    public void onStart() {
 55        super.onStart();
 56        Log.d(Config.LOGTAG, "RtpSessionActivity.onStart()");
 57    }
 58
 59    private void endCall(View view) {
 60        if (this.rtpConnectionReference == null) {
 61            final Intent intent = getIntent();
 62            final Account account = extractAccount(intent);
 63            final Jid with = Jid.of(intent.getStringExtra(EXTRA_WITH));
 64            xmppConnectionService.getJingleConnectionManager().retractSessionProposal(account, with.asBareJid());
 65            finish();
 66        } else {
 67            requireRtpConnection().endCall();
 68        }
 69    }
 70
 71    private void rejectCall(View view) {
 72        requireRtpConnection().rejectCall();
 73        finish();
 74    }
 75
 76    private void acceptCall(View view) {
 77        requireRtpConnection().acceptCall();
 78    }
 79
 80    @Override
 81    protected void refreshUiReal() {
 82
 83    }
 84
 85    @Override
 86    public void onNewIntent(final Intent intent) {
 87        super.onNewIntent(intent);
 88        if (ACTION_ACCEPT_CALL.equals(intent.getAction())) {
 89            Log.d(Config.LOGTAG, "accepting through onNewIntent()");
 90            requireRtpConnection().acceptCall();
 91        }
 92    }
 93
 94    @Override
 95    void onBackendConnected() {
 96        final Intent intent = getIntent();
 97        final Account account = extractAccount(intent);
 98        final Jid with = Jid.of(intent.getStringExtra(EXTRA_WITH));
 99        final String sessionId = intent.getStringExtra(EXTRA_SESSION_ID);
100        if (sessionId != null) {
101            initializeActivityWithRunningRapSession(account, with, sessionId);
102            if (ACTION_ACCEPT_CALL.equals(intent.getAction())) {
103                Log.d(Config.LOGTAG, "intent action was accept");
104                requireRtpConnection().acceptCall();
105            }
106        } else if (asList(ACTION_MAKE_VIDEO_CALL, ACTION_MAKE_VOICE_CALL).contains(intent.getAction())) {
107            xmppConnectionService.getJingleConnectionManager().proposeJingleRtpSession(account, with);
108            binding.with.setText(account.getRoster().getContact(with).getDisplayName());
109        }
110    }
111
112
113    private void initializeActivityWithRunningRapSession(final Account account, Jid with, String sessionId) {
114        final WeakReference<JingleRtpConnection> reference = xmppConnectionService.getJingleConnectionManager()
115                .findJingleRtpConnection(account, with, sessionId);
116        if (reference == null || reference.get() == null) {
117            finish();
118            return;
119        }
120        this.rtpConnectionReference = reference;
121        final RtpEndUserState currentState = requireRtpConnection().getEndUserState();
122        if (currentState == RtpEndUserState.ENDED) {
123            finish();
124            return;
125        }
126        binding.with.setText(getWith().getDisplayName());
127        updateStateDisplay(currentState);
128        updateButtonConfiguration(currentState);
129    }
130
131    private void reInitializeActivityWithRunningRapSession(final Account account, Jid with, String sessionId) {
132        runOnUiThread(() -> {
133            initializeActivityWithRunningRapSession(account, with, sessionId);
134        });
135        final Intent intent = new Intent(Intent.ACTION_VIEW);
136        intent.putExtra(EXTRA_ACCOUNT, account.getJid().toEscapedString());
137        intent.putExtra(EXTRA_WITH, with.toEscapedString());
138        intent.putExtra(EXTRA_SESSION_ID, sessionId);
139        setIntent(intent);
140    }
141
142    private void updateStateDisplay(final RtpEndUserState state) {
143        switch (state) {
144            case INCOMING_CALL:
145                binding.status.setText(R.string.rtp_state_incoming_call);
146                break;
147            case CONNECTING:
148                binding.status.setText(R.string.rtp_state_connecting);
149                break;
150            case CONNECTED:
151                binding.status.setText(R.string.rtp_state_connected);
152                break;
153            case ACCEPTING_CALL:
154                binding.status.setText(R.string.rtp_state_accepting_call);
155                break;
156            case ENDING_CALL:
157                binding.status.setText(R.string.rtp_state_ending_call);
158                break;
159            case FINDING_DEVICE:
160                binding.status.setText(R.string.rtp_state_finding_device);
161                break;
162            case RINGING:
163                binding.status.setText(R.string.rtp_state_ringing);
164                break;
165            case DECLINED_OR_BUSY:
166                binding.status.setText(R.string.rtp_state_declined_or_busy);
167                break;
168            case CONNECTIVITY_ERROR:
169                binding.status.setText(R.string.rtp_state_connectivity_error);
170                break;
171        }
172    }
173
174    private void updateButtonConfiguration(final RtpEndUserState state) {
175        if (state == RtpEndUserState.INCOMING_CALL) {
176            this.binding.rejectCall.setOnClickListener(this::rejectCall);
177            this.binding.rejectCall.setImageResource(R.drawable.ic_call_end_white_48dp);
178            this.binding.rejectCall.show();
179            this.binding.endCall.hide();
180            this.binding.acceptCall.setOnClickListener(this::acceptCall);
181            this.binding.acceptCall.setImageResource(R.drawable.ic_call_white_48dp);
182            this.binding.acceptCall.show();
183        } else if (state == RtpEndUserState.ENDING_CALL) {
184            this.binding.rejectCall.hide();
185            this.binding.endCall.hide();
186            this.binding.acceptCall.hide();
187        } else if (state == RtpEndUserState.DECLINED_OR_BUSY) {
188            this.binding.rejectCall.hide();
189            this.binding.endCall.setOnClickListener(this::exit);
190            this.binding.endCall.setImageResource(R.drawable.ic_clear_white_48dp);
191            this.binding.endCall.show();
192            this.binding.acceptCall.hide();
193        } else if (state == RtpEndUserState.CONNECTIVITY_ERROR) {
194            this.binding.rejectCall.setOnClickListener(this::exit);
195            this.binding.rejectCall.setImageResource(R.drawable.ic_clear_white_48dp);
196            this.binding.rejectCall.show();
197            this.binding.endCall.hide();
198            this.binding.acceptCall.setOnClickListener(this::retry);
199            this.binding.acceptCall.setImageResource(R.drawable.ic_replay_white_48dp);
200            this.binding.acceptCall.show();
201        } else {
202            this.binding.rejectCall.hide();
203            this.binding.endCall.setOnClickListener(this::endCall);
204            this.binding.endCall.setImageResource(R.drawable.ic_call_end_white_48dp);
205            this.binding.endCall.show();
206            this.binding.acceptCall.hide();
207        }
208    }
209
210    private void retry(View view) {
211
212    }
213
214    private void exit(View view) {
215        finish();
216    }
217
218    private Contact getWith() {
219        final AbstractJingleConnection.Id id = requireRtpConnection().getId();
220        final Account account = id.account;
221        return account.getRoster().getContact(id.with);
222    }
223
224    private JingleRtpConnection requireRtpConnection() {
225        final JingleRtpConnection connection = this.rtpConnectionReference != null ? this.rtpConnectionReference.get() : null;
226        if (connection == null) {
227            throw new IllegalStateException("No RTP connection found");
228        }
229        return connection;
230    }
231
232    @Override
233    public void onJingleRtpConnectionUpdate(Account account, Jid with, final String sessionId, RtpEndUserState state) {
234        Log.d(Config.LOGTAG, "onJingleRtpConnectionUpdate(" + state + ")");
235        if (with.isBareJid()) {
236            updateRtpSessionProposalState(with, state);
237            return;
238        }
239        if (this.rtpConnectionReference == null) {
240            //this happens when going from proposed session to actual session
241            reInitializeActivityWithRunningRapSession(account, with, sessionId);
242            return;
243        }
244        final AbstractJingleConnection.Id id = requireRtpConnection().getId();
245        if (account == id.account && id.with.equals(with) && id.sessionId.equals(sessionId)) {
246            if (state == RtpEndUserState.ENDED) {
247                finish();
248                return;
249            }
250            runOnUiThread(() -> {
251                updateStateDisplay(state);
252                updateButtonConfiguration(state);
253            });
254        } else {
255            Log.d(Config.LOGTAG, "received update for other rtp session");
256        }
257    }
258
259    private void updateRtpSessionProposalState(Jid with, RtpEndUserState state) {
260        final Intent intent = getIntent();
261        final String intentExtraWith = intent == null ? null : intent.getStringExtra(EXTRA_WITH);
262        if (intentExtraWith == null) {
263            return;
264        }
265        if (Jid.ofEscaped(intentExtraWith).asBareJid().equals(with)) {
266            runOnUiThread(() -> {
267                updateStateDisplay(state);
268                updateButtonConfiguration(state);
269            });
270        }
271    }
272}