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
+
+
+
+
+