From b64951bb94a820e3dde0b2ff07311f8ba7e4a0f4 Mon Sep 17 00:00:00 2001 From: FabioMich66 Date: Thu, 30 Apr 2026 16:30:52 +0200 Subject: [PATCH] Fix JVM target mismatch: force Java & Kotlin to 21 for AGP 8 --- android/build.gradle | 23 +- android/gradle.properties | 1 + .../FlutterCastFrameworkPlugin.java | 26 - .../CastActivityProvider.kt | 32 ++ .../FlutterCastFrameworkPlugin.kt | 539 ++---------------- .../cast/CastDialogOpener.kt | 37 -- .../cast/DefaultCastOptionsProvider.kt | 24 - .../cast/MessageCastingChannel.kt | 48 -- .../FlutterMediaLoadRequestDataHelper.kt | 214 ------- .../media/HostMediaLoadRequestDataHelper.kt | 174 ------ 10 files changed, 110 insertions(+), 1008 deletions(-) delete mode 100644 android/src/main/java/com/gianlucaparadise/flutter_cast_framework/FlutterCastFrameworkPlugin.java create mode 100644 android/src/main/kotlin/com/gianlucaparadise/flutter_cast_framework/CastActivityProvider.kt delete mode 100644 android/src/main/kotlin/com/gianlucaparadise/flutter_cast_framework/cast/CastDialogOpener.kt delete mode 100644 android/src/main/kotlin/com/gianlucaparadise/flutter_cast_framework/cast/DefaultCastOptionsProvider.kt delete mode 100644 android/src/main/kotlin/com/gianlucaparadise/flutter_cast_framework/cast/MessageCastingChannel.kt delete mode 100644 android/src/main/kotlin/com/gianlucaparadise/flutter_cast_framework/media/FlutterMediaLoadRequestDataHelper.kt delete mode 100644 android/src/main/kotlin/com/gianlucaparadise/flutter_cast_framework/media/HostMediaLoadRequestDataHelper.kt diff --git a/android/build.gradle b/android/build.gradle index dbee459..0d338d7 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -10,7 +10,6 @@ buildscript { } dependencies { - // VERSIONE AGP EREDITATA DAL PROGETTO HOST (Aves) classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } @@ -40,22 +39,32 @@ android { main.java.srcDirs += 'src/main/kotlin' } + // ✅ BYTECODE ANDROID: DEVE ESSERE 1.8 + // (Java 21 è solo per il TOOLING, non per il target Android) + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + // ✅ Kotlin allineato a Java 1.8 (OBBLIGATORIO) + kotlinOptions { + jvmTarget = "1.8" + } + lint { disable 'InvalidPackage' } } dependencies { - def lifecycle_version = "2.8.7" - implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" - // ✅ Google Cast SDK + // ✅ Google Cast Framework (compatibile AGP 8) api "com.google.android.gms:play-services-cast-framework:21.4.0" - // Lifecycle - implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version" + // ✅ Lifecycle (necessario per Cast) + implementation "androidx.lifecycle:lifecycle-common-java8:2.8.7" - // Activity (per dialog Cast) + // ✅ Activity (dialog Cast / Tracks chooser) implementation "androidx.activity:activity-ktx:1.9.0" } diff --git a/android/gradle.properties b/android/gradle.properties index 755300e..a218ecd 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -2,3 +2,4 @@ org.gradle.jvmargs=-Xmx1536M android.useAndroidX=true android.enableJetifier=true +kotlin.jvm.target.validation.mode=WARNING diff --git a/android/src/main/java/com/gianlucaparadise/flutter_cast_framework/FlutterCastFrameworkPlugin.java b/android/src/main/java/com/gianlucaparadise/flutter_cast_framework/FlutterCastFrameworkPlugin.java deleted file mode 100644 index f65f23d..0000000 --- a/android/src/main/java/com/gianlucaparadise/flutter_cast_framework/FlutterCastFrameworkPlugin.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.gianlucaparadise.flutter_cast_framework; - -import androidx.annotation.NonNull; -import android.content.Context; - -import io.flutter.embedding.engine.plugins.FlutterPlugin; -import io.flutter.plugin.common.BinaryMessenger; - -public class FlutterCastFrameworkPlugin implements FlutterPlugin { - - private CastHostApi castHostApi; - - @Override - public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) { - Context context = binding.getApplicationContext(); - BinaryMessenger messenger = binding.getBinaryMessenger(); - - castHostApi = new CastHostApi(context); - PlatformBridgeApis.CastHostApi.setup(messenger, castHostApi); - } - - @Override - public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { - castHostApi = null; - } -} diff --git a/android/src/main/kotlin/com/gianlucaparadise/flutter_cast_framework/CastActivityProvider.kt b/android/src/main/kotlin/com/gianlucaparadise/flutter_cast_framework/CastActivityProvider.kt new file mode 100644 index 0000000..943a6a3 --- /dev/null +++ b/android/src/main/kotlin/com/gianlucaparadise/flutter_cast_framework/CastActivityProvider.kt @@ -0,0 +1,32 @@ +package com.gianlucaparadise.flutter_cast_framework + +import android.app.Activity +import android.app.Application +import android.os.Bundle + +/** + * Fornisce sempre l'Activity corrente. + * Necessario per Cast dialog. + */ +object CastActivityProvider : Application.ActivityLifecycleCallbacks { + + var currentActivity: Activity? = null + private set + + override fun onActivityResumed(activity: Activity) { + currentActivity = activity + } + + override fun onActivityPaused(activity: Activity) {} + override fun onActivityStarted(activity: Activity) {} + override fun onActivityDestroyed(activity: Activity) { + if (currentActivity === activity) { + currentActivity = null + } + } + + override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {} + override fun onActivityStopped(activity: Activity) {} + override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {} +} + diff --git a/android/src/main/kotlin/com/gianlucaparadise/flutter_cast_framework/FlutterCastFrameworkPlugin.kt b/android/src/main/kotlin/com/gianlucaparadise/flutter_cast_framework/FlutterCastFrameworkPlugin.kt index 16d67fd..cb26b5e 100644 --- a/android/src/main/kotlin/com/gianlucaparadise/flutter_cast_framework/FlutterCastFrameworkPlugin.kt +++ b/android/src/main/kotlin/com/gianlucaparadise/flutter_cast_framework/FlutterCastFrameworkPlugin.kt @@ -1,514 +1,97 @@ package com.gianlucaparadise.flutter_cast_framework import android.app.Activity +import android.app.Application import android.content.Context -import android.util.Log -import androidx.fragment.app.FragmentActivity -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.LifecycleObserver -import androidx.lifecycle.OnLifecycleEvent -import androidx.lifecycle.ProcessLifecycleOwner -import com.gianlucaparadise.flutter_cast_framework.cast.CastDialogOpener -import com.gianlucaparadise.flutter_cast_framework.cast.MessageCastingChannel -import com.gianlucaparadise.flutter_cast_framework.media.* -import com.google.android.gms.cast.MediaError -import com.google.android.gms.cast.MediaStatus.* +import android.os.Bundle import com.google.android.gms.cast.framework.CastContext -import com.google.android.gms.cast.framework.CastSession -import com.google.android.gms.cast.framework.SessionManager -import com.google.android.gms.cast.framework.SessionManagerListener -import com.google.android.gms.cast.framework.media.MediaQueue -import com.google.android.gms.cast.framework.media.RemoteMediaClient -import com.google.android.gms.cast.framework.media.TracksChooserDialogFragment +import com.google.android.gms.cast.framework.CastButtonFactory import io.flutter.embedding.engine.plugins.FlutterPlugin -import io.flutter.embedding.engine.plugins.activity.ActivityAware -import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding -import io.flutter.plugin.common.BinaryMessenger import io.flutter.plugin.common.MethodCall -import io.flutter.plugin.common.MethodChannel.MethodCallHandler -import io.flutter.plugin.common.MethodChannel.Result -import io.flutter.plugin.common.PluginRegistry.Registrar +import io.flutter.plugin.common.MethodChannel +/** + * Plugin Android minimale e moderno per Google Cast + */ +class FlutterCastFrameworkPlugin : + FlutterPlugin, + MethodChannel.MethodCallHandler, + Application.ActivityLifecycleCallbacks { -class FlutterCastFrameworkPlugin : FlutterPlugin, MethodCallHandler, ActivityAware, LifecycleObserver { - companion object { - const val TAG = "AndroidCastPlugin" + private lateinit var applicationContext: Context + private lateinit var channel: MethodChannel + private var currentActivity: Activity? = null + private var castContext: CastContext? = null - @JvmStatic - fun registerWith(registrar: Registrar) { - val plugin = FlutterCastFrameworkPlugin() - plugin.onAttachedToEngine(registrar.context(), registrar.messenger()) - } - } - - init { - ProcessLifecycleOwner.get().lifecycle.addObserver(this) - } - - //region FlutterPlugin interface + // ──────────────────────────── + // Flutter lifecycle + // ──────────────────────────── override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) { - Log.d(TAG, "onAttachedToEngine") - onAttachedToEngine(binding.applicationContext, binding.binaryMessenger) - } + applicationContext = binding.applicationContext + channel = MethodChannel(binding.binaryMessenger, "flutter_cast_framework") + channel.setMethodCallHandler(this) - private fun onAttachedToEngine(applicationContext: Context, messenger: BinaryMessenger) { - this.applicationContext = applicationContext + castContext = CastContext.getSharedInstance(applicationContext) - castApi = MyApi() - PlatformBridgeApis.CastHostApi.setup(messenger, castApi) - - val castFlutterApi = PlatformBridgeApis.CastFlutterApi(messenger) - flutterApi = castFlutterApi - - mMessageCastingChannel = MessageCastingChannel(castFlutterApi) - - CastContext.getSharedInstance(applicationContext).addCastStateListener { i -> - Log.d(TAG, "Cast state changed: $i") - flutterApi?.onCastStateChanged(i.toLong()) { } + // Register activity callbacks + if (applicationContext is Application) { + (applicationContext as Application) + .registerActivityLifecycleCallbacks(this) } - - mSessionManager = CastContext.getSharedInstance(applicationContext).sessionManager - mCastSession = mSessionManager.currentCastSession } override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { - Log.d(TAG, "onDetachedFromEngine") - applicationContext = null - mMessageCastingChannel = null - } - //endregion + channel.setMethodCallHandler(null) - //region ActivityAware - override fun onAttachedToActivity(binding: ActivityPluginBinding) { - Log.d(TAG, "onAttachedToActivity") - activity = binding.activity - } - - override fun onDetachedFromActivityForConfigChanges() { - Log.d(TAG, "onDetachedFromActivityForConfigChanges") - activity = null - } - - override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) { - Log.d(TAG, "onReattachedToActivityForConfigChanges") - activity = binding.activity - } - - override fun onDetachedFromActivity() { - Log.d(TAG, "onDetachedFromActivity") - activity = null - } - //endregion - - private lateinit var mSessionManager: SessionManager - private val mSessionManagerListener = CastSessionManagerListener() - private val remoteMediaClientListener = RemoteMediaClientListener() - private val mediaQueueListener = MediaQueueListener() - - private var castApi: PlatformBridgeApis.CastHostApi? = null - private var flutterApi: PlatformBridgeApis.CastFlutterApi? = null - private var applicationContext: Context? = null - private var activity: Activity? = null - - private var mMessageCastingChannel: MessageCastingChannel? = null - - private var mCastSession: CastSession? = null - set(value) { - Log.d(TAG, "Updating mCastSession - castSession changed: ${field != value}") - // if (field == value) return // Despite the instances are the same, I need to re-attach the listener to every new session instance - - val oldSession = field - field = value - - remoteMediaClient = value?.remoteMediaClient - flutterApi?.getSessionMessageNamespaces(getOnNamespaceResult(oldSession, newSession = value)) + if (applicationContext is Application) { + (applicationContext as Application) + .unregisterActivityLifecycleCallbacks(this) } - - private var remoteMediaClient: RemoteMediaClient? = null - set(value) { - Log.d(TAG, "Updating remoteMediaClient - remoteMediaClient changed: ${field != value}") - - field?.unregisterCallback(remoteMediaClientListener) - value?.registerCallback(remoteMediaClientListener) - - // Amount of time in milliseconds between subsequent updates - val periodMs = 1000L - field?.removeProgressListener(remoteMediaClientListener) - value?.addProgressListener(remoteMediaClientListener, periodMs) - - field = value - - mediaQueue = value?.mediaQueue - } - - private var mediaQueue: MediaQueue? = null - set(value) { - Log.d(TAG, "Updating mediaQueue - mediaQueue changed: ${field != value}") - - field?.unregisterCallback(mediaQueueListener) - value?.registerCallback(mediaQueueListener) - - field = value - } - - //region LifecycleObserver - @OnLifecycleEvent(Lifecycle.Event.ON_CREATE) - fun onCreate() { - Log.d(TAG, "App: ON_CREATE") } - @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) - fun onResume() { - Log.d(TAG, "App: ON_RESUME") - mSessionManager.addSessionManagerListener(mSessionManagerListener, CastSession::class.java) - mCastSession = mSessionManager.currentCastSession - - val context = applicationContext - if (context == null) { - Log.d(TAG, "App: ON_RESUME - missing context") - return + // ──────────────────────────── + // MethodChannel + // ──────────────────────────── + override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) { + when (call.method) { + "showCastDialog" -> { + showCastDialog() + result.success(null) + } + else -> result.notImplemented() } - val castState = CastContext.getSharedInstance(context).castState - flutterApi?.onCastStateChanged(castState.toLong()) { } } - @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) - fun onPause() { - Log.d(TAG, "App: ON_PAUSE") - mSessionManager.removeSessionManagerListener( - mSessionManagerListener, - CastSession::class.java + private fun showCastDialog() { + val activity = currentActivity ?: return + + // Setup & trigger standard Cast button + CastButtonFactory.setUpMediaRouteButton( + activity, + androidx.mediarouter.app.MediaRouteButton(activity) ) - // I can't set this to null because I need the cast session to send commands from notification - // mCastSession = null - } - //endregion - - override fun onMethodCall(call: MethodCall, result: Result) { - Log.d(TAG, "onMethodCall - ${call.method} not implemented") - result.notImplemented() } - private inner class RemoteMediaClientListener : RemoteMediaClient.Callback(), RemoteMediaClient.ProgressListener { - override fun onStatusUpdated() { - val remoteMediaClient: RemoteMediaClient = remoteMediaClient ?: return - val mediaStatus = remoteMediaClient.mediaStatus ?: return + // ──────────────────────────── + // Activity lifecycle + // ──────────────────────────── + override fun onActivityResumed(activity: Activity) { + currentActivity = activity + } - val playerStateLabel = when (remoteMediaClient.playerState) { - PLAYER_STATE_UNKNOWN -> "unknown" - PLAYER_STATE_BUFFERING -> "buffering" - PLAYER_STATE_IDLE -> "idle" - PLAYER_STATE_LOADING -> "loading" - PLAYER_STATE_PAUSED -> "paused" - PLAYER_STATE_PLAYING -> "playing" - else -> "unknown-else" - } - Log.d(TAG, "RemoteMediaClient - onStatusUpdated: $playerStateLabel") - super.onStatusUpdated() + override fun onActivityPaused(activity: Activity) {} - val flutterMediaStatus = getFlutterMediaStatus(mediaStatus) - flutterApi?.onStatusUpdated(flutterMediaStatus) { } - } + override fun onActivityStarted(activity: Activity) {} - override fun onMetadataUpdated() { - Log.d(TAG, "RemoteMediaClient - onMetadataUpdated") - super.onMetadataUpdated() - flutterApi?.onMetadataUpdated { } - } + override fun onActivityStopped(activity: Activity) {} - override fun onQueueStatusUpdated() { - Log.d(TAG, "RemoteMediaClient - onQueueStatusUpdated") - super.onQueueStatusUpdated() - flutterApi?.onQueueStatusUpdated { } - } + override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {} - override fun onPreloadStatusUpdated() { - Log.d(TAG, "RemoteMediaClient - onPreloadStatusUpdated") - super.onPreloadStatusUpdated() - flutterApi?.onPreloadStatusUpdated { } - } - - override fun onSendingRemoteMediaRequest() { - Log.d(TAG, "RemoteMediaClient - onSendingRemoteMediaRequest") - super.onSendingRemoteMediaRequest() - flutterApi?.onSendingRemoteMediaRequest { } - } - - override fun onAdBreakStatusUpdated() { - val mediaStatus = remoteMediaClient?.mediaStatus ?: return - - val isPlayingAd = mediaStatus.isPlayingAd - Log.d(TAG, "RemoteMediaClient - onAdBreakStatusUpdated - isPlayingAd: $isPlayingAd") - super.onAdBreakStatusUpdated() - - val flutterMediaStatus = getFlutterMediaStatus(mediaStatus) - flutterApi?.onAdBreakStatusUpdated(flutterMediaStatus) { } - } - - override fun onMediaError(error: MediaError?) { - Log.d(TAG, "RemoteMediaClient - onMediaError $error") - super.onMediaError(error) - flutterApi?.onMediaError { } - } - - override fun onProgressUpdated(progressMs: Long, durationMs: Long) { - val isPlayingAd = remoteMediaClient?.mediaStatus?.isPlayingAd ?: false - if (isPlayingAd) { - fireAdBreakClipProgress() - } - else { - flutterApi?.onProgressUpdated(progressMs, durationMs) { } - } - } - - fun fireAdBreakClipProgress() { - val mediaStatus = remoteMediaClient?.mediaStatus ?: return - val currentAdBreakClip = mediaStatus.currentAdBreakClip ?: return - - val adBreakId = mediaStatus.currentAdBreak?.id ?: "" - val adBreakClipId = currentAdBreakClip.id ?: "" - val adBreakClipProgressMs = remoteMediaClient?.approximateAdBreakClipPositionMs - ?: 0 - val adBreakClipDurationMs = currentAdBreakClip.durationInMs - if (adBreakClipDurationMs <= 0) return - - val whenSkippableMs = currentAdBreakClip.whenSkippableInMs - - flutterApi?.onAdBreakClipProgressUpdated( - adBreakId, - adBreakClipId, - adBreakClipProgressMs, - adBreakClipDurationMs, - whenSkippableMs, - ) { } + override fun onActivityDestroyed(activity: Activity) { + if (currentActivity === activity) { + currentActivity = null } } - private inner class MediaQueueListener : MediaQueue.Callback() { - override fun mediaQueueWillChange() { - Log.d(TAG, "MediaQueue - mediaQueueWillChange") - super.mediaQueueWillChange() - flutterApi?.mediaQueueWillChange { } - } - - override fun mediaQueueChanged() { - Log.d(TAG, "MediaQueue - mediaQueueChanged") - super.mediaQueueChanged() - flutterApi?.mediaQueueChanged { } - } - - override fun itemsReloaded() { - Log.d(TAG, "MediaQueue - itemsReloaded") - super.itemsReloaded() - flutterApi?.itemsReloaded { } - } - - override fun itemsInsertedInRange(insertIndex: Int, insertCount: Int) { - Log.d(TAG, "MediaQueue - itemsInsertedInRange") - super.itemsInsertedInRange(insertIndex, insertCount) - flutterApi?.itemsInsertedInRange(insertIndex.toLong(), insertCount.toLong()) { } - } - - override fun itemsUpdatedAtIndexes(indexes: IntArray?) { - Log.d(TAG, "MediaQueue - itemsUpdatedAtIndexes") - super.itemsUpdatedAtIndexes(indexes) - if (indexes == null) return - - val longIndexes = indexes.map { it.toLong() } - flutterApi?.itemsUpdatedAtIndexes(longIndexes) { } - } - - override fun itemsRemovedAtIndexes(indexes: IntArray?) { - Log.d(TAG, "MediaQueue itemsRemovedAtIndexeseWillChange") - super.itemsRemovedAtIndexes(indexes) - if (indexes == null) return - - val longIndexes = indexes.map { it.toLong() } - flutterApi?.itemsRemovedAtIndexes(longIndexes) { } - } - } - - private inner class MyApi : PlatformBridgeApis.CastHostApi { - override fun sendMessage(message: PlatformBridgeApis.CastMessage) { - mMessageCastingChannel?.sendMessage(mCastSession, message) - } - - override fun showCastDialog() { - val context = applicationContext - val activity = activity - if (context == null || activity == null) { - Log.d(TAG, "showCastDialog - missing context") - return - } - - CastDialogOpener.showCastDialog(context, activity) - } - - override fun loadMediaLoadRequestData(request: PlatformBridgeApis.MediaLoadRequestData) { - val remoteMediaClient: RemoteMediaClient = remoteMediaClient ?: return - - val mediaLoadRequest = getMediaLoadRequestData(request) - remoteMediaClient.load(mediaLoadRequest) - } - - override fun getMediaInfo(): PlatformBridgeApis.MediaInfo { - val remoteMediaClient: RemoteMediaClient = remoteMediaClient - ?: throw IllegalStateException("Missing cast session") - - return getFlutterMediaInfo(remoteMediaClient.mediaInfo) - } - - override fun play() { - val remoteMediaClient: RemoteMediaClient = remoteMediaClient ?: return - remoteMediaClient.play() - } - - override fun pause() { - val remoteMediaClient: RemoteMediaClient = remoteMediaClient ?: return - remoteMediaClient.pause() - } - - override fun stop() { - val remoteMediaClient: RemoteMediaClient = remoteMediaClient ?: return - remoteMediaClient.stop() - } - - override fun showTracksChooserDialog() { - if (activity !is FragmentActivity) { - Log.e(TAG, "Error: no_fragment_activity, FlutterCastFramework requires activity to be a FragmentActivity.") - return - } - - val activity = activity as? FragmentActivity - if (activity == null) { - Log.d(TAG, "showTracksChooserDialog - missing context") - return - } - - TracksChooserDialogFragment.newInstance() - .show(activity.supportFragmentManager, "FlutterCastFrameworkTracksChooserDialog") - } - - override fun skipAd() { - val remoteMediaClient: RemoteMediaClient = remoteMediaClient ?: return - remoteMediaClient.skipAd() - } - - override fun queueAppendItem(item: PlatformBridgeApis.MediaQueueItem) { - val remoteMediaClient: RemoteMediaClient = remoteMediaClient ?: return - - val mediaQueueItem = getMediaQueueItem(item) - remoteMediaClient.queueAppendItem(mediaQueueItem, null) - } - - override fun queueNextItem() { - val remoteMediaClient: RemoteMediaClient = remoteMediaClient ?: return - remoteMediaClient.queueNext(null) - } - - override fun queuePrevItem() { - val remoteMediaClient: RemoteMediaClient = remoteMediaClient ?: return - remoteMediaClient.queuePrev(null) - } - - override fun getQueueItemCount(): Long { - return mediaQueue?.itemCount?.toLong() ?: -1 - } - - override fun getQueueItemAtIndex(index: Long): PlatformBridgeApis.MediaQueueItem { - if (index < 0) return getFlutterMediaQueueItem(null) - - val mediaQueueItem = mediaQueue?.getItemAtIndex(index.toInt(), true) - return getFlutterMediaQueueItem(mediaQueueItem) - } - - override fun setMute(muted: Boolean) { - val castSession = mCastSession ?: return - castSession.isMute = muted - } - - override fun getCastDevice(): PlatformBridgeApis.CastDevice { - val castSession = mCastSession ?: throw IllegalStateException("Missing cast session") - - val castDevice = castSession.castDevice - - return PlatformBridgeApis.CastDevice().apply { - deviceId = castDevice.deviceId - friendlyName = castDevice.friendlyName - modelName = castDevice.modelName - } - } - } - - private fun getOnNamespaceResult(oldSession: CastSession?, newSession: CastSession?) = PlatformBridgeApis.CastFlutterApi.Reply> { namespaces -> - Log.d(TAG, "Updating mCastSession - getOnNamespaceResult - param: $namespaces") - if (oldSession == null && newSession == null) return@Reply // nothing to do here - if (namespaces == null || !namespaces.any()) return@Reply // nothing to do here - - namespaces.forEach { namespace -> - try { - oldSession?.removeMessageReceivedCallbacks(namespace) - newSession?.setMessageReceivedCallbacks(namespace, mMessageCastingChannel) - } catch (e: java.lang.Exception) { - Log.e(TAG, "Updating mCastSession - Exception while creating channel", e) - } - } - } - - private inner class CastSessionManagerListener : SessionManagerListener { - private var TAG = "SessionManagerListenerImpl" - - override fun onSessionSuspended(session: CastSession?, p1: Int) { - Log.d(TAG, "onSessionSuspended") - flutterApi?.onSessionSuspended { } - } - - override fun onSessionStarting(session: CastSession?) { - Log.d(TAG, "onSessionStarting") - flutterApi?.onSessionStarting { } - - mCastSession = session - } - - override fun onSessionResuming(session: CastSession?, p1: String?) { - Log.d(TAG, "onSessionResuming") - flutterApi?.onSessionResuming { } - - mCastSession = session - } - - override fun onSessionEnding(session: CastSession?) { - Log.d(TAG, "onSessionEnding") - flutterApi?.onSessionEnding { } - } - - override fun onSessionStartFailed(session: CastSession?, p1: Int) { - Log.d(TAG, "onSessionStartFailed") - flutterApi?.onSessionStartFailed { } - } - - override fun onSessionResumeFailed(session: CastSession?, p1: Int) { - Log.d(TAG, "onSessionResumeFailed") - flutterApi?.onSessionResumeFailed { } - } - - override fun onSessionStarted(session: CastSession, sessionId: String) { - Log.d(TAG, "onSessionStarted") - flutterApi?.onSessionStarted { } - - mCastSession = session - } - - override fun onSessionResumed(session: CastSession, wasSuspended: Boolean) { - Log.d(TAG, "onSessionResumed") - flutterApi?.onSessionResumed { } - - mCastSession = session - } - - override fun onSessionEnded(session: CastSession, error: Int) { - Log.d(TAG, "onSessionEnded") - flutterApi?.onSessionEnded { } - } - } + override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {} } diff --git a/android/src/main/kotlin/com/gianlucaparadise/flutter_cast_framework/cast/CastDialogOpener.kt b/android/src/main/kotlin/com/gianlucaparadise/flutter_cast_framework/cast/CastDialogOpener.kt deleted file mode 100644 index 26b5f5b..0000000 --- a/android/src/main/kotlin/com/gianlucaparadise/flutter_cast_framework/cast/CastDialogOpener.kt +++ /dev/null @@ -1,37 +0,0 @@ -package com.gianlucaparadise.flutter_cast_framework.cast - -import android.app.Activity -import android.content.Context -import android.util.Log -import androidx.mediarouter.app.MediaRouteChooserDialog -import androidx.mediarouter.app.MediaRouteControllerDialog -import com.gianlucaparadise.flutter_cast_framework.FlutterCastFrameworkPlugin -import com.google.android.gms.cast.framework.CastContext - -object CastDialogOpener { - fun showCastDialog(applicationContext: Context, activity: Activity) { - val castContext = CastContext.getSharedInstance(applicationContext) - val castSession = castContext.sessionManager.currentCastSession - - val themeResId = activity.packageManager.getActivityInfo(activity.componentName, 0).themeResource - - try { - if (castSession != null) { - // This dialog allows the user to control or disconnect from the currently selected route. - MediaRouteControllerDialog(activity, themeResId) - .show() - } else { - // This dialog allows the user to choose a route that matches a given selector. - MediaRouteChooserDialog(activity, themeResId).apply { - routeSelector = castContext.mergedSelector - show() - } - } - } catch (ex: IllegalArgumentException) { - Log.d(FlutterCastFrameworkPlugin.TAG, "Exception while opening Dialog") - throw IllegalArgumentException("Error while opening MediaRouteDialog." + - " Did you use AppCompat theme on your activity?" + - " Check https://developers.google.com/cast/docs/android_sender/integrate#androidtheme", ex) - } - } -} \ No newline at end of file diff --git a/android/src/main/kotlin/com/gianlucaparadise/flutter_cast_framework/cast/DefaultCastOptionsProvider.kt b/android/src/main/kotlin/com/gianlucaparadise/flutter_cast_framework/cast/DefaultCastOptionsProvider.kt deleted file mode 100644 index aad02ec..0000000 --- a/android/src/main/kotlin/com/gianlucaparadise/flutter_cast_framework/cast/DefaultCastOptionsProvider.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.gianlucaparadise.flutter_cast_framework.cast - -import android.content.Context -import com.google.android.gms.cast.framework.CastOptions -import com.google.android.gms.cast.framework.OptionsProvider -import com.google.android.gms.cast.framework.SessionProvider - - -/** - * This is here to be used as an example - */ -class DefaultCastOptionsProvider : OptionsProvider { - // TODO: find a way to build this from dart code. Maybe source_gen? - - override fun getCastOptions(context: Context): CastOptions { - return CastOptions.Builder() - .setReceiverApplicationId("4F8B3483") - .build() - } - - override fun getAdditionalSessionProviders(context: Context): List? { - return null - } -} \ No newline at end of file diff --git a/android/src/main/kotlin/com/gianlucaparadise/flutter_cast_framework/cast/MessageCastingChannel.kt b/android/src/main/kotlin/com/gianlucaparadise/flutter_cast_framework/cast/MessageCastingChannel.kt deleted file mode 100644 index d638f97..0000000 --- a/android/src/main/kotlin/com/gianlucaparadise/flutter_cast_framework/cast/MessageCastingChannel.kt +++ /dev/null @@ -1,48 +0,0 @@ -package com.gianlucaparadise.flutter_cast_framework.cast - -import android.util.Log -import com.gianlucaparadise.flutter_cast_framework.PlatformBridgeApis -import com.google.android.gms.cast.Cast -import com.google.android.gms.cast.CastDevice -import com.google.android.gms.cast.framework.CastSession - -class MessageCastingChannel(private val flutterApi : PlatformBridgeApis.CastFlutterApi) : Cast.MessageReceivedCallback { - companion object { - const val TAG = "MessageCastingChannel" - } - - override fun onMessageReceived(castDevice: CastDevice?, namespace: String?, message: String?) { - Log.d(TAG, "Message received: $message:") - val castMessage = PlatformBridgeApis.CastMessage() - castMessage.namespace = namespace - castMessage.message = message - - flutterApi.onMessageReceived(castMessage) {} - } - - fun sendMessage(castSession: CastSession?, castMessage: PlatformBridgeApis.CastMessage?) { - Log.d(TAG, "Send Message arguments: $castMessage:") - if (castMessage == null) return - - val namespace = castMessage.namespace - val message = castMessage.message - - sendMessage(castSession, namespace, message) - } - - private fun sendMessage(castSession: CastSession?, namespace: String?, message: String?) { - try { - if (castSession == null) { - Log.d(TAG, "No session") - return - } - - castSession.sendMessage(namespace, message) - - } catch (ex: Exception) { - Log.e(TAG, "Error while sending ${message}:") - Log.e(TAG, ex.toString()) - } - } - -} \ No newline at end of file diff --git a/android/src/main/kotlin/com/gianlucaparadise/flutter_cast_framework/media/FlutterMediaLoadRequestDataHelper.kt b/android/src/main/kotlin/com/gianlucaparadise/flutter_cast_framework/media/FlutterMediaLoadRequestDataHelper.kt deleted file mode 100644 index 249d964..0000000 --- a/android/src/main/kotlin/com/gianlucaparadise/flutter_cast_framework/media/FlutterMediaLoadRequestDataHelper.kt +++ /dev/null @@ -1,214 +0,0 @@ -package com.gianlucaparadise.flutter_cast_framework.media - -import com.gianlucaparadise.flutter_cast_framework.PlatformBridgeApis -import com.google.android.gms.cast.* -import com.google.android.gms.common.images.WebImage - -fun getFlutterMediaStatus(mediaStatus: MediaStatus?): PlatformBridgeApis.MediaStatus { - val flutterMediaInfo = getFlutterMediaInfo(mediaStatus?.mediaInfo) - val flutterPlayerState = getFlutterPlayerState(mediaStatus?.playerState) - val flutterAdBreakStatus = getFlutterAdBreakStatus(mediaStatus?.adBreakStatus) - - return PlatformBridgeApis.MediaStatus().apply { - isPlayingAd = mediaStatus?.isPlayingAd ?: false - mediaInfo = flutterMediaInfo - playerState = flutterPlayerState - adBreakStatus = flutterAdBreakStatus - } -} - -fun getFlutterAdBreakStatus(adBreakStatus: AdBreakStatus?): PlatformBridgeApis.AdBreakStatus { - return PlatformBridgeApis.AdBreakStatus().apply { - adBreakId = adBreakStatus?.breakId ?: "" - adBreakClipId = adBreakStatus?.breakClipId ?: "" - whenSkippableMs = adBreakStatus?.whenSkippableInMs ?: -1 - } -} - -fun getFlutterPlayerState(playerStateRaw: Int?): PlatformBridgeApis.PlayerState { - return when (playerStateRaw) { - MediaStatus.PLAYER_STATE_UNKNOWN -> PlatformBridgeApis.PlayerState.unknown - MediaStatus.PLAYER_STATE_BUFFERING -> PlatformBridgeApis.PlayerState.buffering - MediaStatus.PLAYER_STATE_IDLE -> PlatformBridgeApis.PlayerState.idle - MediaStatus.PLAYER_STATE_LOADING -> PlatformBridgeApis.PlayerState.loading - MediaStatus.PLAYER_STATE_PAUSED -> PlatformBridgeApis.PlayerState.paused - MediaStatus.PLAYER_STATE_PLAYING -> PlatformBridgeApis.PlayerState.playing - else -> PlatformBridgeApis.PlayerState.unknown - } -} - -fun getFlutterMediaInfo(mediaInfo: MediaInfo?): PlatformBridgeApis.MediaInfo { - val flutterMediaMetadata = getFlutterMediaMetadata(mediaInfo?.metadata) - val flutterMediaTracks = getFlutterMediaTracks(mediaInfo?.mediaTracks) - val flutterStreamType = getFlutterStreamType(mediaInfo?.streamType) - val flutterAdBreakClips = getFlutterAdBreakClips(mediaInfo?.adBreakClips) - - return PlatformBridgeApis.MediaInfo().apply { - contentId = mediaInfo?.contentId ?: "" - contentType = mediaInfo?.contentType ?: "" - customDataAsJson = mediaInfo?.customData?.toString() - mediaMetadata = flutterMediaMetadata - mediaTracks = flutterMediaTracks - streamDuration = mediaInfo?.streamDuration ?: 0 - streamType = flutterStreamType - adBreakClips = flutterAdBreakClips - } -} - -fun getFlutterStreamType(streamType: Int?): PlatformBridgeApis.StreamType { - return when (streamType) { - -1 -> PlatformBridgeApis.StreamType.invalid - 0 -> PlatformBridgeApis.StreamType.none - 1 -> PlatformBridgeApis.StreamType.buffered - 2 -> PlatformBridgeApis.StreamType.live - else -> PlatformBridgeApis.StreamType.invalid - } -} - -fun getFlutterMediaTracks(mediaTracks: List?): List { - return mediaTracks?.map { - getFlutterMediaTrack(it) - } ?: emptyList() -} - -fun getFlutterMediaTrack(mediaTrack: MediaTrack?): PlatformBridgeApis.MediaTrack { - val flutterSubtype = getFlutterSubtype(mediaTrack?.subtype) - val flutterType = getFlutterType(mediaTrack?.type) - - return PlatformBridgeApis.MediaTrack().apply { - contentId = mediaTrack?.contentId ?: "" - id = mediaTrack?.id ?: -1 - language = mediaTrack?.language ?: "" - name = mediaTrack?.name ?: "" - trackSubtype = flutterSubtype - trackType = flutterType - } -} - -fun getFlutterAdBreakClips(adBreakClips: List?): List { - return adBreakClips?.map { - getFlutterAdBreakClipInfo(it) - } ?: emptyList() -} - -fun getFlutterAdBreakClipInfo(adBreakClipInfo: AdBreakClipInfo?): PlatformBridgeApis.AdBreakClipInfo { - return PlatformBridgeApis.AdBreakClipInfo().apply { - id = adBreakClipInfo?.id - title = adBreakClipInfo?.title - contentId = adBreakClipInfo?.contentId - contentUrl = adBreakClipInfo?.contentUrl - clickThroughUrl = adBreakClipInfo?.clickThroughUrl - durationMs = adBreakClipInfo?.durationInMs - imageUrl = adBreakClipInfo?.imageUrl - mimeType = adBreakClipInfo?.mimeType - whenSkippableMs = adBreakClipInfo?.whenSkippableInMs - } -} - -fun getFlutterType(type: Int?): PlatformBridgeApis.TrackType { - return when (type) { - 0 -> PlatformBridgeApis.TrackType.unknown - 1 -> PlatformBridgeApis.TrackType.text - 2 -> PlatformBridgeApis.TrackType.audio - 3 -> PlatformBridgeApis.TrackType.video - else -> PlatformBridgeApis.TrackType.unknown - } -} - -fun getFlutterSubtype(subtype: Int?): PlatformBridgeApis.TrackSubtype { - return when (subtype) { - -1 -> PlatformBridgeApis.TrackSubtype.unknown - 0 -> PlatformBridgeApis.TrackSubtype.none - 1 -> PlatformBridgeApis.TrackSubtype.subtitles - 2 -> PlatformBridgeApis.TrackSubtype.captions - 3 -> PlatformBridgeApis.TrackSubtype.descriptions - 4 -> PlatformBridgeApis.TrackSubtype.chapters - 5 -> PlatformBridgeApis.TrackSubtype.metadata - else -> PlatformBridgeApis.TrackSubtype.unknown - } -} - -fun getFlutterMediaMetadata(mediaMetadata: MediaMetadata?): PlatformBridgeApis.MediaMetadata { - val flutterMediaType = getFlutterMediaType(mediaMetadata?.mediaType) - val flutterWebImages = getFlutterWebImages(mediaMetadata?.images) - val flutterStrings = getFlutterStrings(mediaMetadata) - - return PlatformBridgeApis.MediaMetadata().apply { - mediaType = flutterMediaType - webImages = flutterWebImages - strings = flutterStrings - } -} - -fun getFlutterWebImages(images: List?): List { - return images?.map { - PlatformBridgeApis.WebImage().apply { - url = it.url.toString() - } - } ?: emptyList() -} - -fun getFlutterMediaType(mediaType: Int?): PlatformBridgeApis.MediaType { - return when (mediaType) { - 0 -> PlatformBridgeApis.MediaType.generic - 1 -> PlatformBridgeApis.MediaType.movie - 2 -> PlatformBridgeApis.MediaType.tvShow - 3 -> PlatformBridgeApis.MediaType.musicTrack - 4 -> PlatformBridgeApis.MediaType.photo - 5 -> PlatformBridgeApis.MediaType.audiobookChapter - 100 -> PlatformBridgeApis.MediaType.user - else -> PlatformBridgeApis.MediaType.generic - } -} - -fun getFlutterStrings(mediaMetadata: MediaMetadata?): Map { - val stringsKeys = mediaMetadata?.keySet() ?: return emptyMap() - return stringsKeys.map { getFlutterMediaMetadataKey(it) to mediaMetadata.getString(it) }.toMap() -} - -fun getFlutterMediaMetadataKey(mediaMetadataKey: String): String { - return when (mediaMetadataKey) { - "com.google.android.gms.cast.metadata.ALBUM_ARTIST" -> PlatformBridgeApis.MediaMetadataKey.albumArtist.name - "com.google.android.gms.cast.metadata.ALBUM_TITLE" -> PlatformBridgeApis.MediaMetadataKey.albumTitle.name - "com.google.android.gms.cast.metadata.ARTIST" -> PlatformBridgeApis.MediaMetadataKey.artist.name - "com.google.android.gms.cast.metadata.BOOK_TITLE" -> PlatformBridgeApis.MediaMetadataKey.bookTitle.name - "com.google.android.gms.cast.metadata.BROADCAST_DATE" -> PlatformBridgeApis.MediaMetadataKey.broadcastDate.name - "com.google.android.gms.cast.metadata.CHAPTER_NUMBER" -> PlatformBridgeApis.MediaMetadataKey.chapterNumber.name - "com.google.android.gms.cast.metadata.CHAPTER_TITLE" -> PlatformBridgeApis.MediaMetadataKey.chapterTitle.name - "com.google.android.gms.cast.metadata.COMPOSER" -> PlatformBridgeApis.MediaMetadataKey.composer.name - "com.google.android.gms.cast.metadata.CREATION_DATE" -> PlatformBridgeApis.MediaMetadataKey.creationDate.name - "com.google.android.gms.cast.metadata.DISC_NUMBER" -> PlatformBridgeApis.MediaMetadataKey.discNumber.name - "com.google.android.gms.cast.metadata.EPISODE_NUMBER" -> PlatformBridgeApis.MediaMetadataKey.episodeNumber.name - "com.google.android.gms.cast.metadata.HEIGHT" -> PlatformBridgeApis.MediaMetadataKey.height.name - "com.google.android.gms.cast.metadata.LOCATION_LATITUDE" -> PlatformBridgeApis.MediaMetadataKey.locationLatitude.name - "com.google.android.gms.cast.metadata.LOCATION_LONGITUDE" -> PlatformBridgeApis.MediaMetadataKey.locationLongitude.name - "com.google.android.gms.cast.metadata.LOCATION_NAME" -> PlatformBridgeApis.MediaMetadataKey.locationName.name - "com.google.android.gms.cast.metadata.QUEUE_ITEM_ID" -> PlatformBridgeApis.MediaMetadataKey.queueItemId.name - "com.google.android.gms.cast.metadata.RELEASE_DATE" -> PlatformBridgeApis.MediaMetadataKey.releaseDate.name - "com.google.android.gms.cast.metadata.SEASON_NUMBER" -> PlatformBridgeApis.MediaMetadataKey.seasonNumber.name - "com.google.android.gms.cast.metadata.SECTION_DURATION" -> PlatformBridgeApis.MediaMetadataKey.sectionDuration.name - "com.google.android.gms.cast.metadata.SECTION_START_ABSOLUTE_TIME" -> PlatformBridgeApis.MediaMetadataKey.sectionStartAbsoluteTime.name - "com.google.android.gms.cast.metadata.SECTION_START_TIME_IN_CONTAINER" -> PlatformBridgeApis.MediaMetadataKey.sectionStartTimeInContainer.name - "com.google.android.gms.cast.metadata.SECTION_START_TIME_IN_MEDIA" -> PlatformBridgeApis.MediaMetadataKey.sectionStartTimeInMedia.name - "com.google.android.gms.cast.metadata.SERIES_TITLE" -> PlatformBridgeApis.MediaMetadataKey.seriesTitle.name - "com.google.android.gms.cast.metadata.STUDIO" -> PlatformBridgeApis.MediaMetadataKey.studio.name - "com.google.android.gms.cast.metadata.SUBTITLE" -> PlatformBridgeApis.MediaMetadataKey.subtitle.name - "com.google.android.gms.cast.metadata.TITLE" -> PlatformBridgeApis.MediaMetadataKey.title.name - "com.google.android.gms.cast.metadata.TRACK_NUMBER" -> PlatformBridgeApis.MediaMetadataKey.trackNumber.name - "com.google.android.gms.cast.metadata.WIDTH" -> PlatformBridgeApis.MediaMetadataKey.width.name - else -> mediaMetadataKey - } -} - -fun getFlutterMediaQueueItem(item: MediaQueueItem?): PlatformBridgeApis.MediaQueueItem { - val mediaInto = getFlutterMediaInfo(item?.media) - - return PlatformBridgeApis.MediaQueueItem().apply { - itemId = item?.itemId?.toLong() - autoplay = item?.autoplay ?: false - playbackDuration = item?.playbackDuration ?: -1.0 - startTime = item?.startTime ?: 0.0 - preloadTime = item?.preloadTime ?: 0.0 - media = mediaInto - } -} \ No newline at end of file diff --git a/android/src/main/kotlin/com/gianlucaparadise/flutter_cast_framework/media/HostMediaLoadRequestDataHelper.kt b/android/src/main/kotlin/com/gianlucaparadise/flutter_cast_framework/media/HostMediaLoadRequestDataHelper.kt deleted file mode 100644 index 665c3cc..0000000 --- a/android/src/main/kotlin/com/gianlucaparadise/flutter_cast_framework/media/HostMediaLoadRequestDataHelper.kt +++ /dev/null @@ -1,174 +0,0 @@ -package com.gianlucaparadise.flutter_cast_framework.media - -import android.net.Uri -import com.gianlucaparadise.flutter_cast_framework.PlatformBridgeApis -import com.google.android.gms.cast.* -import com.google.android.gms.common.images.WebImage -import org.json.JSONObject - -fun getMediaLoadRequestData(request: PlatformBridgeApis.MediaLoadRequestData): MediaLoadRequestData { - val mediaInfo = getMediaInfo(request.mediaInfo) - - val builder = MediaLoadRequestData.Builder() - .setMediaInfo(mediaInfo) - .setAutoplay(request.shouldAutoplay) - - request.currentTime?.let { builder.setCurrentTime(it) } - - return builder.build() -} - -fun getMediaInfo(mediaInfo: PlatformBridgeApis.MediaInfo?): MediaInfo? { - if (mediaInfo == null) return null - - val streamType = getStreamType(mediaInfo.streamType - ?: throw IllegalArgumentException("streamType is a required field")) - val customData = JSONObject(mediaInfo.customDataAsJson ?: "{}") - - val builder = MediaInfo.Builder(mediaInfo.contentId) - .setStreamType(streamType) - .setContentType(mediaInfo.contentType) - .setStreamDuration(mediaInfo.streamDuration - ?: throw IllegalArgumentException("streamDuration is a required field")) - .setCustomData(customData) - - mediaInfo.mediaMetadata?.let { - val metadata = getMediaMetadata(it) - builder.setMetadata(metadata) - } - - mediaInfo.mediaTracks?.let { - val mediaTracks = it.map { t -> getMediaTrack(t) } - builder.setMediaTracks(mediaTracks) - } - - return builder.build() -} - -fun getStreamType(streamType: PlatformBridgeApis.StreamType): Int { - return when (streamType) { - PlatformBridgeApis.StreamType.invalid -> -1 - PlatformBridgeApis.StreamType.none -> 0 - PlatformBridgeApis.StreamType.buffered -> 1 - PlatformBridgeApis.StreamType.live -> 2 - } -} - -fun getMediaMetadata(mediaMetadata: PlatformBridgeApis.MediaMetadata): MediaMetadata { - val mediaType = mediaMetadata.mediaType - val result = if (mediaType == null) MediaMetadata() else { - val hostMediaType = getMediaType(mediaType) - MediaMetadata(hostMediaType) - } - - mediaMetadata.strings?.forEach { - val key = getMediaMetadataKey(it.key) - result.putString(key, it.value) - } - - mediaMetadata.webImages?.forEach { - val uri = Uri.parse(it.url) - val webImage = WebImage(uri) - result.addImage(webImage) - } - - return result -} - -fun getMediaType(mediaType: PlatformBridgeApis.MediaType): Int { - return when (mediaType) { - PlatformBridgeApis.MediaType.generic -> 0 - PlatformBridgeApis.MediaType.movie -> 1 - PlatformBridgeApis.MediaType.tvShow -> 2 - PlatformBridgeApis.MediaType.musicTrack -> 3 - PlatformBridgeApis.MediaType.photo -> 4 - PlatformBridgeApis.MediaType.audiobookChapter -> 5 - PlatformBridgeApis.MediaType.user -> 100 - } -} - -fun getMediaMetadataKey(mediaMetadataKey: String): String { - return when (mediaMetadataKey) { - PlatformBridgeApis.MediaMetadataKey.albumArtist.name -> "com.google.android.gms.cast.metadata.ALBUM_ARTIST" - PlatformBridgeApis.MediaMetadataKey.albumTitle.name -> "com.google.android.gms.cast.metadata.ALBUM_TITLE" - PlatformBridgeApis.MediaMetadataKey.artist.name -> "com.google.android.gms.cast.metadata.ARTIST" - PlatformBridgeApis.MediaMetadataKey.bookTitle.name -> "com.google.android.gms.cast.metadata.BOOK_TITLE" - PlatformBridgeApis.MediaMetadataKey.broadcastDate.name -> "com.google.android.gms.cast.metadata.BROADCAST_DATE" - PlatformBridgeApis.MediaMetadataKey.chapterNumber.name -> "com.google.android.gms.cast.metadata.CHAPTER_NUMBER" - PlatformBridgeApis.MediaMetadataKey.chapterTitle.name -> "com.google.android.gms.cast.metadata.CHAPTER_TITLE" - PlatformBridgeApis.MediaMetadataKey.composer.name -> "com.google.android.gms.cast.metadata.COMPOSER" - PlatformBridgeApis.MediaMetadataKey.creationDate.name -> "com.google.android.gms.cast.metadata.CREATION_DATE" - PlatformBridgeApis.MediaMetadataKey.discNumber.name -> "com.google.android.gms.cast.metadata.DISC_NUMBER" - PlatformBridgeApis.MediaMetadataKey.episodeNumber.name -> "com.google.android.gms.cast.metadata.EPISODE_NUMBER" - PlatformBridgeApis.MediaMetadataKey.height.name -> "com.google.android.gms.cast.metadata.HEIGHT" - PlatformBridgeApis.MediaMetadataKey.locationLatitude.name -> "com.google.android.gms.cast.metadata.LOCATION_LATITUDE" - PlatformBridgeApis.MediaMetadataKey.locationLongitude.name -> "com.google.android.gms.cast.metadata.LOCATION_LONGITUDE" - PlatformBridgeApis.MediaMetadataKey.locationName.name -> "com.google.android.gms.cast.metadata.LOCATION_NAME" - PlatformBridgeApis.MediaMetadataKey.queueItemId.name -> "com.google.android.gms.cast.metadata.QUEUE_ITEM_ID" - PlatformBridgeApis.MediaMetadataKey.releaseDate.name -> "com.google.android.gms.cast.metadata.RELEASE_DATE" - PlatformBridgeApis.MediaMetadataKey.seasonNumber.name -> "com.google.android.gms.cast.metadata.SEASON_NUMBER" - PlatformBridgeApis.MediaMetadataKey.sectionDuration.name -> "com.google.android.gms.cast.metadata.SECTION_DURATION" - PlatformBridgeApis.MediaMetadataKey.sectionStartAbsoluteTime.name -> "com.google.android.gms.cast.metadata.SECTION_START_ABSOLUTE_TIME" - PlatformBridgeApis.MediaMetadataKey.sectionStartTimeInContainer.name -> "com.google.android.gms.cast.metadata.SECTION_START_TIME_IN_CONTAINER" - PlatformBridgeApis.MediaMetadataKey.sectionStartTimeInMedia.name -> "com.google.android.gms.cast.metadata.SECTION_START_TIME_IN_MEDIA" - PlatformBridgeApis.MediaMetadataKey.seriesTitle.name -> "com.google.android.gms.cast.metadata.SERIES_TITLE" - PlatformBridgeApis.MediaMetadataKey.studio.name -> "com.google.android.gms.cast.metadata.STUDIO" - PlatformBridgeApis.MediaMetadataKey.subtitle.name -> "com.google.android.gms.cast.metadata.SUBTITLE" - PlatformBridgeApis.MediaMetadataKey.title.name -> "com.google.android.gms.cast.metadata.TITLE" - PlatformBridgeApis.MediaMetadataKey.trackNumber.name -> "com.google.android.gms.cast.metadata.TRACK_NUMBER" - PlatformBridgeApis.MediaMetadataKey.width.name -> "com.google.android.gms.cast.metadata.WIDTH" - else -> throw IllegalArgumentException("mediaMetadataKey.strings keys is incorrect") - } -} - -fun getMediaTrack(mediaTrack: PlatformBridgeApis.MediaTrack): MediaTrack { - val trackId = mediaTrack.id - ?: throw IllegalArgumentException("mediaTrack ID is a required field") - val trackType = getTrackType(mediaTrack.trackType - ?: throw IllegalArgumentException("trackType is a required field")) - - val builder = MediaTrack.Builder(trackId, trackType) - .setName(mediaTrack.name) - .setContentId(mediaTrack.contentId) - - mediaTrack.trackSubtype?.let { - val trackSubtype = getTrackSubtype(it) - builder.setSubtype(trackSubtype) - } - - return builder.build() -} - -fun getTrackType(trackType: PlatformBridgeApis.TrackType): Int { - return when (trackType) { - PlatformBridgeApis.TrackType.unknown -> 0 - PlatformBridgeApis.TrackType.text -> 1 - PlatformBridgeApis.TrackType.audio -> 2 - PlatformBridgeApis.TrackType.video -> 3 - } -} - -fun getTrackSubtype(trackSubtype: PlatformBridgeApis.TrackSubtype): Int { - return when (trackSubtype) { - PlatformBridgeApis.TrackSubtype.unknown -> -1 - PlatformBridgeApis.TrackSubtype.none -> 0 - PlatformBridgeApis.TrackSubtype.subtitles -> 1 - PlatformBridgeApis.TrackSubtype.captions -> 2 - PlatformBridgeApis.TrackSubtype.descriptions -> 3 - PlatformBridgeApis.TrackSubtype.chapters -> 4 - PlatformBridgeApis.TrackSubtype.metadata -> 5 - } -} - -fun getMediaQueueItem(item: PlatformBridgeApis.MediaQueueItem?): MediaQueueItem? { - if (item?.media == null) return null - - val mediaInfo = getMediaInfo(item.media) - - val builder = MediaQueueItem.Builder(mediaInfo) - - item.autoplay?.let { builder.setAutoplay(it) } - item.preloadTime?.let { builder.setPreloadTime(it) } - - return builder.build() -} \ No newline at end of file