Merge pull request #3 from brarcher/staging
Better recovery from connection disconnects
This commit is contained in:
@@ -15,10 +15,8 @@ The current version of _Protect Baby Monitor_ is rudimentary at best. It is capa
|
||||
of successfully advertising itself on the network, allows clients to connect,
|
||||
and streams audio. Room for improvement includes:
|
||||
|
||||
1. Decent UI
|
||||
2. Hook into audio controls to adjust volume
|
||||
3. Robust usage of the AudioTrack API
|
||||
4. Handle dropped packets gracefully
|
||||
1. Robust usage of the AudioTrack API
|
||||
2. Handle dropped packets gracefully
|
||||
|
||||
At the time this project was started there was no obvious open source solution for a
|
||||
baby monitor for Android. There are both free and paid options available for Android,
|
||||
@@ -36,3 +34,5 @@ proposed changed.
|
||||
|
||||
App icon originals from [WPZOOM](http://www.wpzoom.com/wpzoom/new-freebie-wpzoom-developer-icon-set-154-free-icons)
|
||||
and formatted using [Android Asset Studio](https://romannurik.github.io/AndroidAssetStudio/index.html).
|
||||
|
||||
Audio file originals from [freesound](https://freesound.org).
|
||||
|
||||
BIN
res/raw/upward_beep_chromatic_fifths.ogg
Normal file
BIN
res/raw/upward_beep_chromatic_fifths.ogg
Normal file
Binary file not shown.
@@ -27,7 +27,6 @@ import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.TableLayout;
|
||||
import android.widget.TableRow;
|
||||
import android.widget.TextView;
|
||||
|
||||
public class DiscoverActivity extends Activity
|
||||
{
|
||||
@@ -99,13 +98,13 @@ public class DiscoverActivity extends Activity
|
||||
public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode)
|
||||
{
|
||||
// Called when the resolve fails. Use the error code to debug.
|
||||
Log.e(TAG, "Resolve failed" + errorCode);
|
||||
Log.e(TAG, "Resolve failed: error " + errorCode + " for service: " + serviceInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceResolved(final NsdServiceInfo serviceInfo)
|
||||
{
|
||||
Log.e(TAG, "Resolve Succeeded. " + serviceInfo);
|
||||
Log.i(TAG, "Resolve Succeeded: " + serviceInfo);
|
||||
|
||||
DiscoverActivity.this.runOnUiThread(new Runnable()
|
||||
{
|
||||
@@ -159,7 +158,7 @@ public class DiscoverActivity extends Activity
|
||||
{
|
||||
// When the network service is no longer available.
|
||||
// Internal bookkeeping code goes here.
|
||||
Log.e(TAG, "service lost" + service);
|
||||
Log.e(TAG, "Service lost: " + service);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -171,14 +170,14 @@ public class DiscoverActivity extends Activity
|
||||
@Override
|
||||
public void onStartDiscoveryFailed(String serviceType, int errorCode)
|
||||
{
|
||||
Log.e(TAG, "Discovery failed: Error code:" + errorCode);
|
||||
Log.e(TAG, "Discovery failed: Error code: " + errorCode);
|
||||
nsdManager.stopServiceDiscovery(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopDiscoveryFailed(String serviceType, int errorCode)
|
||||
{
|
||||
Log.e(TAG, "Discovery failed: Error code:" + errorCode);
|
||||
Log.e(TAG, "Discovery failed: Error code: " + errorCode);
|
||||
nsdManager.stopServiceDiscovery(this);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -28,6 +28,7 @@ import android.app.Activity;
|
||||
import android.media.AudioFormat;
|
||||
import android.media.AudioManager;
|
||||
import android.media.AudioTrack;
|
||||
import android.media.MediaPlayer;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.widget.TextView;
|
||||
@@ -139,18 +140,27 @@ public class ListenActivity extends Activity
|
||||
Log.e(TAG, "Failed to stream audio", e);
|
||||
}
|
||||
|
||||
ListenActivity.this.runOnUiThread(new Runnable()
|
||||
if(Thread.currentThread().isInterrupted() == false)
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
final TextView connectedText = (TextView) findViewById(R.id.connectedTo);
|
||||
connectedText.setText("");
|
||||
// If this thread has not been interrupted, likely something
|
||||
// bad happened with the connection to the child device. Play
|
||||
// an alert to notify the user that the connection has been
|
||||
// interrupted.
|
||||
playAlert();
|
||||
|
||||
final TextView statusText = (TextView) findViewById(R.id.textStatus);
|
||||
statusText.setText(R.string.disconnected);
|
||||
}
|
||||
});
|
||||
ListenActivity.this.runOnUiThread(new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
final TextView connectedText = (TextView) findViewById(R.id.connectedTo);
|
||||
connectedText.setText("");
|
||||
|
||||
final TextView statusText = (TextView) findViewById(R.id.textStatus);
|
||||
statusText.setText(R.string.disconnected);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -165,4 +175,26 @@ public class ListenActivity extends Activity
|
||||
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
private void playAlert()
|
||||
{
|
||||
final MediaPlayer mp = MediaPlayer.create(this, R.raw.upward_beep_chromatic_fifths);
|
||||
if(mp != null)
|
||||
{
|
||||
Log.i(TAG, "Playing alert");
|
||||
mp.setOnCompletionListener(new MediaPlayer.OnCompletionListener()
|
||||
{
|
||||
@Override
|
||||
public void onCompletion(MediaPlayer mp)
|
||||
{
|
||||
mp.release();
|
||||
}
|
||||
});
|
||||
mp.start();
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.e(TAG, "Failed to play alert");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,6 @@ public class MonitorActivity extends Activity
|
||||
|
||||
NsdManager.RegistrationListener _registrationListener;
|
||||
|
||||
ServerSocket _serverSocket;
|
||||
Thread _serviceThread;
|
||||
|
||||
private void serviceConnection(Socket socket) throws IOException
|
||||
@@ -67,21 +66,26 @@ public class MonitorActivity extends Activity
|
||||
audioEncoding, bufferSize);
|
||||
|
||||
byte[] buffer = new byte[bufferSize*2];
|
||||
audioRecord.startRecording();
|
||||
|
||||
OutputStream out = socket.getOutputStream();
|
||||
|
||||
socket.setSendBufferSize(bufferSize);
|
||||
Log.d(TAG, "Socket send buffer size: " + socket.getSendBufferSize());
|
||||
|
||||
while (socket.isConnected() && Thread.currentThread().isInterrupted() == false)
|
||||
try
|
||||
{
|
||||
int read = audioRecord.read(buffer, 0, bufferSize);
|
||||
out.write(buffer, 0, read);
|
||||
}
|
||||
audioRecord.startRecording();
|
||||
|
||||
socket.close();
|
||||
audioRecord.stop();
|
||||
OutputStream out = socket.getOutputStream();
|
||||
|
||||
socket.setSendBufferSize(bufferSize);
|
||||
Log.d(TAG, "Socket send buffer size: " + socket.getSendBufferSize());
|
||||
|
||||
while (socket.isConnected() && Thread.currentThread().isInterrupted() == false)
|
||||
{
|
||||
int read = audioRecord.read(buffer, 0, bufferSize);
|
||||
out.write(buffer, 0, read);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
audioRecord.stop();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -94,57 +98,70 @@ public class MonitorActivity extends Activity
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_monitor);
|
||||
|
||||
try
|
||||
_serviceThread = new Thread(new Runnable()
|
||||
{
|
||||
// Initialize a server socket on the next available port.
|
||||
_serverSocket = new ServerSocket(0);
|
||||
|
||||
// Store the chosen port.
|
||||
int localPort = _serverSocket.getLocalPort();
|
||||
|
||||
registerService(localPort);
|
||||
|
||||
_serviceThread = new Thread(new Runnable()
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
while(Thread.currentThread().isInterrupted() == false)
|
||||
{
|
||||
try
|
||||
{
|
||||
Socket socket = _serverSocket.accept();
|
||||
serviceConnection(socket);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
Log.e(TAG, "Failed when serving connection", e);
|
||||
}
|
||||
ServerSocket serverSocket = null;
|
||||
|
||||
try
|
||||
{
|
||||
_serverSocket.close();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
// Initialize a server socket on the next available port.
|
||||
serverSocket = new ServerSocket(0);
|
||||
|
||||
}
|
||||
// Store the chosen port.
|
||||
int localPort = serverSocket.getLocalPort();
|
||||
|
||||
MonitorActivity.this.runOnUiThread(new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
// Register the service so that parent devices can
|
||||
// locate the child device
|
||||
registerService(localPort);
|
||||
|
||||
// Wait for a parent to find us and connect
|
||||
Socket socket = serverSocket.accept();
|
||||
Log.i(TAG, "Connection from parent device received");
|
||||
|
||||
// We now have a client connection.
|
||||
// Unregister so no other clients will
|
||||
// attempt to connect
|
||||
serverSocket.close();
|
||||
serverSocket = null;
|
||||
unregisterService();
|
||||
|
||||
try
|
||||
{
|
||||
final TextView statusText = (TextView) findViewById(R.id.textStatus);
|
||||
statusText.setText(R.string.stopped);
|
||||
serviceConnection(socket);
|
||||
}
|
||||
});
|
||||
finally
|
||||
{
|
||||
socket.close();
|
||||
}
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
Log.e(TAG, "Connection failed", e);
|
||||
}
|
||||
|
||||
// If an exception was thrown before the connection
|
||||
// could be closed, clean it up
|
||||
if(serverSocket != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
serverSocket.close();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
Log.e(TAG, "Failed to close stray connection", e);
|
||||
}
|
||||
serverSocket = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
_serviceThread.start();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
Log.e(TAG, "Failed to create server socket", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
_serviceThread.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -152,13 +169,7 @@ public class MonitorActivity extends Activity
|
||||
{
|
||||
Log.i(TAG, "Baby monitor stop");
|
||||
|
||||
if(_registrationListener != null)
|
||||
{
|
||||
Log.i(TAG, "Unregistering monitoring service");
|
||||
|
||||
_nsdManager.unregisterService(_registrationListener);
|
||||
_registrationListener = null;
|
||||
}
|
||||
unregisterService();
|
||||
|
||||
if(_serviceThread != null)
|
||||
{
|
||||
@@ -190,7 +201,7 @@ public class MonitorActivity extends Activity
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
public void registerService(int port)
|
||||
private void registerService(int port)
|
||||
{
|
||||
NsdServiceInfo serviceInfo = new NsdServiceInfo();
|
||||
serviceInfo.setServiceName("ProtectBabyMonitor");
|
||||
@@ -249,4 +260,19 @@ public class MonitorActivity extends Activity
|
||||
_nsdManager.registerService(
|
||||
serviceInfo, NsdManager.PROTOCOL_DNS_SD, _registrationListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Uhregistered the service and assigns the listener
|
||||
* to null.
|
||||
*/
|
||||
private void unregisterService()
|
||||
{
|
||||
if(_registrationListener != null)
|
||||
{
|
||||
Log.i(TAG, "Unregistering monitoring service");
|
||||
|
||||
_nsdManager.unregisterService(_registrationListener);
|
||||
_registrationListener = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user