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        //TODO reinitialize
 89        if (ACTION_ACCEPT_CALL.equals(intent.getAction())) {
 90            Log.d(Config.LOGTAG, "accepting through onNewIntent()");
 91            requireRtpConnection().acceptCall();
 92        }
 93    }
 94
 95    @Override
 96    void onBackendConnected() {
 97        final Intent intent = getIntent();
 98        final Account account = extractAccount(intent);
 99        final Jid with = Jid.of(intent.getStringExtra(EXTRA_WITH));
100        final String sessionId = intent.getStringExtra(EXTRA_SESSION_ID);
101        if (sessionId != null) {
102            initializeActivityWithRunningRapSession(account, with, sessionId);
103            if (ACTION_ACCEPT_CALL.equals(intent.getAction())) {
104                Log.d(Config.LOGTAG, "intent action was accept");
105                requireRtpConnection().acceptCall();
106            }
107        } else if (asList(ACTION_MAKE_VIDEO_CALL, ACTION_MAKE_VOICE_CALL).contains(intent.getAction())) {
108            xmppConnectionService.getJingleConnectionManager().proposeJingleRtpSession(account, with);
109            binding.with.setText(account.getRoster().getContact(with).getDisplayName());
110        }
111    }
112
113
114    private void initializeActivityWithRunningRapSession(final Account account, Jid with, String sessionId) {
115        final WeakReference<JingleRtpConnection> reference = xmppConnectionService.getJingleConnectionManager()
116                .findJingleRtpConnection(account, with, sessionId);
117        if (reference == null || reference.get() == null) {
118            finish();
119            return;
120        }
121        this.rtpConnectionReference = reference;
122        final RtpEndUserState currentState = requireRtpConnection().getEndUserState();
123        if (currentState == RtpEndUserState.ENDED) {
124            finish();
125            return;
126        }
127        binding.with.setText(getWith().getDisplayName());
128        updateStateDisplay(currentState);
129        updateButtonConfiguration(currentState);
130    }
131
132    private void reInitializeActivityWithRunningRapSession(final Account account, Jid with, String sessionId) {
133        runOnUiThread(() -> {
134            initializeActivityWithRunningRapSession(account, with, sessionId);
135        });
136        final Intent intent = new Intent(Intent.ACTION_VIEW);
137        intent.putExtra(EXTRA_ACCOUNT, account.getJid().toEscapedString());
138        intent.putExtra(EXTRA_WITH, with.toEscapedString());
139        intent.putExtra(EXTRA_SESSION_ID, sessionId);
140        setIntent(intent);
141    }
142
143    private void updateStateDisplay(final RtpEndUserState state) {
144        switch (state) {
145            case INCOMING_CALL:
146                binding.status.setText(R.string.rtp_state_incoming_call);
147                break;
148            case CONNECTING:
149                binding.status.setText(R.string.rtp_state_connecting);
150                break;
151            case CONNECTED:
152                binding.status.setText(R.string.rtp_state_connected);
153                break;
154            case ACCEPTING_CALL:
155                binding.status.setText(R.string.rtp_state_accepting_call);
156                break;
157            case ENDING_CALL:
158                binding.status.setText(R.string.rtp_state_ending_call);
159                break;
160            case FINDING_DEVICE:
161                binding.status.setText(R.string.rtp_state_finding_device);
162                break;
163            case RINGING:
164                binding.status.setText(R.string.rtp_state_ringing);
165                break;
166            case DECLINED_OR_BUSY:
167                binding.status.setText(R.string.rtp_state_declined_or_busy);
168                break;
169            case CONNECTIVITY_ERROR:
170                binding.status.setText(R.string.rtp_state_connectivity_error);
171                break;
172            case APPLICATION_ERROR:
173                binding.status.setText(R.string.rtp_state_application_failure);
174                break;
175        }
176    }
177
178    private void updateButtonConfiguration(final RtpEndUserState state) {
179        if (state == RtpEndUserState.INCOMING_CALL) {
180            this.binding.rejectCall.setOnClickListener(this::rejectCall);
181            this.binding.rejectCall.setImageResource(R.drawable.ic_call_end_white_48dp);
182            this.binding.rejectCall.show();
183            this.binding.endCall.hide();
184            this.binding.acceptCall.setOnClickListener(this::acceptCall);
185            this.binding.acceptCall.setImageResource(R.drawable.ic_call_white_48dp);
186            this.binding.acceptCall.show();
187        } else if (state == RtpEndUserState.ENDING_CALL) {
188            this.binding.rejectCall.hide();
189            this.binding.endCall.hide();
190            this.binding.acceptCall.hide();
191        } else if (state == RtpEndUserState.DECLINED_OR_BUSY) {
192            this.binding.rejectCall.hide();
193            this.binding.endCall.setOnClickListener(this::exit);
194            this.binding.endCall.setImageResource(R.drawable.ic_clear_white_48dp);
195            this.binding.endCall.show();
196            this.binding.acceptCall.hide();
197        } else if (state == RtpEndUserState.CONNECTIVITY_ERROR || state == RtpEndUserState.APPLICATION_ERROR) {
198            this.binding.rejectCall.setOnClickListener(this::exit);
199            this.binding.rejectCall.setImageResource(R.drawable.ic_clear_white_48dp);
200            this.binding.rejectCall.show();
201            this.binding.endCall.hide();
202            this.binding.acceptCall.setOnClickListener(this::retry);
203            this.binding.acceptCall.setImageResource(R.drawable.ic_replay_white_48dp);
204            this.binding.acceptCall.show();
205        } else {
206            this.binding.rejectCall.hide();
207            this.binding.endCall.setOnClickListener(this::endCall);
208            this.binding.endCall.setImageResource(R.drawable.ic_call_end_white_48dp);
209            this.binding.endCall.show();
210            this.binding.acceptCall.hide();
211        }
212    }
213
214    private void retry(View view) {
215
216    }
217
218    private void exit(View view) {
219        finish();
220    }
221
222    private Contact getWith() {
223        final AbstractJingleConnection.Id id = requireRtpConnection().getId();
224        final Account account = id.account;
225        return account.getRoster().getContact(id.with);
226    }
227
228    private JingleRtpConnection requireRtpConnection() {
229        final JingleRtpConnection connection = this.rtpConnectionReference != null ? this.rtpConnectionReference.get() : null;
230        if (connection == null) {
231            throw new IllegalStateException("No RTP connection found");
232        }
233        return connection;
234    }
235
236    @Override
237    public void onJingleRtpConnectionUpdate(Account account, Jid with, final String sessionId, RtpEndUserState state) {
238        Log.d(Config.LOGTAG, "onJingleRtpConnectionUpdate(" + state + ")");
239        if (with.isBareJid()) {
240            updateRtpSessionProposalState(with, state);
241            return;
242        }
243        if (this.rtpConnectionReference == null) {
244            //this happens when going from proposed session to actual session
245            reInitializeActivityWithRunningRapSession(account, with, sessionId);
246            return;
247        }
248        final AbstractJingleConnection.Id id = requireRtpConnection().getId();
249        if (account == id.account && id.with.equals(with) && id.sessionId.equals(sessionId)) {
250            if (state == RtpEndUserState.ENDED) {
251                finish();
252                return;
253            }
254            runOnUiThread(() -> {
255                updateStateDisplay(state);
256                updateButtonConfiguration(state);
257            });
258        } else {
259            Log.d(Config.LOGTAG, "received update for other rtp session");
260        }
261    }
262
263    private void updateRtpSessionProposalState(Jid with, RtpEndUserState state) {
264        final Intent intent = getIntent();
265        final String intentExtraWith = intent == null ? null : intent.getStringExtra(EXTRA_WITH);
266        if (intentExtraWith == null) {
267            return;
268        }
269        if (Jid.ofEscaped(intentExtraWith).asBareJid().equals(with)) {
270            runOnUiThread(() -> {
271                updateStateDisplay(state);
272                updateButtonConfiguration(state);
273            });
274        }
275    }
276}