From 934b98d1990e5f70da1fd0cda04888db713849df Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sat, 18 Apr 2020 17:51:21 +0200 Subject: [PATCH] add microphone availability check --- .../services/AppRTCAudioManager.java | 37 +++++++++++++++++++ .../ui/ConversationFragment.java | 17 ++++++--- .../conversations/ui/RtpSessionActivity.java | 15 +++++++- .../xmpp/jingle/JingleRtpConnection.java | 1 + src/main/res/values/strings.xml | 1 + 5 files changed, 64 insertions(+), 7 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/services/AppRTCAudioManager.java b/src/main/java/eu/siacs/conversations/services/AppRTCAudioManager.java index db1b8e1e1..07d52891b 100644 --- a/src/main/java/eu/siacs/conversations/services/AppRTCAudioManager.java +++ b/src/main/java/eu/siacs/conversations/services/AppRTCAudioManager.java @@ -15,7 +15,10 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; import android.media.AudioDeviceInfo; +import android.media.AudioFormat; import android.media.AudioManager; +import android.media.AudioRecord; +import android.media.MediaRecorder; import android.os.Build; import android.support.annotation.Nullable; import android.util.Log; @@ -111,6 +114,40 @@ public class AppRTCAudioManager { return new AppRTCAudioManager(context, speakerPhonePreference); } + public static boolean isMicrophoneAvailable(final Context context) { + AudioRecord audioRecord = null; + boolean available = true; + try { + final int sampleRate = 44100; + final int channel = AudioFormat.CHANNEL_IN_MONO; + final int format = AudioFormat.ENCODING_PCM_16BIT; + final int bufferSize = AudioRecord.getMinBufferSize(sampleRate, channel, format); + audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, sampleRate, channel, format, bufferSize); + audioRecord.startRecording(); + final short[] buffer = new short[bufferSize]; + final int audioStatus = audioRecord.read(buffer, 0, bufferSize); + if (audioStatus == AudioRecord.ERROR_INVALID_OPERATION || audioStatus == AudioRecord.STATE_UNINITIALIZED) + available = false; + } catch (Exception e) { + available = false; + } finally { + release(audioRecord); + + } + return available; + } + + private static void release(final AudioRecord audioRecord) { + if (audioRecord == null) { + return; + } + try { + audioRecord.release(); + } catch (Exception e) { + //ignore + } + } + /** * This method is called when the proximity sensor reports a state change, * e.g. from "NEAR to FAR" or from "FAR to NEAR". diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java index 03c90320f..8d1a0f1ab 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java +++ b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java @@ -82,6 +82,7 @@ import eu.siacs.conversations.entities.Transferable; import eu.siacs.conversations.entities.TransferablePlaceholder; import eu.siacs.conversations.http.HttpDownloadConnection; import eu.siacs.conversations.persistance.FileBackend; +import eu.siacs.conversations.services.AppRTCAudioManager; import eu.siacs.conversations.services.MessageArchiveService; import eu.siacs.conversations.services.QuickConversationsService; import eu.siacs.conversations.services.XmppConnectionService; @@ -1272,12 +1273,16 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke private void triggerRtpSession(final String action) { - final Contact contact = conversation.getContact(); - final Intent intent = new Intent(activity, RtpSessionActivity.class); - intent.setAction(action); - intent.putExtra(RtpSessionActivity.EXTRA_ACCOUNT, contact.getAccount().getJid().toEscapedString()); - intent.putExtra(RtpSessionActivity.EXTRA_WITH, contact.getJid().asBareJid().toEscapedString()); - startActivity(intent); + if (AppRTCAudioManager.isMicrophoneAvailable(getActivity())) { + final Contact contact = conversation.getContact(); + final Intent intent = new Intent(activity, RtpSessionActivity.class); + intent.setAction(action); + intent.putExtra(RtpSessionActivity.EXTRA_ACCOUNT, contact.getAccount().getJid().toEscapedString()); + intent.putExtra(RtpSessionActivity.EXTRA_WITH, contact.getJid().asBareJid().toEscapedString()); + startActivity(intent); + } else { + Toast.makeText(getActivity(), R.string.microphone_unavailable, Toast.LENGTH_SHORT).show(); + } } private void handleAttachmentSelection(MenuItem item) { diff --git a/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java b/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java index 7a3545166..7b6c1fefe 100644 --- a/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java @@ -9,6 +9,7 @@ import android.databinding.DataBindingUtil; import android.os.Build; import android.os.Bundle; import android.os.PowerManager; +import android.os.SystemClock; import android.support.annotation.NonNull; import android.support.annotation.StringRes; import android.util.Log; @@ -126,7 +127,19 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe } if (PermissionUtils.hasPermission(this, permissions, REQUEST_ACCEPT_CALL)) { putScreenInCallMode(); + checkRecorderAndAcceptCall(); + } + } + + private void checkRecorderAndAcceptCall() { + final long start = SystemClock.elapsedRealtime(); + final boolean isMicrophoneAvailable = AppRTCAudioManager.isMicrophoneAvailable(this); + final long stop = SystemClock.elapsedRealtime(); + Log.d(Config.LOGTAG, "checking microphone availability took " + (stop - start) + "ms"); + if (isMicrophoneAvailable) { requireRtpConnection().acceptCall(); + } else { + Toast.makeText(this, R.string.microphone_unavailable, Toast.LENGTH_SHORT).show(); } } @@ -247,7 +260,7 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (PermissionUtils.allGranted(grantResults)) { if (requestCode == REQUEST_ACCEPT_CALL) { - requireRtpConnection().acceptCall(); + checkRecorderAndAcceptCall(); } } else { @StringRes int res; 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 f47eb8f4f..3067f18f0 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java @@ -950,6 +950,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": not sending session-terminate after connectivity error because session is already in state " + this.state); return; } + //we need to call close sendSessionTerminate(Reason.CONNECTIVITY_ERROR); } else { updateEndUserState(); diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 1ac409fdc..9b05af9e3 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -914,6 +914,7 @@ Missed call Audio call Video call + Microphone unavailable View %1$d Participant View %1$d Participants