From 13a000dfa24d5fb8141d20f6d8584e1bd49184db Mon Sep 17 00:00:00 2001 From: sh123 Date: Wed, 6 Oct 2021 21:11:59 +0300 Subject: [PATCH 01/23] Added voicemail recording basic scaffolding --- .../radio/codec2talkie/AudioProcessor.java | 5 +- .../com/radio/codec2talkie/MainActivity.java | 8 +++- .../protocol/ProtocolFactory.java | 23 ++++++--- .../codec2talkie/protocol/VoicemailProxy.java | 48 +++++++++++++++++++ .../codec2talkie/settings/PreferenceKeys.java | 1 + codec2talkie/src/main/res/values/strings.xml | 2 + codec2talkie/src/main/res/xml/preferences.xml | 7 +++ 7 files changed, 85 insertions(+), 9 deletions(-) create mode 100644 codec2talkie/src/main/java/com/radio/codec2talkie/protocol/VoicemailProxy.java diff --git a/codec2talkie/src/main/java/com/radio/codec2talkie/AudioProcessor.java b/codec2talkie/src/main/java/com/radio/codec2talkie/AudioProcessor.java index 6fa43c2..c7e5fc7 100644 --- a/codec2talkie/src/main/java/com/radio/codec2talkie/AudioProcessor.java +++ b/codec2talkie/src/main/java/com/radio/codec2talkie/AudioProcessor.java @@ -86,13 +86,14 @@ public class AudioProcessor extends Thread { private final Context _context; public AudioProcessor(TransportFactory.TransportType transportType, ProtocolFactory.ProtocolType protocolType, - int codec2Mode, Handler onPlayerStateChanged, Context context) throws IOException { + boolean voicemailEnabled, int codec2Mode, + Handler onPlayerStateChanged, Context context) throws IOException { _onPlayerStateChanged = onPlayerStateChanged; _context = context; _transport = TransportFactory.create(transportType); - _protocol = ProtocolFactory.create(protocolType); + _protocol = ProtocolFactory.create(protocolType, voicemailEnabled); _processPeriodicTimer = new Timer(); diff --git a/codec2talkie/src/main/java/com/radio/codec2talkie/MainActivity.java b/codec2talkie/src/main/java/com/radio/codec2talkie/MainActivity.java index 5223dad..30ce216 100644 --- a/codec2talkie/src/main/java/com/radio/codec2talkie/MainActivity.java +++ b/codec2talkie/src/main/java/com/radio/codec2talkie/MainActivity.java @@ -427,9 +427,15 @@ public class MainActivity extends AppCompatActivity { } speedModeInfo += ", " + protocolType.toString(); + + boolean voicemailEnabled = _sharedPreferences.getBoolean(PreferenceKeys.CODEC2_VOICEMAIL, false); + + if (voicemailEnabled) { + speedModeInfo += ", VM"; + } _textCodecMode.setText(speedModeInfo); - _audioProcessor = new AudioProcessor(transportType, protocolType, codec2ModeId, onAudioProcessorStateChanged, getApplicationContext()); + _audioProcessor = new AudioProcessor(transportType, protocolType, voicemailEnabled, codec2ModeId, onAudioProcessorStateChanged, getApplicationContext()); _audioProcessor.start(); } catch (IOException e) { e.printStackTrace(); diff --git a/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/ProtocolFactory.java b/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/ProtocolFactory.java index 2b85950..b96befd 100644 --- a/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/ProtocolFactory.java +++ b/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/ProtocolFactory.java @@ -8,7 +8,7 @@ public class ProtocolFactory { KISS_BUFFERED("KISS BUFFERED"), KISS_PARROT("KISS PARROT"); - private String _name; + private final String _name; ProtocolType(String name) { _name = name; @@ -20,17 +20,28 @@ public class ProtocolFactory { } }; - public static Protocol create(ProtocolType protocolType) { + public static Protocol create(ProtocolType protocolType, boolean voicemailEnabled) { + Protocol proto; switch (protocolType) { case KISS: - return new Kiss(); + proto = new Kiss(); + break; case KISS_BUFFERED: - return new KissBuffered(); + proto = new KissBuffered(); + break; case KISS_PARROT: - return new KissParrot(); + proto = new KissParrot(); + break; case RAW: default: - return new Raw(); + proto = new Raw(); + break; } + + if (voicemailEnabled) { + proto = new VoicemailProxy(proto); + } + + return proto; } } diff --git a/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/VoicemailProxy.java b/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/VoicemailProxy.java new file mode 100644 index 0000000..ad37b7c --- /dev/null +++ b/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/VoicemailProxy.java @@ -0,0 +1,48 @@ +package com.radio.codec2talkie.protocol; + +import android.content.Context; + +import com.radio.codec2talkie.transport.Transport; + +import java.io.IOException; + +public class VoicemailProxy implements Protocol { + + Protocol _protocol; + + public VoicemailProxy(Protocol protocol) { + _protocol = protocol; + } + + @Override + public void initialize(Transport transport, Context context) throws IOException { + _protocol.initialize(transport, context); + } + + @Override + public void send(byte[] frame) throws IOException { + _protocol.send(frame); + // write to file + } + + @Override + public boolean receive(Callback callback) throws IOException { + return _protocol.receive(new Callback() { + @Override + protected void onReceiveAudioFrames(byte[] audioFrames) { + callback.onReceiveAudioFrames(audioFrames); + // write to file + } + + @Override + protected void onReceiveSignalLevel(byte[] rawData) { + callback.onReceiveSignalLevel(rawData); + } + }); + } + + @Override + public void flush() throws IOException { + _protocol.flush(); + } +} diff --git a/codec2talkie/src/main/java/com/radio/codec2talkie/settings/PreferenceKeys.java b/codec2talkie/src/main/java/com/radio/codec2talkie/settings/PreferenceKeys.java index d50d487..85d63a2 100644 --- a/codec2talkie/src/main/java/com/radio/codec2talkie/settings/PreferenceKeys.java +++ b/codec2talkie/src/main/java/com/radio/codec2talkie/settings/PreferenceKeys.java @@ -6,6 +6,7 @@ public final class PreferenceKeys { public static String CODEC2_MODE = "codec2_mode"; public static String CODEC2_TEST_MODE = "codec2_test_mode"; + public static String CODEC2_VOICEMAIL = "codec2_voicemail"; public static String KISS_ENABLED = "kiss_enable"; public static String KISS_BUFFERED_ENABLED = "kiss_buffered_enable"; diff --git a/codec2talkie/src/main/res/values/strings.xml b/codec2talkie/src/main/res/values/strings.xml index 3766b3d..3715500 100644 --- a/codec2talkie/src/main/res/values/strings.xml +++ b/codec2talkie/src/main/res/values/strings.xml @@ -26,6 +26,8 @@ Mode/Speed Record/Playback Test Mode Enables own voice recording/playback without transmission + Enable Voicemail + Voicemail style transmission and receive recording MODE_450=10 MODE_700C=8 diff --git a/codec2talkie/src/main/res/xml/preferences.xml b/codec2talkie/src/main/res/xml/preferences.xml index 6d0e7fc..5c33183 100644 --- a/codec2talkie/src/main/res/xml/preferences.xml +++ b/codec2talkie/src/main/res/xml/preferences.xml @@ -40,6 +40,13 @@ app:defaultValue="false"> + + + Date: Wed, 6 Oct 2021 21:40:49 +0300 Subject: [PATCH 02/23] Add context for file api access --- .../java/com/radio/codec2talkie/protocol/VoicemailProxy.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/VoicemailProxy.java b/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/VoicemailProxy.java index ad37b7c..06af174 100644 --- a/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/VoicemailProxy.java +++ b/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/VoicemailProxy.java @@ -9,6 +9,7 @@ import java.io.IOException; public class VoicemailProxy implements Protocol { Protocol _protocol; + Context _context; public VoicemailProxy(Protocol protocol) { _protocol = protocol; @@ -16,6 +17,7 @@ public class VoicemailProxy implements Protocol { @Override public void initialize(Transport transport, Context context) throws IOException { + _context = context; _protocol.initialize(transport, context); } From 9ece300471940b9a2aa2c85eeafdb40b3f05c4ad Mon Sep 17 00:00:00 2001 From: sh123 Date: Thu, 7 Oct 2021 12:49:32 +0300 Subject: [PATCH 03/23] Pass codec2 mode id to Voicemail protocol so that it could be included on recording --- .../main/java/com/radio/codec2talkie/AudioProcessor.java | 2 +- .../com/radio/codec2talkie/protocol/ProtocolFactory.java | 4 ++-- .../com/radio/codec2talkie/protocol/VoicemailProxy.java | 7 +++++-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/codec2talkie/src/main/java/com/radio/codec2talkie/AudioProcessor.java b/codec2talkie/src/main/java/com/radio/codec2talkie/AudioProcessor.java index c7e5fc7..71d0659 100644 --- a/codec2talkie/src/main/java/com/radio/codec2talkie/AudioProcessor.java +++ b/codec2talkie/src/main/java/com/radio/codec2talkie/AudioProcessor.java @@ -93,7 +93,7 @@ public class AudioProcessor extends Thread { _context = context; _transport = TransportFactory.create(transportType); - _protocol = ProtocolFactory.create(protocolType, voicemailEnabled); + _protocol = ProtocolFactory.create(protocolType, codec2Mode, voicemailEnabled); _processPeriodicTimer = new Timer(); diff --git a/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/ProtocolFactory.java b/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/ProtocolFactory.java index b96befd..e768384 100644 --- a/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/ProtocolFactory.java +++ b/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/ProtocolFactory.java @@ -20,7 +20,7 @@ public class ProtocolFactory { } }; - public static Protocol create(ProtocolType protocolType, boolean voicemailEnabled) { + public static Protocol create(ProtocolType protocolType, int codec2ModeId, boolean voicemailEnabled) { Protocol proto; switch (protocolType) { case KISS: @@ -39,7 +39,7 @@ public class ProtocolFactory { } if (voicemailEnabled) { - proto = new VoicemailProxy(proto); + proto = new VoicemailProxy(proto, codec2ModeId); } return proto; diff --git a/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/VoicemailProxy.java b/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/VoicemailProxy.java index 06af174..91c903b 100644 --- a/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/VoicemailProxy.java +++ b/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/VoicemailProxy.java @@ -8,11 +8,14 @@ import java.io.IOException; public class VoicemailProxy implements Protocol { - Protocol _protocol; Context _context; - public VoicemailProxy(Protocol protocol) { + final Protocol _protocol; + final int _codec2ModeId; + + public VoicemailProxy(Protocol protocol, int codec2ModeId) { _protocol = protocol; + _codec2ModeId = codec2ModeId; } @Override From f516d7577c2e76c507900e44cae88db4b0c28edb Mon Sep 17 00:00:00 2001 From: sh123 Date: Fri, 8 Oct 2021 20:02:54 +0300 Subject: [PATCH 04/23] Increase version up to 1.4 --- codec2talkie/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codec2talkie/build.gradle b/codec2talkie/build.gradle index 3b44ab1..68668a3 100644 --- a/codec2talkie/build.gradle +++ b/codec2talkie/build.gradle @@ -11,7 +11,7 @@ android { minSdkVersion 23 targetSdkVersion 30 versionCode 1 - versionName "1.3" + versionName "1.4" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } From db0e3f3bcc2ea6a7bf7c2b28e2c9c5363cc70ed4 Mon Sep 17 00:00:00 2001 From: sh123 Date: Fri, 8 Oct 2021 21:56:12 +0300 Subject: [PATCH 05/23] Implemented storing and rotating voicemails --- .../radio/codec2talkie/protocol/Callback.java | 2 - .../codec2talkie/protocol/VoicemailProxy.java | 71 ++++++++++++++++++- .../codec2talkie/tools/StorageTools.java | 29 ++++++++ 3 files changed, 98 insertions(+), 4 deletions(-) create mode 100644 codec2talkie/src/main/java/com/radio/codec2talkie/tools/StorageTools.java diff --git a/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/Callback.java b/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/Callback.java index 1e8c7e4..b618263 100644 --- a/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/Callback.java +++ b/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/Callback.java @@ -1,7 +1,5 @@ package com.radio.codec2talkie.protocol; -import java.io.IOException; - public abstract class Callback { abstract protected void onReceiveAudioFrames(byte [] frame); abstract protected void onReceiveSignalLevel(byte [] rawData); diff --git a/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/VoicemailProxy.java b/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/VoicemailProxy.java index 91c903b..9949846 100644 --- a/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/VoicemailProxy.java +++ b/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/VoicemailProxy.java @@ -2,13 +2,27 @@ package com.radio.codec2talkie.protocol; import android.content.Context; +import com.radio.codec2talkie.tools.StorageTools; import com.radio.codec2talkie.transport.Transport; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; +import java.util.Timer; +import java.util.TimerTask; public class VoicemailProxy implements Protocol { + private final int ROTATION_DELAY_MS = 10000; + Context _context; + File _storage; + FileOutputStream _activeStream; + Timer _fileRotationTimer; final Protocol _protocol; final int _codec2ModeId; @@ -21,13 +35,14 @@ public class VoicemailProxy implements Protocol { @Override public void initialize(Transport transport, Context context) throws IOException { _context = context; + _storage = StorageTools.getStorage(context); _protocol.initialize(transport, context); } @Override public void send(byte[] frame) throws IOException { _protocol.send(frame); - // write to file + writeToFile(frame); } @Override @@ -36,7 +51,7 @@ public class VoicemailProxy implements Protocol { @Override protected void onReceiveAudioFrames(byte[] audioFrames) { callback.onReceiveAudioFrames(audioFrames); - // write to file + writeToFile(audioFrames); } @Override @@ -50,4 +65,56 @@ public class VoicemailProxy implements Protocol { public void flush() throws IOException { _protocol.flush(); } + + private void writeToFile(byte[] rawData) { + stopRotationTimer(); + if (_activeStream == null) { + try { + _activeStream = new FileOutputStream(new File(_storage, getNewFileName())); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } + } + try { + _activeStream.write(rawData); + } catch (IOException e) { + e.printStackTrace(); + } + startRotationTimer(); + } + + private String getNewFileName() { + SimpleDateFormat formatter = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss", Locale.US); + return formatter.format(new Date()) + "_" + _codec2ModeId + ".c2"; + } + + private void startRotationTimer() { + _fileRotationTimer = new Timer(); + _fileRotationTimer.schedule(new TimerTask() { + @Override + public void run() { + if (_activeStream != null) { + try { + _activeStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + try { + _activeStream = new FileOutputStream(new File(_storage, getNewFileName())); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } + } + }, ROTATION_DELAY_MS); + } + + private void stopRotationTimer() { + try { + if (_fileRotationTimer != null) { + _fileRotationTimer.cancel(); + _fileRotationTimer.purge(); + } + } catch (IllegalStateException ignored) {} + } } diff --git a/codec2talkie/src/main/java/com/radio/codec2talkie/tools/StorageTools.java b/codec2talkie/src/main/java/com/radio/codec2talkie/tools/StorageTools.java new file mode 100644 index 0000000..0789f69 --- /dev/null +++ b/codec2talkie/src/main/java/com/radio/codec2talkie/tools/StorageTools.java @@ -0,0 +1,29 @@ +package com.radio.codec2talkie.tools; + +import android.content.Context; +import android.os.Environment; + +import androidx.core.content.ContextCompat; + +import java.io.File; + +public class StorageTools { + + public static boolean isExternalStorageAvailable() { + return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED); + } + + public static File getExternalStorage(Context context) { + File[] externalStorageVolumes = + ContextCompat.getExternalFilesDirs(context, null); + return externalStorageVolumes[0]; + } + + public static File getStorage(Context context) { + if (isExternalStorageAvailable()) { + return getExternalStorage(context); + } else { + return context.getFilesDir(); + } + } +} From 04df7fa129322949adb03ed7db6e06b9fe61bd27 Mon Sep 17 00:00:00 2001 From: sh123 Date: Sat, 9 Oct 2021 11:09:45 +0300 Subject: [PATCH 06/23] Implemented recording into app directory --- .../codec2talkie/protocol/VoicemailProxy.java | 52 +++++++++++++------ 1 file changed, 37 insertions(+), 15 deletions(-) diff --git a/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/VoicemailProxy.java b/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/VoicemailProxy.java index 9949846..c740820 100644 --- a/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/VoicemailProxy.java +++ b/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/VoicemailProxy.java @@ -1,7 +1,9 @@ package com.radio.codec2talkie.protocol; import android.content.Context; +import android.util.Log; +import com.radio.codec2talkie.MainActivity; import com.radio.codec2talkie.tools.StorageTools; import com.radio.codec2talkie.transport.Transport; @@ -17,6 +19,8 @@ import java.util.TimerTask; public class VoicemailProxy implements Protocol { + private static final String TAG = MainActivity.class.getSimpleName(); + private final int ROTATION_DELAY_MS = 10000; Context _context; @@ -68,24 +72,46 @@ public class VoicemailProxy implements Protocol { private void writeToFile(byte[] rawData) { stopRotationTimer(); + createStreamIfNotExists(); + writeToStream(rawData); + startRotationTimer(); + } + + private void writeToStream(byte[] rawData) { + try { + if (_activeStream != null) { + _activeStream.write(rawData); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + + private void createStreamIfNotExists() { if (_activeStream == null) { try { - _activeStream = new FileOutputStream(new File(_storage, getNewFileName())); + Date date = new Date(); + File newDirectory = new File(_storage, getNewDirectoryName(date)); + if (newDirectory.mkdirs()) { + File newAudioFile = new File(newDirectory, getNewFileName(date)); + _activeStream = new FileOutputStream(newAudioFile); + } else { + Log.e(TAG, "Failed to create directory for voicemails"); + } } catch (FileNotFoundException e) { e.printStackTrace(); } } - try { - _activeStream.write(rawData); - } catch (IOException e) { - e.printStackTrace(); - } - startRotationTimer(); } - private String getNewFileName() { - SimpleDateFormat formatter = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss", Locale.US); - return formatter.format(new Date()) + "_" + _codec2ModeId + ".c2"; + private String getNewDirectoryName(Date date) { + SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd", Locale.US); + return df.format(date); + } + + private String getNewFileName(Date date) { + SimpleDateFormat tf = new SimpleDateFormat("HH_mm_ss", Locale.US); + return tf.format(date) + "_M" + _codec2ModeId + ".c2"; } private void startRotationTimer() { @@ -100,11 +126,7 @@ public class VoicemailProxy implements Protocol { e.printStackTrace(); } } - try { - _activeStream = new FileOutputStream(new File(_storage, getNewFileName())); - } catch (FileNotFoundException e) { - e.printStackTrace(); - } + _activeStream = null; } }, ROTATION_DELAY_MS); } From af833d2182320490290d1fc64d5c90aa3fd4225d Mon Sep 17 00:00:00 2001 From: sh123 Date: Sat, 9 Oct 2021 11:18:30 +0300 Subject: [PATCH 07/23] Improve directory handling --- .../com/radio/codec2talkie/protocol/VoicemailProxy.java | 7 +++---- codec2talkie/src/main/res/values/strings.xml | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/VoicemailProxy.java b/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/VoicemailProxy.java index c740820..e3dbe4b 100644 --- a/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/VoicemailProxy.java +++ b/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/VoicemailProxy.java @@ -92,12 +92,11 @@ public class VoicemailProxy implements Protocol { try { Date date = new Date(); File newDirectory = new File(_storage, getNewDirectoryName(date)); - if (newDirectory.mkdirs()) { - File newAudioFile = new File(newDirectory, getNewFileName(date)); - _activeStream = new FileOutputStream(newAudioFile); - } else { + if (!newDirectory.exists() && !newDirectory.mkdirs()) { Log.e(TAG, "Failed to create directory for voicemails"); } + File newAudioFile = new File(newDirectory, getNewFileName(date)); + _activeStream = new FileOutputStream(newAudioFile); } catch (FileNotFoundException e) { e.printStackTrace(); } diff --git a/codec2talkie/src/main/res/values/strings.xml b/codec2talkie/src/main/res/values/strings.xml index 3715500..50b1493 100644 --- a/codec2talkie/src/main/res/values/strings.xml +++ b/codec2talkie/src/main/res/values/strings.xml @@ -27,7 +27,7 @@ Record/Playback Test Mode Enables own voice recording/playback without transmission Enable Voicemail - Voicemail style transmission and receive recording + Record incoming and outgoing transmissions MODE_450=10 MODE_700C=8 From c5987177dd33eea984601455c3f94e4b71d41bd2 Mon Sep 17 00:00:00 2001 From: sh123 Date: Sat, 9 Oct 2021 12:09:46 +0300 Subject: [PATCH 08/23] Added voicemail main menu entry --- codec2talkie/src/main/res/menu/main_menu.xml | 3 +++ codec2talkie/src/main/res/values/strings.xml | 1 + 2 files changed, 4 insertions(+) diff --git a/codec2talkie/src/main/res/menu/main_menu.xml b/codec2talkie/src/main/res/menu/main_menu.xml index 01c45d2..b3e9cf0 100644 --- a/codec2talkie/src/main/res/menu/main_menu.xml +++ b/codec2talkie/src/main/res/menu/main_menu.xml @@ -3,6 +3,9 @@ + diff --git a/codec2talkie/src/main/res/values/strings.xml b/codec2talkie/src/main/res/values/strings.xml index 50b1493..2d46d28 100644 --- a/codec2talkie/src/main/res/values/strings.xml +++ b/codec2talkie/src/main/res/values/strings.xml @@ -105,5 +105,6 @@ Keep screen ON Prevent screen switching off when app is active + Voicemail \ No newline at end of file From 032ad5863b8d7b675a9dc4b7ffc11cfcf80cec46 Mon Sep 17 00:00:00 2001 From: sh123 Date: Sat, 9 Oct 2021 13:46:45 +0300 Subject: [PATCH 09/23] Add layut for voicemail list --- .../main/java/com/radio/codec2talkie/VoicemailActivity.java | 2 ++ codec2talkie/src/main/res/layout/activity_voicemail.xml | 6 ++++++ 2 files changed, 8 insertions(+) create mode 100644 codec2talkie/src/main/java/com/radio/codec2talkie/VoicemailActivity.java create mode 100644 codec2talkie/src/main/res/layout/activity_voicemail.xml diff --git a/codec2talkie/src/main/java/com/radio/codec2talkie/VoicemailActivity.java b/codec2talkie/src/main/java/com/radio/codec2talkie/VoicemailActivity.java new file mode 100644 index 0000000..07a8ed0 --- /dev/null +++ b/codec2talkie/src/main/java/com/radio/codec2talkie/VoicemailActivity.java @@ -0,0 +1,2 @@ +package com.radio.codec2talkie;public class VoicemailActivity { +} diff --git a/codec2talkie/src/main/res/layout/activity_voicemail.xml b/codec2talkie/src/main/res/layout/activity_voicemail.xml new file mode 100644 index 0000000..cdc89f2 --- /dev/null +++ b/codec2talkie/src/main/res/layout/activity_voicemail.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file From bdfbb1d7e7ab0e2e1bd1bb87506d68cc8ea45820 Mon Sep 17 00:00:00 2001 From: sh123 Date: Sat, 9 Oct 2021 15:16:12 +0300 Subject: [PATCH 10/23] Added basic voicemail navigation --- codec2talkie/src/main/AndroidManifest.xml | 3 + .../com/radio/codec2talkie/MainActivity.java | 11 +++ .../radio/codec2talkie/VoicemailActivity.java | 94 ++++++++++++++++++- .../codec2talkie/protocol/VoicemailProxy.java | 5 +- .../main/res/layout/activity_voicemail.xml | 13 ++- 5 files changed, 121 insertions(+), 5 deletions(-) diff --git a/codec2talkie/src/main/AndroidManifest.xml b/codec2talkie/src/main/AndroidManifest.xml index e2cbc63..a580852 100644 --- a/codec2talkie/src/main/AndroidManifest.xml +++ b/codec2talkie/src/main/AndroidManifest.xml @@ -21,6 +21,9 @@ + diff --git a/codec2talkie/src/main/java/com/radio/codec2talkie/MainActivity.java b/codec2talkie/src/main/java/com/radio/codec2talkie/MainActivity.java index 30ce216..14b44e1 100644 --- a/codec2talkie/src/main/java/com/radio/codec2talkie/MainActivity.java +++ b/codec2talkie/src/main/java/com/radio/codec2talkie/MainActivity.java @@ -59,6 +59,7 @@ public class MainActivity extends AppCompatActivity { private final static int REQUEST_CONNECT_USB = 2; private final static int REQUEST_PERMISSIONS = 3; private final static int REQUEST_SETTINGS = 4; + private final static int REQUEST_VOICEMAIL = 5; // S9 level at -93 dBm as per VHF Managers Handbook private final static int S_METER_S0_VALUE_DB = -153; @@ -90,6 +91,7 @@ public class MainActivity extends AppCompatActivity { @SuppressLint("ClickableViewAccessibility") @Override protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); String appName = getResources().getString(R.string.app_name); @@ -166,6 +168,11 @@ public class MainActivity extends AppCompatActivity { startActivityForResult(bluetoothConnectIntent, REQUEST_CONNECT_BT); } + protected void startVoicemailActivity() { + Intent voicemailIntent = new Intent(this, VoicemailActivity.class); + startActivityForResult(voicemailIntent, REQUEST_VOICEMAIL); + } + protected boolean requestPermissions() { List permissionsToRequest = new LinkedList(); @@ -229,6 +236,10 @@ public class MainActivity extends AppCompatActivity { startActivityForResult(settingsIntent, REQUEST_SETTINGS); return true; } + if (itemId == R.id.voicemail) { + startVoicemailActivity(); + return true; + } else if (itemId == R.id.exit) { stopRunning(); return true; diff --git a/codec2talkie/src/main/java/com/radio/codec2talkie/VoicemailActivity.java b/codec2talkie/src/main/java/com/radio/codec2talkie/VoicemailActivity.java index 07a8ed0..6a40ef7 100644 --- a/codec2talkie/src/main/java/com/radio/codec2talkie/VoicemailActivity.java +++ b/codec2talkie/src/main/java/com/radio/codec2talkie/VoicemailActivity.java @@ -1,2 +1,94 @@ -package com.radio.codec2talkie;public class VoicemailActivity { +package com.radio.codec2talkie; + +import android.content.Intent; +import android.os.Bundle; +import android.view.KeyEvent; +import android.view.View; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.ListView; + +import androidx.appcompat.app.AppCompatActivity; + +import com.radio.codec2talkie.tools.StorageTools; + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +public class VoicemailActivity extends AppCompatActivity { + + private static final String TAG = MainActivity.class.getSimpleName(); + + private File _root; + private File _currentDirectory; + private ArrayAdapter _dirAdapter; + private ListView _listVoicemail; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_voicemail); + + _dirAdapter = new ArrayAdapter<>(this, R.layout.support_simple_spinner_dropdown_item); + _listVoicemail = findViewById(R.id.listVoicemail); + _listVoicemail.setOnItemClickListener(onFileClickListener); + _listVoicemail.setAdapter(_dirAdapter); + + _root = StorageTools.getStorage(getApplicationContext()); + loadFiles(_root); + } + + private final AdapterView.OnItemClickListener onFileClickListener = (parent, view, position, id) -> { + Object selectedItem = (String)parent.getAdapter().getItem(position); + File selectedFile = new File(_root, selectedItem.toString()); + if (selectedFile.isDirectory()) { + loadFiles(selectedFile); + } else { + // play file + } + }; + + private void loadFiles(File directory) { + _dirAdapter.clear(); + _currentDirectory = directory; + + String title = directory.getName(); + if (_root.getAbsolutePath().equals(directory.getAbsolutePath())) { + title = "Voicemails"; + } + setTitle(title); + + List dirList = new ArrayList(); + String[] fileList = directory.list(); + if (fileList != null) { + dirList.addAll(Arrays.asList(fileList)); + } + Collections.sort(dirList); + + for (Object dirElement: dirList) { + _dirAdapter.add(dirElement); + } + _listVoicemail.setVisibility(View.VISIBLE); + } + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) { + if (!_root.getAbsolutePath().equals(_currentDirectory.getAbsolutePath())) { + _currentDirectory = _currentDirectory.getParentFile(); + if (_currentDirectory != null) { + loadFiles(_currentDirectory); + return true; + } + } + } + return super.onKeyDown(keyCode, event); + } + + protected void onActivityResult(int requestCode, int resultCode, Intent Data) { + super.onActivityResult(requestCode, resultCode, Data); + } } diff --git a/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/VoicemailProxy.java b/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/VoicemailProxy.java index e3dbe4b..2490487 100644 --- a/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/VoicemailProxy.java +++ b/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/VoicemailProxy.java @@ -109,8 +109,9 @@ public class VoicemailProxy implements Protocol { } private String getNewFileName(Date date) { - SimpleDateFormat tf = new SimpleDateFormat("HH_mm_ss", Locale.US); - return tf.format(date) + "_M" + _codec2ModeId + ".c2"; + SimpleDateFormat tf = new SimpleDateFormat("HH_mm_ss", Locale.ENGLISH); + String codec2mode = String.format(Locale.ENGLISH, "%02d", _codec2ModeId); + return codec2mode + "_" + tf.format(date) + ".c2"; } private void startRotationTimer() { diff --git a/codec2talkie/src/main/res/layout/activity_voicemail.xml b/codec2talkie/src/main/res/layout/activity_voicemail.xml index cdc89f2..d475771 100644 --- a/codec2talkie/src/main/res/layout/activity_voicemail.xml +++ b/codec2talkie/src/main/res/layout/activity_voicemail.xml @@ -1,6 +1,15 @@ + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical" + tools:context=".VoicemailActivity"> + + \ No newline at end of file From 7ee83f701322105cc53e1dd88709dfe0b822ba89 Mon Sep 17 00:00:00 2001 From: sh123 Date: Sat, 9 Oct 2021 15:35:12 +0300 Subject: [PATCH 11/23] Added delete all option --- .../radio/codec2talkie/VoicemailActivity.java | 41 +++++++++++++++++++ .../src/main/res/menu/voicemail_menu.xml | 9 ++++ codec2talkie/src/main/res/values/strings.xml | 2 + 3 files changed, 52 insertions(+) create mode 100644 codec2talkie/src/main/res/menu/voicemail_menu.xml diff --git a/codec2talkie/src/main/java/com/radio/codec2talkie/VoicemailActivity.java b/codec2talkie/src/main/java/com/radio/codec2talkie/VoicemailActivity.java index 6a40ef7..fbba181 100644 --- a/codec2talkie/src/main/java/com/radio/codec2talkie/VoicemailActivity.java +++ b/codec2talkie/src/main/java/com/radio/codec2talkie/VoicemailActivity.java @@ -2,7 +2,10 @@ package com.radio.codec2talkie; import android.content.Intent; import android.os.Bundle; +import android.util.Log; import android.view.KeyEvent; +import android.view.Menu; +import android.view.MenuItem; import android.view.View; import android.widget.AdapterView; import android.widget.ArrayAdapter; @@ -10,6 +13,7 @@ import android.widget.ListView; import androidx.appcompat.app.AppCompatActivity; +import com.radio.codec2talkie.settings.SettingsActivity; import com.radio.codec2talkie.tools.StorageTools; import java.io.File; @@ -74,6 +78,21 @@ public class VoicemailActivity extends AppCompatActivity { _listVoicemail.setVisibility(View.VISIBLE); } + private void deleteAll() { + File[] fileList = _currentDirectory.listFiles(); + if (fileList != null) { + for (File file : fileList) { + if (!file.delete()) { + Log.e(TAG, file.getName() + " cannot be deleted"); + } + } + } + loadFiles(_currentDirectory); + } + + private void playAll() { + } + @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) { @@ -88,6 +107,28 @@ public class VoicemailActivity extends AppCompatActivity { return super.onKeyDown(keyCode, event); } + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.voicemail_menu, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) + { + int itemId = item.getItemId(); + + if (itemId == R.id.voicemail_play_all) { + playAll(); + return true; + } + if (itemId == R.id.voicemail_delete_all) { + deleteAll(); + return true; + } + return super.onOptionsItemSelected(item); + } + protected void onActivityResult(int requestCode, int resultCode, Intent Data) { super.onActivityResult(requestCode, resultCode, Data); } diff --git a/codec2talkie/src/main/res/menu/voicemail_menu.xml b/codec2talkie/src/main/res/menu/voicemail_menu.xml new file mode 100644 index 0000000..b05422c --- /dev/null +++ b/codec2talkie/src/main/res/menu/voicemail_menu.xml @@ -0,0 +1,9 @@ + + + + + \ No newline at end of file diff --git a/codec2talkie/src/main/res/values/strings.xml b/codec2talkie/src/main/res/values/strings.xml index 2d46d28..8cc3493 100644 --- a/codec2talkie/src/main/res/values/strings.xml +++ b/codec2talkie/src/main/res/values/strings.xml @@ -106,5 +106,7 @@ Keep screen ON Prevent screen switching off when app is active Voicemail + Play All + Delete All \ No newline at end of file From 43722c68075b76aae8056f3b47a27f9e993dc5ec Mon Sep 17 00:00:00 2001 From: sh123 Date: Sat, 9 Oct 2021 15:35:55 +0300 Subject: [PATCH 12/23] Cleanup --- .../main/java/com/radio/codec2talkie/VoicemailActivity.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/codec2talkie/src/main/java/com/radio/codec2talkie/VoicemailActivity.java b/codec2talkie/src/main/java/com/radio/codec2talkie/VoicemailActivity.java index fbba181..6f398f3 100644 --- a/codec2talkie/src/main/java/com/radio/codec2talkie/VoicemailActivity.java +++ b/codec2talkie/src/main/java/com/radio/codec2talkie/VoicemailActivity.java @@ -12,8 +12,6 @@ import android.widget.ArrayAdapter; import android.widget.ListView; import androidx.appcompat.app.AppCompatActivity; - -import com.radio.codec2talkie.settings.SettingsActivity; import com.radio.codec2talkie.tools.StorageTools; import java.io.File; @@ -46,7 +44,7 @@ public class VoicemailActivity extends AppCompatActivity { } private final AdapterView.OnItemClickListener onFileClickListener = (parent, view, position, id) -> { - Object selectedItem = (String)parent.getAdapter().getItem(position); + Object selectedItem = parent.getAdapter().getItem(position); File selectedFile = new File(_root, selectedItem.toString()); if (selectedFile.isDirectory()) { loadFiles(selectedFile); From a26d997038e55cb2f462aec32206af1ffac0a95c Mon Sep 17 00:00:00 2001 From: sh123 Date: Sat, 9 Oct 2021 20:04:51 +0300 Subject: [PATCH 13/23] Add confirmation dialog --- .../radio/codec2talkie/VoicemailActivity.java | 16 +++++++++++++++- codec2talkie/src/main/res/values/strings.xml | 4 ++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/codec2talkie/src/main/java/com/radio/codec2talkie/VoicemailActivity.java b/codec2talkie/src/main/java/com/radio/codec2talkie/VoicemailActivity.java index 6f398f3..d8e03e3 100644 --- a/codec2talkie/src/main/java/com/radio/codec2talkie/VoicemailActivity.java +++ b/codec2talkie/src/main/java/com/radio/codec2talkie/VoicemailActivity.java @@ -1,5 +1,6 @@ package com.radio.codec2talkie; +import android.content.DialogInterface; import android.content.Intent; import android.os.Bundle; import android.util.Log; @@ -11,6 +12,7 @@ import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.ListView; +import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; import com.radio.codec2talkie.tools.StorageTools; @@ -105,6 +107,18 @@ public class VoicemailActivity extends AppCompatActivity { return super.onKeyDown(keyCode, event); } + private void runConfirmation() { + AlertDialog.Builder alertBuilder = new AlertDialog.Builder(this); + alertBuilder.setMessage(R.string.voicemail_remove_all_confirmation_message) + .setTitle(R.string.voicemail_remove_all_confirmation_title) + .setPositiveButton(R.string.ok, (dialog, id) -> { + deleteAll();; + }) + .setNegativeButton(R.string.cancel, (dialog, id) -> { + }) + .show(); + } + @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.voicemail_menu, menu); @@ -121,7 +135,7 @@ public class VoicemailActivity extends AppCompatActivity { return true; } if (itemId == R.id.voicemail_delete_all) { - deleteAll(); + runConfirmation(); return true; } return super.onOptionsItemSelected(item); diff --git a/codec2talkie/src/main/res/values/strings.xml b/codec2talkie/src/main/res/values/strings.xml index 8cc3493..e81fd19 100644 --- a/codec2talkie/src/main/res/values/strings.xml +++ b/codec2talkie/src/main/res/values/strings.xml @@ -108,5 +108,9 @@ Voicemail Play All Delete All + Remove Voicemails from the list? + Remove confirmation + OK + Cancel \ No newline at end of file From 8da4a5c9d837ac72138254d5961f392ccd977a56 Mon Sep 17 00:00:00 2001 From: sh123 Date: Sat, 9 Oct 2021 20:19:47 +0300 Subject: [PATCH 14/23] Rename voicemail to recorder --- codec2talkie/src/main/AndroidManifest.xml | 2 +- .../com/radio/codec2talkie/MainActivity.java | 3 +- ...ailActivity.java => RecorderActivity.java} | 36 +++++++++---------- ...ty_voicemail.xml => activity_recorder.xml} | 4 +-- .../{voicemail_menu.xml => recorder_menu.xml} | 4 +-- codec2talkie/src/main/res/values/strings.xml | 15 ++++---- 6 files changed, 32 insertions(+), 32 deletions(-) rename codec2talkie/src/main/java/com/radio/codec2talkie/{VoicemailActivity.java => RecorderActivity.java} (80%) rename codec2talkie/src/main/res/layout/{activity_voicemail.xml => activity_recorder.xml} (78%) rename codec2talkie/src/main/res/menu/{voicemail_menu.xml => recorder_menu.xml} (72%) diff --git a/codec2talkie/src/main/AndroidManifest.xml b/codec2talkie/src/main/AndroidManifest.xml index a580852..5355d3c 100644 --- a/codec2talkie/src/main/AndroidManifest.xml +++ b/codec2talkie/src/main/AndroidManifest.xml @@ -22,7 +22,7 @@ android:name=".settings.BluetoothSettingsActivity" android:configChanges="orientation|screenSize" /> _dirAdapter; - private ListView _listVoicemail; + private ListView _recordingList; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setContentView(R.layout.activity_voicemail); + setContentView(R.layout.activity_recorder); _dirAdapter = new ArrayAdapter<>(this, R.layout.support_simple_spinner_dropdown_item); - _listVoicemail = findViewById(R.id.listVoicemail); - _listVoicemail.setOnItemClickListener(onFileClickListener); - _listVoicemail.setAdapter(_dirAdapter); + _recordingList = findViewById(R.id.listRecorder); + _recordingList.setOnItemClickListener(onFileClickListener); + _recordingList.setAdapter(_dirAdapter); _root = StorageTools.getStorage(getApplicationContext()); loadFiles(_root); @@ -61,21 +59,23 @@ public class VoicemailActivity extends AppCompatActivity { String title = directory.getName(); if (_root.getAbsolutePath().equals(directory.getAbsolutePath())) { - title = "Voicemails"; + title = getString(R.string.recorder_name); } setTitle(title); List dirList = new ArrayList(); - String[] fileList = directory.list(); + File[] fileList = directory.listFiles(); if (fileList != null) { - dirList.addAll(Arrays.asList(fileList)); + for (File file : fileList) { + dirList.add(file.getName()); + } } Collections.sort(dirList); for (Object dirElement: dirList) { _dirAdapter.add(dirElement); } - _listVoicemail.setVisibility(View.VISIBLE); + _recordingList.setVisibility(View.VISIBLE); } private void deleteAll() { @@ -109,10 +109,10 @@ public class VoicemailActivity extends AppCompatActivity { private void runConfirmation() { AlertDialog.Builder alertBuilder = new AlertDialog.Builder(this); - alertBuilder.setMessage(R.string.voicemail_remove_all_confirmation_message) - .setTitle(R.string.voicemail_remove_all_confirmation_title) + alertBuilder.setMessage(R.string.recorder_remove_all_confirmation_message) + .setTitle(R.string.recorder_remove_all_confirmation_title) .setPositiveButton(R.string.ok, (dialog, id) -> { - deleteAll();; + deleteAll(); }) .setNegativeButton(R.string.cancel, (dialog, id) -> { }) @@ -121,7 +121,7 @@ public class VoicemailActivity extends AppCompatActivity { @Override public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.voicemail_menu, menu); + getMenuInflater().inflate(R.menu.recorder_menu, menu); return true; } @@ -130,11 +130,11 @@ public class VoicemailActivity extends AppCompatActivity { { int itemId = item.getItemId(); - if (itemId == R.id.voicemail_play_all) { + if (itemId == R.id.recorder_play_all) { playAll(); return true; } - if (itemId == R.id.voicemail_delete_all) { + if (itemId == R.id.recorder_delete_all) { runConfirmation(); return true; } diff --git a/codec2talkie/src/main/res/layout/activity_voicemail.xml b/codec2talkie/src/main/res/layout/activity_recorder.xml similarity index 78% rename from codec2talkie/src/main/res/layout/activity_voicemail.xml rename to codec2talkie/src/main/res/layout/activity_recorder.xml index d475771..7b408c6 100644 --- a/codec2talkie/src/main/res/layout/activity_voicemail.xml +++ b/codec2talkie/src/main/res/layout/activity_recorder.xml @@ -5,10 +5,10 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" - tools:context=".VoicemailActivity"> + tools:context=".RecorderActivity"> diff --git a/codec2talkie/src/main/res/menu/voicemail_menu.xml b/codec2talkie/src/main/res/menu/recorder_menu.xml similarity index 72% rename from codec2talkie/src/main/res/menu/voicemail_menu.xml rename to codec2talkie/src/main/res/menu/recorder_menu.xml index b05422c..344283f 100644 --- a/codec2talkie/src/main/res/menu/voicemail_menu.xml +++ b/codec2talkie/src/main/res/menu/recorder_menu.xml @@ -1,9 +1,9 @@ \ No newline at end of file diff --git a/codec2talkie/src/main/res/values/strings.xml b/codec2talkie/src/main/res/values/strings.xml index e81fd19..cdabc9a 100644 --- a/codec2talkie/src/main/res/values/strings.xml +++ b/codec2talkie/src/main/res/values/strings.xml @@ -24,10 +24,10 @@ Codec2 Settings Mode/Speed - Record/Playback Test Mode - Enables own voice recording/playback without transmission - Enable Voicemail - Record incoming and outgoing transmissions + Echo Test Mode + Records and plays recording without transmission + Enable Recorder + Record incoming and outgoing transmissions for future playback MODE_450=10 MODE_700C=8 @@ -105,12 +105,13 @@ Keep screen ON Prevent screen switching off when app is active - Voicemail + Recorder Play All Delete All - Remove Voicemails from the list? - Remove confirmation + Remove recordings from the list? + Remove confirmation OK Cancel + Recorder \ No newline at end of file From 5b3e6cdf357e99611299f7b790f0e374b87b35da Mon Sep 17 00:00:00 2001 From: sh123 Date: Sat, 9 Oct 2021 20:27:17 +0300 Subject: [PATCH 15/23] Code cleanup --- codec2talkie/src/main/AndroidManifest.xml | 2 +- .../com/radio/codec2talkie/AudioProcessor.java | 10 ++-------- .../java/com/radio/codec2talkie/MainActivity.java | 3 ++- .../{ => recorder}/RecorderActivity.java | 14 +++++++------- .../src/main/res/layout/activity_recorder.xml | 2 +- codec2talkie/src/main/res/values/strings.xml | 1 + 6 files changed, 14 insertions(+), 18 deletions(-) rename codec2talkie/src/main/java/com/radio/codec2talkie/{ => recorder}/RecorderActivity.java (95%) diff --git a/codec2talkie/src/main/AndroidManifest.xml b/codec2talkie/src/main/AndroidManifest.xml index 5355d3c..50e679e 100644 --- a/codec2talkie/src/main/AndroidManifest.xml +++ b/codec2talkie/src/main/AndroidManifest.xml @@ -22,7 +22,7 @@ android:name=".settings.BluetoothSettingsActivity" android:configChanges="orientation|screenSize" /> dirList = new ArrayList(); + List dirList = new ArrayList<>(); File[] fileList = directory.listFiles(); if (fileList != null) { for (File file : fileList) { @@ -111,11 +114,8 @@ public class RecorderActivity extends AppCompatActivity { AlertDialog.Builder alertBuilder = new AlertDialog.Builder(this); alertBuilder.setMessage(R.string.recorder_remove_all_confirmation_message) .setTitle(R.string.recorder_remove_all_confirmation_title) - .setPositiveButton(R.string.ok, (dialog, id) -> { - deleteAll(); - }) - .setNegativeButton(R.string.cancel, (dialog, id) -> { - }) + .setPositiveButton(R.string.ok, (dialog, id) -> deleteAll()) + .setNegativeButton(R.string.cancel, (dialog, id) -> {}) .show(); } diff --git a/codec2talkie/src/main/res/layout/activity_recorder.xml b/codec2talkie/src/main/res/layout/activity_recorder.xml index 7b408c6..343ffde 100644 --- a/codec2talkie/src/main/res/layout/activity_recorder.xml +++ b/codec2talkie/src/main/res/layout/activity_recorder.xml @@ -5,7 +5,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" - tools:context=".RecorderActivity"> + tools:context=".recorder.RecorderActivity"> OK Cancel Recorder + REC \ No newline at end of file From 86a218a62351031959511562e177b34b83150454 Mon Sep 17 00:00:00 2001 From: sh123 Date: Sat, 9 Oct 2021 20:33:40 +0300 Subject: [PATCH 16/23] Small renames --- codec2talkie/src/main/res/menu/main_menu.xml | 6 +++--- codec2talkie/src/main/res/values/strings.xml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/codec2talkie/src/main/res/menu/main_menu.xml b/codec2talkie/src/main/res/menu/main_menu.xml index b3e9cf0..5d2522f 100644 --- a/codec2talkie/src/main/res/menu/main_menu.xml +++ b/codec2talkie/src/main/res/menu/main_menu.xml @@ -1,11 +1,11 @@ - + diff --git a/codec2talkie/src/main/res/values/strings.xml b/codec2talkie/src/main/res/values/strings.xml index 53195b2..918837a 100644 --- a/codec2talkie/src/main/res/values/strings.xml +++ b/codec2talkie/src/main/res/values/strings.xml @@ -2,7 +2,7 @@ Codec2 Talkie PUSH TO TALK - Preferences + Settings Exit TNC Settings From 68cac5991d989834dea297d42687e335fa43691e Mon Sep 17 00:00:00 2001 From: sh123 Date: Sat, 9 Oct 2021 20:37:31 +0300 Subject: [PATCH 17/23] Add line separators into main menu --- .../com/radio/codec2talkie/MainActivity.java | 2 ++ codec2talkie/src/main/res/menu/main_menu.xml | 24 ++++++++++++------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/codec2talkie/src/main/java/com/radio/codec2talkie/MainActivity.java b/codec2talkie/src/main/java/com/radio/codec2talkie/MainActivity.java index 11641b7..b5c34db 100644 --- a/codec2talkie/src/main/java/com/radio/codec2talkie/MainActivity.java +++ b/codec2talkie/src/main/java/com/radio/codec2talkie/MainActivity.java @@ -3,6 +3,7 @@ package com.radio.codec2talkie; import androidx.appcompat.app.AppCompatActivity; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; +import androidx.core.view.MenuCompat; import androidx.preference.PreferenceManager; import android.Manifest; @@ -222,6 +223,7 @@ public class MainActivity extends AppCompatActivity { @Override public boolean onCreateOptionsMenu(Menu menu) { + MenuCompat.setGroupDividerEnabled(menu, true); getMenuInflater().inflate(R.menu.main_menu, menu); return true; } diff --git a/codec2talkie/src/main/res/menu/main_menu.xml b/codec2talkie/src/main/res/menu/main_menu.xml index 5d2522f..0f6bce0 100644 --- a/codec2talkie/src/main/res/menu/main_menu.xml +++ b/codec2talkie/src/main/res/menu/main_menu.xml @@ -1,12 +1,18 @@ - - - + + + + + + + + + \ No newline at end of file From 4a392548add1ccff2c212ad1e077414cf5515bbd Mon Sep 17 00:00:00 2001 From: sh123 Date: Sat, 9 Oct 2021 20:42:40 +0300 Subject: [PATCH 18/23] Code cleanup --- .../src/main/java/com/radio/codec2talkie/MainActivity.java | 6 ++++-- .../main/java/com/radio/codec2talkie/audio/AudioPlayer.java | 4 ++++ .../com/radio/codec2talkie/{ => audio}/AudioProcessor.java | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) create mode 100644 codec2talkie/src/main/java/com/radio/codec2talkie/audio/AudioPlayer.java rename codec2talkie/src/main/java/com/radio/codec2talkie/{ => audio}/AudioProcessor.java (99%) diff --git a/codec2talkie/src/main/java/com/radio/codec2talkie/MainActivity.java b/codec2talkie/src/main/java/com/radio/codec2talkie/MainActivity.java index b5c34db..0f13529 100644 --- a/codec2talkie/src/main/java/com/radio/codec2talkie/MainActivity.java +++ b/codec2talkie/src/main/java/com/radio/codec2talkie/MainActivity.java @@ -1,5 +1,6 @@ package com.radio.codec2talkie; +import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; @@ -36,6 +37,7 @@ import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; +import com.radio.codec2talkie.audio.AudioProcessor; import com.radio.codec2talkie.connect.BluetoothConnectActivity; import com.radio.codec2talkie.connect.SocketHandler; import com.radio.codec2talkie.protocol.ProtocolFactory; @@ -175,7 +177,7 @@ public class MainActivity extends AppCompatActivity { } protected boolean requestPermissions() { - List permissionsToRequest = new LinkedList(); + List permissionsToRequest = new LinkedList<>(); for (String permission : _requiredPermissions) { if (ContextCompat.checkSelfPermission(MainActivity.this, permission) == PackageManager.PERMISSION_DENIED) { @@ -316,7 +318,7 @@ public class MainActivity extends AppCompatActivity { }; @Override - public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == REQUEST_PERMISSIONS) { boolean allGranted = true; diff --git a/codec2talkie/src/main/java/com/radio/codec2talkie/audio/AudioPlayer.java b/codec2talkie/src/main/java/com/radio/codec2talkie/audio/AudioPlayer.java new file mode 100644 index 0000000..01e75e9 --- /dev/null +++ b/codec2talkie/src/main/java/com/radio/codec2talkie/audio/AudioPlayer.java @@ -0,0 +1,4 @@ +package com.radio.codec2talkie.audio; + +public class AudioPlayer extends Thread { +} diff --git a/codec2talkie/src/main/java/com/radio/codec2talkie/AudioProcessor.java b/codec2talkie/src/main/java/com/radio/codec2talkie/audio/AudioProcessor.java similarity index 99% rename from codec2talkie/src/main/java/com/radio/codec2talkie/AudioProcessor.java rename to codec2talkie/src/main/java/com/radio/codec2talkie/audio/AudioProcessor.java index cdbc897..2b783ef 100644 --- a/codec2talkie/src/main/java/com/radio/codec2talkie/AudioProcessor.java +++ b/codec2talkie/src/main/java/com/radio/codec2talkie/audio/AudioProcessor.java @@ -1,4 +1,4 @@ -package com.radio.codec2talkie; +package com.radio.codec2talkie.audio; import android.content.Context; import android.media.AudioAttributes; From b4937c56f7432d3ab64b10c9d2eed5459990cd6f Mon Sep 17 00:00:00 2001 From: sh123 Date: Sun, 10 Oct 2021 11:17:59 +0300 Subject: [PATCH 19/23] Basic recording playback --- .../radio/codec2talkie/audio/AudioPlayer.java | 139 ++++++++++++++++++ .../recorder/RecorderActivity.java | 23 ++- .../src/main/res/menu/recorder_menu.xml | 3 - 3 files changed, 155 insertions(+), 10 deletions(-) diff --git a/codec2talkie/src/main/java/com/radio/codec2talkie/audio/AudioPlayer.java b/codec2talkie/src/main/java/com/radio/codec2talkie/audio/AudioPlayer.java index 01e75e9..4fda797 100644 --- a/codec2talkie/src/main/java/com/radio/codec2talkie/audio/AudioPlayer.java +++ b/codec2talkie/src/main/java/com/radio/codec2talkie/audio/AudioPlayer.java @@ -1,4 +1,143 @@ package com.radio.codec2talkie.audio; +import android.content.Context; +import android.media.AudioAttributes; +import android.media.AudioFormat; +import android.media.AudioTrack; +import android.os.Handler; +import android.os.Message; +import android.util.Log; + +import com.radio.codec2talkie.MainActivity; +import com.ustadmobile.codec2.Codec2; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; + public class AudioPlayer extends Thread { + private static final String TAG = MainActivity.class.getSimpleName(); + + public static final int PLAYER_STARTED = 1; + public static final int PLAYER_PLAYING_FILE = 2; + public static final int PLAYER_PLAYED_FILE= 3; + public static final int PLAYER_ERROR = 4; + public static final int PLAYER_STOPPED = 4; + + private final Handler _onPlayerStateChanged; + private final Context _context; + private final File[] _files; + + private final int AUDIO_SAMPLE_SIZE = 8000; + + private AudioTrack _systemAudioPlayer; + private short[] _playbackAudioBuffer; + + private long _codec2Con; + + private int _audioBufferSize; + private int _codec2FrameSize; + + private int _currentStatus = PLAYER_STOPPED; + + public AudioPlayer(File[] files, Handler onPlayerStateChanged, Context context) { + + _onPlayerStateChanged = onPlayerStateChanged; + _context = context; + _files = files; + + constructSystemAudioDevices(); + } + + private void constructSystemAudioDevices() { + int _audioPlayerMinBufferSize = AudioTrack.getMinBufferSize( + AUDIO_SAMPLE_SIZE, + AudioFormat.CHANNEL_OUT_MONO, + AudioFormat.ENCODING_PCM_16BIT); + _systemAudioPlayer = new AudioTrack.Builder() + .setAudioAttributes(new AudioAttributes.Builder() + .setUsage(AudioAttributes.USAGE_MEDIA) + .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH) + .build()) + .setAudioFormat(new AudioFormat.Builder() + .setEncoding(AudioFormat.ENCODING_PCM_16BIT) + .setSampleRate(AUDIO_SAMPLE_SIZE) + .setChannelMask(AudioFormat.CHANNEL_OUT_MONO) + .build()) + .setTransferMode(AudioTrack.MODE_STREAM) + .setBufferSizeInBytes(10 * _audioPlayerMinBufferSize) + .build(); + } + + private void constructCodec2(int codecMode) { + _codec2Con = Codec2.create(codecMode); + + _audioBufferSize = Codec2.getSamplesPerFrame(_codec2Con); + _codec2FrameSize = Codec2.getBitsSize(_codec2Con); // returns number of bytes + + _playbackAudioBuffer = new short[_audioBufferSize]; + } + + private void sendStatusUpdate(int newStatus) { + if (newStatus != _currentStatus) { + _currentStatus = newStatus; + Message msg = Message.obtain(); + msg.what = newStatus; + + _onPlayerStateChanged.sendMessage(msg); + } + } + + private void playFile(File file) { + String codec2ModeStr = file.getName().substring(0, 2); + int codec2Mode = Integer.parseInt(codec2ModeStr); + constructCodec2(codec2Mode); + + FileInputStream inputStream; + try { + inputStream = new FileInputStream(file); + } catch (FileNotFoundException e) { + sendStatusUpdate(PLAYER_ERROR); + e.printStackTrace(); + return; + } + + byte[] codec2Buffer = new byte[_codec2FrameSize]; + + try { + while (inputStream.read(codec2Buffer) == _codec2FrameSize) { + Codec2.decode(_codec2Con, _playbackAudioBuffer, codec2Buffer); + _systemAudioPlayer.write(_playbackAudioBuffer, 0, _audioBufferSize); + } + } catch (IOException e) { + sendStatusUpdate(PLAYER_ERROR); + e.printStackTrace(); + return; + } + + try { + inputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private void play() { + _systemAudioPlayer.play(); + for (File file : _files) { + sendStatusUpdate(PLAYER_PLAYING_FILE); + playFile(file); + sendStatusUpdate(PLAYER_PLAYED_FILE); + } + _systemAudioPlayer.stop(); + _systemAudioPlayer.release(); + } + + @Override + public void run() { + sendStatusUpdate(PLAYER_STARTED); + play(); + sendStatusUpdate(PLAYER_STOPPED); + } } diff --git a/codec2talkie/src/main/java/com/radio/codec2talkie/recorder/RecorderActivity.java b/codec2talkie/src/main/java/com/radio/codec2talkie/recorder/RecorderActivity.java index e7ee102..f4cb96a 100644 --- a/codec2talkie/src/main/java/com/radio/codec2talkie/recorder/RecorderActivity.java +++ b/codec2talkie/src/main/java/com/radio/codec2talkie/recorder/RecorderActivity.java @@ -2,6 +2,9 @@ package com.radio.codec2talkie.recorder; import android.content.Intent; import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; import android.util.Log; import android.view.KeyEvent; import android.view.Menu; @@ -16,6 +19,7 @@ import androidx.appcompat.app.AppCompatActivity; import com.radio.codec2talkie.MainActivity; import com.radio.codec2talkie.R; +import com.radio.codec2talkie.audio.AudioPlayer; import com.radio.codec2talkie.tools.StorageTools; import java.io.File; @@ -46,13 +50,22 @@ public class RecorderActivity extends AppCompatActivity { loadFiles(_root); } + private final Handler onPlayerStateChanged = new Handler(Looper.getMainLooper()) { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + } + } + }; + private final AdapterView.OnItemClickListener onFileClickListener = (parent, view, position, id) -> { Object selectedItem = parent.getAdapter().getItem(position); - File selectedFile = new File(_root, selectedItem.toString()); + File selectedFile = new File(_currentDirectory, selectedItem.toString()); if (selectedFile.isDirectory()) { loadFiles(selectedFile); } else { - // play file + File[] files = {selectedFile}; + new AudioPlayer(files, onPlayerStateChanged, this).start(); } }; @@ -129,11 +142,7 @@ public class RecorderActivity extends AppCompatActivity { public boolean onOptionsItemSelected(MenuItem item) { int itemId = item.getItemId(); - - if (itemId == R.id.recorder_play_all) { - playAll(); - return true; - } + if (itemId == R.id.recorder_delete_all) { runConfirmation(); return true; diff --git a/codec2talkie/src/main/res/menu/recorder_menu.xml b/codec2talkie/src/main/res/menu/recorder_menu.xml index 344283f..7e0a45d 100644 --- a/codec2talkie/src/main/res/menu/recorder_menu.xml +++ b/codec2talkie/src/main/res/menu/recorder_menu.xml @@ -1,8 +1,5 @@ - From f01d1829a18fa706ca99049207ec581d278f64f4 Mon Sep 17 00:00:00 2001 From: sh123 Date: Sun, 10 Oct 2021 14:25:32 +0300 Subject: [PATCH 20/23] Add PlayAll --- .../radio/codec2talkie/audio/AudioPlayer.java | 20 +++++++++++++++++-- .../recorder/RecorderActivity.java | 11 ++++++++-- .../src/main/res/menu/recorder_menu.xml | 3 +++ 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/codec2talkie/src/main/java/com/radio/codec2talkie/audio/AudioPlayer.java b/codec2talkie/src/main/java/com/radio/codec2talkie/audio/AudioPlayer.java index 4fda797..e2baa71 100644 --- a/codec2talkie/src/main/java/com/radio/codec2talkie/audio/AudioPlayer.java +++ b/codec2talkie/src/main/java/com/radio/codec2talkie/audio/AudioPlayer.java @@ -6,7 +6,6 @@ import android.media.AudioFormat; import android.media.AudioTrack; import android.os.Handler; import android.os.Message; -import android.util.Log; import com.radio.codec2talkie.MainActivity; import com.ustadmobile.codec2.Codec2; @@ -15,6 +14,7 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; +import java.util.Arrays; public class AudioPlayer extends Thread { private static final String TAG = MainActivity.class.getSimpleName(); @@ -35,6 +35,7 @@ public class AudioPlayer extends Thread { private short[] _playbackAudioBuffer; private long _codec2Con; + private int _codec2Mode; private int _audioBufferSize; private int _codec2FrameSize; @@ -46,6 +47,9 @@ public class AudioPlayer extends Thread { _onPlayerStateChanged = onPlayerStateChanged; _context = context; _files = files; + _codec2Con = 0; + + Arrays.sort(_files); constructSystemAudioDevices(); } @@ -71,6 +75,9 @@ public class AudioPlayer extends Thread { } private void constructCodec2(int codecMode) { + if (_codec2Con != 0) { + Codec2.destroy(_codec2Con); + } _codec2Con = Codec2.create(codecMode); _audioBufferSize = Codec2.getSamplesPerFrame(_codec2Con); @@ -92,7 +99,12 @@ public class AudioPlayer extends Thread { private void playFile(File file) { String codec2ModeStr = file.getName().substring(0, 2); int codec2Mode = Integer.parseInt(codec2ModeStr); - constructCodec2(codec2Mode); + + // reconstruct codec2 on mode change + if (_codec2Con == 0 || _codec2Mode != codec2Mode) { + _codec2Mode = codec2Mode; + constructCodec2(codec2Mode); + } FileInputStream inputStream; try { @@ -126,12 +138,16 @@ public class AudioPlayer extends Thread { private void play() { _systemAudioPlayer.play(); for (File file : _files) { + if (file.isDirectory() || !file.getName().endsWith(".c2")) continue; sendStatusUpdate(PLAYER_PLAYING_FILE); playFile(file); sendStatusUpdate(PLAYER_PLAYED_FILE); } _systemAudioPlayer.stop(); _systemAudioPlayer.release(); + if (_codec2Con != 0) { + Codec2.destroy(_codec2Con); + } } @Override diff --git a/codec2talkie/src/main/java/com/radio/codec2talkie/recorder/RecorderActivity.java b/codec2talkie/src/main/java/com/radio/codec2talkie/recorder/RecorderActivity.java index f4cb96a..fef1405 100644 --- a/codec2talkie/src/main/java/com/radio/codec2talkie/recorder/RecorderActivity.java +++ b/codec2talkie/src/main/java/com/radio/codec2talkie/recorder/RecorderActivity.java @@ -5,6 +5,7 @@ import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.Message; +import android.provider.MediaStore; import android.util.Log; import android.view.KeyEvent; import android.view.Menu; @@ -107,6 +108,8 @@ public class RecorderActivity extends AppCompatActivity { } private void playAll() { + File[] files = _currentDirectory.listFiles(); + new AudioPlayer(files, onPlayerStateChanged, this).start(); } @Override @@ -142,8 +145,12 @@ public class RecorderActivity extends AppCompatActivity { public boolean onOptionsItemSelected(MenuItem item) { int itemId = item.getItemId(); - - if (itemId == R.id.recorder_delete_all) { + + if (itemId == R.id.recorder_play_all) { + playAll(); + return true; + } + else if (itemId == R.id.recorder_delete_all) { runConfirmation(); return true; } diff --git a/codec2talkie/src/main/res/menu/recorder_menu.xml b/codec2talkie/src/main/res/menu/recorder_menu.xml index 7e0a45d..344283f 100644 --- a/codec2talkie/src/main/res/menu/recorder_menu.xml +++ b/codec2talkie/src/main/res/menu/recorder_menu.xml @@ -1,5 +1,8 @@ + From 95cc01be5f1e71767faad824cb2f378c81f7d490 Mon Sep 17 00:00:00 2001 From: sh123 Date: Sun, 10 Oct 2021 15:38:57 +0300 Subject: [PATCH 21/23] Added playback stop option --- .../radio/codec2talkie/audio/AudioPlayer.java | 37 ++++++++++++------ .../recorder/RecorderActivity.java | 39 +++++++++++++++++-- .../src/main/res/layout/activity_recorder.xml | 18 ++++++++- .../src/main/res/menu/recorder_menu.xml | 7 +++- codec2talkie/src/main/res/values/strings.xml | 10 ++++- 5 files changed, 90 insertions(+), 21 deletions(-) diff --git a/codec2talkie/src/main/java/com/radio/codec2talkie/audio/AudioPlayer.java b/codec2talkie/src/main/java/com/radio/codec2talkie/audio/AudioPlayer.java index e2baa71..b54d8c1 100644 --- a/codec2talkie/src/main/java/com/radio/codec2talkie/audio/AudioPlayer.java +++ b/codec2talkie/src/main/java/com/radio/codec2talkie/audio/AudioPlayer.java @@ -23,7 +23,7 @@ public class AudioPlayer extends Thread { public static final int PLAYER_PLAYING_FILE = 2; public static final int PLAYER_PLAYED_FILE= 3; public static final int PLAYER_ERROR = 4; - public static final int PLAYER_STOPPED = 4; + public static final int PLAYER_STOPPED = 5; private final Handler _onPlayerStateChanged; private final Context _context; @@ -42,6 +42,8 @@ public class AudioPlayer extends Thread { private int _currentStatus = PLAYER_STOPPED; + private boolean _stopPlayback = false; + public AudioPlayer(File[] files, Handler onPlayerStateChanged, Context context) { _onPlayerStateChanged = onPlayerStateChanged; @@ -86,17 +88,18 @@ public class AudioPlayer extends Thread { _playbackAudioBuffer = new short[_audioBufferSize]; } - private void sendStatusUpdate(int newStatus) { + private void sendStatusUpdate(int newStatus, String fileName) { if (newStatus != _currentStatus) { _currentStatus = newStatus; Message msg = Message.obtain(); msg.what = newStatus; + msg.obj = fileName; _onPlayerStateChanged.sendMessage(msg); } } - private void playFile(File file) { + private boolean playFile(File file) { String codec2ModeStr = file.getName().substring(0, 2); int codec2Mode = Integer.parseInt(codec2ModeStr); @@ -110,22 +113,25 @@ public class AudioPlayer extends Thread { try { inputStream = new FileInputStream(file); } catch (FileNotFoundException e) { - sendStatusUpdate(PLAYER_ERROR); + sendStatusUpdate(PLAYER_ERROR, file.getName()); e.printStackTrace(); - return; + return false; } byte[] codec2Buffer = new byte[_codec2FrameSize]; try { while (inputStream.read(codec2Buffer) == _codec2FrameSize) { + if (_stopPlayback) { + return false; + } Codec2.decode(_codec2Con, _playbackAudioBuffer, codec2Buffer); _systemAudioPlayer.write(_playbackAudioBuffer, 0, _audioBufferSize); } } catch (IOException e) { - sendStatusUpdate(PLAYER_ERROR); + sendStatusUpdate(PLAYER_ERROR, file.getName()); e.printStackTrace(); - return; + return false; } try { @@ -133,15 +139,18 @@ public class AudioPlayer extends Thread { } catch (IOException e) { e.printStackTrace(); } + return true; } private void play() { _systemAudioPlayer.play(); for (File file : _files) { if (file.isDirectory() || !file.getName().endsWith(".c2")) continue; - sendStatusUpdate(PLAYER_PLAYING_FILE); - playFile(file); - sendStatusUpdate(PLAYER_PLAYED_FILE); + sendStatusUpdate(PLAYER_PLAYING_FILE, file.getName()); + if (!playFile(file)) { + break; + } + sendStatusUpdate(PLAYER_PLAYED_FILE, file.getName()); } _systemAudioPlayer.stop(); _systemAudioPlayer.release(); @@ -152,8 +161,12 @@ public class AudioPlayer extends Thread { @Override public void run() { - sendStatusUpdate(PLAYER_STARTED); + sendStatusUpdate(PLAYER_STARTED, null); play(); - sendStatusUpdate(PLAYER_STOPPED); + sendStatusUpdate(PLAYER_STOPPED, null); + } + + public void stopPlayback() { + _stopPlayback = true; } } diff --git a/codec2talkie/src/main/java/com/radio/codec2talkie/recorder/RecorderActivity.java b/codec2talkie/src/main/java/com/radio/codec2talkie/recorder/RecorderActivity.java index fef1405..7ee3150 100644 --- a/codec2talkie/src/main/java/com/radio/codec2talkie/recorder/RecorderActivity.java +++ b/codec2talkie/src/main/java/com/radio/codec2talkie/recorder/RecorderActivity.java @@ -14,6 +14,7 @@ import android.view.View; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.ListView; +import android.widget.TextView; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; @@ -36,6 +37,8 @@ public class RecorderActivity extends AppCompatActivity { private File _currentDirectory; private ArrayAdapter _dirAdapter; private ListView _recordingList; + private TextView _textPlaybackStatus; + private AudioPlayer _audioPlayer; @Override protected void onCreate(Bundle savedInstanceState) { @@ -47,6 +50,9 @@ public class RecorderActivity extends AppCompatActivity { _recordingList.setOnItemClickListener(onFileClickListener); _recordingList.setAdapter(_dirAdapter); + _textPlaybackStatus = findViewById(R.id.textPlaybackStatus); + _textPlaybackStatus.setText(R.string.player_status_stopped); + _root = StorageTools.getStorage(getApplicationContext()); loadFiles(_root); } @@ -55,6 +61,22 @@ public class RecorderActivity extends AppCompatActivity { @Override public void handleMessage(Message msg) { switch (msg.what) { + case AudioPlayer.PLAYER_STARTED: + _textPlaybackStatus.setText(R.string.player_status_started); + break; + case AudioPlayer.PLAYER_STOPPED: + _textPlaybackStatus.setText(getString(R.string.player_status_stopped)); + _audioPlayer = null; + break; + case AudioPlayer.PLAYER_ERROR: + _textPlaybackStatus.setText(getString(R.string.player_status_error, msg.obj)); + break; + case AudioPlayer.PLAYER_PLAYING_FILE: + _textPlaybackStatus.setText(getString(R.string.player_status_playing_file, msg.obj)); + break; + case AudioPlayer.PLAYER_PLAYED_FILE: + _textPlaybackStatus.setText(getString(R.string.player_status_played_file, msg.obj)); + break; } } }; @@ -64,9 +86,10 @@ public class RecorderActivity extends AppCompatActivity { File selectedFile = new File(_currentDirectory, selectedItem.toString()); if (selectedFile.isDirectory()) { loadFiles(selectedFile); - } else { + } else if (_audioPlayer == null) { File[] files = {selectedFile}; - new AudioPlayer(files, onPlayerStateChanged, this).start(); + _audioPlayer = new AudioPlayer(files, onPlayerStateChanged, this); + _audioPlayer.start(); } }; @@ -108,8 +131,11 @@ public class RecorderActivity extends AppCompatActivity { } private void playAll() { - File[] files = _currentDirectory.listFiles(); - new AudioPlayer(files, onPlayerStateChanged, this).start(); + if (_audioPlayer == null) { + File[] files = _currentDirectory.listFiles(); + _audioPlayer = new AudioPlayer(files, onPlayerStateChanged, this); + _audioPlayer.start(); + } } @Override @@ -154,6 +180,11 @@ public class RecorderActivity extends AppCompatActivity { runConfirmation(); return true; } + else if (itemId == R.id.recorder_stop) { + if (_audioPlayer != null) { + _audioPlayer.stopPlayback(); + } + } return super.onOptionsItemSelected(item); } diff --git a/codec2talkie/src/main/res/layout/activity_recorder.xml b/codec2talkie/src/main/res/layout/activity_recorder.xml index 343ffde..bd507eb 100644 --- a/codec2talkie/src/main/res/layout/activity_recorder.xml +++ b/codec2talkie/src/main/res/layout/activity_recorder.xml @@ -7,9 +7,25 @@ android:orientation="vertical" tools:context=".recorder.RecorderActivity"> + + + android:layout_height="match_parent" + android:layout_marginTop="32dp" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/textPlaybackStatus"> \ No newline at end of file diff --git a/codec2talkie/src/main/res/menu/recorder_menu.xml b/codec2talkie/src/main/res/menu/recorder_menu.xml index 344283f..f2a1615 100644 --- a/codec2talkie/src/main/res/menu/recorder_menu.xml +++ b/codec2talkie/src/main/res/menu/recorder_menu.xml @@ -1,9 +1,12 @@ + + android:title="@string/recorder_menu_play_all"> + android:title="@string/recorder_menu_delete_all"> \ No newline at end of file diff --git a/codec2talkie/src/main/res/values/strings.xml b/codec2talkie/src/main/res/values/strings.xml index 918837a..dfd6177 100644 --- a/codec2talkie/src/main/res/values/strings.xml +++ b/codec2talkie/src/main/res/values/strings.xml @@ -106,13 +106,19 @@ Keep screen ON Prevent screen switching off when app is active Recorder - Play All - Delete All + Play All + Delete All Remove recordings from the list? Remove confirmation OK Cancel Recorder REC + Playback stopped + Playback started + Playback error, %1$s + Playing file: %1$s + Played file: %1$s + Stop \ No newline at end of file From 50aeb3f90b199ea26fe97865599f9fdf331dda63 Mon Sep 17 00:00:00 2001 From: sh123 Date: Sun, 10 Oct 2021 17:18:11 +0300 Subject: [PATCH 22/23] Code cleanup --- .../com/radio/codec2talkie/MainActivity.java | 2 - .../codec2talkie/protocol/VoicemailProxy.java | 2 +- .../recorder/RecorderActivity.java | 41 +++++++++++++++---- .../src/main/res/layout/activity_recorder.xml | 5 ++- codec2talkie/src/main/res/menu/main_menu.xml | 2 +- codec2talkie/src/main/res/values/strings.xml | 11 ++--- codec2talkie/src/main/res/xml/preferences.xml | 4 +- 7 files changed, 47 insertions(+), 20 deletions(-) diff --git a/codec2talkie/src/main/java/com/radio/codec2talkie/MainActivity.java b/codec2talkie/src/main/java/com/radio/codec2talkie/MainActivity.java index 0f13529..04eaf88 100644 --- a/codec2talkie/src/main/java/com/radio/codec2talkie/MainActivity.java +++ b/codec2talkie/src/main/java/com/radio/codec2talkie/MainActivity.java @@ -56,8 +56,6 @@ import java.util.Locale; public class MainActivity extends AppCompatActivity { - private static final String TAG = MainActivity.class.getSimpleName(); - private final static int REQUEST_CONNECT_BT = 1; private final static int REQUEST_CONNECT_USB = 2; private final static int REQUEST_PERMISSIONS = 3; diff --git a/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/VoicemailProxy.java b/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/VoicemailProxy.java index 2490487..70cbe8a 100644 --- a/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/VoicemailProxy.java +++ b/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/VoicemailProxy.java @@ -109,7 +109,7 @@ public class VoicemailProxy implements Protocol { } private String getNewFileName(Date date) { - SimpleDateFormat tf = new SimpleDateFormat("HH_mm_ss", Locale.ENGLISH); + SimpleDateFormat tf = new SimpleDateFormat("HHmmss", Locale.ENGLISH); String codec2mode = String.format(Locale.ENGLISH, "%02d", _codec2ModeId); return codec2mode + "_" + tf.format(date) + ".c2"; } diff --git a/codec2talkie/src/main/java/com/radio/codec2talkie/recorder/RecorderActivity.java b/codec2talkie/src/main/java/com/radio/codec2talkie/recorder/RecorderActivity.java index 7ee3150..43b667d 100644 --- a/codec2talkie/src/main/java/com/radio/codec2talkie/recorder/RecorderActivity.java +++ b/codec2talkie/src/main/java/com/radio/codec2talkie/recorder/RecorderActivity.java @@ -5,7 +5,6 @@ import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.Message; -import android.provider.MediaStore; import android.util.Log; import android.view.KeyEvent; import android.view.Menu; @@ -48,6 +47,7 @@ public class RecorderActivity extends AppCompatActivity { _dirAdapter = new ArrayAdapter<>(this, R.layout.support_simple_spinner_dropdown_item); _recordingList = findViewById(R.id.listRecorder); _recordingList.setOnItemClickListener(onFileClickListener); + _recordingList.setOnItemLongClickListener(onFileLongClickListener); _recordingList.setAdapter(_dirAdapter); _textPlaybackStatus = findViewById(R.id.textPlaybackStatus); @@ -93,6 +93,20 @@ public class RecorderActivity extends AppCompatActivity { } }; + private final AdapterView.OnItemLongClickListener onFileLongClickListener = (parent, view, position, id) -> { + Object selectedItem = parent.getAdapter().getItem(position); + File selectedFile = new File(_currentDirectory, selectedItem.toString()); + if (selectedFile.isDirectory()) { + runDeleteFromDirectoryConfirmation(selectedFile); + if (selectedFile.delete()) { + loadFiles(_currentDirectory); + } + } else { + runDeleteFileConfirmation(selectedFile); + } + return true; + }; + private void loadFiles(File directory) { _dirAdapter.clear(); _currentDirectory = directory; @@ -118,8 +132,8 @@ public class RecorderActivity extends AppCompatActivity { _recordingList.setVisibility(View.VISIBLE); } - private void deleteAll() { - File[] fileList = _currentDirectory.listFiles(); + private void deleteAll(File directory) { + File[] fileList = directory.listFiles(); if (fileList != null) { for (File file : fileList) { if (!file.delete()) { @@ -152,11 +166,24 @@ public class RecorderActivity extends AppCompatActivity { return super.onKeyDown(keyCode, event); } - private void runConfirmation() { + private void runDeleteFromDirectoryConfirmation(File directory) { AlertDialog.Builder alertBuilder = new AlertDialog.Builder(this); - alertBuilder.setMessage(R.string.recorder_remove_all_confirmation_message) + alertBuilder.setMessage(getString(R.string.recorder_remove_all_confirmation_message, directory.getName())) .setTitle(R.string.recorder_remove_all_confirmation_title) - .setPositiveButton(R.string.ok, (dialog, id) -> deleteAll()) + .setPositiveButton(R.string.ok, (dialog, id) -> deleteAll(directory)) + .setNegativeButton(R.string.cancel, (dialog, id) -> {}) + .show(); + } + + private void runDeleteFileConfirmation(File file) { + AlertDialog.Builder alertBuilder = new AlertDialog.Builder(this); + alertBuilder.setMessage(getString(R.string.recorder_remove_file_confirmation_message, file.getName())) + .setTitle(R.string.recorder_remove_all_confirmation_title) + .setPositiveButton(R.string.ok, (dialog, id) -> { + if (file.delete()) { + loadFiles(_currentDirectory); + } + }) .setNegativeButton(R.string.cancel, (dialog, id) -> {}) .show(); } @@ -177,7 +204,7 @@ public class RecorderActivity extends AppCompatActivity { return true; } else if (itemId == R.id.recorder_delete_all) { - runConfirmation(); + runDeleteFromDirectoryConfirmation(_currentDirectory); return true; } else if (itemId == R.id.recorder_stop) { diff --git a/codec2talkie/src/main/res/layout/activity_recorder.xml b/codec2talkie/src/main/res/layout/activity_recorder.xml index bd507eb..12778f2 100644 --- a/codec2talkie/src/main/res/layout/activity_recorder.xml +++ b/codec2talkie/src/main/res/layout/activity_recorder.xml @@ -12,8 +12,9 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="8dp" + android:layout_marginTop="8dp" android:text="status" - android:textSize="16sp" + android:textSize="14sp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> @@ -26,6 +27,6 @@ app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@+id/textPlaybackStatus"> + app:layout_constraintTop_toBottomOf="@+id/textPlaybackStatus" /> \ No newline at end of file diff --git a/codec2talkie/src/main/res/menu/main_menu.xml b/codec2talkie/src/main/res/menu/main_menu.xml index 0f6bce0..57a315c 100644 --- a/codec2talkie/src/main/res/menu/main_menu.xml +++ b/codec2talkie/src/main/res/menu/main_menu.xml @@ -3,7 +3,7 @@ + android:title="@string/menu_recorder"> Mode/Speed Echo Test Mode Records and plays recording without transmission - Enable Recorder - Record incoming and outgoing transmissions for future playback + Enable Recorder + Record incoming and outgoing transmissions for future playback MODE_450=10 MODE_700C=8 @@ -54,7 +54,7 @@ Set CSMA and TX delay/tail parameters CSMA persistence P - Set CSMA persistence P (1-255) + Set CSMA persistence P (1–255) CSMA slot time Set CSMA slot time (milliseconds / 10) @@ -105,10 +105,10 @@ Keep screen ON Prevent screen switching off when app is active - Recorder + Play Recordings Play All Delete All - Remove recordings from the list? + Remove all recordings from %1$s? Remove confirmation OK Cancel @@ -120,5 +120,6 @@ Playing file: %1$s Played file: %1$s Stop + Remove recording %1$s? \ No newline at end of file diff --git a/codec2talkie/src/main/res/xml/preferences.xml b/codec2talkie/src/main/res/xml/preferences.xml index 5c33183..79961f1 100644 --- a/codec2talkie/src/main/res/xml/preferences.xml +++ b/codec2talkie/src/main/res/xml/preferences.xml @@ -42,8 +42,8 @@ From bc563a11565fe669b2ca0e68e03baa2b92c8ed73 Mon Sep 17 00:00:00 2001 From: sh123 Date: Sun, 10 Oct 2021 17:23:07 +0300 Subject: [PATCH 23/23] Update readme --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 3865579..fbf90d1 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,8 @@ It does not deal with radio management, modulation, etc, it is up to your modem - Keep screen ON - **Codec2** - Set Codec2 mode/speed from 450 up to 3200 bps - - Enable/disable loopback test mode + - Enable/disable echo/loopback test mode + - Enable/disable RX/TX recorder - **TNC parameters** - Change default baud rate for USB port - Set default Bluetooth device for automatic connectivity on startup @@ -61,6 +62,7 @@ It does not deal with radio management, modulation, etc, it is up to your modem - Enable/Disable KISS non-real time buffered playback mode - Enable/Disable KISS extensions for radio module control and signal levels (modem must support them to work correctly!) - Set radio parameters (frequency, bandwidth, spreading factor, coding rate, power, sync word, crc checksum enable/disable) +- **Recording player**, simple player, which allows TX/RX recording playback and removal # Suitable radios and modems - Tested, works: