klappt mit multiplen vom originalen child monitor

This commit is contained in:
pi
2025-10-25 10:28:54 +02:00
parent a08967059b
commit 46cf690ba3
20 changed files with 113 additions and 46 deletions

View File

@@ -5,7 +5,7 @@ android {
compileSdk 34 compileSdk 34
defaultConfig { defaultConfig {
applicationId "de.rochefort.childmonitor" applicationId "com.example.childmonitor_multiple"
minSdkVersion 21 minSdkVersion 21
targetSdkVersion 34 targetSdkVersion 34
versionCode 14 versionCode 14
@@ -21,7 +21,7 @@ android {
dependencies { dependencies {
} }
namespace 'de.rochefort.childmonitor' namespace 'com.example.childmonitor_multiple'
lint { lint {
abortOnError true abortOnError true
warning 'MissingTranslation' warning 'MissingTranslation'

View File

@@ -14,10 +14,9 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with Child Monitor. If not, see <http://www.gnu.org/licenses/>. * along with Child Monitor. If not, see <http://www.gnu.org/licenses/>.
*/ */
package de.rochefort.childmonitor package com.example.childmonitor_multiple
import android.media.AudioFormat import android.media.AudioFormat
import de.rochefort.childmonitor.audio.G711UCodec
object AudioCodecDefines { object AudioCodecDefines {
const val FREQUENCY = 8000 const val FREQUENCY = 8000

View File

@@ -14,7 +14,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with Child Monitor. If not, see <http://www.gnu.org/licenses/>. * along with Child Monitor. If not, see <http://www.gnu.org/licenses/>.
*/ */
package de.rochefort.childmonitor package com.example.childmonitor_multiple
import android.app.Activity import android.app.Activity
import android.content.Intent import android.content.Intent

View File

@@ -14,7 +14,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with Child Monitor. If not, see <http://www.gnu.org/licenses/>. * along with Child Monitor. If not, see <http://www.gnu.org/licenses/>.
*/ */
package de.rochefort.childmonitor package com.example.childmonitor_multiple
import android.app.Activity import android.app.Activity
import android.content.ComponentName import android.content.ComponentName
@@ -28,7 +28,7 @@ import android.util.Log
import android.widget.TextView import android.widget.TextView
import android.widget.Toast import android.widget.Toast
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import de.rochefort.childmonitor.ListenService.ListenBinder import com.example.childmonitor_multiple.ListenService.ListenBinder
class ListenActivity : Activity() { class ListenActivity : Activity() {
// Don't attempt to unbind from the service unless the client has received some // Don't attempt to unbind from the service unless the client has received some

View File

@@ -14,7 +14,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with Child Monitor. If not, see <http://www.gnu.org/licenses/>. * along with Child Monitor. If not, see <http://www.gnu.org/licenses/>.
*/ */
package de.rochefort.childmonitor package com.example.childmonitor_multiple
import android.app.Notification import android.app.Notification
import android.app.NotificationChannel import android.app.NotificationChannel

View File

@@ -14,7 +14,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with Child Monitor. If not, see <http://www.gnu.org/licenses/>. * along with Child Monitor. If not, see <http://www.gnu.org/licenses/>.
*/ */
package de.rochefort.childmonitor package com.example.childmonitor_multiple
import android.app.Activity import android.app.Activity
import android.content.ComponentName import android.content.ComponentName
@@ -28,7 +28,7 @@ import android.util.Log
import android.widget.TextView import android.widget.TextView
import android.widget.Toast import android.widget.Toast
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import de.rochefort.childmonitor.MonitorService.MonitorBinder import com.example.childmonitor_multiple.MonitorService.MonitorBinder
class MonitorActivity : Activity() { class MonitorActivity : Activity() {
private val connection: ServiceConnection = object : ServiceConnection { private val connection: ServiceConnection = object : ServiceConnection {

View File

@@ -14,7 +14,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with Child Monitor. If not, see <http://www.gnu.org/licenses/>. * along with Child Monitor. If not, see <http://www.gnu.org/licenses/>.
*/ */
package de.rochefort.childmonitor package com.example.childmonitor_multiple
import android.app.Notification import android.app.Notification
import android.app.NotificationChannel import android.app.NotificationChannel
@@ -62,11 +62,11 @@ class MonitorService : Service() {
val bufferSize = AudioRecord.getMinBufferSize(frequency, channelConfiguration, audioEncoding) val bufferSize = AudioRecord.getMinBufferSize(frequency, channelConfiguration, audioEncoding)
val audioRecord: AudioRecord = try { val audioRecord: AudioRecord = try {
AudioRecord( AudioRecord(
MediaRecorder.AudioSource.MIC, MediaRecorder.AudioSource.MIC,
frequency, frequency,
channelConfiguration, channelConfiguration,
audioEncoding, audioEncoding,
bufferSize bufferSize
) )
} catch (e: SecurityException) { } catch (e: SecurityException) {
// This should never happen, we asked for permission before // This should never happen, we asked for permission before
@@ -91,6 +91,64 @@ class MonitorService : Service() {
audioRecord.stop() audioRecord.stop()
} }
} }
private fun handleMultiClientStreaming(serverSocket: ServerSocket, clients: MutableList<Socket>) {
val frequency: Int = AudioCodecDefines.FREQUENCY
val channelConfiguration: Int = AudioCodecDefines.CHANNEL_CONFIGURATION_IN
val audioEncoding: Int = AudioCodecDefines.ENCODING
val bufferSize = AudioRecord.getMinBufferSize(frequency, channelConfiguration, audioEncoding)
val audioRecord = AudioRecord(
MediaRecorder.AudioSource.MIC,
frequency,
channelConfiguration,
audioEncoding,
bufferSize
)
val pcmBufferSize = bufferSize * 2
val pcmBuffer = ShortArray(pcmBufferSize)
val ulawBuffer = ByteArray(pcmBufferSize)
try {
audioRecord.startRecording()
Log.i(TAG, "Mehrere Clients aktiv Aufnahme gestartet.")
while (!Thread.currentThread().isInterrupted && !serverSocket.isClosed) {
val read = audioRecord.read(pcmBuffer, 0, bufferSize)
if (read > 0) {
val encoded: Int = AudioCodecDefines.CODEC.encode(pcmBuffer, read, ulawBuffer, 0)
synchronized(clients) {
val iterator = clients.iterator()
while (iterator.hasNext()) {
val client = iterator.next()
try {
val out = client.getOutputStream()
out.write(ulawBuffer, 0, encoded)
} catch (e: IOException) {
try { client.close() } catch (ignored: IOException) {}
iterator.remove()
Log.w(TAG, "Client getrennt: ${client.inetAddress}")
}
}
}
}
}
} catch (e: Exception) {
Log.e(TAG, "Streaming-Fehler", e)
} finally {
audioRecord.stop()
audioRecord.release()
synchronized(clients) {
clients.forEach {
try { it.close() } catch (_: IOException) {}
}
clients.clear()
}
Log.i(TAG, "Audioaufnahme beendet, alle Clients getrennt.")
}
}
override fun onCreate() { override fun onCreate() {
Log.i(TAG, "ChildMonitor start") Log.i(TAG, "ChildMonitor start")
@@ -130,15 +188,25 @@ class MonitorService : Service() {
// Register the service so that parent devices can // Register the service so that parent devices can
// locate the child device // locate the child device
registerService(localPort) registerService(localPort)
serverSocket.accept().use { socket -> val clients = mutableListOf<Socket>()
Log.i(TAG, "Connection from parent device received")
// Thread zum Akzeptieren neuer Clients
Thread {
try {
while (!Thread.currentThread().isInterrupted) {
val client = serverSocket.accept()
client.tcpNoDelay = true
synchronized(clients) { clients.add(client) }
Log.i(TAG, "Neuer Client verbunden: ${client.inetAddress}")
}
} catch (e: IOException) {
Log.e(TAG, "Client-Akzeptierungsfehler: ${e.message}")
}
}.start()
// Aufnahme und Verteilung starten
handleMultiClientStreaming(serverSocket, clients)
// We now have a client connection.
// Unregister so no other clients will
// attempt to connect
unregisterService()
serviceConnection(socket)
}
} }
} catch (e: Exception) { } catch (e: Exception) {
if (this.connectionToken == currentToken) { if (this.connectionToken == currentToken) {
@@ -195,7 +263,7 @@ class MonitorService : Service() {
} }
} }
nsdManager.registerService( nsdManager.registerService(
serviceInfo, NsdManager.PROTOCOL_DNS_SD, registrationListener) serviceInfo, NsdManager.PROTOCOL_DNS_SD, registrationListener)
} }
private fun unregisterService() { private fun unregisterService() {
@@ -209,9 +277,9 @@ class MonitorService : Service() {
private fun createNotificationChannel() { private fun createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val serviceChannel = NotificationChannel( val serviceChannel = NotificationChannel(
CHANNEL_ID, CHANNEL_ID,
"Foreground Service Channel", "Foreground Service Channel",
NotificationManager.IMPORTANCE_DEFAULT NotificationManager.IMPORTANCE_DEFAULT
) )
this.notificationManager.createNotificationChannel(serviceChannel) this.notificationManager.createNotificationChannel(serviceChannel)
} }
@@ -222,9 +290,9 @@ class MonitorService : Service() {
// Set the info for the views that show in the notification panel. // Set the info for the views that show in the notification panel.
val b = NotificationCompat.Builder(this, CHANNEL_ID) val b = NotificationCompat.Builder(this, CHANNEL_ID)
b.setSmallIcon(R.drawable.listening_notification) // the status icon b.setSmallIcon(R.drawable.listening_notification) // the status icon
.setOngoing(true) .setOngoing(true)
.setTicker(text) // the status text .setTicker(text) // the status text
.setContentTitle(text) // the label of the entry .setContentTitle(text) // the label of the entry
return b.build() return b.build()
} }

View File

@@ -14,7 +14,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with Child Monitor. If not, see <http://www.gnu.org/licenses/>. * along with Child Monitor. If not, see <http://www.gnu.org/licenses/>.
*/ */
package de.rochefort.childmonitor package com.example.childmonitor_multiple
import android.Manifest import android.Manifest
import android.app.Activity import android.app.Activity

View File

@@ -14,7 +14,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with Child Monitor. If not, see <http://www.gnu.org/licenses/>. * along with Child Monitor. If not, see <http://www.gnu.org/licenses/>.
*/ */
package de.rochefort.childmonitor package com.example.childmonitor_multiple
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper

View File

@@ -14,7 +14,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with Child Monitor. If not, see <http://www.gnu.org/licenses/>. * along with Child Monitor. If not, see <http://www.gnu.org/licenses/>.
*/ */
package de.rochefort.childmonitor package com.example.childmonitor_multiple
import android.content.Context import android.content.Context
import android.graphics.Canvas import android.graphics.Canvas

View File

@@ -16,7 +16,7 @@
* Taken from https://android.googlesource.com/platform/external/nist-sip/+/6f95fdeab4481188b6260041b41d1db12b101266/src/com/android/sip/media/G711UCodec.java * Taken from https://android.googlesource.com/platform/external/nist-sip/+/6f95fdeab4481188b6260041b41d1db12b101266/src/com/android/sip/media/G711UCodec.java
* Adopted to the needs of the Child Monitor project. * Adopted to the needs of the Child Monitor project.
*/ */
package de.rochefort.childmonitor.audio package com.example.childmonitor_multiple
/** /**
* G.711 codec. This class provides u-law conversion. * G.711 codec. This class provides u-law conversion.

View File

@@ -8,7 +8,7 @@
android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin" android:paddingTop="@dimen/activity_vertical_margin"
tools:context="de.rochefort.childmonitor.MonitorActivity" > tools:context="com.example.childmonitor_multiple.MonitorActivity" >
<TextView <TextView
android:id="@+id/parentDeviceTitle" android:id="@+id/parentDeviceTitle"

View File

@@ -8,7 +8,7 @@
android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin" android:paddingTop="@dimen/activity_vertical_margin"
tools:context="de.rochefort.childmonitor.MonitorActivity" > tools:context="com.example.childmonitor_multiple.MonitorActivity" >
<TextView <TextView
android:id="@+id/parentDeviceTitle" android:id="@+id/parentDeviceTitle"

View File

@@ -8,7 +8,7 @@
android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin" android:paddingTop="@dimen/activity_vertical_margin"
tools:context="de.rochefort.childmonitor.MonitorActivity" > tools:context="com.example.childmonitor_multiple.MonitorActivity" >
<TextView <TextView
android:id="@+id/parentDeviceTitle" android:id="@+id/parentDeviceTitle"

View File

@@ -9,7 +9,7 @@
android:paddingTop="@dimen/activity_vertical_margin" android:paddingTop="@dimen/activity_vertical_margin"
android:paddingRight="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingBottom="@dimen/activity_vertical_margin" android:paddingBottom="@dimen/activity_vertical_margin"
tools:context="de.rochefort.childmonitor.MonitorActivity"> tools:context="com.example.childmonitor_multiple.MonitorActivity">
<TextView <TextView
android:id="@id/parentDeviceTitle" android:id="@id/parentDeviceTitle"
@@ -59,7 +59,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="15dip" /> android:layout_height="15dip" />
<de.rochefort.childmonitor.VolumeView <com.example.childmonitor_multiple.VolumeView
android:id="@+id/volume" android:id="@+id/volume"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" /> android:layout_height="match_parent" />

View File

@@ -9,7 +9,7 @@
android:paddingRight="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin" android:paddingTop="@dimen/activity_vertical_margin"
android:keepScreenOn="true" android:keepScreenOn="true"
tools:context="de.rochefort.childmonitor.MonitorActivity" > tools:context="com.example.childmonitor_multiple.MonitorActivity" >
<TextView <TextView
android:id="@+id/childDeviceTitle" android:id="@+id/childDeviceTitle"
@@ -34,7 +34,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical" android:orientation="vertical"
android:keepScreenOn="true" android:keepScreenOn="true"
tools:context="de.rochefort.childmonitor.MonitorActivity" > tools:context="com.example.childmonitor_multiple.MonitorActivity" >
<TextView <TextView
android:id="@+id/serviceTitle" android:id="@+id/serviceTitle"

View File

@@ -8,7 +8,7 @@
android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin" android:paddingTop="@dimen/activity_vertical_margin"
tools:context="de.rochefort.childmonitor.StartActivity" > tools:context="com.example.childmonitor_multiple.StartActivity" >
<Button <Button
android:id="@+id/useChildDevice" android:id="@+id/useChildDevice"

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<string name="app_name">Child Monitor</string> <string name="app_name">Child Monitor Multiple Clients</string>
<string name="action_settings">Einstellungen</string> <string name="action_settings">Einstellungen</string>
<string name="useAsParentDevice">Als Eltern-Gerät benutzen</string> <string name="useAsParentDevice">Als Eltern-Gerät benutzen</string>
<string name="useAsChildDevice">Als Kind-Gerät benutzen</string> <string name="useAsChildDevice">Als Kind-Gerät benutzen</string>

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<string name="app_name">ChildMonitor</string> <string name="app_name">Child Monitor Multiple Clients</string>
<string name="action_settings">Instellingen</string> <string name="action_settings">Instellingen</string>
<string name="useAsParentDevice">Gebruiken als ouderapparaat</string> <string name="useAsParentDevice">Gebruiken als ouderapparaat</string>
<string name="useAsChildDevice">Gebruiken als kindapparaat</string> <string name="useAsChildDevice">Gebruiken als kindapparaat</string>

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<string name="app_name">Child Monitor</string> <string name="app_name">Child Monitor Multiple Clients</string>
<string name="action_settings">Settings</string> <string name="action_settings">Settings</string>
<string name="useAsParentDevice">Use as Parent Device</string> <string name="useAsParentDevice">Use as Parent Device</string>
<string name="useAsChildDevice">Use as Child Device</string> <string name="useAsChildDevice">Use as Child Device</string>