3 retys added frst attempt
This commit is contained in:
@@ -1,19 +1,3 @@
|
||||
/*
|
||||
* This file is part of Child Monitor.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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 com.example.childmonitor_multiple
|
||||
|
||||
import android.app.Activity
|
||||
@@ -34,6 +18,8 @@ class ListenActivity : Activity() {
|
||||
// Don't attempt to unbind from the service unless the client has received some
|
||||
// information about the service's state.
|
||||
private var shouldUnbind = false
|
||||
private lateinit var statusText: TextView
|
||||
|
||||
private val connection: ServiceConnection = object : ServiceConnection {
|
||||
override fun onServiceConnected(className: ComponentName, service: IBinder) {
|
||||
// This is called when the connection with the service has been
|
||||
@@ -42,10 +28,11 @@ class ListenActivity : Activity() {
|
||||
// service that we know is running in our own process, we can
|
||||
// cast its IBinder to a concrete class and directly access it.
|
||||
val bs = (service as ListenBinder).service
|
||||
Toast.makeText(this@ListenActivity, R.string.connect,
|
||||
Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(this@ListenActivity, R.string.connect, Toast.LENGTH_SHORT).show()
|
||||
|
||||
val connectedText = findViewById<TextView>(R.id.connectedTo)
|
||||
connectedText.text = bs.childDeviceName
|
||||
|
||||
val volumeView = findViewById<VolumeView>(R.id.volume)
|
||||
volumeView.volumeHistory = bs.volumeHistory
|
||||
bs.onUpdate = { volumeView.postInvalidate() }
|
||||
@@ -77,7 +64,7 @@ class ListenActivity : Activity() {
|
||||
// (and thus won't be supporting component replacement by other
|
||||
// applications).
|
||||
if (bindService(intent, connection, BIND_AUTO_CREATE)) {
|
||||
this.shouldUnbind = true
|
||||
shouldUnbind = true
|
||||
Log.i(TAG, "Bound listen service")
|
||||
} else {
|
||||
Log.e(TAG, "Error: The requested service doesn't " +
|
||||
@@ -86,32 +73,29 @@ class ListenActivity : Activity() {
|
||||
}
|
||||
|
||||
private fun doUnbindAndStopService() {
|
||||
if (this.shouldUnbind) {
|
||||
// Release information about the service's state.
|
||||
if (shouldUnbind) {
|
||||
unbindService(connection)
|
||||
this.shouldUnbind = false
|
||||
shouldUnbind = false
|
||||
}
|
||||
val context: Context = this
|
||||
val intent = Intent(context, ListenService::class.java)
|
||||
context.stopService(intent)
|
||||
stopService(Intent(this, ListenService::class.java))
|
||||
}
|
||||
|
||||
fun postErrorMessage() {
|
||||
val status = findViewById<TextView>(R.id.textStatus)
|
||||
status.post { status.setText(R.string.disconnected) }
|
||||
statusText.post {
|
||||
statusText.text = "Verbindung getrennt (3 Fehlversuche)"
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
val bundle = this.intent.extras
|
||||
ensureServiceRunningAndBind(bundle)
|
||||
this.volumeControlStream = AudioManager.STREAM_MUSIC
|
||||
setContentView(R.layout.activity_listen)
|
||||
val statusText = findViewById<TextView>(R.id.textStatus)
|
||||
statusText.setText(R.string.listening)
|
||||
statusText = findViewById(R.id.textStatus)
|
||||
statusText.text = "Starte Verbindung…"
|
||||
volumeControlStream = AudioManager.STREAM_MUSIC
|
||||
ensureServiceRunningAndBind(intent.extras)
|
||||
}
|
||||
|
||||
public override fun onDestroy() {
|
||||
override fun onDestroy() {
|
||||
doUnbindAndStopService()
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
@@ -49,6 +49,9 @@ class ListenService : Service() {
|
||||
var childDeviceName: String? = null
|
||||
private set
|
||||
|
||||
var onError: (() -> Unit)? = null
|
||||
var onUpdate: (() -> Unit)? = null
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
this.notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
|
||||
@@ -62,11 +65,12 @@ class ListenService : Service() {
|
||||
val name = it.getString("name")
|
||||
childDeviceName = name
|
||||
val n = buildNotification(name)
|
||||
val foregroundServiceType = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK else 0 // Keep the linter happy
|
||||
val foregroundServiceType = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
|
||||
ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK else 0
|
||||
ServiceCompat.startForeground(this, ID, n, foregroundServiceType)
|
||||
val address = it.getString("address")
|
||||
val port = it.getInt("port")
|
||||
doListen(address, port)
|
||||
doListenWithRetries(address, port)
|
||||
}
|
||||
return START_REDELIVER_INTENT
|
||||
}
|
||||
@@ -82,61 +86,76 @@ class ListenService : Service() {
|
||||
Toast.makeText(this, R.string.stopped, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
override fun onBind(intent: Intent): IBinder {
|
||||
return binder
|
||||
}
|
||||
|
||||
private fun buildNotification(name: String?): Notification {
|
||||
// In this sample, we'll use the same text for the ticker and the expanded notification
|
||||
val text = getText(R.string.listening)
|
||||
|
||||
// The PendingIntent to launch our activity if the user selects this notification
|
||||
val contentIntent = PendingIntent.getActivity(this, 0,
|
||||
Intent(this, ListenActivity::class.java), PendingIntent.FLAG_IMMUTABLE)
|
||||
|
||||
// Set the info for the views that show in the notification panel.
|
||||
val b = 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
|
||||
.setContentText(name) // the contents of the entry
|
||||
.setContentIntent(contentIntent)
|
||||
return b.build()
|
||||
}
|
||||
|
||||
private fun createNotificationChannel() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
val serviceChannel = NotificationChannel(
|
||||
CHANNEL_ID,
|
||||
"Foreground Service Channel",
|
||||
NotificationManager.IMPORTANCE_DEFAULT
|
||||
)
|
||||
notificationManager.createNotificationChannel(serviceChannel)
|
||||
}
|
||||
}
|
||||
override fun onBind(intent: Intent): IBinder = binder
|
||||
|
||||
inner class ListenBinder : Binder() {
|
||||
val service: ListenService
|
||||
get() = this@ListenService
|
||||
}
|
||||
|
||||
var onError: (() -> Unit)? = null
|
||||
var onUpdate: (() -> Unit)? = null
|
||||
private fun doListen(address: String?, port: Int) {
|
||||
private fun buildNotification(name: String?): Notification {
|
||||
// In this sample, we'll use the same text for the ticker and the expanded notification
|
||||
val text = getText(R.string.listening)
|
||||
val contentIntent = PendingIntent.getActivity(
|
||||
this, 0,
|
||||
Intent(this, ListenActivity::class.java),
|
||||
PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
return NotificationCompat.Builder(this, CHANNEL_ID)
|
||||
.setSmallIcon(R.drawable.listening_notification)
|
||||
.setOngoing(true)
|
||||
.setTicker(text)
|
||||
.setContentTitle(text)
|
||||
.setContentText(name)
|
||||
.setContentIntent(contentIntent)
|
||||
.build()
|
||||
}
|
||||
|
||||
private fun createNotificationChannel() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
val serviceChannel = NotificationChannel(
|
||||
CHANNEL_ID,
|
||||
"Foreground Service Channel",
|
||||
NotificationManager.IMPORTANCE_DEFAULT
|
||||
)
|
||||
notificationManager.createNotificationChannel(serviceChannel)
|
||||
}
|
||||
}
|
||||
|
||||
/** Neuer Wrapper mit 3 automatischen Reconnect-Versuchen **/
|
||||
private fun doListenWithRetries(address: String?, port: Int) {
|
||||
val lt = Thread {
|
||||
try {
|
||||
val socket = Socket(address, port)
|
||||
socket.soTimeout = 30_000
|
||||
val success = streamAudio(socket)
|
||||
if (!success) {
|
||||
playAlert()
|
||||
onError?.invoke()
|
||||
var attempts = 0
|
||||
var connected = false
|
||||
|
||||
while (attempts < 3 && !connected && !Thread.currentThread().isInterrupted) {
|
||||
try {
|
||||
Log.i(TAG, "Verbindungsversuch ${attempts + 1} zu $address:$port …")
|
||||
val socket = Socket(address, port)
|
||||
socket.soTimeout = 30_000
|
||||
connected = streamAudio(socket)
|
||||
if (!connected) {
|
||||
Log.w(TAG, "Streaming fehlgeschlagen, Versuch ${attempts + 1}")
|
||||
playAlert()
|
||||
attempts++
|
||||
Thread.sleep(2000)
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
attempts++
|
||||
Log.e(TAG, "Fehler beim Verbindungsaufbau (Versuch $attempts von 3)", e)
|
||||
if (attempts < 3) Thread.sleep(2000)
|
||||
}
|
||||
} catch (e : IOException) {
|
||||
Log.e(TAG, "Error opening socket to $address on port $port", e)
|
||||
}
|
||||
|
||||
if (!connected) {
|
||||
Log.e(TAG, "Nach 3 Versuchen keine Verbindung möglich.")
|
||||
playAlert()
|
||||
onError?.invoke()
|
||||
} else {
|
||||
Log.i(TAG, "Verbindung erfolgreich aufgebaut.")
|
||||
}
|
||||
}
|
||||
|
||||
this.listenThread = lt
|
||||
lt.start()
|
||||
}
|
||||
@@ -148,10 +167,12 @@ class ListenService : Service() {
|
||||
channelConfiguration,
|
||||
audioEncoding,
|
||||
bufferSize,
|
||||
AudioTrack.MODE_STREAM)
|
||||
AudioTrack.MODE_STREAM
|
||||
)
|
||||
|
||||
try {
|
||||
audioTrack.play()
|
||||
} catch (e : java.lang.IllegalStateException) {
|
||||
} catch (e: IllegalStateException) {
|
||||
Log.e(TAG, "Failed to play streamed audio audio for other reason", e)
|
||||
return false
|
||||
}
|
||||
@@ -165,14 +186,12 @@ class ListenService : Service() {
|
||||
|
||||
val readBuffer = ByteArray(byteBufferSize)
|
||||
val decodedBuffer = ShortArray(byteBufferSize * 2)
|
||||
try {
|
||||
|
||||
return try {
|
||||
while (!Thread.currentThread().isInterrupted) {
|
||||
val len = inputStream.read(readBuffer)
|
||||
if (len < 0) {
|
||||
// If the current thread was not interrupted this means the remote stopped streaming
|
||||
return Thread.currentThread().isInterrupted
|
||||
}
|
||||
val decoded: Int = AudioCodecDefines.CODEC.decode(decodedBuffer, readBuffer, len, 0)
|
||||
if (len < 0) return false
|
||||
val decoded = AudioCodecDefines.CODEC.decode(decodedBuffer, readBuffer, len, 0)
|
||||
if (decoded > 0) {
|
||||
audioTrack.write(decodedBuffer, 0, decoded)
|
||||
val decodedBytes = ShortArray(decoded)
|
||||
@@ -181,13 +200,16 @@ class ListenService : Service() {
|
||||
onUpdate?.invoke()
|
||||
}
|
||||
}
|
||||
return true
|
||||
true
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Connection failed", e)
|
||||
return false
|
||||
Log.e(TAG, "Verbindungsfehler im Stream", e)
|
||||
false
|
||||
} finally {
|
||||
audioTrack.stop()
|
||||
socket.close()
|
||||
try {
|
||||
audioTrack.stop()
|
||||
audioTrack.release()
|
||||
socket.close()
|
||||
} catch (_: Exception) {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,7 +217,7 @@ class ListenService : Service() {
|
||||
val mp = MediaPlayer.create(this, R.raw.upward_beep_chromatic_fifths)
|
||||
if (mp != null) {
|
||||
Log.i(TAG, "Playing alert")
|
||||
mp.setOnCompletionListener { obj: MediaPlayer -> obj.release() }
|
||||
mp.setOnCompletionListener { it.release() }
|
||||
mp.start()
|
||||
} else {
|
||||
Log.e(TAG, "Failed to play alert")
|
||||
|
||||
Reference in New Issue
Block a user