From 358271b767b4c594833541b87a9ad66352528b12 Mon Sep 17 00:00:00 2001 From: Ketroc <21rocket@gmail.com> Date: Sat, 3 Jul 2021 11:20:38 -0400 Subject: [PATCH] WIP - dialpad and dtmf sending --- .../conversations/ui/RtpSessionActivity.java | 60 +++ .../conversations/ui/widget/DialpadView.java | 73 ++++ .../xmpp/jingle/JingleRtpConnection.java | 12 + .../xmpp/jingle/ToneManager.java | 2 +- .../xmpp/jingle/WebRTCWrapper.java | 37 ++ src/main/res/layout/activity_rtp_session.xml | 18 +- src/main/res/layout/dialpad.xml | 368 ++++++++++++++++++ src/main/res/menu/activity_rtp_session.xml | 6 + src/main/res/values/dimens.xml | 7 + src/main/res/values/strings.xml | 1 + src/main/res/values/styles.xml | 12 +- 11 files changed, 589 insertions(+), 7 deletions(-) create mode 100644 src/main/java/eu/siacs/conversations/ui/widget/DialpadView.java create mode 100644 src/main/res/layout/dialpad.xml diff --git a/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java b/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java index 65beae35dbfc0c658d835db41e0f507b3deefa14..2f08eff86c966e494bbc5d578ba5101688989474 100644 --- a/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java @@ -38,6 +38,7 @@ import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import org.checkerframework.checker.nullness.compatqual.NullableDecl; +import org.jetbrains.annotations.NotNull; import org.webrtc.RendererCommon; import org.webrtc.SurfaceViewRenderer; import org.webrtc.VideoTrack; @@ -149,6 +150,35 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON); this.binding = DataBindingUtil.setContentView(this, R.layout.activity_rtp_session); setSupportActionBar(binding.toolbar); + + //TODO: remove this - for testing dialpad input + //((DialpadView)findViewById(R.id.action_dialpad)). + + findViewById(R.id.dialpad_1_holder).setOnClickListener(view -> dialpadPressed(view)); + findViewById(R.id.dialpad_2_holder).setOnClickListener(view -> dialpadPressed(view)); + findViewById(R.id.dialpad_3_holder).setOnClickListener(view -> dialpadPressed(view)); + findViewById(R.id.dialpad_4_holder).setOnClickListener(view -> dialpadPressed(view)); + findViewById(R.id.dialpad_5_holder).setOnClickListener(view -> dialpadPressed(view)); + findViewById(R.id.dialpad_6_holder).setOnClickListener(view -> dialpadPressed(view)); + findViewById(R.id.dialpad_7_holder).setOnClickListener(view -> dialpadPressed(view)); + findViewById(R.id.dialpad_8_holder).setOnClickListener(view -> dialpadPressed(view)); + findViewById(R.id.dialpad_9_holder).setOnClickListener(view -> dialpadPressed(view)); + findViewById(R.id.dialpad_0_holder).setOnClickListener(view -> dialpadPressed(view)); + findViewById(R.id.dialpad_asterisk_holder).setOnClickListener(view -> dialpadPressed(view)); + findViewById(R.id.dialpad_pound_holder).setOnClickListener(view -> dialpadPressed(view)); + + if (savedInstanceState != null) { + int dialpad_visibility = savedInstanceState.getInt("dialpad_visibility"); + System.out.println("dialpad_visibility onCreate = " + dialpad_visibility); + findViewById(R.id.dialpad).setVisibility(dialpad_visibility); + } + } + + + + private void dialpadPressed(View dialpadKeyHolderView) { + JingleRtpConnection rtpConnection = requireRtpConnection(); + rtpConnection.applyDtmfTone(dialpadKeyHolderView.getTag().toString()); } @Override @@ -156,8 +186,10 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe 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 dialpad = menu.findItem(R.id.action_dialpad); help.setVisible(isHelpButtonVisible()); gotoChat.setVisible(isSwitchToConversationVisible()); + dialpad.setVisible(isAudioOnlyConversation()); return super.onCreateOptionsMenu(menu); } @@ -192,12 +224,29 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe return connection != null && STATES_SHOWING_SWITCH_TO_CHAT.contains(connection.getEndUserState()); } + private boolean isAudioOnlyConversation() { + final JingleRtpConnection connection = + this.rtpConnectionReference != null ? this.rtpConnectionReference.get() : null; + return connection != null && + connection.getEndUserState() == RtpEndUserState.CONNECTED && + !connection.isVideoEnabled(); + } + private void switchToConversation() { final Contact contact = getWith(); final Conversation conversation = xmppConnectionService.findOrCreateConversation(contact.getAccount(), contact.getJid(), false, true); switchToConversation(conversation); } + private void toggleDialpadVisibility() { + if (binding.dialpad.getVisibility() == View.VISIBLE) { + binding.dialpad.setVisibility(View.GONE); + } + else { + binding.dialpad.setVisibility(View.VISIBLE); + } + } + public boolean onOptionsItemSelected(final MenuItem item) { switch (item.getItemId()) { case R.id.action_help: @@ -206,6 +255,9 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe case R.id.action_goto_chat: switchToConversation(); break; + case R.id.action_dialpad: + toggleDialpadVisibility(); + break; } return super.onOptionsItemSelected(item); } @@ -1187,6 +1239,14 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe } } + @Override + protected void onSaveInstanceState(@NonNull @NotNull Bundle outState) { + super.onSaveInstanceState(outState); + int visibility = findViewById(R.id.action_dialpad).getVisibility(); + System.out.println("visibility onSave = " + visibility); + outState.putInt("dialpad_visibility", visibility); + } + private void updateRtpSessionProposalState(final Account account, final Jid with, final RtpEndUserState state) { final Intent currentIntent = getIntent(); final String withExtra = currentIntent == null ? null : currentIntent.getStringExtra(EXTRA_WITH); diff --git a/src/main/java/eu/siacs/conversations/ui/widget/DialpadView.java b/src/main/java/eu/siacs/conversations/ui/widget/DialpadView.java new file mode 100644 index 0000000000000000000000000000000000000000..3bd13541d54c774c3b0a29e8b41a6e5267cf737e --- /dev/null +++ b/src/main/java/eu/siacs/conversations/ui/widget/DialpadView.java @@ -0,0 +1,73 @@ +/* + * Copyright 2012-2015 the original author or authors. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package eu.siacs.conversations.ui.widget; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; + +import androidx.constraintlayout.widget.ConstraintLayout; +import eu.siacs.conversations.R; +import eu.siacs.conversations.xmpp.jingle.JingleRtpConnection; + +public class DialpadView extends ConstraintLayout implements View.OnClickListener { + + public DialpadView(Context context) { + super(context); + init(); + } + + public DialpadView(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + public DialpadView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(); + } + + private void init() { + inflate(getContext(), R.layout.dialpad, this); + initViews(); + } + + private void initViews() { + findViewById(R.id.dialpad_1_holder).setOnClickListener(this); + findViewById(R.id.dialpad_2_holder).setOnClickListener(this); + findViewById(R.id.dialpad_3_holder).setOnClickListener(this); + findViewById(R.id.dialpad_4_holder).setOnClickListener(this); + findViewById(R.id.dialpad_5_holder).setOnClickListener(this); + findViewById(R.id.dialpad_6_holder).setOnClickListener(this); + findViewById(R.id.dialpad_7_holder).setOnClickListener(this); + findViewById(R.id.dialpad_8_holder).setOnClickListener(this); + findViewById(R.id.dialpad_9_holder).setOnClickListener(this); + findViewById(R.id.dialpad_0_holder).setOnClickListener(this); + findViewById(R.id.dialpad_asterisk_holder).setOnClickListener(this); + findViewById(R.id.dialpad_pound_holder).setOnClickListener(this); + } + + @Override + public void onClick(View v) { + /* TODO: this widget doesn't know anything about the RTP Connection, + so how to make this widget generic but also able to send touch-tone sounds + */ + System.out.println("v.getTag() = " + v.getTag()); + } + +} diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java index 9ed4d188f5eba3c8c88d9723c07fa62c11393d05..f221d6fadd464de0b8b07a6792945e23e35481c6 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java @@ -20,6 +20,7 @@ import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; +import org.webrtc.DtmfSender; import org.webrtc.EglBase; import org.webrtc.IceCandidate; import org.webrtc.PeerConnection; @@ -231,6 +232,17 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web } } + //TODO: remove - hack to test dtmfSending + public DtmfSender getDtmfSender() { + return webRTCWrapper.getDtmfSender(); + } + + //FIXME: possible implementation + public boolean applyDtmfTone(String tone) { + return webRTCWrapper.applyDtmfTone(tone); + } + + private void receiveSessionTerminate(final JinglePacket jinglePacket) { respondOk(jinglePacket); final JinglePacket.ReasonWrapper wrapper = jinglePacket.getReason(); diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/ToneManager.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/ToneManager.java index e368d3b09ef0e7a6d4f7ca8761121c755db69402..5c49b34e1934f99a60e9913a64139297e9503c30 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/ToneManager.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/ToneManager.java @@ -148,7 +148,7 @@ class ToneManager { } } - private void startTone(final int toneType, final int durationMs) { + public void startTone(final int toneType, final int durationMs) { if (toneGenerator != null) { this.toneGenerator.startTone(toneType, durationMs); } else { diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/WebRTCWrapper.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/WebRTCWrapper.java index 0b990db43e533e21197b1b12a4b87b2aa4a889ac..ae001ac610406488dc0388dd0d6fb1ab8d4b7863 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/WebRTCWrapper.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/WebRTCWrapper.java @@ -1,6 +1,7 @@ package eu.siacs.conversations.xmpp.jingle; import android.content.Context; +import android.media.ToneGenerator; import android.os.Build; import android.os.Handler; import android.os.Looper; @@ -8,6 +9,7 @@ import android.util.Log; import com.google.common.base.Optional; import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.util.concurrent.Futures; @@ -25,6 +27,7 @@ import org.webrtc.CandidatePairChangeEvent; import org.webrtc.DataChannel; import org.webrtc.DefaultVideoDecoderFactory; import org.webrtc.DefaultVideoEncoderFactory; +import org.webrtc.DtmfSender; import org.webrtc.EglBase; import org.webrtc.IceCandidate; import org.webrtc.MediaConstraints; @@ -47,6 +50,7 @@ import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Queue; +import java.util.Map; import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -83,6 +87,25 @@ public class WebRTCWrapper { .add("GT-I9505") // Samsung Galaxy S4 (jfltexx) .build(); + private static final int TONE_DURATION = 200; + private static final Map TONE_CODES; + static { + ImmutableMap.Builder builder = new ImmutableMap.Builder<>(); + builder.put("0", ToneGenerator.TONE_DTMF_0); + builder.put("1", ToneGenerator.TONE_DTMF_1); + builder.put("2", ToneGenerator.TONE_DTMF_2); + builder.put("3", ToneGenerator.TONE_DTMF_3); + builder.put("4", ToneGenerator.TONE_DTMF_4); + builder.put("5", ToneGenerator.TONE_DTMF_5); + builder.put("6", ToneGenerator.TONE_DTMF_6); + builder.put("7", ToneGenerator.TONE_DTMF_7); + builder.put("8", ToneGenerator.TONE_DTMF_8); + builder.put("9", ToneGenerator.TONE_DTMF_9); + builder.put("*", ToneGenerator.TONE_DTMF_S); + builder.put("#", ToneGenerator.TONE_DTMF_P); + TONE_CODES = builder.build(); + } + private static final int CAPTURING_RESOLUTION = 1920; private static final int CAPTURING_MAX_FRAME_RATE = 30; @@ -507,6 +530,20 @@ public class WebRTCWrapper { return peerConnection; } + //TODO: remove - hack to test dtmfSending + public DtmfSender getDtmfSender() { + return peerConnection.getSenders().get(0).dtmf(); + } + + public boolean applyDtmfTone(String tone) { + if (toneManager == null || peerConnection.getSenders().isEmpty()) { + return false; + } + peerConnection.getSenders().get(0).dtmf().insertDtmf(tone, TONE_DURATION, 100); + toneManager.startTone(TONE_CODES.get(tone), TONE_DURATION); + return true; + } + void addIceCandidate(IceCandidate iceCandidate) { requirePeerConnection().addIceCandidate(iceCandidate); } diff --git a/src/main/res/layout/activity_rtp_session.xml b/src/main/res/layout/activity_rtp_session.xml index 0bdca47760059988ea8b33915154d7466e20fb39..fc37e7df8e37967179cf251d1c0a7df4fd85375a 100644 --- a/src/main/res/layout/activity_rtp_session.xml +++ b/src/main/res/layout/activity_rtp_session.xml @@ -77,6 +77,14 @@ android:textAppearance="@style/TextAppearance.Conversations.Title.Monospace" tools:text="01:23" /> + + + tools:visibility="gone" /> + tools:visibility="gone" /> @@ -189,7 +197,7 @@ android:layout_centerVertical="true" android:layout_margin="@dimen/in_call_fab_margin" android:layout_toStartOf="@+id/end_call" - android:visibility="gone" + android:visibility="visible" app:backgroundTint="?color_background_primary" app:elevation="4dp" app:fabSize="mini" @@ -215,7 +223,7 @@ android:layout_centerVertical="true" android:layout_margin="@dimen/in_call_fab_margin" android:layout_toEndOf="@+id/end_call" - android:visibility="gone" + android:visibility="visible" app:backgroundTint="?color_background_primary" app:elevation="4dp" app:fabSize="mini" @@ -228,7 +236,7 @@ android:layout_centerVertical="true" android:layout_margin="@dimen/in_call_fab_margin" android:layout_toEndOf="@+id/in_call_action_right" - android:visibility="gone" + android:visibility="visible" app:backgroundTint="?color_background_primary" app:elevation="4dp" app:fabSize="mini" diff --git a/src/main/res/layout/dialpad.xml b/src/main/res/layout/dialpad.xml new file mode 100644 index 0000000000000000000000000000000000000000..2a2208c945ed3b3febe4b4ae03e8dd743d990c12 --- /dev/null +++ b/src/main/res/layout/dialpad.xml @@ -0,0 +1,368 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/res/menu/activity_rtp_session.xml b/src/main/res/menu/activity_rtp_session.xml index 04756490ab87c062ab6cd44a434920b05eda16c6..cbe68daaf34a174fa764158078a2d3fb8362df97 100644 --- a/src/main/res/menu/activity_rtp_session.xml +++ b/src/main/res/menu/activity_rtp_session.xml @@ -12,5 +12,11 @@ android:id="@+id/action_goto_chat" android:icon="?attr/icon_goto_chat" android:title="@string/switch_to_conversation" + app:showAsAction="ifRoom" /> + + \ No newline at end of file diff --git a/src/main/res/values/dimens.xml b/src/main/res/values/dimens.xml index baa9d4ea902bb97ceb5e50d31bd16b7ec07186f0..16a8cef1ac844d79e10e7aab61292cc6f4f4a8d6 100644 --- a/src/main/res/values/dimens.xml +++ b/src/main/res/values/dimens.xml @@ -44,4 +44,11 @@ 128dp 96dp 24dp + + 30sp + 12sp + 8dp + 16dp + 4dp + 20sp diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 8b5e67eb22ee82c50e17cadd645f916f404389c1..5ad945e6f25d8b903419df06af6b596c02256e5d 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -1,6 +1,7 @@ Settings + Dialpad New conversation Manage accounts Manage account diff --git a/src/main/res/values/styles.xml b/src/main/res/values/styles.xml index b6b8b76b0cb71caedcd947635e8e3ee4bd4b766c..05b2841147610db013835c9a7e1b46a89f11e4bb 100644 --- a/src/main/res/values/styles.xml +++ b/src/main/res/values/styles.xml @@ -159,4 +159,14 @@ - \ No newline at end of file + + + + +