Improve Google Cast connection speed
This commit is contained in:
parent
055719330d
commit
af8a793e05
4 changed files with 283 additions and 60 deletions
|
|
@ -69,6 +69,10 @@ override fun onDetachedFromEngine(
|
|||
) {
|
||||
channel.setMethodCallHandler(null)
|
||||
|
||||
if (::dialogLauncher.isInitialized) {
|
||||
dialogLauncher.dispose()
|
||||
}
|
||||
|
||||
if (::videoOptimizer.isInitialized) {
|
||||
videoOptimizer.dispose()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.aves.google_cast.cast
|
||||
|
||||
import android.app.Activity
|
||||
import android.util.Log
|
||||
import android.view.ViewGroup
|
||||
import androidx.mediarouter.app.MediaRouteButton
|
||||
import com.google.android.gms.cast.framework.CastButtonFactory
|
||||
|
|
@ -10,22 +11,74 @@ class CastDialogLauncher(
|
|||
private val channel: MethodChannel
|
||||
) {
|
||||
|
||||
companion object {
|
||||
private const val TAG = "GCAST_NATIVE"
|
||||
}
|
||||
|
||||
private var currentButton: MediaRouteButton? = null
|
||||
|
||||
fun showCastDialog(activity: Activity) {
|
||||
activity.runOnUiThread {
|
||||
try {
|
||||
removeCurrentButton()
|
||||
|
||||
val button = MediaRouteButton(activity)
|
||||
CastButtonFactory.setUpMediaRouteButton(activity, button)
|
||||
|
||||
val root = activity.findViewById<ViewGroup>(android.R.id.content)
|
||||
root?.addView(button, ViewGroup.LayoutParams(1, 1))
|
||||
CastButtonFactory.setUpMediaRouteButton(
|
||||
activity,
|
||||
button
|
||||
)
|
||||
|
||||
button.performClick()
|
||||
val root = activity.findViewById<ViewGroup>(
|
||||
android.R.id.content
|
||||
)
|
||||
|
||||
root?.addView(
|
||||
button,
|
||||
ViewGroup.LayoutParams(1, 1)
|
||||
)
|
||||
|
||||
currentButton = button
|
||||
|
||||
val clicked = button.performClick()
|
||||
|
||||
Log.d(
|
||||
TAG,
|
||||
"Cast dialog performClick=$clicked"
|
||||
)
|
||||
|
||||
button.postDelayed(
|
||||
{ removeCurrentButton() },
|
||||
1_000L
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
removeCurrentButton()
|
||||
|
||||
channel.invokeMethod(
|
||||
"onCastError",
|
||||
mapOf("message" to (e.message ?: "unknown"))
|
||||
mapOf(
|
||||
"message" to
|
||||
(e.message ?: "Unknown Cast dialog error")
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun dispose() {
|
||||
currentButton?.post {
|
||||
removeCurrentButton()
|
||||
}
|
||||
}
|
||||
|
||||
private fun removeCurrentButton() {
|
||||
val button = currentButton ?: return
|
||||
|
||||
try {
|
||||
(button.parent as? ViewGroup)?.removeView(button)
|
||||
} catch (_: Exception) {
|
||||
}
|
||||
|
||||
currentButton = null
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -243,20 +243,32 @@ override fun onMediaError(mediaError: MediaError) {
|
|||
// ------------------------------------------------------------
|
||||
// CLIENT ATTACH
|
||||
// ------------------------------------------------------------
|
||||
fun attachCurrentClientIfNeeded() {
|
||||
val client = ctx.getClient() ?: run {
|
||||
Log.d("GCAST_NATIVE", "attachCurrentClientIfNeeded(): ctx.getClient() == null")
|
||||
fun attachClient(client: RemoteMediaClient?) {
|
||||
if (client == null) {
|
||||
Log.d("GCAST_NATIVE", "attachClient(): client == null")
|
||||
detachCurrentClient()
|
||||
return
|
||||
}
|
||||
|
||||
val identity = System.identityHashCode(client)
|
||||
|
||||
Log.d("GCAST_NATIVE", "attachCurrentClientIfNeeded(): attaching to client identity=$identity")
|
||||
Log.d(
|
||||
"GCAST_NATIVE",
|
||||
"attachClient(): direct client identity=$identity",
|
||||
)
|
||||
|
||||
if (mediaClientListenerAdded && lastClientIdentity == identity) {
|
||||
Log.d(
|
||||
"GCAST_NATIVE",
|
||||
"attachClient(): client already attached",
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
if (!mediaClientListenerAdded || lastClientIdentity != identity) {
|
||||
try {
|
||||
client.unregisterCallback(mediaClientCallback)
|
||||
} catch (_: Exception) {}
|
||||
} catch (_: Exception) {
|
||||
}
|
||||
|
||||
client.registerCallback(mediaClientCallback)
|
||||
|
||||
|
|
@ -269,6 +281,30 @@ override fun onMediaError(mediaError: MediaError) {
|
|||
lastVolume = null
|
||||
lastMuted = null
|
||||
}
|
||||
|
||||
fun detachCurrentClient() {
|
||||
val client = ctx.getClient()
|
||||
|
||||
if (client != null) {
|
||||
try {
|
||||
client.unregisterCallback(mediaClientCallback)
|
||||
} catch (_: Exception) {
|
||||
}
|
||||
}
|
||||
|
||||
resetClientTracking()
|
||||
}
|
||||
|
||||
fun attachCurrentClientIfNeeded() {
|
||||
val client = ctx.getClient() ?: run {
|
||||
Log.d(
|
||||
"GCAST_NATIVE",
|
||||
"attachCurrentClientIfNeeded(): ctx.getClient() == null",
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
attachClient(client)
|
||||
}
|
||||
|
||||
fun resetClientTracking() {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.aves.google_cast.cast
|
|||
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.os.SystemClock
|
||||
import android.util.Log
|
||||
import com.google.android.gms.cast.framework.CastSession
|
||||
import com.google.android.gms.cast.framework.SessionManagerListener
|
||||
|
|
@ -11,90 +12,219 @@ class CastSessionHandler(
|
|||
private val channel: MethodChannel
|
||||
) : SessionManagerListener<CastSession> {
|
||||
|
||||
companion object {
|
||||
private const val TAG = "GCAST_NATIVE"
|
||||
}
|
||||
|
||||
private val mainHandler = Handler(Looper.getMainLooper())
|
||||
|
||||
private var readySent = false
|
||||
private var currentSession: CastSession? = null
|
||||
private var connectionStartedAtMs: Long = 0L
|
||||
|
||||
// ⭐ mediaClient viene assegnato DOPO la creazione
|
||||
private var mediaClient: CastMediaClient? = null
|
||||
|
||||
fun setMediaClient(client: CastMediaClient) {
|
||||
mediaClient = client
|
||||
}
|
||||
|
||||
private fun beginConnectionTiming() {
|
||||
if (connectionStartedAtMs == 0L) {
|
||||
connectionStartedAtMs = SystemClock.elapsedRealtime()
|
||||
}
|
||||
}
|
||||
|
||||
private fun elapsedMs(): Long {
|
||||
if (connectionStartedAtMs == 0L) return 0L
|
||||
return SystemClock.elapsedRealtime() - connectionStartedAtMs
|
||||
}
|
||||
|
||||
override fun onSessionStarting(session: CastSession) {
|
||||
Log.d("GCAST_NATIVE", "onSessionStarting")
|
||||
currentSession = session
|
||||
beginConnectionTiming()
|
||||
|
||||
mediaClient?.attachCurrentClientIfNeeded() // ⭐ PATCH
|
||||
trySendReadyWhenClientExists()
|
||||
Log.i(
|
||||
TAG,
|
||||
"CAST_TIMING onSessionStarting t=${elapsedMs()}ms"
|
||||
)
|
||||
|
||||
currentSession = session
|
||||
readySent = false
|
||||
|
||||
session.remoteMediaClient?.let {
|
||||
mediaClient?.attachClient(it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSessionStarted(session: CastSession, sessionId: String) {
|
||||
Log.d("GCAST_NATIVE", "onSessionStarted sessionId=$sessionId")
|
||||
override fun onSessionStarted(
|
||||
session: CastSession,
|
||||
sessionId: String
|
||||
) {
|
||||
beginConnectionTiming()
|
||||
|
||||
Log.i(
|
||||
TAG,
|
||||
"CAST_TIMING onSessionStarted " +
|
||||
"sessionId=$sessionId t=${elapsedMs()}ms"
|
||||
)
|
||||
|
||||
currentSession = session
|
||||
|
||||
mediaClient?.attachCurrentClientIfNeeded() // ⭐ PATCH
|
||||
trySendReadyWhenClientExists()
|
||||
mediaClient?.attachClient(session.remoteMediaClient)
|
||||
sendReadyIfPossible(session)
|
||||
}
|
||||
|
||||
override fun onSessionResuming(session: CastSession, sessionId: String) {
|
||||
Log.d("GCAST_NATIVE", "onSessionResuming sessionId=$sessionId")
|
||||
override fun onSessionResuming(
|
||||
session: CastSession,
|
||||
sessionId: String
|
||||
) {
|
||||
beginConnectionTiming()
|
||||
|
||||
Log.i(
|
||||
TAG,
|
||||
"CAST_TIMING onSessionResuming " +
|
||||
"sessionId=$sessionId t=${elapsedMs()}ms"
|
||||
)
|
||||
|
||||
currentSession = session
|
||||
readySent = false
|
||||
}
|
||||
|
||||
override fun onSessionResumed(
|
||||
session: CastSession,
|
||||
wasSuspended: Boolean
|
||||
) {
|
||||
beginConnectionTiming()
|
||||
|
||||
Log.i(
|
||||
TAG,
|
||||
"CAST_TIMING onSessionResumed " +
|
||||
"wasSuspended=$wasSuspended t=${elapsedMs()}ms"
|
||||
)
|
||||
|
||||
currentSession = session
|
||||
|
||||
mediaClient?.attachCurrentClientIfNeeded() // ⭐ PATCH
|
||||
trySendReadyWhenClientExists()
|
||||
mediaClient?.attachClient(session.remoteMediaClient)
|
||||
sendReadyIfPossible(session)
|
||||
}
|
||||
|
||||
override fun onSessionResumed(session: CastSession, wasSuspended: Boolean) {
|
||||
Log.d("GCAST_NATIVE", "onSessionResumed wasSuspended=$wasSuspended")
|
||||
override fun onSessionSuspended(
|
||||
session: CastSession,
|
||||
reason: Int
|
||||
) {
|
||||
Log.i(
|
||||
TAG,
|
||||
"onSessionSuspended reason=$reason t=${elapsedMs()}ms"
|
||||
)
|
||||
|
||||
currentSession = session
|
||||
readySent = false
|
||||
|
||||
mediaClient?.attachCurrentClientIfNeeded() // ⭐ PATCH
|
||||
trySendReadyWhenClientExists()
|
||||
mediaClient?.detachCurrentClient()
|
||||
}
|
||||
|
||||
override fun onSessionSuspended(session: CastSession, reason: Int) {
|
||||
Log.d("GCAST_NATIVE", "onSessionSuspended reason=$reason")
|
||||
currentSession = session
|
||||
|
||||
mediaClient?.attachCurrentClientIfNeeded() // ⭐ PATCH
|
||||
trySendReadyWhenClientExists()
|
||||
}
|
||||
|
||||
override fun onSessionEnded(session: CastSession, error: Int) {
|
||||
Log.d("GCAST_NATIVE", "onSessionEnded error=$error")
|
||||
override fun onSessionEnded(
|
||||
session: CastSession,
|
||||
error: Int
|
||||
) {
|
||||
Log.i(
|
||||
TAG,
|
||||
"onSessionEnded error=$error t=${elapsedMs()}ms"
|
||||
)
|
||||
|
||||
currentSession = null
|
||||
readySent = false
|
||||
connectionStartedAtMs = 0L
|
||||
|
||||
mediaClient?.detachCurrentClient()
|
||||
|
||||
mainHandler.post {
|
||||
Log.d("GCAST_NATIVE", "invokeMethod onCastStopped")
|
||||
Log.d(TAG, "invokeMethod onCastStopped")
|
||||
channel.invokeMethod("onCastStopped", null)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSessionStartFailed(session: CastSession, error: Int) {
|
||||
Log.d("GCAST_NATIVE", "onSessionStartFailed error=$error")
|
||||
override fun onSessionStartFailed(
|
||||
session: CastSession,
|
||||
error: Int
|
||||
) {
|
||||
Log.e(
|
||||
TAG,
|
||||
"onSessionStartFailed error=$error t=${elapsedMs()}ms"
|
||||
)
|
||||
|
||||
currentSession = null
|
||||
readySent = false
|
||||
connectionStartedAtMs = 0L
|
||||
|
||||
mediaClient?.detachCurrentClient()
|
||||
|
||||
mainHandler.post {
|
||||
channel.invokeMethod(
|
||||
"onCastError",
|
||||
mapOf(
|
||||
"message" to "Cast session start failed",
|
||||
"errorCode" to error
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSessionResumeFailed(session: CastSession, error: Int) {
|
||||
Log.d("GCAST_NATIVE", "onSessionResumeFailed error=$error")
|
||||
override fun onSessionResumeFailed(
|
||||
session: CastSession,
|
||||
error: Int
|
||||
) {
|
||||
Log.e(
|
||||
TAG,
|
||||
"onSessionResumeFailed error=$error t=${elapsedMs()}ms"
|
||||
)
|
||||
|
||||
currentSession = null
|
||||
readySent = false
|
||||
connectionStartedAtMs = 0L
|
||||
|
||||
mediaClient?.detachCurrentClient()
|
||||
|
||||
mainHandler.post {
|
||||
channel.invokeMethod(
|
||||
"onCastError",
|
||||
mapOf(
|
||||
"message" to "Cast session resume failed",
|
||||
"errorCode" to error
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSessionEnding(session: CastSession) {
|
||||
Log.d("GCAST_NATIVE", "onSessionEnding")
|
||||
Log.d(TAG, "onSessionEnding")
|
||||
}
|
||||
|
||||
private fun trySendReadyWhenClientExists() {
|
||||
val session = currentSession ?: return
|
||||
val client = session.remoteMediaClient ?: return
|
||||
private fun sendReadyIfPossible(session: CastSession) {
|
||||
val client = session.remoteMediaClient ?: run {
|
||||
Log.d(
|
||||
TAG,
|
||||
"sendReadyIfPossible(): remoteMediaClient == null"
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
if (readySent) return
|
||||
if (readySent) {
|
||||
Log.d(
|
||||
TAG,
|
||||
"sendReadyIfPossible(): ready already sent"
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
mediaClient?.attachClient(client)
|
||||
readySent = true
|
||||
|
||||
mainHandler.post {
|
||||
Log.d("GCAST_NATIVE", "invokeMethod onCastReady")
|
||||
Log.i(
|
||||
TAG,
|
||||
"CAST_TIMING invokeMethod onCastReady " +
|
||||
"t=${elapsedMs()}ms"
|
||||
)
|
||||
channel.invokeMethod("onCastReady", null)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue