Merge pull request #3 from brarcher/staging

Better recovery from connection disconnects
This commit is contained in:
brarcher
2015-12-28 17:22:25 -05:00
5 changed files with 138 additions and 81 deletions

View File

@@ -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, of successfully advertising itself on the network, allows clients to connect,
and streams audio. Room for improvement includes: and streams audio. Room for improvement includes:
1. Decent UI 1. Robust usage of the AudioTrack API
2. Hook into audio controls to adjust volume 2. Handle dropped packets gracefully
3. Robust usage of the AudioTrack API
4. Handle dropped packets gracefully
At the time this project was started there was no obvious open source solution for a 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, 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) 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). and formatted using [Android Asset Studio](https://romannurik.github.io/AndroidAssetStudio/index.html).
Audio file originals from [freesound](https://freesound.org).

Binary file not shown.

View File

@@ -27,7 +27,6 @@ import android.view.View;
import android.widget.Button; import android.widget.Button;
import android.widget.TableLayout; import android.widget.TableLayout;
import android.widget.TableRow; import android.widget.TableRow;
import android.widget.TextView;
public class DiscoverActivity extends Activity public class DiscoverActivity extends Activity
{ {
@@ -99,13 +98,13 @@ public class DiscoverActivity extends Activity
public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode)
{ {
// Called when the resolve fails. Use the error code to debug. // 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 @Override
public void onServiceResolved(final NsdServiceInfo serviceInfo) public void onServiceResolved(final NsdServiceInfo serviceInfo)
{ {
Log.e(TAG, "Resolve Succeeded. " + serviceInfo); Log.i(TAG, "Resolve Succeeded: " + serviceInfo);
DiscoverActivity.this.runOnUiThread(new Runnable() DiscoverActivity.this.runOnUiThread(new Runnable()
{ {
@@ -159,7 +158,7 @@ public class DiscoverActivity extends Activity
{ {
// When the network service is no longer available. // When the network service is no longer available.
// Internal bookkeeping code goes here. // Internal bookkeeping code goes here.
Log.e(TAG, "service lost" + service); Log.e(TAG, "Service lost: " + service);
} }
@Override @Override
@@ -171,14 +170,14 @@ public class DiscoverActivity extends Activity
@Override @Override
public void onStartDiscoveryFailed(String serviceType, int errorCode) 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); nsdManager.stopServiceDiscovery(this);
} }
@Override @Override
public void onStopDiscoveryFailed(String serviceType, int errorCode) 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); nsdManager.stopServiceDiscovery(this);
} }
}; };

View File

@@ -28,6 +28,7 @@ import android.app.Activity;
import android.media.AudioFormat; import android.media.AudioFormat;
import android.media.AudioManager; import android.media.AudioManager;
import android.media.AudioTrack; import android.media.AudioTrack;
import android.media.MediaPlayer;
import android.os.Bundle; import android.os.Bundle;
import android.util.Log; import android.util.Log;
import android.widget.TextView; import android.widget.TextView;
@@ -139,18 +140,27 @@ public class ListenActivity extends Activity
Log.e(TAG, "Failed to stream audio", e); Log.e(TAG, "Failed to stream audio", e);
} }
ListenActivity.this.runOnUiThread(new Runnable() if(Thread.currentThread().isInterrupted() == false)
{ {
@Override // If this thread has not been interrupted, likely something
public void run() // bad happened with the connection to the child device. Play
{ // an alert to notify the user that the connection has been
final TextView connectedText = (TextView) findViewById(R.id.connectedTo); // interrupted.
connectedText.setText(""); playAlert();
final TextView statusText = (TextView) findViewById(R.id.textStatus); ListenActivity.this.runOnUiThread(new Runnable()
statusText.setText(R.string.disconnected); {
} @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(); 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");
}
}
} }

View File

@@ -42,7 +42,6 @@ public class MonitorActivity extends Activity
NsdManager.RegistrationListener _registrationListener; NsdManager.RegistrationListener _registrationListener;
ServerSocket _serverSocket;
Thread _serviceThread; Thread _serviceThread;
private void serviceConnection(Socket socket) throws IOException private void serviceConnection(Socket socket) throws IOException
@@ -67,21 +66,26 @@ public class MonitorActivity extends Activity
audioEncoding, bufferSize); audioEncoding, bufferSize);
byte[] buffer = new byte[bufferSize*2]; byte[] buffer = new byte[bufferSize*2];
audioRecord.startRecording();
OutputStream out = socket.getOutputStream(); try
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); audioRecord.startRecording();
out.write(buffer, 0, read);
}
socket.close(); OutputStream out = socket.getOutputStream();
audioRecord.stop();
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 @Override
@@ -94,57 +98,70 @@ public class MonitorActivity extends Activity
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_monitor); setContentView(R.layout.activity_monitor);
try _serviceThread = new Thread(new Runnable()
{ {
// Initialize a server socket on the next available port. @Override
_serverSocket = new ServerSocket(0); public void run()
// Store the chosen port.
int localPort = _serverSocket.getLocalPort();
registerService(localPort);
_serviceThread = new Thread(new Runnable()
{ {
@Override while(Thread.currentThread().isInterrupted() == false)
public void run()
{ {
try ServerSocket serverSocket = null;
{
Socket socket = _serverSocket.accept();
serviceConnection(socket);
}
catch (IOException e)
{
Log.e(TAG, "Failed when serving connection", e);
}
try try
{ {
_serverSocket.close(); // Initialize a server socket on the next available port.
} serverSocket = new ServerSocket(0);
catch (IOException e)
{
} // Store the chosen port.
int localPort = serverSocket.getLocalPort();
MonitorActivity.this.runOnUiThread(new Runnable() // Register the service so that parent devices can
{ // locate the child device
@Override registerService(localPort);
public void run()
// 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); serviceConnection(socket);
statusText.setText(R.string.stopped);
} }
}); 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(); });
} _serviceThread.start();
catch (IOException e)
{
Log.e(TAG, "Failed to create server socket", e);
}
} }
@Override @Override
@@ -152,13 +169,7 @@ public class MonitorActivity extends Activity
{ {
Log.i(TAG, "Baby monitor stop"); Log.i(TAG, "Baby monitor stop");
if(_registrationListener != null) unregisterService();
{
Log.i(TAG, "Unregistering monitoring service");
_nsdManager.unregisterService(_registrationListener);
_registrationListener = null;
}
if(_serviceThread != null) if(_serviceThread != null)
{ {
@@ -190,7 +201,7 @@ public class MonitorActivity extends Activity
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
} }
public void registerService(int port) private void registerService(int port)
{ {
NsdServiceInfo serviceInfo = new NsdServiceInfo(); NsdServiceInfo serviceInfo = new NsdServiceInfo();
serviceInfo.setServiceName("ProtectBabyMonitor"); serviceInfo.setServiceName("ProtectBabyMonitor");
@@ -249,4 +260,19 @@ public class MonitorActivity extends Activity
_nsdManager.registerService( _nsdManager.registerService(
serviceInfo, NsdManager.PROTOCOL_DNS_SD, _registrationListener); 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;
}
}
} }