~singpolyma/cheogram-android

c178e9ad33ae3dd09621d5b11c704e0661951285 — Daniel Gultsch 1 year, 9 months ago 4e8cead
add switch to video menu item to call
M src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java => src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java +120 -19
@@ 65,6 65,7 @@ import eu.siacs.conversations.utils.TimeFrameUtils;
import eu.siacs.conversations.xml.Namespace;
import eu.siacs.conversations.xmpp.Jid;
import eu.siacs.conversations.xmpp.jingle.AbstractJingleConnection;
import eu.siacs.conversations.xmpp.jingle.ContentAddition;
import eu.siacs.conversations.xmpp.jingle.JingleConnectionManager;
import eu.siacs.conversations.xmpp.jingle.JingleRtpConnection;
import eu.siacs.conversations.xmpp.jingle.Media;


@@ 101,9 102,12 @@ public class RtpSessionActivity extends XmppActivity
            Arrays.asList(
                    RtpEndUserState.CONNECTING,
                    RtpEndUserState.CONNECTED,
                    RtpEndUserState.RECONNECTING);
                    RtpEndUserState.RECONNECTING,
                    RtpEndUserState.INCOMING_CONTENT_ADD);
    private static final List<RtpEndUserState> STATES_CONSIDERED_CONNECTED =
            Arrays.asList(RtpEndUserState.CONNECTED, RtpEndUserState.RECONNECTING);
            Arrays.asList(
                    RtpEndUserState.CONNECTED,
                    RtpEndUserState.RECONNECTING);
    private static final List<RtpEndUserState> STATES_SHOWING_PIP_PLACEHOLDER =
            Arrays.asList(
                    RtpEndUserState.ACCEPTING_CALL,


@@ 111,6 115,8 @@ public class RtpSessionActivity extends XmppActivity
                    RtpEndUserState.RECONNECTING);
    private static final String PROXIMITY_WAKE_LOCK_TAG = "conversations:in-rtp-session";
    private static final int REQUEST_ACCEPT_CALL = 0x1111;
    private static final int REQUEST_ACCEPT_CONTENT = 0x1112;
    private static final int REQUEST_ADD_CONTENT = 0x1113;
    private WeakReference<JingleRtpConnection> rtpConnectionReference;

    private ActivityRtpSessionBinding binding;


@@ 164,8 170,10 @@ public class RtpSessionActivity extends XmppActivity
        getMenuInflater().inflate(R.menu.activity_rtp_session, menu);
        final MenuItem help = menu.findItem(R.id.action_help);
        final MenuItem gotoChat = menu.findItem(R.id.action_goto_chat);
        final MenuItem switchToVideo = menu.findItem(R.id.action_switch_to_video);
        help.setVisible(Config.HELP != null && isHelpButtonVisible());
        gotoChat.setVisible(isSwitchToConversationVisible());
        switchToVideo.setVisible(isSwitchToVideoVisible());
        return super.onCreateOptionsMenu(menu);
    }



@@ 203,6 211,15 @@ public class RtpSessionActivity extends XmppActivity
                && STATES_SHOWING_SWITCH_TO_CHAT.contains(connection.getEndUserState());
    }

    private boolean isSwitchToVideoVisible() {
        final JingleRtpConnection connection =
                this.rtpConnectionReference != null ? this.rtpConnectionReference.get() : null;
        if (connection == null) {
            return false;
        }
        return Media.audioOnly(connection.getMedia()) && STATES_CONSIDERED_CONNECTED.contains(connection.getEndUserState());
    }

    private void switchToConversation() {
        final Contact contact = getWith();
        final Conversation conversation =


@@ 215,10 232,13 @@ public class RtpSessionActivity extends XmppActivity
        switch (item.getItemId()) {
            case R.id.action_help:
                launchHelpInBrowser();
                break;
                return true;
            case R.id.action_goto_chat:
                switchToConversation();
                break;
                return true;
            case R.id.action_switch_to_video:
                requestPermissionAndSwitchToVideo();
                return true;
        }
        return super.onOptionsItemSelected(item);
    }


