From 14348a2a2c573079e4369ed91cb9993482caa3b6 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 | 9 +
src/main/res/values/strings.xml | 1 +
src/main/res/values/styles.xml | 13 +
11 files changed, 593 insertions(+), 6 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 ad016a3b35602bd7427b6eccdf5d35ba8e07cf88..14beef2cb76da5946b80a6eafe53effabcdb5487 100644
--- a/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java
@@ -35,6 +35,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;
@@ -138,6 +139,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
@@ -145,8 +175,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);
}
@@ -181,12 +213,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:
@@ -195,6 +244,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);
}
@@ -1159,6 +1211,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 67c32d72aa425be24e58cb1879ed86c001011bd7..231e631f0b5bdcc4ef91df77985e0a84a41d1553 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;
@@ -229,6 +230,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 4fb9dee1697a12c416bd5caf77f130e44e46a89c..e50f67e2cb5cc731abcc443fb40075b6fda4d8b9 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 7b9caa66cf9ea532165be5e9c3c219e6581e25f1..b561ee1af14fde5a7ae9031241a498d425f1dc7e 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;
@@ -26,6 +28,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;
@@ -46,6 +49,7 @@ import org.webrtc.voiceengine.WebRtcAudioEffects;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import javax.annotation.Nonnull;
@@ -75,6 +79,25 @@ public class WebRTCWrapper {
.add("MI 5")
.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;
@@ -494,6 +517,20 @@ public class WebRTCWrapper {
}
}
+ //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 26fa4d496b95e85ce3278d3bb20182ac3c10de20..5c32dd94c01c2f7099b373b388331fef589e4b56 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..4bcfddaa148ee55c5dacec0ae83103b447e10f64 100644
--- a/src/main/res/values/dimens.xml
+++ b/src/main/res/values/dimens.xml
@@ -44,4 +44,13 @@
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 9f3ba84acd886ed014e8832def43e93a99031982..d8dba55886ee9f22bc183a4a88d4cc5dbfd5d849 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..7f70736025917022f7bc80e4daf330b82ca5b75e 100644
--- a/src/main/res/values/styles.xml
+++ b/src/main/res/values/styles.xml
@@ -159,4 +159,17 @@
+
+
+
+
+
+
+
\ No newline at end of file