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

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,6 +140,14 @@ public class ListenActivity extends Activity
Log.e(TAG, "Failed to stream audio", e); Log.e(TAG, "Failed to stream audio", e);
} }
if(Thread.currentThread().isInterrupted() == false)
{
// 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();
ListenActivity.this.runOnUiThread(new Runnable() ListenActivity.this.runOnUiThread(new Runnable()
{ {
@Override @Override
@@ -152,6 +161,7 @@ public class ListenActivity extends Activity
} }
}); });
} }
}
}); });
_listenThread.start(); _listenThread.start();
@@ -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,6 +66,9 @@ public class MonitorActivity extends Activity
audioEncoding, bufferSize); audioEncoding, bufferSize);
byte[] buffer = new byte[bufferSize*2]; byte[] buffer = new byte[bufferSize*2];
try
{
audioRecord.startRecording(); audioRecord.startRecording();
OutputStream out = socket.getOutputStream(); OutputStream out = socket.getOutputStream();
@@ -79,10 +81,12 @@ public class MonitorActivity extends Activity
int read = audioRecord.read(buffer, 0, bufferSize); int read = audioRecord.read(buffer, 0, bufferSize);
out.write(buffer, 0, read); out.write(buffer, 0, read);
} }
}
socket.close(); finally
{
audioRecord.stop(); audioRecord.stop();
} }
}
@Override @Override
protected void onCreate(Bundle savedInstanceState) protected void onCreate(Bundle savedInstanceState)
@@ -94,71 +98,78 @@ public class MonitorActivity extends Activity
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_monitor); setContentView(R.layout.activity_monitor);
try
{
// 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() _serviceThread = new Thread(new Runnable()
{ {
@Override @Override
public void run() public void run()
{ {
while(Thread.currentThread().isInterrupted() == false)
{
ServerSocket serverSocket = null;
try
{
// Initialize a server socket on the next available port.
serverSocket = new ServerSocket(0);
// Store the chosen port.
int localPort = serverSocket.getLocalPort();
// 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 try
{ {
Socket socket = _serverSocket.accept();
serviceConnection(socket); serviceConnection(socket);
} }
finally
{
socket.close();
}
}
catch(IOException e) catch(IOException e)
{ {
Log.e(TAG, "Failed when serving connection", 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 try
{ {
_serverSocket.close(); serverSocket.close();
} }
catch (IOException e) catch (IOException e)
{ {
Log.e(TAG, "Failed to close stray connection", e);
}
serverSocket = null;
} }
MonitorActivity.this.runOnUiThread(new Runnable()
{
@Override
public void run()
{
final TextView statusText = (TextView) findViewById(R.id.textStatus);
statusText.setText(R.string.stopped);
} }
});
} }
}); });
_serviceThread.start(); _serviceThread.start();
} }
catch (IOException e)
{
Log.e(TAG, "Failed to create server socket", e);
}
}
@Override @Override
protected void onDestroy() protected void onDestroy()
{ {
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;
}
}
} }