@@ 272,9 292,60 @@ public class RtpSessionActivity extends XmppActivity
        requestPermissionsAndAcceptCall();
    }

    private void acceptContentAdd() {
        try {
            requireRtpConnection()
                    .acceptContentAdd(requireRtpConnection().getPendingContentAddition().summary);
        } catch (final IllegalStateException e) {
            Toast.makeText(this, e.getMessage(), Toast.LENGTH_SHORT).show();
        }
    }

    private void requestPermissionAndSwitchToVideo() {
        final List<String> permissions = permissions(ImmutableSet.of(Media.VIDEO, Media.AUDIO));
        if (PermissionUtils.hasPermission(this, permissions, REQUEST_ADD_CONTENT)) {
            switchToVideo();
        }
    }

    private void switchToVideo() {
        try {
            requireRtpConnection().addMedia(Media.VIDEO);
        } catch (final IllegalStateException e) {
            Toast.makeText(this, e.getMessage(), Toast.LENGTH_SHORT).show();
        }
    }

    private void acceptContentAdd(final ContentAddition contentAddition) {
        if (contentAddition == null || contentAddition.direction != ContentAddition.Direction.INCOMING) {
            Log.d(Config.LOGTAG,"ignore press on content-accept button");
            return;
        }
        requestPermissionAndAcceptContentAdd(contentAddition);
    }

    private void requestPermissionAndAcceptContentAdd(final ContentAddition contentAddition) {
        final List<String> permissions = permissions(contentAddition.media());
        if (PermissionUtils.hasPermission(this, permissions, REQUEST_ACCEPT_CONTENT)) {
            requireRtpConnection().acceptContentAdd(contentAddition.summary);
        }
    }

    private void rejectContentAdd(final View view) {
        requireRtpConnection().rejectContentAdd();
    }

    private void requestPermissionsAndAcceptCall() {
        final List<String> permissions = permissions(getMedia());
        if (PermissionUtils.hasPermission(this, permissions, REQUEST_ACCEPT_CALL)) {
            putScreenInCallMode();
            checkRecorderAndAcceptCall();
        }
    }

    private List<String> permissions(final Set<Media> media) {
        final ImmutableList.Builder<String> permissions = ImmutableList.builder();
        if (getMedia().contains(Media.VIDEO)) {
        if (media.contains(Media.VIDEO)) {
            permissions.add(Manifest.permission.CAMERA).add(Manifest.permission.RECORD_AUDIO);
        } else {
            permissions.add(Manifest.permission.RECORD_AUDIO);


@@ 282,10 353,7 @@ public class RtpSessionActivity extends XmppActivity
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
            permissions.add(Manifest.permission.BLUETOOTH_CONNECT);
        }
        if (PermissionUtils.hasPermission(this, permissions.build(), REQUEST_ACCEPT_CALL)) {
            putScreenInCallMode();
            checkRecorderAndAcceptCall();
        }
        return permissions.build();
    }

    private void checkRecorderAndAcceptCall() {


@@ 516,6 584,10 @@ public class RtpSessionActivity extends XmppActivity
        if (PermissionUtils.allGranted(permissionResult.grantResults)) {
            if (requestCode == REQUEST_ACCEPT_CALL) {
                checkRecorderAndAcceptCall();
            } else if (requestCode == REQUEST_ACCEPT_CONTENT) {
                acceptContentAdd();
            } else if (requestCode == REQUEST_ADD_CONTENT) {
                switchToVideo();
            }
        } else {
            @StringRes int res;


@@ 598,8 670,8 @@ public class RtpSessionActivity extends XmppActivity
    private boolean isConnected() {
        final JingleRtpConnection connection =
                this.rtpConnectionReference != null ? this.rtpConnectionReference.get() : null;
        return connection != null
                && STATES_CONSIDERED_CONNECTED.contains(connection.getEndUserState());
        final RtpEndUserState endUserState = connection == null ? null : connection.getEndUserState();
        return STATES_CONSIDERED_CONNECTED.contains(endUserState) || endUserState == RtpEndUserState.INCOMING_CONTENT_ADD;
    }

    private boolean switchToPictureInPicture() {


@@ 691,6 763,7 @@ public class RtpSessionActivity extends XmppActivity
            return true;
        }
        final Set<Media> media = getMedia();
        final ContentAddition contentAddition = getPendingContentAddition();
        if (currentState == RtpEndUserState.INCOMING_CALL) {
            getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
        }


@@ 700,9 773,9 @@ public class RtpSessionActivity extends XmppActivity
        }
        setWidth(currentState);
        updateVideoViews(currentState);
        updateStateDisplay(currentState, media);
        updateStateDisplay(currentState, media, contentAddition);
        updateVerifiedShield(verified && STATES_SHOWING_SWITCH_TO_CHAT.contains(currentState));
        updateButtonConfiguration(currentState, media);
        updateButtonConfiguration(currentState, media, contentAddition);
        updateIncomingCallScreen(currentState);
        invalidateOptionsMenu();
        return false;


@@ 753,10 826,10 @@ public class RtpSessionActivity extends XmppActivity
    }

    private void updateStateDisplay(final RtpEndUserState state) {
        updateStateDisplay(state, Collections.emptySet());
        updateStateDisplay(state, Collections.emptySet(), null);
    }

    private void updateStateDisplay(final RtpEndUserState state, final Set<Media> media) {
    private void updateStateDisplay(final RtpEndUserState state, final Set<Media> media, final ContentAddition contentAddition) {
        switch (state) {
            case INCOMING_CALL:
                Preconditions.checkArgument(media.size() > 0, "Media must not be empty");


@@ 766,6 839,13 @@ public class RtpSessionActivity extends XmppActivity
                    setTitle(R.string.rtp_state_incoming_call);
                }
                break;
            case INCOMING_CONTENT_ADD:
                if (contentAddition != null && contentAddition.media().contains(Media.VIDEO)) {
                    setTitle(R.string.rtp_state_content_add_video);
                } else {
                    setTitle(R.string.rtp_state_content_add);
                }
                break;
            case CONNECTING:
                setTitle(R.string.rtp_state_connecting);
                break;


@@ 857,12 937,16 @@ public class RtpSessionActivity extends XmppActivity
        return requireRtpConnection().getMedia();
    }

    public ContentAddition getPendingContentAddition() {
        return requireRtpConnection().getPendingContentAddition();
    }

    private void updateButtonConfiguration(final RtpEndUserState state) {
        updateButtonConfiguration(state, Collections.emptySet());
        updateButtonConfiguration(state, Collections.emptySet(), null);
    }

    @SuppressLint("RestrictedApi")
    private void updateButtonConfiguration(final RtpEndUserState state, final Set<Media> media) {
    private void updateButtonConfiguration(final RtpEndUserState state, final Set<Media> media, final ContentAddition contentAddition) {
        if (state == RtpEndUserState.ENDING_CALL || isPictureInPicture()) {
            this.binding.rejectCall.setVisibility(View.INVISIBLE);
            this.binding.endCall.setVisibility(View.INVISIBLE);


@@ 877,6 961,16 @@ public class RtpSessionActivity extends XmppActivity
            this.binding.acceptCall.setOnClickListener(this::acceptCall);
            this.binding.acceptCall.setImageResource(R.drawable.ic_call_white_48dp);
            this.binding.acceptCall.setVisibility(View.VISIBLE);
        } else if (state == RtpEndUserState.INCOMING_CONTENT_ADD) {
            this.binding.rejectCall.setContentDescription(getString(R.string.reject_switch_to_video));
            this.binding.rejectCall.setOnClickListener(this::rejectContentAdd);
            this.binding.rejectCall.setImageResource(R.drawable.ic_clear_white_48dp);
            this.binding.rejectCall.setVisibility(View.VISIBLE);
            this.binding.endCall.setVisibility(View.INVISIBLE);
            this.binding.acceptCall.setContentDescription(getString(R.string.accept));
            this.binding.acceptCall.setOnClickListener((v -> acceptContentAdd(contentAddition)));
            this.binding.acceptCall.setImageResource(R.drawable.ic_baseline_check_24);
            this.binding.acceptCall.setVisibility(View.VISIBLE);
        } else if (state == RtpEndUserState.DECLINED_OR_BUSY) {
            this.binding.rejectCall.setContentDescription(getString(R.string.exit));
            this.binding.rejectCall.setOnClickListener(this::exit);


@@ 1051,6 1145,12 @@ public class RtpSessionActivity extends XmppActivity
    }

    private void disableVideo(View view) {
        final JingleRtpConnection rtpConnection = requireRtpConnection();
        final ContentAddition pending = rtpConnection.getPendingContentAddition();
        if (pending != null && pending.direction == ContentAddition.Direction.OUTGOING) {
            rtpConnection.retractContentAdd();
            return;
        }
        requireRtpConnection().setVideoEnabled(false);
        updateInCallButtonConfigurationVideo(false, requireRtpConnection().isCameraSwitchable());
    }


@@ 1279,6 1379,7 @@ public class RtpSessionActivity extends XmppActivity
        final AbstractJingleConnection.Id id = requireRtpConnection().getId();
        final boolean verified = requireRtpConnection().isVerified();
        final Set<Media> media = getMedia();
        final ContentAddition contentAddition = getPendingContentAddition();
        final Contact contact = getWith();
        if (account == id.account && id.with.equals(with) && id.sessionId.equals(sessionId)) {
            if (state == RtpEndUserState.ENDED) {


@@ 1287,10 1388,10 @@ public class RtpSessionActivity extends XmppActivity
            }
            runOnUiThread(
                    () -> {
                        updateStateDisplay(state, media);
                        updateStateDisplay(state, media, contentAddition);
                        updateVerifiedShield(
                                verified && STATES_SHOWING_SWITCH_TO_CHAT.contains(state));
                        updateButtonConfiguration(state, media);
                        updateButtonConfiguration(state, media, contentAddition);
                        updateVideoViews(state);
                        updateIncomingCallScreen(state, contact);
                        invalidateOptionsMenu();

A src/main/res/drawable/ic_baseline_check_24.xml => src/main/res/drawable/ic_baseline_check_24.xml +5 -0
@@ 0,0 1,5 @@
<vector android:height="24dp" android:tint="#FFFFFF"
    android:viewportHeight="24" android:viewportWidth="24"
    android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
    <path android:fillColor="@android:color/white" android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z"/>
</vector>

M src/main/res/menu/activity_rtp_session.xml => src/main/res/menu/activity_rtp_session.xml +3 -0
@@ 13,4 13,7 @@
        android:icon="?attr/icon_goto_chat"
        android:title="@string/switch_to_conversation"
        app:showAsAction="always" />
    <item android:id="@+id/action_switch_to_video"
        android:title="@string/switch_to_video"
        app:showAsAction="never"/>
</menu>
\ No newline at end of file

M src/main/res/values/strings.xml => src/main/res/values/strings.xml +4 -0
@@ 909,6 909,8 @@
    <string name="make_call">Make call</string>
    <string name="rtp_state_incoming_call">Incoming call</string>
    <string name="rtp_state_incoming_video_call">Incoming video call</string>
    <string name="rtp_state_content_add_video">Switch to video call?</string>
    <string name="rtp_state_content_add">Add additional tracks?</string>
    <string name="rtp_state_connecting">Connecting</string>
    <string name="rtp_state_connected">Connected</string>
    <string name="rtp_state_reconnecting">Reconnecting</string>


@@ 995,5 997,7 @@
    <string name="account_status_temporary_auth_failure">Temporary authentication failure</string>
    <string name="delete_avatar">Delete avatar</string>
    <string name="audio_video_disabled_tor">Calls are disabled when using Tor</string>
    <string name="switch_to_video">Switch to video</string>
    <string name="reject_switch_to_video">Reject switch to video request</string>

</resources>