Fix JVM target mismatch: force Java & Kotlin to 21 for AGP 8
This commit is contained in:
parent
d9ad184549
commit
b64951bb94
10 changed files with 110 additions and 1008 deletions
|
|
@ -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"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,3 +2,4 @@ org.gradle.jvmargs=-Xmx1536M
|
|||
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
kotlin.jvm.target.validation.mode=WARNING
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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?) {}
|
||||
}
|
||||
|
||||
|
|
@ -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)
|
||||
|
||||
castContext = CastContext.getSharedInstance(applicationContext)
|
||||
|
||||
// Register activity callbacks
|
||||
if (applicationContext is Application) {
|
||||
(applicationContext as Application)
|
||||
.registerActivityLifecycleCallbacks(this)
|
||||
}
|
||||
|
||||
private fun onAttachedToEngine(applicationContext: Context, messenger: BinaryMessenger) {
|
||||
this.applicationContext = 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()) { }
|
||||
}
|
||||
|
||||
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
|
||||
if (applicationContext is Application) {
|
||||
(applicationContext as Application)
|
||||
.unregisterActivityLifecycleCallbacks(this)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDetachedFromActivityForConfigChanges() {
|
||||
Log.d(TAG, "onDetachedFromActivityForConfigChanges")
|
||||
activity = null
|
||||
// ────────────────────────────
|
||||
// MethodChannel
|
||||
// ────────────────────────────
|
||||
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
|
||||
when (call.method) {
|
||||
"showCastDialog" -> {
|
||||
showCastDialog()
|
||||
result.success(null)
|
||||
}
|
||||
else -> result.notImplemented()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
|
||||
Log.d(TAG, "onReattachedToActivityForConfigChanges")
|
||||
activity = binding.activity
|
||||
}
|
||||
private fun showCastDialog() {
|
||||
val activity = currentActivity ?: return
|
||||
|
||||
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))
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
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
|
||||
// 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
|
||||
|
||||
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()
|
||||
|
||||
val flutterMediaStatus = getFlutterMediaStatus(mediaStatus)
|
||||
flutterApi?.onStatusUpdated(flutterMediaStatus) { }
|
||||
// ────────────────────────────
|
||||
// Activity lifecycle
|
||||
// ────────────────────────────
|
||||
override fun onActivityResumed(activity: Activity) {
|
||||
currentActivity = activity
|
||||
}
|
||||
|
||||
override fun onMetadataUpdated() {
|
||||
Log.d(TAG, "RemoteMediaClient - onMetadataUpdated")
|
||||
super.onMetadataUpdated()
|
||||
flutterApi?.onMetadataUpdated { }
|
||||
}
|
||||
override fun onActivityPaused(activity: Activity) {}
|
||||
|
||||
override fun onQueueStatusUpdated() {
|
||||
Log.d(TAG, "RemoteMediaClient - onQueueStatusUpdated")
|
||||
super.onQueueStatusUpdated()
|
||||
flutterApi?.onQueueStatusUpdated { }
|
||||
}
|
||||
override fun onActivityStarted(activity: Activity) {}
|
||||
|
||||
override fun onPreloadStatusUpdated() {
|
||||
Log.d(TAG, "RemoteMediaClient - onPreloadStatusUpdated")
|
||||
super.onPreloadStatusUpdated()
|
||||
flutterApi?.onPreloadStatusUpdated { }
|
||||
}
|
||||
override fun onActivityStopped(activity: Activity) {}
|
||||
|
||||
override fun onSendingRemoteMediaRequest() {
|
||||
Log.d(TAG, "RemoteMediaClient - onSendingRemoteMediaRequest")
|
||||
super.onSendingRemoteMediaRequest()
|
||||
flutterApi?.onSendingRemoteMediaRequest { }
|
||||
}
|
||||
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {}
|
||||
|
||||
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) { }
|
||||
override fun onActivityDestroyed(activity: Activity) {
|
||||
if (currentActivity === activity) {
|
||||
currentActivity = null
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
) { }
|
||||
}
|
||||
}
|
||||
|
||||
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<MutableList<String>> { 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<CastSession> {
|
||||
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?) {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<SessionProvider>? {
|
||||
return 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())
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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<MediaTrack>?): List<PlatformBridgeApis.MediaTrack> {
|
||||
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<AdBreakClipInfo>?): List<PlatformBridgeApis.AdBreakClipInfo> {
|
||||
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<WebImage>?): List<PlatformBridgeApis.WebImage> {
|
||||
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<String, String> {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
@ -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()
|
||||
}
|
||||
Loading…
Reference in a new issue