Split-off MonitorService
Same motivation as for the parent device: The monitor activity will be quickly destroyed, unless it is connected to a foreground service. By moving the thread and audio listener to a service, this should be avoided.
This commit is contained in:
@@ -26,10 +26,15 @@
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@android:style/Theme.Holo">
|
||||
|
||||
<service
|
||||
android:name=".ListenService"
|
||||
android:enabled="true"
|
||||
android:exported="false"/>
|
||||
<service
|
||||
android:name=".MonitorService"
|
||||
android:enabled="true"
|
||||
android:exported="false"/>
|
||||
|
||||
<activity
|
||||
android:name=".StartActivity"
|
||||
@@ -55,5 +60,4 @@
|
||||
android:configChanges="orientation|screenSize"
|
||||
android:parentActivityName=".DiscoverActivity" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@@ -1,159 +1,76 @@
|
||||
/*
|
||||
* This file is part of Child Monitor.
|
||||
*
|
||||
* <p>
|
||||
* Child Monitor is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* <p>
|
||||
* Child Monitor is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* <p>
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Child Monitor. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package de.rochefort.childmonitor;
|
||||
|
||||
import static de.rochefort.childmonitor.AudioCodecDefines.CODEC;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.media.AudioRecord;
|
||||
import android.media.MediaRecorder;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.LinkAddress;
|
||||
import android.net.Network;
|
||||
import android.net.NetworkInfo;
|
||||
import android.net.nsd.NsdManager;
|
||||
import android.net.nsd.NsdServiceInfo;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.util.Log;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.net.InetAddress;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class MonitorActivity extends Activity {
|
||||
final static String TAG = "ChildMonitor";
|
||||
|
||||
private NsdManager nsdManager;
|
||||
|
||||
private NsdManager.RegistrationListener registrationListener;
|
||||
|
||||
private ServerSocket currentSocket;
|
||||
|
||||
private Object connectionToken;
|
||||
|
||||
private int currentPort;
|
||||
|
||||
private void serviceConnection(Socket socket) {
|
||||
runOnUiThread(() -> {
|
||||
final TextView statusText = findViewById(R.id.textStatus);
|
||||
statusText.setText(R.string.streaming);
|
||||
});
|
||||
|
||||
final int frequency = AudioCodecDefines.FREQUENCY;
|
||||
final int channelConfiguration = AudioCodecDefines.CHANNEL_CONFIGURATION_IN;
|
||||
final int audioEncoding = AudioCodecDefines.ENCODING;
|
||||
|
||||
final int bufferSize = AudioRecord.getMinBufferSize(frequency, channelConfiguration, audioEncoding);
|
||||
final AudioRecord audioRecord;
|
||||
try {
|
||||
audioRecord = new AudioRecord(
|
||||
MediaRecorder.AudioSource.MIC,
|
||||
frequency,
|
||||
channelConfiguration,
|
||||
audioEncoding,
|
||||
bufferSize
|
||||
);
|
||||
} catch (SecurityException e) {
|
||||
// This should never happen, we asked for permission before
|
||||
throw new RuntimeException(e);
|
||||
private final ServiceConnection connection = new ServiceConnection() {
|
||||
public void onServiceConnected(ComponentName className, IBinder service) {
|
||||
// This is called when the connection with the service has been
|
||||
// established, giving us the service object we can use to
|
||||
// interact with the service. Because we have bound to a explicit
|
||||
// service that we know is running in our own process, we can
|
||||
// cast its IBinder to a concrete class and directly access it.
|
||||
MonitorService bs = ((MonitorService.MonitorBinder) service).getService();
|
||||
bs.setMonitorActivity(MonitorActivity.this);
|
||||
}
|
||||
|
||||
final int pcmBufferSize = bufferSize*2;
|
||||
final short[] pcmBuffer = new short[pcmBufferSize];
|
||||
final byte[] ulawBuffer = new byte[pcmBufferSize];
|
||||
|
||||
try {
|
||||
audioRecord.startRecording();
|
||||
final OutputStream out = socket.getOutputStream();
|
||||
|
||||
socket.setSendBufferSize(pcmBufferSize);
|
||||
Log.d(TAG, "Socket send buffer size: " + socket.getSendBufferSize());
|
||||
|
||||
while (socket.isConnected() && currentSocket != null && !Thread.currentThread().isInterrupted()) {
|
||||
final int read = audioRecord.read(pcmBuffer, 0, bufferSize);
|
||||
int encoded = CODEC.encode(pcmBuffer, read, ulawBuffer, 0);
|
||||
out.write(ulawBuffer, 0, encoded);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Connection failed", e);
|
||||
} finally {
|
||||
audioRecord.stop();
|
||||
}
|
||||
}
|
||||
|
||||
public void onServiceDisconnected(ComponentName className) {
|
||||
// This is called when the connection with the service has been
|
||||
// unexpectedly disconnected -- that is, its process crashed.
|
||||
// Because it is running in our same process, we should never
|
||||
// see this happen.
|
||||
}
|
||||
};
|
||||
private boolean shouldUnbind;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
Log.i(TAG, "ChildMonitor start");
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_monitor);
|
||||
|
||||
nsdManager = (NsdManager)this.getSystemService(Context.NSD_SERVICE);
|
||||
currentPort = 10000;
|
||||
currentSocket = null;
|
||||
final Object currentToken = new Object();
|
||||
connectionToken = currentToken;
|
||||
|
||||
new Thread(() -> {
|
||||
while(Objects.equals(connectionToken, currentToken)) {
|
||||
try (ServerSocket serverSocket = new ServerSocket(currentPort)) {
|
||||
currentSocket = serverSocket;
|
||||
// Store the chosen port.
|
||||
final 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
|
||||
try (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
|
||||
unregisterService();
|
||||
serviceConnection(socket);
|
||||
}
|
||||
} catch(Exception e) {
|
||||
// Just in case
|
||||
currentPort++;
|
||||
Log.e(TAG, "Failed to open server socket. Port increased to " + currentPort, e);
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
|
||||
final TextView addressText = findViewById(R.id.address);
|
||||
|
||||
List<String> listenAddresses = getListenAddresses();
|
||||
if(!listenAddresses.isEmpty()) {
|
||||
if (!listenAddresses.isEmpty()) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < listenAddresses.size(); i++) {
|
||||
String listenAddress = listenAddresses.get(i);
|
||||
sb.append(listenAddress);
|
||||
if (i != listenAddresses.size() -1) {
|
||||
if (i != listenAddresses.size() - 1) {
|
||||
sb.append("\n\n");
|
||||
}
|
||||
}
|
||||
@@ -162,10 +79,11 @@ public class MonitorActivity extends Activity {
|
||||
addressText.setText(R.string.notConnected);
|
||||
}
|
||||
|
||||
ensureServiceRunningAndBind();
|
||||
}
|
||||
|
||||
private List<String> getListenAddresses() {
|
||||
ConnectivityManager cm = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
List<String> listenAddresses = new ArrayList<>();
|
||||
if (cm != null) {
|
||||
for (Network network : cm.getAllNetworks()) {
|
||||
@@ -186,87 +104,37 @@ public class MonitorActivity extends Activity {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
Log.i(TAG, "ChildMonitor stop");
|
||||
|
||||
unregisterService();
|
||||
|
||||
connectionToken = null;
|
||||
if(currentSocket != null) {
|
||||
try {
|
||||
currentSocket.close();
|
||||
currentSocket = null;
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Failed to close active socket on port "+currentPort);
|
||||
}
|
||||
}
|
||||
super.onStop();
|
||||
public void onDestroy() {
|
||||
doUnbindAndStopService();
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
private void registerService(final int port) {
|
||||
final NsdServiceInfo serviceInfo = new NsdServiceInfo();
|
||||
serviceInfo.setServiceName("ChildMonitor on " + Build.MODEL);
|
||||
serviceInfo.setServiceType("_childmonitor._tcp.");
|
||||
serviceInfo.setPort(port);
|
||||
|
||||
registrationListener = new NsdManager.RegistrationListener() {
|
||||
@Override
|
||||
public void onServiceRegistered(NsdServiceInfo nsdServiceInfo) {
|
||||
// Save the service name. Android may have changed it in order to
|
||||
// resolve a conflict, so update the name you initially requested
|
||||
// with the name Android actually used.
|
||||
final String serviceName = nsdServiceInfo.getServiceName();
|
||||
|
||||
Log.i(TAG, "Service name: " + serviceName);
|
||||
|
||||
MonitorActivity.this.runOnUiThread(() -> {
|
||||
final TextView statusText = findViewById(R.id.textStatus);
|
||||
statusText.setText(R.string.waitingForParent);
|
||||
|
||||
final TextView serviceText = findViewById(R.id.textService);
|
||||
serviceText.setText(serviceName);
|
||||
|
||||
final TextView portText = findViewById(R.id.port);
|
||||
portText.setText(Integer.toString(port));
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRegistrationFailed(NsdServiceInfo serviceInfo, int errorCode) {
|
||||
// Registration failed! Put debugging code here to determine why.
|
||||
Log.e(TAG, "Registration failed: " + errorCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceUnregistered(NsdServiceInfo arg0) {
|
||||
// Service has been unregistered. This only happens when you call
|
||||
// NsdManager.unregisterService() and pass in this listener.
|
||||
|
||||
Log.i(TAG, "Unregistering service");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) {
|
||||
// Unregistration failed. Put debugging code here to determine why.
|
||||
|
||||
Log.e(TAG, "Unregistration failed: " + errorCode);
|
||||
}
|
||||
};
|
||||
|
||||
nsdManager.registerService(
|
||||
serviceInfo, NsdManager.PROTOCOL_DNS_SD, registrationListener);
|
||||
void ensureServiceRunningAndBind() {
|
||||
final Context context = this;
|
||||
final Intent intent = new Intent(context, MonitorService.class);
|
||||
ContextCompat.startForegroundService(context, intent);
|
||||
// Attempts to establish a connection with the service. We use an
|
||||
// explicit class name because we want a specific service
|
||||
// implementation that we know will be running in our own process
|
||||
// (and thus won't be supporting component replacement by other
|
||||
// applications).
|
||||
if (bindService(intent, connection, Context.BIND_AUTO_CREATE)) {
|
||||
shouldUnbind = true;
|
||||
Log.i(TAG, "Bound service");
|
||||
} else {
|
||||
Log.e(TAG, "Error: The requested service doesn't " +
|
||||
"exist, or this client isn't allowed access to it.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
void doUnbindAndStopService() {
|
||||
if (shouldUnbind) {
|
||||
// Release information about the service's state.
|
||||
unbindService(connection);
|
||||
shouldUnbind = false;
|
||||
}
|
||||
final Context context = this;
|
||||
final Intent intent = new Intent(context, MonitorService.class);
|
||||
context.stopService(intent);
|
||||
}
|
||||
}
|
||||
|
||||
314
app/src/main/java/de/rochefort/childmonitor/MonitorService.java
Normal file
314
app/src/main/java/de/rochefort/childmonitor/MonitorService.java
Normal file
@@ -0,0 +1,314 @@
|
||||
/**
|
||||
* This file is part of Child Monitor.
|
||||
* <p>
|
||||
* Child Monitor is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* <p>
|
||||
* Child Monitor is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* <p>
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Child Monitor. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package de.rochefort.childmonitor;
|
||||
|
||||
import static de.rochefort.childmonitor.AudioCodecDefines.CODEC;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.Service;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.media.AudioRecord;
|
||||
import android.media.MediaRecorder;
|
||||
import android.net.nsd.NsdManager;
|
||||
import android.net.nsd.NsdServiceInfo;
|
||||
import android.os.Binder;
|
||||
import android.os.Build;
|
||||
import android.os.IBinder;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.util.Log;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.util.Objects;
|
||||
|
||||
public class MonitorService extends Service {
|
||||
final static String TAG = "MonitorService";
|
||||
final static String CHANNEL_ID = TAG;
|
||||
public static final int ID = 1338;
|
||||
private final IBinder binder = new MonitorBinder();
|
||||
private NsdManager nsdManager;
|
||||
private NsdManager.RegistrationListener registrationListener;
|
||||
private ServerSocket currentSocket;
|
||||
private Object connectionToken;
|
||||
private int currentPort;
|
||||
private NotificationManager notificationManager;
|
||||
private Thread monitorThread;
|
||||
private MonitorActivity monitorActivity;
|
||||
|
||||
public void setMonitorActivity(MonitorActivity monitorActivity) {
|
||||
this.monitorActivity = monitorActivity;
|
||||
}
|
||||
|
||||
private void serviceConnection(Socket socket) {
|
||||
final MonitorActivity ma = monitorActivity;
|
||||
if (ma != null) {
|
||||
ma.runOnUiThread(() -> {
|
||||
final TextView statusText = monitorActivity.findViewById(R.id.textStatus);
|
||||
statusText.setText(R.string.streaming);
|
||||
});
|
||||
}
|
||||
|
||||
final int frequency = AudioCodecDefines.FREQUENCY;
|
||||
final int channelConfiguration = AudioCodecDefines.CHANNEL_CONFIGURATION_IN;
|
||||
final int audioEncoding = AudioCodecDefines.ENCODING;
|
||||
|
||||
final int bufferSize = AudioRecord.getMinBufferSize(frequency, channelConfiguration, audioEncoding);
|
||||
final AudioRecord audioRecord;
|
||||
try {
|
||||
audioRecord = new AudioRecord(
|
||||
MediaRecorder.AudioSource.MIC,
|
||||
frequency,
|
||||
channelConfiguration,
|
||||
audioEncoding,
|
||||
bufferSize
|
||||
);
|
||||
} catch (SecurityException e) {
|
||||
// This should never happen, we asked for permission before
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
final int pcmBufferSize = bufferSize * 2;
|
||||
final short[] pcmBuffer = new short[pcmBufferSize];
|
||||
final byte[] ulawBuffer = new byte[pcmBufferSize];
|
||||
|
||||
try {
|
||||
audioRecord.startRecording();
|
||||
final OutputStream out = socket.getOutputStream();
|
||||
|
||||
socket.setSendBufferSize(pcmBufferSize);
|
||||
Log.d(TAG, "Socket send buffer size: " + socket.getSendBufferSize());
|
||||
|
||||
while (socket.isConnected() && currentSocket != null && !Thread.currentThread().isInterrupted()) {
|
||||
final int read = audioRecord.read(pcmBuffer, 0, bufferSize);
|
||||
int encoded = CODEC.encode(pcmBuffer, read, ulawBuffer, 0);
|
||||
out.write(ulawBuffer, 0, encoded);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Connection failed", e);
|
||||
} finally {
|
||||
audioRecord.stop();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
Log.i(TAG, "ChildMonitor start");
|
||||
super.onCreate();
|
||||
notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
|
||||
|
||||
nsdManager = (NsdManager) this.getSystemService(Context.NSD_SERVICE);
|
||||
currentPort = 10000;
|
||||
currentSocket = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
Log.i(TAG, "Received start id " + startId + ": " + intent);
|
||||
// Display a notification about us starting. We put an icon in the status bar.
|
||||
createNotificationChannel();
|
||||
Notification n = buildNotification();
|
||||
startForeground(ID, n);
|
||||
ensureMonitorThread();
|
||||
|
||||
return START_REDELIVER_INTENT;
|
||||
}
|
||||
|
||||
private void ensureMonitorThread() {
|
||||
Thread mt = monitorThread;
|
||||
if (mt != null && mt.isAlive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Object currentToken = new Object();
|
||||
connectionToken = currentToken;
|
||||
|
||||
monitorThread = new Thread(() -> {
|
||||
while (Objects.equals(connectionToken, currentToken)) {
|
||||
try (ServerSocket serverSocket = new ServerSocket(currentPort)) {
|
||||
currentSocket = serverSocket;
|
||||
// Store the chosen port.
|
||||
final 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
|
||||
try (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
|
||||
unregisterService();
|
||||
serviceConnection(socket);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Just in case
|
||||
currentPort++;
|
||||
Log.e(TAG, "Failed to open server socket. Port increased to " + currentPort, e);
|
||||
}
|
||||
}
|
||||
});
|
||||
monitorThread.start();
|
||||
}
|
||||
|
||||
private void registerService(final int port) {
|
||||
final NsdServiceInfo serviceInfo = new NsdServiceInfo();
|
||||
serviceInfo.setServiceName("ChildMonitor on " + Build.MODEL);
|
||||
serviceInfo.setServiceType("_childmonitor._tcp.");
|
||||
serviceInfo.setPort(port);
|
||||
|
||||
registrationListener = new NsdManager.RegistrationListener() {
|
||||
@Override
|
||||
public void onServiceRegistered(NsdServiceInfo nsdServiceInfo) {
|
||||
// Save the service name. Android may have changed it in order to
|
||||
// resolve a conflict, so update the name you initially requested
|
||||
// with the name Android actually used.
|
||||
final String serviceName = nsdServiceInfo.getServiceName();
|
||||
|
||||
Log.i(TAG, "Service name: " + serviceName);
|
||||
|
||||
final MonitorActivity ma = monitorActivity;
|
||||
if (ma != null) {
|
||||
ma.runOnUiThread(() -> {
|
||||
final TextView statusText = ma.findViewById(R.id.textStatus);
|
||||
statusText.setText(R.string.waitingForParent);
|
||||
|
||||
final TextView serviceText = ma.findViewById(R.id.textService);
|
||||
serviceText.setText(serviceName);
|
||||
|
||||
final TextView portText = ma.findViewById(R.id.port);
|
||||
portText.setText(Integer.toString(port));
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRegistrationFailed(NsdServiceInfo serviceInfo, int errorCode) {
|
||||
// Registration failed! Put debugging code here to determine why.
|
||||
Log.e(TAG, "Registration failed: " + errorCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceUnregistered(NsdServiceInfo arg0) {
|
||||
// Service has been unregistered. This only happens when you call
|
||||
// NsdManager.unregisterService() and pass in this listener.
|
||||
|
||||
Log.i(TAG, "Unregistering service");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) {
|
||||
// Unregistration failed. Put debugging code here to determine why.
|
||||
|
||||
Log.e(TAG, "Unregistration failed: " + errorCode);
|
||||
}
|
||||
};
|
||||
|
||||
nsdManager.registerService(
|
||||
serviceInfo, NsdManager.PROTOCOL_DNS_SD, registrationListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister 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;
|
||||
}
|
||||
}
|
||||
|
||||
private void createNotificationChannel() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
NotificationChannel serviceChannel = new NotificationChannel(
|
||||
CHANNEL_ID,
|
||||
"Foreground Service Channel",
|
||||
NotificationManager.IMPORTANCE_DEFAULT
|
||||
);
|
||||
notificationManager.createNotificationChannel(serviceChannel);
|
||||
}
|
||||
}
|
||||
|
||||
private Notification buildNotification() {
|
||||
CharSequence text = "Child Device";
|
||||
// Set the info for the views that show in the notification panel.
|
||||
NotificationCompat.Builder b = new NotificationCompat.Builder(this, CHANNEL_ID);
|
||||
b.setSmallIcon(R.drawable.listening_notification) // the status icon
|
||||
.setOngoing(true)
|
||||
.setTicker(text) // the status text
|
||||
.setContentTitle(text); // the label of the entry
|
||||
return b.build();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
Thread mt = monitorThread;
|
||||
if (mt != null) {
|
||||
mt.interrupt();
|
||||
monitorThread = null;
|
||||
}
|
||||
|
||||
unregisterService();
|
||||
|
||||
connectionToken = null;
|
||||
if (currentSocket != null) {
|
||||
try {
|
||||
currentSocket.close();
|
||||
currentSocket = null;
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Failed to close active socket on port " + currentPort);
|
||||
}
|
||||
}
|
||||
|
||||
// Cancel the persistent notification.
|
||||
int NOTIFICATION = R.string.listening;
|
||||
notificationManager.cancel(NOTIFICATION);
|
||||
|
||||
stopForeground(true);
|
||||
// Tell the user we stopped.
|
||||
Toast.makeText(this, R.string.stopped, Toast.LENGTH_SHORT).show();
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return binder;
|
||||
}
|
||||
|
||||
public class MonitorBinder extends Binder {
|
||||
MonitorService getService() {
|
||||
return MonitorService.this;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user