codec2_talkie/codec2talkie/src/main/java/com/radio/codec2talkie/app/AppWorker.java

519 wiersze
18 KiB
Java
Czysty Zwykły widok Historia

package com.radio.codec2talkie.app;
2022-07-06 09:25:18 +00:00
import android.app.Application;
import android.content.Context;
import android.content.SharedPreferences;
import android.media.AudioAttributes;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.AudioTrack;
import android.media.MediaRecorder;
2020-12-03 13:25:02 +00:00
import android.os.Handler;
2021-01-30 19:32:25 +00:00
import android.os.Looper;
2020-12-03 13:25:02 +00:00
import android.os.Message;
2021-01-28 14:12:16 +00:00
import android.util.Log;
2022-07-18 17:17:40 +00:00
import android.util.Pair;
import androidx.preference.PreferenceManager;
import java.io.IOException;
2021-01-30 19:32:25 +00:00
import java.util.Timer;
import java.util.TimerTask;
2022-07-04 19:18:49 +00:00
import com.radio.codec2talkie.R;
2022-07-18 17:17:40 +00:00
import com.radio.codec2talkie.protocol.message.TextMessage;
2022-07-10 16:55:47 +00:00
import com.radio.codec2talkie.storage.log.LogItem;
import com.radio.codec2talkie.storage.log.LogItemRepository;
2022-07-03 09:05:00 +00:00
import com.radio.codec2talkie.protocol.ProtocolCallback;
import com.radio.codec2talkie.protocol.Protocol;
import com.radio.codec2talkie.protocol.ProtocolFactory;
import com.radio.codec2talkie.protocol.position.Position;
import com.radio.codec2talkie.settings.PreferenceKeys;
2022-07-18 19:24:48 +00:00
import com.radio.codec2talkie.storage.message.MessageItem;
import com.radio.codec2talkie.storage.message.MessageItemRepository;
2021-01-27 10:13:07 +00:00
import com.radio.codec2talkie.tools.AudioTools;
import com.radio.codec2talkie.transport.Transport;
2021-01-27 10:52:54 +00:00
import com.radio.codec2talkie.transport.TransportFactory;
2020-11-29 19:37:39 +00:00
public class AppWorker extends Thread {
private static final String TAG = AppWorker.class.getSimpleName();
2020-12-12 09:25:56 +00:00
2021-10-09 17:27:17 +00:00
private static final int AUDIO_MIN_LEVEL = -70;
private static final int AUDIO_MAX_LEVEL = 0;
2022-07-09 11:33:27 +00:00
private static final int AUDIO_SAMPLE_SIZE = 8000;
2021-01-31 12:07:20 +00:00
2022-07-09 11:33:27 +00:00
private static final int PROCESS_INTERVAL_MS = 20;
private static final int LISTEN_AFTER_MS = 1500;
2020-12-04 20:20:14 +00:00
2022-07-09 11:33:27 +00:00
private boolean _needTransmission = false;
2022-07-05 11:59:15 +00:00
private AppMessage _currentStatus = AppMessage.EV_DISCONNECTED;
2020-12-03 08:59:13 +00:00
private final Protocol _protocol;
2021-01-27 10:52:54 +00:00
private final Transport _transport;
private final int _codec2Mode;
// input data, bt -> audio
2021-01-27 10:52:54 +00:00
private AudioTrack _systemAudioPlayer;
2020-12-02 19:45:03 +00:00
2020-12-02 16:33:38 +00:00
// output data., mic -> bt
2021-01-27 10:52:54 +00:00
private AudioRecord _systemAudioRecorder;
private final short[] _recordAudioBuffer;
2020-12-02 19:45:03 +00:00
2020-12-04 20:20:14 +00:00
// callbacks
private final Handler _onPlayerStateChanged;
private Handler _onMessageReceived;
private final Timer _processPeriodicTimer;
2020-12-04 13:50:01 +00:00
2021-01-30 19:32:25 +00:00
// listen timer
private Timer _listenTimer;
2022-07-18 19:24:48 +00:00
// storage integration
2022-07-06 09:25:18 +00:00
private final LogItemRepository _logItemRepository;
2022-07-18 19:24:48 +00:00
private final MessageItemRepository _messageItemRepository;
2022-07-06 09:25:18 +00:00
2021-02-10 15:49:33 +00:00
private final Context _context;
private final SharedPreferences _sharedPreferences;
2022-07-04 19:18:49 +00:00
public AppWorker(TransportFactory.TransportType transportType,
Handler onPlayerStateChanged, Context context) throws IOException {
2020-12-03 13:25:02 +00:00
_onPlayerStateChanged = onPlayerStateChanged;
_context = context;
_sharedPreferences = PreferenceManager.getDefaultSharedPreferences(_context);
2022-07-04 19:18:49 +00:00
String codec2ModeName = _sharedPreferences.getString(PreferenceKeys.CODEC2_MODE, _context.getResources().getStringArray(R.array.codec2_modes)[0]);
_codec2Mode = AudioTools.extractCodec2ModeId(codec2ModeName);
2021-01-27 11:01:00 +00:00
_transport = TransportFactory.create(transportType);
2022-07-04 19:18:49 +00:00
_protocol = ProtocolFactory.create(_codec2Mode, context);
2021-01-27 10:52:54 +00:00
_processPeriodicTimer = new Timer();
2022-06-30 15:59:13 +00:00
_recordAudioBuffer = new short[_protocol.getPcmAudioBufferSize()];
2022-07-06 09:25:18 +00:00
_logItemRepository = new LogItemRepository((Application)context);
2022-07-18 19:24:48 +00:00
_messageItemRepository = new MessageItemRepository((Application)context);
2022-07-06 09:25:18 +00:00
2021-01-27 10:52:54 +00:00
constructSystemAudioDevices();
}
private void constructSystemAudioDevices() {
2020-12-04 20:20:14 +00:00
int _audioRecorderMinBufferSize = AudioRecord.getMinBufferSize(
2020-12-03 17:17:11 +00:00
AUDIO_SAMPLE_SIZE,
AudioFormat.CHANNEL_IN_MONO,
AudioFormat.ENCODING_PCM_16BIT);
boolean isVoiceCommunication = _sharedPreferences.getBoolean(PreferenceKeys.APP_AUDIO_INPUT_VOICE_COMMUNICATION, false);
int audioSource = MediaRecorder.AudioSource.MIC;
if (isVoiceCommunication) {
audioSource = MediaRecorder.AudioSource.VOICE_COMMUNICATION;
}
2021-01-27 10:52:54 +00:00
_systemAudioRecorder = new AudioRecord(
audioSource,
2020-12-03 17:17:11 +00:00
AUDIO_SAMPLE_SIZE,
2020-11-29 19:37:39 +00:00
AudioFormat.CHANNEL_IN_MONO,
AudioFormat.ENCODING_PCM_16BIT,
10 * _audioRecorderMinBufferSize);
2020-12-04 20:20:14 +00:00
int _audioPlayerMinBufferSize = AudioTrack.getMinBufferSize(
2020-12-03 17:17:11 +00:00
AUDIO_SAMPLE_SIZE,
AudioFormat.CHANNEL_OUT_MONO,
AudioFormat.ENCODING_PCM_16BIT);
boolean isSpeakerOutput = _sharedPreferences.getBoolean(PreferenceKeys.APP_AUDIO_OUTPUT_SPEAKER, true);
int usage = AudioAttributes.USAGE_VOICE_COMMUNICATION;
if (isSpeakerOutput) {
usage = AudioAttributes.USAGE_MEDIA;
}
2021-01-27 10:52:54 +00:00
_systemAudioPlayer = new AudioTrack.Builder()
.setAudioAttributes(new AudioAttributes.Builder()
.setUsage(usage)
.setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
.build())
.setAudioFormat(new AudioFormat.Builder()
.setEncoding(AudioFormat.ENCODING_PCM_16BIT)
2020-12-03 17:17:11 +00:00
.setSampleRate(AUDIO_SAMPLE_SIZE)
.setChannelMask(AudioFormat.CHANNEL_OUT_MONO)
.build())
2020-12-04 13:50:01 +00:00
.setTransferMode(AudioTrack.MODE_STREAM)
.setBufferSizeInBytes(10 * _audioPlayerMinBufferSize)
.build();
2020-12-03 17:17:11 +00:00
}
public static int getAudioMinLevel() {
return AUDIO_MIN_LEVEL;
}
public static int getAudioMaxLevel() {
return AUDIO_MAX_LEVEL;
}
public String getTransportName() {
return _transport.name();
}
2022-07-05 07:47:58 +00:00
public void startReceive() {
2022-07-09 11:33:27 +00:00
_needTransmission = false;
}
2022-07-05 07:47:58 +00:00
public void startTransmit() {
2022-07-09 11:33:27 +00:00
_needTransmission = true;
}
public void stopRunning() {
2022-07-05 11:59:15 +00:00
if (_currentStatus == AppMessage.EV_DISCONNECTED) return;
Log.i(TAG, "stopRunning()");
Message msg = new Message();
2022-07-05 11:59:15 +00:00
msg.what = AppMessage.CMD_QUIT.toInt();
_onMessageReceived.sendMessage(msg);
}
2022-07-09 11:33:27 +00:00
public void sendPositionToTnc(Position position) {
2022-07-05 11:59:15 +00:00
if (_currentStatus == AppMessage.EV_DISCONNECTED) return;
Message msg = new Message();
2022-07-09 11:33:27 +00:00
msg.what = AppMessage.CMD_SEND_LOCATION_TO_TNC.toInt();
2022-07-03 12:19:07 +00:00
msg.obj = position;
_onMessageReceived.sendMessage(msg);
}
2022-07-18 17:17:40 +00:00
public void sendTextMessage(TextMessage textMessage) {
Message msg = Message.obtain();
msg.what = AppMessage.CMD_SEND_MESSAGE.toInt();
msg.obj = textMessage;
_onMessageReceived.sendMessage(msg);
}
2022-07-05 11:59:15 +00:00
private void sendStatusUpdate(AppMessage newStatus, String note) {
2022-07-03 17:49:11 +00:00
2021-01-31 18:22:47 +00:00
if (newStatus != _currentStatus) {
_currentStatus = newStatus;
Message msg = Message.obtain();
2022-07-05 11:59:15 +00:00
msg.what = newStatus.toInt();
2022-07-01 12:18:38 +00:00
if (note != null) {
msg.obj = note;
}
2021-01-31 18:22:47 +00:00
_onPlayerStateChanged.sendMessage(msg);
}
2022-07-05 11:59:15 +00:00
if (newStatus != AppMessage.EV_LISTENING) {
2022-07-03 17:49:11 +00:00
restartListening();
}
2020-12-10 14:25:11 +00:00
}
2021-02-06 11:45:09 +00:00
private void sendRxRadioLevelUpdate(int rssi, int snr) {
Message msg = Message.obtain();
2022-07-05 11:59:15 +00:00
msg.what = AppMessage.EV_RX_RADIO_LEVEL.toInt();
2021-02-06 11:45:09 +00:00
msg.arg1 = rssi;
msg.arg2 = snr;
_onPlayerStateChanged.sendMessage(msg);
}
2021-01-27 10:52:54 +00:00
private void sendRxAudioLevelUpdate(short [] pcmAudioSamples) {
2020-12-09 17:10:01 +00:00
Message msg = Message.obtain();
2022-07-05 11:59:15 +00:00
msg.what = AppMessage.EV_RX_LEVEL.toInt();
2021-01-27 10:52:54 +00:00
msg.arg1 = AudioTools.getSampleLevelDb(pcmAudioSamples);
_onPlayerStateChanged.sendMessage(msg);
}
private void sendTxAudioLevelUpdate(short [] pcmAudioSamples) {
Message msg = Message.obtain();
2022-07-05 11:59:15 +00:00
msg.what = AppMessage.EV_TX_LEVEL.toInt();
2021-01-27 10:13:07 +00:00
msg.arg1 = AudioTools.getSampleLevelDb(pcmAudioSamples);
2020-12-09 17:10:01 +00:00
_onPlayerStateChanged.sendMessage(msg);
}
2021-01-27 10:13:07 +00:00
private void recordAndSendAudioFrame() throws IOException {
_systemAudioRecorder.read(_recordAudioBuffer, 0, _recordAudioBuffer.length);
2022-07-01 14:18:37 +00:00
_protocol.sendPcmAudio(null, null, _codec2Mode, _recordAudioBuffer);
2020-12-02 19:45:03 +00:00
}
2022-07-03 09:05:00 +00:00
private final ProtocolCallback _protocolCallback = new ProtocolCallback() {
2022-06-29 19:23:02 +00:00
@Override
protected void onReceivePosition(Position position) {
2022-06-29 19:23:02 +00:00
throw new UnsupportedOperationException();
}
@Override
protected void onReceivePcmAudio(String src, String dst, int codec, short[] pcmFrame) {
2022-07-01 12:18:38 +00:00
String note = (src == null ? "UNK" : src) + "→" + (dst == null ? "UNK" : dst);
2022-07-10 16:22:29 +00:00
sendStatusUpdate(AppMessage.EV_VOICE_RECEIVED, note);
sendRxAudioLevelUpdate(pcmFrame);
_systemAudioPlayer.write(pcmFrame, 0, pcmFrame.length);
}
@Override
protected void onReceiveCompressedAudio(String src, String dst, int codec2Mode, byte[] audioFrame) {
2022-06-29 21:13:48 +00:00
throw new UnsupportedOperationException();
}
2022-07-18 19:24:48 +00:00
@Override
protected void onReceiveTextMessage(TextMessage textMessage) {
String note = (textMessage.src == null ? "UNK" : textMessage.src) + "→" +
(textMessage.dst == null ? "UNK" : textMessage.dst);
2022-07-18 20:08:05 +00:00
sendStatusUpdate(AppMessage.EV_TEXT_MESSAGE_RECEIVED, note);
2022-07-18 19:24:48 +00:00
MessageItem messageItem = new MessageItem();
messageItem.setTimestampEpoch(System.currentTimeMillis());
messageItem.setNeedsAck(false); // TODO
messageItem.setIsTransmit(false);
messageItem.setSrcCallsign(textMessage.src);
messageItem.setDstCallsign(textMessage.dst);
messageItem.setMessage(textMessage.text);
_messageItemRepository.insertMessageItem(messageItem);
2022-07-19 08:57:15 +00:00
Log.i(TAG, "message received: " + textMessage.text);
2022-07-18 19:24:48 +00:00
}
2022-06-26 13:45:43 +00:00
@Override
protected void onReceiveData(String src, String dst, byte[] data) {
2022-07-03 17:49:11 +00:00
String note = (src == null ? "UNK" : src) + "→" + (dst == null ? "UNK" : dst);
2022-07-10 16:22:29 +00:00
sendStatusUpdate(AppMessage.EV_DATA_RECEIVED, note);
2022-06-26 13:45:43 +00:00
}
@Override
2022-07-01 08:51:55 +00:00
protected void onReceiveSignalLevel(short rssi, short snr) {
sendRxRadioLevelUpdate(rssi, snr);
}
2022-07-01 14:39:45 +00:00
@Override
protected void onReceiveLog(String logData) {
2022-07-05 18:32:22 +00:00
Log.i(TAG, "RX: " + logData);
2022-07-06 08:32:31 +00:00
storeLogData(logData, false);
2022-07-01 14:39:45 +00:00
}
2022-07-01 14:18:37 +00:00
@Override
protected void onTransmitPcmAudio(String src, String dst, int codec, short[] frame) {
String note = (src == null ? "UNK" : src) + "→" + (dst == null ? "UNK" : dst);
2022-07-10 16:22:29 +00:00
sendStatusUpdate(AppMessage.EV_TRANSMITTED_VOICE, note);
2022-07-01 14:18:37 +00:00
sendTxAudioLevelUpdate(frame);
}
@Override
protected void onTransmitCompressedAudio(String src, String dst, int codec, byte[] frame) {
throw new UnsupportedOperationException();
}
2022-07-18 19:24:48 +00:00
@Override
protected void onTransmitTextMessage(TextMessage textMessage) {
String note = (textMessage.src == null ? "UNK" : textMessage.src) + "→" +
(textMessage.dst == null ? "UNK" : textMessage.dst);
2022-07-18 20:08:05 +00:00
sendStatusUpdate(AppMessage.EV_TEXT_MESSAGE_TRANSMITTED, note);
2022-07-18 19:24:48 +00:00
MessageItem messageItem = new MessageItem();
messageItem.setTimestampEpoch(System.currentTimeMillis());
messageItem.setNeedsAck(false); // TODO
messageItem.setIsTransmit(true);
messageItem.setSrcCallsign(textMessage.src);
messageItem.setDstCallsign(textMessage.dst);
messageItem.setMessage(textMessage.text);
_messageItemRepository.insertMessageItem(messageItem);
}
2022-07-01 14:18:37 +00:00
@Override
protected void onTransmitData(String src, String dst, byte[] data) {
String note = (src == null ? "UNK" : src) + "→" + (dst == null ? "UNK" : dst);
2022-07-10 16:22:29 +00:00
sendStatusUpdate(AppMessage.EV_TRANSMITTED_VOICE, note);
2022-07-01 14:18:37 +00:00
}
2022-07-01 14:39:45 +00:00
@Override
protected void onTransmitLog(String logData) {
2022-07-05 18:32:22 +00:00
Log.i(TAG, "TX: " + logData);
2022-07-06 08:32:31 +00:00
storeLogData(logData, true);
2022-07-01 14:39:45 +00:00
}
@Override
2021-10-19 18:20:11 +00:00
protected void onProtocolRxError() {
2022-07-05 11:59:15 +00:00
sendStatusUpdate(AppMessage.EV_RX_ERROR, null);
2021-10-19 18:20:11 +00:00
Log.e(TAG, "Protocol RX error");
}
2022-07-01 14:18:37 +00:00
@Override
protected void onProtocolTxError() {
2022-07-05 11:59:15 +00:00
sendStatusUpdate(AppMessage.EV_TX_ERROR, null);
2022-07-01 14:18:37 +00:00
Log.e(TAG, "Protocol TX error");
}
};
2022-07-06 08:32:31 +00:00
void storeLogData(String logData, boolean isTransmit) {
2022-07-06 09:25:18 +00:00
// TODO, parse through aprs data
2022-07-06 08:32:31 +00:00
String[] callsignData = logData.split(">");
if (callsignData.length >= 2) {
LogItem logItem = new LogItem();
logItem.setTimestampEpoch(System.currentTimeMillis());
logItem.setSrcCallsign(callsignData[0]);
logItem.setLogLine(logData);
logItem.setIsTransmit(isTransmit);
2022-07-06 09:25:18 +00:00
_logItemRepository.insertLogItem(logItem);
2022-07-06 08:32:31 +00:00
}
}
2021-01-30 19:32:25 +00:00
private void restartListening() {
cancelListening();
startListening();
}
private void startListening() {
2022-07-05 11:59:15 +00:00
if (_currentStatus == AppMessage.EV_LISTENING) {
2021-01-30 19:32:25 +00:00
return;
}
_listenTimer = new Timer();
_listenTimer.schedule(new TimerTask() {
@Override
public void run() {
onListening();
}
2021-01-31 12:07:20 +00:00
}, LISTEN_AFTER_MS);
2021-01-30 19:32:25 +00:00
}
private void cancelListening() {
try {
if (_listenTimer != null) {
_listenTimer.cancel();
_listenTimer.purge();
}
} catch (IllegalStateException e) {
e.printStackTrace();
}
}
private void onListening() {
sendRxAudioLevelUpdate(null);
sendTxAudioLevelUpdate(null);
2021-02-06 11:45:09 +00:00
sendRxRadioLevelUpdate(0, 0);
2022-07-05 11:59:15 +00:00
sendStatusUpdate(AppMessage.EV_LISTENING, null);
2021-01-30 19:32:25 +00:00
}
2020-12-03 17:17:11 +00:00
private void processRecordPlaybackToggle() throws IOException {
2020-12-04 15:11:47 +00:00
// playback -> recording
2022-07-09 11:33:27 +00:00
if (_needTransmission && _systemAudioRecorder.getRecordingState() != AudioRecord.RECORDSTATE_RECORDING) {
2021-01-27 10:52:54 +00:00
_systemAudioPlayer.stop();
_systemAudioRecorder.startRecording();
sendRxAudioLevelUpdate(null);
2020-12-03 12:50:53 +00:00
}
2020-12-04 15:11:47 +00:00
// recording -> playback
2022-07-09 11:33:27 +00:00
if (!_needTransmission && _systemAudioRecorder.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING) {
2021-01-27 10:52:54 +00:00
_protocol.flush();
_systemAudioRecorder.stop();
_systemAudioPlayer.play();
sendTxAudioLevelUpdate(null);
2020-12-03 12:50:53 +00:00
}
}
2020-12-04 20:11:37 +00:00
private void cleanup() {
Log.i(TAG, "cleanup() started");
2021-01-27 10:52:54 +00:00
_systemAudioRecorder.stop();
_systemAudioRecorder.release();
2020-12-04 20:11:37 +00:00
2021-01-27 10:52:54 +00:00
_systemAudioPlayer.stop();
_systemAudioPlayer.release();
2020-12-04 20:11:37 +00:00
try {
2021-01-27 10:52:54 +00:00
_protocol.flush();
} catch (IOException e) {
e.printStackTrace();
}
_protocol.close();
try {
_transport.close();
} catch (IOException e) {
e.printStackTrace();
2020-12-12 09:54:03 +00:00
}
Log.i(TAG, "cleanup() completed");
2020-12-04 20:11:37 +00:00
}
private void processRxTx() throws IOException {
2021-01-27 10:52:54 +00:00
processRecordPlaybackToggle();
// recording
if (_systemAudioRecorder.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING) {
recordAndSendAudioFrame();
} else {
// playback
2022-07-01 14:18:37 +00:00
if (_protocol.receive()) {
2022-07-05 11:59:15 +00:00
sendStatusUpdate(AppMessage.EV_RECEIVING, null);
2021-01-27 10:52:54 +00:00
}
}
}
private void quitProcessing() {
Log.i(TAG, "quitProcessing()");
_processPeriodicTimer.cancel();
_processPeriodicTimer.purge();
Looper.myLooper().quitSafely();
}
2022-07-09 11:33:27 +00:00
private void onWorkerIncomingMessage(Message msg) {
2022-07-05 11:59:15 +00:00
switch (AppMessage.values()[msg.what]) {
case CMD_PROCESS:
try {
processRxTx();
} catch (IOException e) {
e.printStackTrace();
quitProcessing();
}
break;
2022-07-05 11:59:15 +00:00
case CMD_QUIT:
quitProcessing();
break;
2022-07-09 11:33:27 +00:00
case CMD_SEND_LOCATION_TO_TNC:
try {
2022-07-03 12:19:07 +00:00
_protocol.sendPosition((Position)msg.obj);
} catch (IOException e) {
e.printStackTrace();
quitProcessing();
}
break;
2022-07-18 17:17:40 +00:00
case CMD_SEND_MESSAGE:
TextMessage textMessage = (TextMessage) msg.obj;
try {
_protocol.sendTextMessage(textMessage);
} catch (IOException e) {
e.printStackTrace();
quitProcessing();
}
break;
default:
break;
}
}
2022-07-09 11:33:27 +00:00
private void startWorkerMessageHandler() {
_onMessageReceived = new Handler(Looper.myLooper()) {
@Override
public void handleMessage(Message msg) {
2022-07-09 11:33:27 +00:00
onWorkerIncomingMessage(msg);
}
};
_processPeriodicTimer.schedule(new TimerTask() {
@Override
public void run() {
Message msg = new Message();
2022-07-05 11:59:15 +00:00
msg.what = AppMessage.CMD_PROCESS.toInt();
_onMessageReceived.sendMessage(msg);
}
2021-01-31 12:07:20 +00:00
}, 0, PROCESS_INTERVAL_MS);
2021-01-27 10:52:54 +00:00
}
2020-12-02 16:27:58 +00:00
@Override
public void run() {
Log.i(TAG, "Starting message loop");
setPriority(Thread.MAX_PRIORITY);
Looper.prepare();
2021-01-28 07:24:14 +00:00
2022-07-05 11:59:15 +00:00
sendStatusUpdate(AppMessage.EV_CONNECTED, null);
2021-01-30 19:32:25 +00:00
_systemAudioPlayer.play();
2021-01-28 07:24:14 +00:00
2020-12-03 13:25:02 +00:00
try {
2022-07-01 14:18:37 +00:00
_protocol.initialize(_transport, _context, _protocolCallback);
2022-07-09 11:33:27 +00:00
startWorkerMessageHandler();
Looper.loop();
} catch (IOException e) {
2020-12-03 13:25:02 +00:00
e.printStackTrace();
}
2020-12-04 15:11:47 +00:00
2020-12-04 20:11:37 +00:00
cleanup();
2022-07-05 11:59:15 +00:00
sendStatusUpdate(AppMessage.EV_DISCONNECTED, null);
Log.i(TAG, "Exiting message loop");
}
}