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,
|
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).
|
||||||
|
|||||||
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.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
|
||||||
|
|||||||
@@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user